PostConstruct注解如何工作?
需求:给UserService里的一个属性admin(User)赋值,赋的值不是乱来的,是从数据库查询的
方法1:
用@PostConstruct注解,该注解使用在方法上 表明bean初始化(初始化前)时执行该方法
@Component
public class UserService {
@Autowired
private OrderService orderService;
private User admin;
@PostConstruct
public void a(){
// mysql--》管理员信息--》user对象--》admin
}
}
在a方法中对admin进行赋值
方法2:
让userService实现InitializingBean接口 重写afterPropertiesSet 在该方法中对admin赋值
@Component
public class UserService implements InitializingBean {
@Autowired
private OrderService orderService;
private User admin;
@Override
public void afterPropertiesSet() throws Exception {
// mysql--》管理员信息--》user对象--》admin
}
}
Bean初始化和Bean实例化区别
实例化:调用无参构造实例化对象
初始化:执行这个bean里的方法(afterPropertiesSet())
什么是初始化后?
进行AOP,然后把生成的代理对象放入单例池
在整个bean创建过程中,首先调用类的无参构造,创建出普通对象,然后进行依赖注入,再初始化,初始化后生成代理对象放入Map单例池然后生成bean对象
推断构造方法?
在初始化对象的时候,优先调用无参构造方法。如果类中有两个构造方法且没有无参构造,这个时候Spring无法决定应该调用哪个构造,就会所以报错,但是可以在构造方法上加@Autowired注解来告诉Spring调用哪个方法;如果只有一个构造方法(无论是有参还是无参),这时Spring就知道应该调用哪个了(只有一个)
什么是先bytype再byname
@Component
public class UserService{
@Autowired
private OrderService orderService;
private User admin;
@Autowired
public UserService(OrderService orderService){
this.orderService=orderService;
System.out.println(1);
}
}
如上代码,在调用userservice构造方法时,需要传入一个orderservice,这个orderservice会从map中找,如果map中有就从map中拿,如果没有那么就创建一个orderservice bean(这样会出现循环依赖)
Map中是<beanName,Bean对象>
byname就是根据入参的名字orderService去map找匹配的beanName,如果入参不叫orderservice,那就会找不到
bytype就是根据入参的类型OrderService去找相应的bean,但是可以有多个bean是同一个类型(比如orderService、orderService1、orderService2)这时候就需要按照名字找了
AOP底层如何工作?
@Aspect
@Component
public class userserviceaspect {
@Before("execution(public void com.example.springdemo.service.UserService.test())")
public void testBefore(JoinPoint joinPoint){
System.out.println("testbefore");
}
}
aop会根据userService和他的aspect生成一个UserServiceProxy的代理对象,代理对象里的target就是普通的userservice对象。在调用test时,会先执行aspect的前置通知方法,然后执行target的test方法
Spring事务底层是如何工作的?
开启事务需要在启动类加@EnableTransactionManagement和@Configuration注解,事务类@Transactional
同类调用方法事务不生效:
spring基于AOP机制实现事务管理,调用userservice的方法时,实际是通过userservice的代理对象调用userservice的方法,代理类在执行目标方法前后,加入了事务管理的代码。只有通过注入的studentservice调用事务方法,才会走代理类,才会执行事务管理,如果在同类直接调用,没走代理类,事务会失效
可以通过在userservice自身内部@Autowired一个自己
@Configuration注解的作用?
上一个问题说了要加@Configuration注解是为了解决一个问题:如上两图,Spring是基于AOP进行事务管理,调用test方法时会在执行目标方法之前通过事务管理器新建一个数据库连接,此时在目标方法test()中又调用jdbcTemplate时又会创建一个数据库连接,这个连接跟事务管理器的连接不是同一个,这就导致在走到jdbcTemplate.excute()这一行时,jdbc的这个连接自动开启事务然后执行insert最后自动提交了(throw时这个事务已经被提交),再走到下一行抛出异常时,事务管理器建立的连接的事务发生回滚,jdbcTemplate没有回滚甚至已经提交了。这样就会导致数据库产生不必要的脏数据。
加了@Configuration后,可以达到jdbcTemplate和事务管理器是同一个connection的效果
加入@Configuration后,在dateSource()方法执行时会进行一些逻辑判断,如果有这个bean那就直接拿这个bean,如果没有再创建,这就可以使jdbcTemplate和事务管理器是同一个datasource
Spring为何要用三级缓存来解决循环依赖?
AService依赖BService、BService依赖AService 二者循环依赖
1.AService在创建bean过程中,首先会加入creatingSet 说明AService正在创建
2.然后AService会创建一个AService的普通对象,并放入第三级缓存singletonFactories中(singletonFactories是一个map,其中保存的是<beanName,lambda表达式>,这个lambda不会立即执行)
3.然后会填充AService的属性BService,首先会去一级缓存单例池singletonObjects中找有没有BService的对象(代理对象),发现没有,那么就开始创建BService对象
4.BService加入creatingSet,创建普通对象。
5.填充BService的属性AService,先去一级缓存找AService,没有,然后看creatingSet发现有Aservice,说明AService正在创建,此时发生了循环依赖。然后就去二级缓存earlySingletonObjects找有没有AService,没有,再去三级缓存singletonFactories找,三级缓存中有,此时lambda才会执行,如果有AOP,那么就会执行AOP返回代理对象(此时的代理对象不是最后的代理对象,还需要填充其他属性,进行初始化等操作),如果没有AOP,那么直接返回普通对象,然后把他放入earlySingletonObjects二级缓存中,(二级缓存可以保证单例,假设没有二级缓存,AService与BService相互依赖,CService与BService相互依赖,此时AC都会创建一个代理对象,这两个代理对象是一个类,但不是一个对象,导致非单例)
6.BService填充其他属性,进行其他的操作(初始化),然后放入单例池,此时BService的属性 AService还不是最终的代理对象,需要等接下来Aservice完成整个创建操作才能完全体
7.AService填充其他属性,进行其他操作,放入单例池。
构造方法的循环依赖
构造方法
@Component
public class AService {
private BService bService;
@Lazy
public AService(BService bService){
this.bService=bService;
}
}
当使用构造方法给BService注入值,无法解决循环依赖问题,可以使用@lazy来懒加载,然后自行传入BService
事务传播机制
都是@Transactional的参数
REQUIRED(默认):如果当前没有事务,创建一个事务,如果存在,则加入
SUPPORTS:当前存在事务,则加入事务,不存在就以非事务方法执行
MANDATORY:当前存在事务,加入,不存在,抛出异常
REQUIRES_NEW:创建一个新事物,如果存在当前事务,则挂起该事务
NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起当前事务
NEVER:不使用事务,如果当前事务存在,则抛出异常
NESTED:如果当前事务存在,则在嵌套事务中执行,否则跟REQUIRED的操作一样
Spring中事务隔离级别:
ISOLATION_DEFAULT:使用数据库默认的事务隔离级别
ISOLATION_READ_UNCOMMITTED:读未提交,允许事务在执行过程中,读取其他事务未提交的数据
ISOLATION_READ_COMMITTED:读已提交,允许事务执行过程中,读取其他事务已提交的数据
ISOLATION_REPEATABLE_READ:课重复读,在同一事务内,任意时刻的查询结果一致
ISOLATION_SERIALIZABLE:所有事务依次执行
Spring中的Bean线程安全吗,如果不安全,要如何处理
不安全
spring作用域: 1.sington 2.prototype:为每个bean请求创建一个实例 3.为每个request请求创建一个实例,请求完成后失效 4.session:与request类似 5.global-session:全局作用域
对于prototype,每次生成一个新的,无线程安全问题
对于sington,默认线程不安全,但是开发中大部分bean都是无状态的(实例没有属性对象,不能保存数据,是不变的,比如controller、service、dao,dao操作数据库时的Connection是有状态的,但是Spring事务管理器使用ThreadLocal为不同线程维护了一套独立的Connection副本,保证线程间不会相互影响),不需要保证线程安全。有状态的bean(pojo),可以保存对象,线程不安全的,解决方法:修改作用域prototype或者用ThreadLocal
Spring框架中的设计模式
简单工厂:BeanFactory 根据标识来获取Bean对象(getBean(beanName)方法)
工厂方法:FactoryBean(工厂的Bean对象):spring在使用getBean()调用获取该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个bean.getObject()方法的返回值
单例模式:单例bean
适配器模式:Spring定义了一个适配接口,使得每一种controller有一种对应的适配器实现类,让适配器代替controller执行相应的方法,这样在扩展controller时,只需要增加一个适配器类就完成了MVC的扩展了
装饰器模式:类名中含有wrapper和decorator的类
动态代理:AOP
观察者模式:事件驱动模型 listener的实现
Spring容器启动流程
1.首先进行扫描,扫描得到所有的BeanDefinition,并放入一个map中
2.筛选出非懒加载的单例BeanDefinition进行创建Bean,对于多例Bean不需要在启动过程中去进行创建,对于多例Bean会在每次获取Bean时利用BeanDefinition进行创建
3.利用BeanDefinition创建Bean就是Bean的创建生命周期,这期间包含了合并BeanDefinition,推断构造方法、实例化、属性填充、初始化前、初始化、初始化后等步骤,其中AOP就是发生在初始化后
4.单例Bean创建完成之后,Spring会发布一个容器启动时间
5.Spring启动结束
6.在源码中会更复杂,比如源码会提供一些模板方法来让子类实现,源码中还涉及一些BeanFactoryPostProcessor和BeanPostProcessor的注册,spring的扫描结束通过BeanFactoryPostProcessor来实现的,依赖注入就是通过BeanPostProcessor来实现的
Spring如何处理事务
Spring当中支持编程式事务管理和声明式事务管理
编程式事务可以使用TransactionTemplate:
声明式事务:使用@Transactional 声明式事务只能针对方法级别,无法控制代码级别
Spring事务失效场景
1.发生自调用,调用了本类方法,此时this不是代理对象,是普通对象
2.方法不是public
3.数据库不支持事务
4.没有被Spring管理
5.异常没正确抛出
Spring事务是如何实现的
1.Spring事务底层是基于数据库事务和AOP机制
2.首先对于使用@Transactional注解的Bean,Spring会创建一个代理对象作为Bean
3,当调用代理对象方法时,会先判断该方法上是否加了@Transactional注解
4.如果加了,那么则利用事务管理器创建一个数据库连接
5.并且修改数据库连接的autocommit属性为false,禁止此链接自动提交
6.执行当前方法,方法中会执行sql
7.执行完当前方法后,如果没有出现异常就直接提交事务
8.如果出现按异常,并且这个异常需要回滚就回滚事务,否则依然提交事务
9.Spring事务的隔离级别对应的就是数据库的隔离级别
10.Spring事务传播机制是Spring事务自己实现的,也是Spring事务最复杂的
11.Spring事务的传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为需要新开一个事务,那么实际上就是先建立一个数据库连接,在此新数据库连接上执行sql