spring
约 6741 字大约 22 分钟
1. Spring是什么?特性?
- Spring是一个轻量级、非入侵式的控制反转(IoC)和面向切面(AOP)的框架。
- IoC和DI的支持:管理对象生命周期和依赖关系
- AOP编程的支持:面向切面编程可以实现对程序进行权限拦截、运行监控等切面功能。
- 声明式事务的支持:支持通过配置就来完成对事务的管理,而不需要通过硬编码的方式。
- 快捷测试的支持:支持Junit注解测试Spring程序。
- 快速集成功能:方便集成各种优秀框架
- 复杂API模板封装:对JDBC、JavaMail等提供了模板化的封装,降低应用难度
2. Spring有哪些模块?
- Spring Core:Spring 核心,它是框架最基础的部分,提供 IoC 和依赖注入 DI 特性。
- Spring Context:Spring 上下文容器,它是 BeanFactory 功能加强的一个子接口。
- Spring Web:它提供 Web 应用开发的支持。
- Spring MVC:它针对 Web 应用中 MVC 思想的实现。
- Spring DAO:提供对 JDBC 抽象层,简化了 JDBC 编码,同时,编码更具有健壮性。
- Spring ORM:它支持用于流行的 ORM 框架的整合,比如:Spring + Hibernate、Spring + iBatis、Spring + JDO 的整合等
- Spring AOP:即面向切面编程,它提供了与 AOP 联盟兼容的编程实现。
3. Spring常用注解?
- Web:
- @Controller:组合注解(组合了@Component注解),应用在MVC层(控制层)。
- @RestController:@Controller和@ResponseBody的组合注解,注解在类上则该Controller的所有方法都默认加上了@ResponseBody。
- @RequestMapping:用于映射Web请求,包括访问路径和参数。Restful接口根据请求类型使用不同的注解:@GetMapping、@PostMapping、@PutMapping、@DeleteMapping
- @ResponseBody:将返回值放在response内,通常返回json数据。
- @RequestBody:将request的参数放在request体中
- @PathVariable:用于接收路径参数,比如@RequestMapping(“/hello/{name}”)申明的路径,将注解放在参数中前,即可获取该值,通常作为Restful的接口实现方法。
- 容器:
- @Component:将类变为Spring管理的Bean。
- @Service:组合注解(组合了@Component注解),应用在service层(业务逻辑层)。
- @Repository:组合注解(组合了@Component注解),应用在dao层(数据访问层)。
- @Autowired:Spring提供的工具(由Spring的依赖注入工具(BeanPostProcessor、BeanFactoryPostProcessor)自动注入)。
- @Qualifier:用于区分两个以上相同类型的Bean
- @Configuration:声明当前类是一个配置类(相当于一个Spring配置的xml文件)
- @Value:可用在字段,构造器参数跟方法参数指定默认值,支持#{}跟${}方式。一般将SpringbBoot中的application.properties配置的属性值赋值给变量。
- @Bean:注解在方法上,声明当前方法的返回值为一个Bean。返回的Bean对应的类中可以定义init()方法和destroy()方法,然后在@Bean(initMethod=”init”,destroyMethod=”destroy”)定义,在构造之后执行init,在销毁之前执行destroy。
- @Scope:定义采用什么模式创建Bean(方法上,得有@Bean)包括:Singleton、Prototype、Request、Session、GlobalSession。
- AOP:
- @Aspect:声明一个切面(类上)使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
- @After:在方法执行之后执行(方法上)。
- @Before:在方法执行之前执行(方法上)。
- @Around:在方法执行之前与之后执行(方法上)。
- @PointCut:声明切点在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)。
- @Aspect:声明一个切面(类上)使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
- 事务:@Transactional:在要开启事务的方法上使用,即可声明式开启事务。
5. Spring中都用到了哪些设计模式?
- 工厂模式—使用工厂模式通过BeanFactory、ApplicationContext创建bean对象。
- 代理模式—SpringAOP功能功能就是通过代理模式来实现的,分为动态代理和静态代理。
- 单例模式—Spring中定义的Bean默认为单例模式。
- 模板方法—解决代码重复的问题。比如RestTemplate、JmsTemplate、JdbcTemplate。
- 观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。
- 适配器模式:SpringAOP的增强或通知(Advice)使用到了适配器模式、SpringMVC中也是用到了适配器模式适配Controller。
- 策略模式:Spring中有一个Resource接口,它的不同实现类,会根据不同的策略去访问资源。
- 前端控制器—Spring提供了DispatcherServlet来对请求进行分发。
- 视图帮助(ViewHelper)—Spring提供了一系列的JSP标签,高效宏来辅助将分散的代码整合在视图里。
- 依赖注入—贯穿于BeanFactory/ApplicationContext接口的核心理念。
12. 什么是ioc控制反转?好处?
- Inverse Of Control反转控制:将对象的生命周期交给spring容器管理
- 它将最小化应用程序中的代码量
- 它以最小的影响和最少的侵入机制促进松耦合
- 支持即时的实例化和延迟加载Bean对象
- 易于测试,因为不需要单元测试用例中的任何单例或JNDI查找机制。
16. spring的di
- dependency Injection依赖注入,是ioc的实现方式
- 注入方式:set/属性方法注入、构造方法注入、字段注入
- 注入类型:值类型注入(8大基本数据类型)、引用类型注入 将依赖对象注入
set方法注入
<bean name="user" class="cn.itcast.bean.User" >
<!--值类型注入: 为User对象中名为name的属性注入tom作为值 -->
<property name="name" value="tom" ></property>
<!-- 引用类型注入: 为car属性注入下方配置的car对象 -->
<property name="car" ref="car" ></property>
<!--数组类型注入: 如果数组中只准备类一个值(对象),直接使用value|ref-->
<property name="arr" value="tom"></property>
<property name="arr">
<array>
<value>tom</value>
<ref bean="user"/>
<array>
</property>
<!-- List类型注入:如果list中只准备类一个值(对象),直接使用value|ref-->
<property name="list" value="jack"></property>
<property name="list">
<list>
<value>jack</value>
<ref bean="user"/>
<list>
</property>
<!-- Properties类型注入 -->
<property name="prop">
<props>
<prop key="driverClass">com.jdbc.mysql.Driver</prop>
<props>
</property>
<!-- Map类型注入 -->
<property name="map">
<map>
<entry key="url" value="jdbc:mysql:///crm"></entry>
<map>
</property>
</bean>
<!-- 将car对象配置到容器中 -->
<bean name="car" class="cn.itcast.bean.Car" >
<property name="name" value="兰博基尼" ></property>
</bean>
构造函数注入
<bean name="user2" class="cn.itcast.bean.User" >
<!-- name属性: 构造函数的参数名 index属性:
构造函数的参数索引 type属性: 构造函数的参数类型-->
<constructor-arg name="name" index="0" type="java.lang.Integer" value="999"></constructor-arg>
<constructor-arg name="car" index="1" ref="car"></constructor-arg>
</bean>
14. 简述Spring IoC的实现机制?
- 原理:工厂模式加反射机制
- a. 加载配置文件,解析成BeanDefinition放在Map里
- b. BeanFactory调用getBean的时候,从BeanDefinition所属的Map里,拿出Class对象进行实例化,如果有依赖关系,将递归调用getBean方法 —— 完成依赖注入。
13. 说说applicationContext&BeanFactory
- BeanFactory接口:创建并管理各种类的对象。每次在获得对象时才会创建对象.最常用的是XmlBeanFactory,根据XML文件中内容创建相应Bean
- ApplicationContext建立在BeanFactoty基础上。每次容器启动时就会创建容器中配置的所有对象.并提供更多功能
- 1、ClassPathXmlApplicationContext :从 ClassPath 的 XML 配置文件中读取上下文,并生成上下文定义。应用程序上下文从程序环境变量中取得。
- 2、FileSystemXmlApplicationContext :由文件系统中的XML配置文件读取上下文
- 3、XmlWebApplicationContext :由Web应用的XML文件读取上下文。在 Spring MVC 使用
- 4、Spring Boot 的ConfigServletWebServerApplicationContext
17. Spring容器启动阶段会干什么?
- IoC容器工作的过程可以划分两个阶段
- 容器启动阶段:加载配置,分析配置信息,装配到BeanDefinition,其他后处理
- Bean实例化阶段:实例化对象,装配依赖, 生命周期回调, 对象其他处理, 注册回调接口
18. Spring Bean在容器的生命周期是什么样的?
- 实例化Bean对象:
- 根据配置中Bean Definition中实例化Bean对象(通过XML,Java注解或Java Config代码提供)
- 属性赋值:Aware相关的属性,注入到Bean对象
- 如果Bean实现BeanNameAware接口,则工厂通过传递Bean的beanName调用setBeanName(String name)方法
- 如果Bean实现BeanFactoryAware接口,工厂通过传递自身的实例来调用setBeanFactory(BeanFactory beanFactory)方法
- 初始化
- @PostConstruct
- 如果存在与Bean关联的任何BeanPostProcessor,则调用#preProcessBeforeInitialization(Object bean, String beanName) 方法。
- 如果Bean实现InitializingBean接口,则会调用#afterPropertiesSet() 方法。
- 如果Bean指定init方法(例如 的 init-method 属性),那么将调用该方法。
- 如果存在与Bean关联的任何 BeanPostProcessor,则调用#postProcessAfterInitialization(Object bean, String beanName) 方法。
- 销毁:
- @PreDestroy
- 如果Bean实现DisposableBean接口,当 spring 容器关闭时,会调用 #destroy() 方法。
- 如果bean指定destroy方法(例如 的 destroy-method 属性),那么将调用该方法。
15. Spring有哪些自动装配的方式?
- byName根据名称进行自动匹配
- byType根据类型进行自动匹配
- constructor spring根据bean构造函数入参类型自动装配
- autodetect 如果Bean提供了默认的构造函数,则采用byType,否则采用constructor。
4. spring中的bean作用域scope类型√
- singleton(默认):被标识为单例的对象,在spring容器中只存在一个实例
- prototype:被标识为多例的对象,每次获取都会创建新对象
- request:web环境下.对象与request生命周期一致.
- session:web环境下,对象与session生命周期一致.
- Application:对象与Web Application生命周期一致,只能在同一个webapplication中获取
8. spring框架中的单例bean是线程安全的吗?√
- 如果单例Bean是无状态的,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。
- 如果bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。解决办法就是将多态bean的作用域由“singleton”变更为“prototype”或者将变量放入ThreadLocal中
6. 如何解决spring bean中的循环依赖?√为何使用三级缓存解决循环依赖而不是二级缓存?
- 单例模式下Spring可以解决哪些情况的循环依赖
- 多例模式不支持,无限创建对象
- AB均采用构造器注入,不支持。直接抛出BeanCurrentlylnCreationException异常。
- AB均采用setter注入,支持
- AB均采用属性自动注入,支持
- A中注入的B为setter注入,B中注入的A为构造器注入,支持
- B中注入的A为setter注入,A中注入的B为构造器注入,不支持
- 第四种可以,第五种不可以的原因是Spring在创建Bean时默认会根据自然排序进行创建,所以A会先于B进行创建。
- spring通过三级缓存解决循环依赖,
- singletonObjects 一级缓存。保存实例化、属性赋值、初始化完成的bean实例
- earlySingletonObjects 二级缓存 。保存实例化完成的bean实例
- singletonFactories 三级缓存,用于保存bean创建工厂,以便后面有机会创建代理对象
- 实例化过程
- A实例化并把A的ObjectFactory加入第三级缓存
- A填充属性需要注入B -> B实例化并把B的ObjectFactory加入第三级缓存
- B填充属性需要注入A -> 从第三级缓存移除A对象,A代理对象加入第二级缓存(此时A还是半成品,B注入的是A代理对象)
- B属性注入完成,创建B代理对象(此时B是完成品) -> 从第三级缓存移除B对象,B代理对象加入第一级缓存
- A填充属性注入B代理对象,从第二级缓存移除A代理对象,A代理对象加入第一级缓存
- A实例化并把A的ObjectFactory加入第三级缓存
- 如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象
7. @Autowired 的实现原理?
- 在Bean的初始化阶段,会通过Bean后置处理器来进行前置和后置的处理。@Autowired功能是通过后置处理器
AutowiredAnnotationBeanPostProcessor来完成的 - Spring在创建bean的过程中,最终会调用到doCreateBean()方法,在doCreateBean()方法中会调用populateBean()方法,来为bean进行属性填充,完成自动装配等工作。
- 在populateBean()方法中一共调用了两次后置处理器,第一次是为了判断是否需要属性填充,如果不需要进行属性填充,那么就会直接进行return,如果需要进行属性填充,那么方法就会继续向下执行,后面会进行第二次后置处理器的调用,这个时候,就会调用到AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues()方法,在该方法中就会进行@Autowired注解的解析,然后实现自动装配
//属性赋值
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
//…………
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
PropertyValues pvsToUse;
for(Iterator var9 = this.getBeanPostProcessorCache().instantiationAware.iterator(); var9.hasNext(); pvs = pvsToUse) {
InstantiationAwareBeanPostProcessor bp = (InstantiationAwareBeanPostProcessor)var9.next();
pvsToUse = bp.postProcessProperties((PropertyValues)pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = this.filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
//执行后处理器,填充属性,完成自动装配
//调用InstantiationAwareBeanPostProcessor的postProcessPropertyValues()方法
pvsToUse = bp.postProcessPropertyValues((PropertyValues)pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
}
}
//…………
}
//先调用 findAutowiringMetadata()方法解析出 bean 中带有@Autowired 注解、@Inject 和@Value 注解的属性和方法。然后调用 metadata.inject()方法,进行属性填充。
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
//@Autowired注解、@Inject和@Value注解的属性和方法
InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
//属性填充
metadata.inject(bean, beanName, pvs);
return pvs;
} catch (BeanCreationException var6) {
throw var6;
} catch (Throwable var7) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var7);
}
}
19. spring的AOP以及实现方式,通知类型
- AOP(Aspect-Oriented Programming),面向切面编程:通过动态代理将通知织入目标对象.把一些业务逻辑中的相同代码抽取到一个独立的模块中,提高代码的可重用性。
- 实现方式
- 动态代理(优先):基于接口,被代理对象必须要实现接口,才能产生代理对象.
- cglib代理:基于继承,对目标对象进行继承代理.目标对象不被final修饰。通过ASM读取目标类的字节码,然后修改字节码生成新的类
- 应用:用于增强方法,权限认证、日志、事务、参数校验
- 常用术语
- Joinpoint(连接点):目标对象中,被拦截到的方法
- Poincut(切入点):目标对象,已经增强的方法。pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice
- Advice(通知/增强):指拦截到连接点之后要执行的增强代码
- 前置通知(Before advice):在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext 中在 < aop:aspect > 里面使用 < aop:before > 元素进行声明;
- 后置通知(After advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext 中在 < aop:aspect > 里面使用 < aop:after > 元素进行声明。
- 返回后通知(After return advice :在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext 中在 < aop:aspect > 里面使用 << after-returning >> 元素进行声明。
- 环绕通知(Around advice):在 join point 前和 joint point 退出后都执行的 advice。ApplicationContext 中在 < aop:aspect > 里面使用 < aop:around > 元素进行声明。
- 抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。ApplicationContext 中在 < aop:aspect > 里面使用 < aop:after-throwing > 元素进行声明。
- Target(目标对象):被代理的对象
- Weaving(织入):将 aspect 和其他对象连接起来, 并创建 adviced object 的过程.
- Proxy(代理):将通知织入到目标对象之后,形成代理对象 Advice + Target Object = Advised Object = Proxy 。
- aspect(切面):切入点+通知 可以简单地认为, 使用 @Aspect 注解的类就是切面。
22. spring事务配置,处理方式?
- 声明式事务:基于AOP,通过使用注解@Transactional或基于XML的配置事务,从而事务管理与业务代码分离。但无法用到代码块级别
- 编程式事务:通过TransactionTemplate和PlatformTransactionManager编码的方式实现事务管理,需要在代码中显式的调用事务的获得、提交、回滚。灵活性高,但维护困难
public class AccountService {
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void transfer(final String out, final String in, final Double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// 转出
accountDao.outMoney(out, money);
// 转入
accountDao.inMoney(in, money);
}
});
}
}
24. Spring 的事务隔离级别?
- Spring的接口TransactionDefinition中定义了表示隔离级别的常量,当然其实主要还是对应数据库的事务隔离级别:
- ISOLATION_DEFAULT:使用后端数据库默认的隔离界别,MySQL 默认可重复读,Oracle 默认读已提交。
- ISOLATION_READ_UNCOMMITTED:读未提交
- ISOLATION_READ_COMMITTED:读已提交
- ISOLATION_REPEATABLE_READ:可重复读
- ISOLATION_SERIALIZABLE:串行化
27. 事务传播行为?有什么用?√
- 事务的传播行为propagation(service方法调用另外一个service方法的时候,仅限于不同类方法间相互调用)
- 事务的传播机制是指在一个事务中,对于多个事务性操作之间的关系和协调的处理方式。它决定了在一个事务方法中调用其他事务方法时,事务的范围和属性等。
- 支持当前事务
- PROPAGATION_REQUIRED如果当前存在事务,则使用该事务。如果当前没有事务,就新建一个(默认)!!!
- PROPAGATION_SUPPORTS如果当前存在事务,则使用该事务。如果当前没有事务,就不使用事务
- PROPAGATION_MANDATORY如果当前存在事务,则使用该事务。如果当前没有事务,抛出异常
- 不支持当前事务的情况
- PROPAGATION_REQUIRES_NEW创建一个新的事务,如果有事务存在,挂起当前事务,
- PROPAGATION_NOT_SUPPORTED以非事务方式运行,如果有事务存在,挂起当前事务
- PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
- PROPAGATION_NESTED如果当前事务存在,则创建一个事务作为当前事务的嵌套事务来运行,如果当前没有事务,则等价PROPAGATION_REQUIRED
- 以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。
- 事务传播机制是使用 ThreadLocal 实现的,所以,如果调用的方法是在新线程中的,事务传播会失效。
- 在 Spring 中,只有通过 Spring 容器的 AOP 代理调用的公开方法(public method)上的@Transactional注解才会生效。Spring 默认使用基于 JDK 的动态代理(当接口存在时)或基于 CGLIB 的代理(当只有类时)来实现事务。这两种代理机制都只能代理公开的方法。
28. 声明式事务实现原理了解吗?
- Spring的声明式事务管理是通过AOP(面向切面编程)和代理机制实现的。
- 第一步,在Bean初始化阶段创建代理对象:
- Spring容器在初始化单例Bean的时候,会遍历所有的BeanPostProcessor实现类,并执行其postProcessAfterInitialization方法。
- 在执行postProcessAfterInitialization方法时会遍历容器中所有的切面,查找与当前Bean匹配的切面,这里会获取事务的属性切面,也就是@Transactional注解及其属性值。
- 然后根据得到的切面创建一个代理对象,默认使用JDK动态代理创建代理,如果目标类是接口,则使用JDK动态代理,否则使用Cglib。
- 第二步,在执行目标方法时进行事务增强操作:
- 当通过代理对象调用Bean方法的时候,会触发对应的AOP增强拦截器,声明式事务是一种环绕增强,对应接口为MethodInterceptor,事务增强对该接口的实现为TransactionInterceptor
- 事务拦截器TransactionInterceptor在invoke方法中,通过调用父类TransactionAspectSupport的invokeWithinTransaction方法进行事务处理,包括开启事务、事务提交、异常回滚等。
29. 声明式事务在哪些情况下会失效?
- @Transactional应用在非public修饰的方法上。因为在SpringAOP代理时,TransactionInterceptor(事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy的内部类)的intercept方法或JdkDynamicAopProxy的invoke方法会间接调用AbstractFallbackTransactionAttributeSource的computeTransactionAttribute方法,获取Transactional注解的事务配置信息。
protectedTransactionAttributecomputeTransactionAttribute(Methodmethod,Class<?>targetClass){
//Don'tallowno-publicmethodsasrequired.
if(allowPublicMethodsOnly()&&!Modifier.isPublic(method.getModifiers())){
return null;
}
}
- @Transactional注解属性propagation设置错误
- TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行;错误使用场景:在业务逻辑必须运行在事务环境下以确保数据一致性的情况下使用SUPPORTS。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:总是以非事务方式执行,如果当前存在事务,则挂起该事务。错误使用场景:在需要事务支持的操作中使用NOT_SUPPORTED。
- TransactionDefinition.PROPAGATION_NEVER:总是以非事务方式执行,如果当前存在事务,则抛出异常。错误使用场景:在应该在事务环境下执行的操作中使用NEVER。
- @Transactional注解属性rollbackFor设置错误
- rollbackFor用来指定能够触发事务回滚的异常类型。Spring默认抛出未检查unchecked异常(继承自RuntimeException的异常)或者Error才回滚事务,其他异常不会触发回滚事务。
- 同一个类中方法调用,导致@Transactional失效
- 由SpringAOP代理造成的,因为只有事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
9. spring注册bean的几种方式
- 直接编码
- 配置文件 xml的bean标签
- 注解
- @Component、@Service、@Controller
- @Bean
- java config
10. spring中的@Required、@Autowired、@Qualifier注解的作用?
- @Required注解,应用于Bean属性setter方法。表示属性必须注入否则抛出 BeanInitializationException 异常。
- @Autowired用于在setter方法,构造函数,具有任意名称或多个参数的属性或方法上自动装配 Bean
- @Qualifier指定id自动装配哪个bean
11. 解释什么叫延迟加载?
- 容器启动之后默认会创建所有作用域为单例的Bean,但是有的业务场景不需要提前创建。此时设置lazy-init="true"。容器启动之后不会默认创建作用域为单例的Bean,而是在获得该Bean时才创建
20. java热部署
- spring-boot-devtools、Spring Loaded实现原理,nio的WatchService监听文件夹变化,同时实现classLoader的findclass方法重新将class加载进内存
21. Spring容器创建过程
AbstractApplicationContext调用refresh()方法通过调用obtainFreshBeanFactory()方法创建Bean工厂AnnotatedBeanDefinitionReader扫描注解doScan方法和XmlBeanDefinitionReader的doLoadBeanDefinitions方法sax方式解析xml将其封装成document对象,使用BeanDefinitionReaderUtils.registerBeanDefinition并注册到BeanDefinitionRegistry缓存中
然后调用postProcessBeanFactory调用子类的BeanFactory的后置处理器,然后调用invokeBeanFactoryPostProcessors()执行后置处理器
第三注册BeanPostProcessors()到BeanFactory中
第四注册监听器
第五实例化所有的Bean放到singletonObjects单例池里面
23. @Transactional 注解有哪些属性?如何使用?
- 属性:propagation事务传播行为,isolation事务隔离级别、rollbackFor导致事务回滚的异常类数组、timeout事务超时回滚、readonly读写或只读事务
- @Transactional 可用在接口、接口方法、类、类方法上。只被应用到 public 方法上,方法级别注解覆盖类级别的注解。
25. Spring 事务如何和不同的数据持久层框架(Spring JDBC、Hibernate、Spring JPA、MyBatis)做集成?
- Spring通过org.springframework.transaction.PlatformTransactionManager进行事务管理管理
public interface PlatformTransactionManager {
// 根据事务定义 TransactionDefinition ,获得 TransactionStatus 。
//为什么不是创建事务呢?因为如果当前如果已经有事务,则不会进行创建,一般来说会跟当前线程进行绑定。
//为什么返回TransactionStatus对象?因为TransactionStatus 中包含事务属性和事务的其它信息,例如是否只读、是否为新创建的事务等等
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
//为什么根据 TransactionStatus 情况,进行提交?例如说,带@Transactional注解的A方法,会调用 @Transactional 注解的B方法
//在B方法结束调用后,会执行PlatformTransactionManager#commit(TransactionStatus status) 方法,此处事务是不能、也不会提交的
//而是在A方法结束调用后,执行PlatformTransactionManager#commit(TransactionStatus status) 方法,提交事务
void commit(TransactionStatus status) throws TransactionException;
// 为什么根据 TransactionStatus 情况,进行回滚?原因同上
void rollback(TransactionStatus status) throws TransactionException;
}
- 不同的数据持久层框架,会有其对应的PlatformTransactionManager实现类,都基于AbstractPlatformTransactionManager这个骨架类。
- HibernateTransactionManager,和Hibernate5事务管理做集成。
- DataSourceTransactionManager,和JDBC事务管理做集成。也适用于MyBatis、Spring JDBC等等
- JpaTransactionManager,和JPA事务管理做集成。
26. 为什么在Spring事务中不能切换数据源?
- 在Spring的事务管理中,数据库连接会和当前线程所绑定,即使设置了另外一个数据源,使用的还是当前的数据源连接。
- 而且多个数据源且需要事务的场景会带来多事务一致性的问题
- 推荐除非了读写分离所带来的多数据源,其它情况下,建议只有一个数据源
30. 拦截器与过滤器区别√
- 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
- 拦截器不依赖与servlet容器,过滤器依赖servlet容器。
- 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
- 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
- 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
- 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
31. spring区分环境
- spring.profiles.active=dev----application-dev.properties