Spring源码问题

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值