• 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
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在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
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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);
    }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    如上代码,在调用userservice构造方法时,需要传入一个orderservice,这个orderservice会从map中找,如果map中有就从map中拿,如果没有那么就创建一个orderservice bean(这样会出现循环依赖)
    Map中是
    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");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    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,其中保存的是,这个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;
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    当使用构造方法给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

  • 相关阅读:
    华为OD机试真题-分积木-2023年OD统一考试(B卷)
    【从零开始学习 SystemVerilog】3.5、SystemVerilog 控制流——阻塞(Blocking)与非阻塞(Non-Blocking)
    图论篇--代码随想录算法训练营第五十六天打卡| 108. 冗余连接,109. 冗余连接II
    Java下部笔记
    【论文笔记】Perception, Planning, Control, and Coordination for Autonomous Vehicles
    Vue3 + Tsx 集成 ace-editor编辑器
    浅析量化交易系统交易者的核心理念
    c语言 编程及答案
    备战金九银十,Java研发面试题+答案整合PDF,走到哪刷到哪
    代码随想录打卡第四十四天|● 01 二维背包问题 ●一维背包问题-滚动数组 ● 416. 分割等和子集
  • 原文地址:https://blog.csdn.net/qq_56892136/article/details/126608795