Java面试问题汇总(SSM)

Maven 1.dependency

  • groupId
    定义隶属的实际项目
  • artifactId
    定义项目中的一个模块
  • version
    定义依赖或项目的版本
2.maven依赖 生命周期scope
  • compile
    默认值,表示当前依赖参与项目的编译、测试、运行阶段,属于强依赖,打包时,会打到包里去
  • test
    仅仅参与测试,包括测试用例的编译和执行,比如Junit
  • runtime
    依赖仅参与运行周期中的使用,一般这种类库都是接口与实现相分离的类库,比如JDBC,在编译时依赖相关的接口,在具体运行时,需要mysql等驱动程序
  • provided
    该依赖在打包过程中,不需要打进去,这个由运行环境提供,比如tomcat或者基础类库等
  • system
    使用上与provided相同,不同之处在于该依赖不从maven仓库中提取,而是从本地文件系统中提取,其会参与systemPath的属性进行提取依赖
  • import
    只能在dependencyManagement中使用,能解决Maven单继承问题,import依赖关系实际上并不参与限制依赖关系的传递性
3.本地jar包安装到Maven仓库 mvn install:install-file
-Dfile=本地jar包路径
-DgroupId=jar包的groupId
-DartifactId=jar包artifactId
-Dversion=jar包版本
-Dpackaging=jar
Mybatis 1.MyBatis 编程步骤
  1. 创建 SqlSessionFactory 对象。
  2. 通过 SqlSessionFactory 获取 SqlSession 对象。
  3. 通过 SqlSession 获得 Mapper 代理对象。
  4. 通过 Mapper 代理对象,执行数据库操作。
  5. 执行成功,则使用 SqlSession 提交事务。
  6. 执行失败,则使用 SqlSession 回滚事务。
  7. 最终,关闭会话。
2.MyBatis 如何执行批量插入
  1. 使用Sql 拼接,有语句大小限制
    INSERT INTO [表名]([列名],[列名]) VALUES ([列值],[列值])), ([列值],[列值])), ([列值],[列值]));

  2. 使用Mybatis 的批量插入方式
private static SqlSessionFactory sqlSessionFactory; @Test public void testBatch() { // 创建要插入的用户的名字的数组 List names = new ArrayList<>(); names.add("张三"); names.add("李四"); names.add("李二"); names.add("王五"); // 获得执行器类型为 Batch 的 SqlSession 对象,并且 autoCommit = false ,禁止事务自动提交 try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false)) { // 获得 Mapper 对象 UserMapper mapper = session.getMapper(UserMapper.class); // 循环插入 for (String name : names) { mapper.insertUser(name); } // 提交批量操作 session.commit(); } }

  1. 使用循环一条条插入
3.Mybatis 的 XML Mapper文件中,不同的 XML 映射文件,id 是否可以重复? 不同的 XML Mapper 文件,如果配置了 "namespace" ,那么 id 可以重复;如果没有配置 "namespace" ,那么 id 不能重复。毕竟"namespace" 不是必须的,只是最佳实践而已。
原因就是,namespace + id 是作为 Map 的 key 使用的。如果没有 "namespace",就剩下 id ,那么 id 重复会导致数据互相覆盖。如果有了 "namespace",自然 id 就可以重复,"namespace"不同,namespace + id 自然也就不同。
4.简述 Mybatis 的 XML 映射文件和 Mybatis 内部数据结构之间的映射关系 Mybatis 将所有 XML 配置信息都封装到 All-In-One 重量级对象Configuration内部。
在 XML Mapper 文件中:
  • 标签,会被解析为 ParameterMap 对象,其每个子元素会被解析为 ParameterMapping 对象。
  • 标签,会被解析为 ResultMap 对象,其每个子元素会被解析为 ResultMapping 对象。
  • 每一个 标签,均会被解析为一个 MappedStatement 对象,标签内的 SQL 会被解析为一个 BoundSql 对象。
5.通常一个 XML 映射文件,都会写一个 Mapper 接口与之对应。请问,这个 Mapper 接口的工作原理是什么?Mapper 接口里的方法,参数不同时,方法能重载吗? Mapper 接口,对应的关系如下:
  • 接口的全限名,就是映射文件中的 "namespace" 的值。
  • 接口的方法名,就是映射文件中 MappedStatement 的 "id" 值。
  • 接口方法内的参数,就是传递给 SQL 的参数。
Mapper 接口是没有实现类的,当调用接口方法时,接口全限名 + 方法名拼接字符串作为 key 值,可唯一定位一个对应的 MappedStatement 。举例:com.mybatis3.mappers.StudentDao.findStudentById ,可以唯一找到 "namespace"com.mybatis3.mappers.StudentDao 下面 "id"findStudentById 的 MappedStatement 。
总结来说,在 Mybatis 中,每一个 标签,都会被解析为一个 MappedStatement 对象。
另外,Mapper 接口的实现类,通过 MyBatis 使用 JDK Proxy 自动生成其代理对象 Proxy ,而代理对象 Proxy 会拦截接口方法,从而“调用”对应的 MappedStatement 方法,最终执行 SQL ,返回执行结果。整体流程如下图:
Java面试问题汇总(SSM)
文章图片

其中,SqlSession 在调用 Executor 之前,会获得对应的 MappedStatement 方法。例如:DefaultSqlSession#select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) 方法,代码如下:
// DefaultSqlSession.java@Override public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { // 获得 MappedStatement 对象 MappedStatement ms = configuration.getMappedStatement(statement); // 执行查询 executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database.Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }

Mapper 接口里的方法,是不能重载的,因为是全限名 + 方法名的保存和寻找策略
Spring 6.spring框架的优点 Spring是一个轻量级的DI和AOP容器框架,在项目的中的使用越来越广泛,它的优点主要有以下几点:
Spring是一个非侵入式框架,其目标是使应用程序代码对框架的依赖最小化,应用代码可以在没有Spring或者其他容器的情况运行。
Spring提供了一个一致的编程模型,使应用直接使用POJO开发,从而可以使运行环境隔离开来。
Spring推动应用的设计风格向面向对象及面向接口编程转变,提高了代码的重用性和可测试性。
Spring改进了结构体系的选择,虽然作为应用平台,Spring可以帮助我们选择不同的技术实现,比如从Hibernate切换到其他的ORM工具,从Struts切换到Spring MVC,尽管我们通常不会这么做,但是我们在技术方案上选择使用Spring作为应用平台,Spring至少为我们提供了这种可能性的选择,从而降低了平台锁定风险。
-5.aop的应用场景 Authentication 权限 ,Caching 缓存 ,Context passing 内容传递 ,Error handling 错误处理 ,Lazy loading 懒加载 ,Debugging 调试 ,logging, tracing, profiling and monitoring 记录跟踪 优化 校准,Performance optimization 性能优化 ,Persistence 持久化 ,Resource pooling 资源池 ,Synchronization 同步,Transactions 事务。
-4.AOP中的连接点(Joinpoint)、切点(Pointcut)、增强(Advice)、引介(Introduction)、织入(Weaving)、切面(Aspect) a. 连接点(Joinpoint):程序执行的某个特定位置(如:某个方法调用前、调用后,方法抛出异常后)。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。Spring仅支持方法的连接点。
b. 切点(Pointcut):如果连接点相当于数据中的记录,那么切点相当于查询条件,一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的连接点。
c. 增强(Advice):增强是织入到目标类连接点上的一段程序代码。Spring提供的增强接口都是带方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。
d. 引介(Introduction):引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过引介功能,可以动态的未该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
e. 织入(Weaving):织入是将增强添加到目标类具体连接点上的过程,AOP有三种织入方式:①编译期织入:需要特殊的Java编译期(例如AspectJ的ajc);②装载期织入:要求使用特殊的类加载器,在装载类的时候对类进行增强;③运行时织入:在运行时为目标类生成代理实现增强。Spring采用了动态代理的方式实现了运行时织入,而AspectJ采用了编译期织入和装载期织入的方式。
f. 切面(Aspect):切面是由切点和增强(引介)组成的,它包括了对横切关注功能的定义,也包括了对连接点的定义。
-3.autowired 和resource区别是什么 1、共同点
两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
2、不同点
(1)@Autowired
@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired; 只按照byType注入。
@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。
(2)@Resource
@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
-2.依赖注入的方式有哪几种 1、Set注入 2、构造器注入 3、接口注入
-1.Spring中自动装配的方式有哪些
  • no:不进行自动装配,手动设置Bean的依赖关系。
  • byName:根据Bean的名字进行自动装配。
  • byType:根据Bean的类型进行自动装配。
  • constructor:类似于byType,不过是应用于构造器的参数,如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会导致错误。
  • autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配。
自动装配没有自定义装配方式那么精确,而且不能自动装配简单属性(基本类型、字符串等),在使用时应注意。
0.什么是IoC和DI?并且简要说明一下DI是如何实现的 IoC叫控制反转,是Inversion of Control的缩写,DI(Dependency Injection)叫依赖注入,是对IoC更简单的诠释。控制反转是把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的"控制反转"就是对组件对象控制权的转移,从程序代码本身转移到了外部容器,由容器来创建对象并管理对象之间的依赖关系。IoC体现了好莱坞原则 - "Don’t call me, we will call you"。依赖注入的基本原则是应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由容器负责,查找资源的逻辑应该从应用组件的代码中抽取出来,交给容器来完成。DI是对IoC更准确的描述,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。
一个类A需要用到接口B中的方法,那么就需要为类A和接口B建立关联或依赖关系,最原始的方法是在类A中创建一个接口B的实现类C的实例,但这种方法需要开发人员自行维护二者的依赖关系,也就是说当依赖关系发生变动的时候需要修改代码并重新构建整个系统。如果通过一个容器来管理这些对象以及对象的依赖关系,则只需要在类A中定义好用于关联接口B的方法(构造器或setter方法),将类A和接口B的实现类C放入容器中,通过对容器的配置来实现二者的关联。
依赖注入可以通过setter方法注入(设值注入)、构造器注入和接口注入三种方式来实现,Spring支持setter注入和构造器注入,通常使用构造器注入来注入必须的依赖关系,对于可选的依赖关系,则setter注入是更好的选择,setter注入需要类提供无参构造器或者无参的静态工厂方法来创建对象。
1.BeanFactory 与ApplicationContext 是干什么的,两者的区别 BeanFactory、ApplicationContext都代表容器,BeanFactory是一个基础接口,实现了容器基础的功能,ApplicationContext是器的高级形态,增加了许多了特性,顶级父类是BeanFactory。
跟FactoryBean的区别是:
FactoryBean 是一个Bean,用于生产修饰其他的Bean实例,典型应用是AOP代理类,使用'&'获取FactoryBean本身
BeanFactory 是一个工厂,是容器的顶层接口
2.BeanPostProcessor 的实现 Bean的后置处理器,是一个监听器,可以监听容器触发的事件。将它向IOC容器注册后,容器中管理的Bean具备了接收IOC容器事件回调的能力。BeanPostProcessor是一个接口类,有两个接口方法,postProcessBeforeInitialization提供Bean初始化前的回调入口;postProcessAfterInitialization 提供Bean初始化后的回调入口AbstractAutowireCapableBeanFactory#initializeBean,这个类可以对项目中的Bean进行修饰,所有Bean都会调用该实现。
3.BeanDefinition 的实现 BeanDefinition抽象了对Bean的定义,其是容器实现依赖反转功能的核心数据结构
4.Spring IOC容器的实现 Spring提供了各种各样的容器,有DefaultListableBeanFactory、FileSystemXmlApplicationContext等,这些容器都是基于BeanFactory,BeanFactory实现了容器的基础功能,包括containsBean能够判断容器是否含有指定名称的Bean,getBean获取指定名称参数的Bean等。
Spring通过refresh()方法对容器进行初始化和资源的载入
【Java面试问题汇总(SSM)】首先通过ResourceLoader的Resource接口定位到存储Bean信息的路径
第二个过程是BeanDefinition载入,把定义好的Bean表示成IOC容器的内部数据结构BeanDefinition,通过定义BeanDefinition来管理应用的各种对象及依赖关系,其是容器实现依赖反转功能的核心数据结构
第三个过程是BeanDefinition注册,容器解析得到BeanDefinition后,需要在容器中注册,这由IOC实现BeanDefinitionRegistry接口来实现,注册过程是IOC容器内部维护了一个ConcurrentHasmap来保存得到的BeanDefinition。如果某些Bean设置了lazyinit属性,Bean的依赖注入会在这个过程预先完成,而不需要等到第一次使用Bean的时候才触发。
5.Spring DI(依赖注入)的实现 Spring 的依赖注入发生在以下两种情况:
  1. 用户第一次调用getBean()方法
  2. bean配置了lazy-init=false,容器会在解析注册Bean定义的时候进行预实例化,触发依赖注入
getBean()方法定义在BeanFactory接口中,具体实现在子类AbstractBeanFactory中,过程如下:
  1. getBean()方法最终是委托给doGetBean方法来实例化Bean,doGetBean方法会先从缓存中找是否有创建过,没有再从父工厂中去查找
  2. 如果父工厂中没有找到,会根据Bean定义的模式来创建Bean,单例模式的Bean会先从缓存中查找,确保只创建一次,原型模式的Bean每次都会创建,其他模式根据配置的不同生命周期来选择合适的方法创建。创建的具体方法通过匿名类中getObject,并委托给createBean来完成bean的实例化。
  3. 在createBean中,先对Bean进行一些准备工作,然后会应用配置的前后处理器,如果创建成功就直接返回该代理Bean
  4. 没有创建代理Bean的话,会创建指定的Bean实例,委托给doCreateBean完成,该过程会通过提前实例化依赖Bean,并写入缓存来解决Bean的循环依赖
  5. 通过populateBean注入Bean属性,并调用init-method初始化方法
  6. 注册实例化的Bean
6.Spring如何解决循环依赖问题 比如A依赖B, B依赖A.
创建A的时候,会把A对应的ObjectFactory放入缓存中,当注入的时候发现需要B, 就会去调用B对象,B对象会先从singletonObjects 查找,没有再从earlySingletonObjects找,还没有就会调用singletonFactory创建对象B,B对象也是先从singletonObjects,earlySingletonObjects,singletonFactories三个缓存中搜索,只要找到就返回,相关方法AbstractBeanFactory.doGetBean()
7.Spring Bean 生命周期
  1. Bean实例的创建
  2. 为Bean实例设置属性
  3. 调用Bean的初始化方法
  4. 应用可以通过IOC容器使用Bean
  5. 当容器关闭时,调用Bean的销毁方法
8.Spring Bean的作用域,默认是哪个? Singleton: 单例模式,IOC容器中只会存在一个共享的Bean实例,是Spring的默认模式;
prototype: 原型模式,每次从IOC容器获取Bean的时候,都会创建一个新的Bean实例;
request: 每次请求都生成一个实例;
session: 在一次请求会话中,容器返回该Bean的同一实例,不同的Session请求不同的实例,实例仅在该Session内有效,请求结束,则实例销毁;
globalsession: 全局的session中,容器返回该Bean的同一个实例,仅在portlet context 有效
9.AOP两种代理方式 AOP面向切面编程,可以通过预编译和运行时动态代理,实现在不修改源代码的情况下给程序动态添加功能。
程序入口是:AbstractAutowireCapableBeanFactory -> doCreateBean -> initializeBean -> applyBeanPostProcessorsAfterInitialization
默认策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理
  1. JDK 动态代理
    主要涉及java.lang.reflect中的两个类,Proxy 和 InvocationHandler
    InvocationHandler是一个接口,通过实现该接口来定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编辑在一起。只能为实现接口的类创建代理。
  2. Cglib 代理
    是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,Cglib封装了asm,可以在运行期动态生成新的class。可以是普通类,也可以是实现接口的类
10.Spring AOP实现原理 通过JDK代理,和CGLIB代理两种方式生成动态代理,构造不同的回调方法来对拦截器链的调用,比如JdkDynamicAopProxy的invoke方法,Cglib2AopProxy中的DynamicAdvisedInterceptor的intercept方法,首先获取配置的拦截器链,通过ReflectiveMethodInvocation的proceed方法实现对拦截器链的调用, 首先需要根据配置来对拦截器进行匹配,匹配成功后,拦截器发挥作用,在对拦截器调用完成后,再对目标对象的方法调用,这样一个普通的Java对象的功能就得到了增强
11.哪些方法不能被AOP增强
  1. 基于JDK代理,除public外的其他所有方法,包括public static也不能被增强
  2. 基于CGLIB代理,由于其通过生成目标类子类的方式来增强,因此不能被子类继承的方法都不能被增强,private、static、final 方法
12.AOP 切点函数
类别 函数 入参 说明
方法切入点函数 execution() 方法匹配模式串 满足某一匹配模式的所有目标类方法连接点。
如execution(* greetTo(..)) 表示所有目标类中的greetTo()方法
@annotation() 方法注解类名 标注了特定注解的目标类方法连接点。
如@annotation(com.smart.anno.NeedTest)表示任何标注了@NeedTest注解的目标类方法
方法入参切入点函数 args() 类名 通过判断目标类方法运行时入参对象的类型定义指定连接点。
如args(com.smart.Waiter)表示所有有且仅有一个按类型匹配于Waiter入参的方法
@args() 类型注解类名 通过判断目标类方法运行时入参对象的类是否标注特定注解来指定连接点。
如@args(com.smart.Monitorable)表示任何这样的一个目标方法:它有一个入参且入参对象的类标注@Monitorable注解
目标类切点函数 within() 类名匹配串 表示特定域下的所有连接点。
如within(com.smart.service.*) 表示com.smart.service 包中的所有连接点,即包中所有类的所有方法;
而within(com.smart.service.*Service)表示在com.smart.service包中所有以Service结尾的类的所有连接点
target() 类名 假如目标类按类型匹配于指定类,则目标类的所有连接点匹配这个切点
如通过target(com.smart.Waiter),Waiter及Waiter实现类NaiveWaiter中的所有连接点都匹配该切点
@within() 类型注解类名 假如目标类型按类型匹配于某个类A, 且类A标注了特定注解,则目标类的所有连接点匹配该切点
如@within(com.smart.Monitorable) 假如Waiter类标注了@Monitorable注解,则Waiter的所有连接点都匹配该切点,说是这个注解也会匹配Waiter的子类,但试了后并没有用,Spring 5.1
@target 类型注解类名 假如目标类标注了特定注解,则目标类的所有连接点都匹配该切点。
如@target(com.smart.Monitorable),假如NaiveWaiter标注了@Monitorable,则NaiveWaiter的所有连接点都匹配这个切点
代理类切点函数 this() 类名 代理类按类型匹配于指定类,则被代理的目标类的所有连接点都匹配该切点。
如this(com.smart.Seller) 匹配任何运行期对象为Seller类型的类
13.六种增强类型
  1. @Before 前置增强,相当于BeforeAdvice
  2. @AfterReturning 后置增强,相当于AfterReturningAdvice
  3. @Around 环绕增强,相当于MethodInterceptor
  4. @AfterThrowing 抛出增强,相当于ThrowsAdvice
  5. @AfterFinal增强,不管抛出异常还是正常退出,都会执行,没有对应的增强接口,一般用于释放资源
  6. @DeclareParents 引介增强,相当于IntroductionInterceptor
14.Spring MVC运行流程
  1. 客户端请求到DispatcherServlet
  2. DispatcherServlet根据请求地址查询映射处理器HandleMapping,获取Handler
  3. 请求HandlerAdatper执行Handler
  4. 执行相应的Controller方法,执行完毕返回ModelAndView
  5. 通过ViewResolver解析视图,返回View
  6. 渲染视图,将Model数据转换为Response响应
  7. 将结果返回给客户端
2,3 两步都在DispatcherServlet -> doDispatch中进行处理
15.Spring MVC 启动流程
  1. 在Tomcat启动的时候,ServletContext 会根据web.xml加载ContextLoaderListener,继而通过ContextLoaderListener 载入IOC容器,具体过程有ContextLoader完成,这个IOC容器是在Web环境下使用的WebApplicationContext, 这个容器在后面的DispatcherServlet中作为双亲根上下文来使用
  2. IOC容器加载完成后,开始加载DIspatcherServlet,这是Spring MVC的核心,由HttpServletBean -> initServeltBean启动(HttpServletBean是DispatcherServlet的父类,HttpServletBean继承了HttpServlet),最终调用DispatcherServlet -> initStrategies 方法对HandlerMapping、ViewResolver等进行初始化,至此,DispatcherServelt就初始化完成了,它持有一个第一步完成的上下文作为根上下文,以自己的Servlet名称命名的IOC容器,这个容器是一个WebApplicationContext对象。
16.Spring 事务实现方式、事务的传播机制、默认的事务类别·
  1. 事务实现方式
    • 声明式,在xml文件中通过tx:advice来配置事务
    • 注解式,在xml文件中定一个事务管理对象(DataSourceTransactionManager),然后加入\, 这样就可以使用@Transactional注解配置事务
  2. 事务的传播机制
    一共7种事务传播行为,相关code: AbstractPlatformTransactionManager -> getTransaction
    • PROPAGATION_REQUIRED
      如果当前没有事务,则新建一个事务;如果已经存在一个事务,则加入到这个事务中,这也是默认事务类别
    • PROPAGATION_SUPPORTS
      支持当前事务。如果当前没有事务,则以非事务方式执行
    • PROPAGATION_MANDATORY
      使用当前事务。如果当前没有事务,则抛出异常
    • PROPAGATION_REQUIRES_NEW
      新建事务。如果当前存在事务,则把当前事务挂起
    • PROPAGATION_NOT_SUPPORTED
      以非事务方式执行操作。如果当前存在事务,则把当前事务挂起
    • PROPAGATION_NEVER
      以非事务方式执行。如果当前存在事务,则抛出异常
    • PROPAGATION_NESTED
      如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作
17.Spring 事务模版 TransactionTemplate 事务模版是对原始事务管理方式的封装,原始事务管理是基于TransactionDefinitionPlatformTransactionManagerTransactionStatus 的编程式事务
事务模版主要通过execute(TransactionCallback action)来执行事务,TransactionCallback 有两种方式一种是有返回值TransactionCallback,一种是没有返回值TransactionCallbackWithoutResult。
18.Spring 事务底层原理
  1. 事务的准备
    在声明式事务处理中,需要Ioc容器配置TransactionProxyFactoryBean,其父类AbstractSingletonProxyFactoryBean实现了InitializingBeean接口,因此在初始化过程中会调用afterPropertiesSet方法,这个方法实例化了ProxyFactory, 并为其设置了通知,目标对象后,最终返回Proxy代理对象,对象建立起来后,在调用其代理方法的时候,会调用相应的TransactionInterceptor拦截器,在这个调用中,会根据TransactionAttribute配置的事务属性进行配置,为事务处理做好准备
  2. 事务拦截器实现
    经过TransactionProxyFactoryBean的AOP包装后,此时如果对目标对象进行方法调用,实际上起作用的是一个Proxy代理对象,拦截器会拦截其中的事务处理,在调用Proxy对象的代理方法时会触发invoke回调,其中会根据事务属性配置决定具体用哪一个PlatformTransactionManager来完成事务操作
19.什么是 Spring Boot? Spring Boot是 Spring 的子项目,正如其名字,提供 Spring 的引导( Boot )的功能。
通过 Spring Boot ,我们开发者可以快速配置 Spring 项目,引入各种 Spring MVC、Spring Transaction、Spring AOP、MyBatis 等等框架,而无需不断重复编写繁重的 Spring 配置,降低了 Spring 的使用成本。
20.Spring Boot启动流程
  1. 启动类里面调用SpringApplication.run方法
  2. 在run方法中,首先构造SpringApplication对象,然后再调用run方法
  3. 在构造SpringApplication对象中,做了如下工作
    • 将sources放入primarySources变量中
    • 判断webApplication是什么类型的
    • 设置ApplicationContextInitializer,ApplicationListener,通过加载META-INF/spring.factories中配置的类
    • 找到main方法找到启动主类
  4. run方法中,做的工作
    • StopWatch主要是监控启动过程,统计启动时间,检测应用是否已经启动或者停止。
    • 加载SpringApplicationRunListener(也是通过META-INF/spring.factories),默认加载的是EventPublishingRunListener
    • 调用RunListener.starting()方法。
    • 根据args创建应用参数解析器ApplicationArguments;
    • 准备环境变量:获取环境变量environment,将应用参数放入到环境变量持有对象中,监听器监听环境变量对象的变化(listener.environmentPrepared)
    • 打印Banner信息(SpringBootBanner)
    • 创建SpringBoot的应用上下文(AnnotationConfigEmbeddedWebApplicationContext)
    • prepareContext上下文之前的准备
    • refreshContext刷新上下文
    • afterRefresh(ApplicationRunner,CommandLineRunner接口实现类的启动)
    • 返回上下文对象
21.什么是 Spring Boot 自动配置?
  1. Spring Boot 在启动时扫描项目所依赖的 jar 包,寻找包含spring.factories 文件的 jar 包。
  2. 根据 spring.factories 配置加载 AutoConfigure 类。
  3. 根据 @Conditional 等条件注解的条件,进行自动配置并将 Bean 注入 Spring IoC 中。

    推荐阅读