Spring Bean生命周期以及其中的一些底层原理

Bean创建的生命周期

UserService.class
--> 推断构造方法
> 1. 判断使用哪一个构造方法
> 2. 判断构造方法有没有入参
> 1. 没有入参
> 2. 有入参
先ByType,再ByName
尝试去找,先找Type,看看找到了几个
1. 找到一个,那么就是这个
2. 找到多个,那么就去找名字
1. 找到名字,那么就是这个
2. 找不到名字,就抛出异常
3. 一个都没有找到,那么就尝试去创建
这里可能会出现循环依赖问题
--> 普通对象
--> 依赖注入(注入加入了@autowired注解的属性)
1. 找到加了Autowired注解的属性
2. 给加了Autowired注解的属性去赋值
也是先ByType,再ByName

--> 初始化前(执行a方法,方法加了@PostConstruct注解)
1. 找到加了@PostConstruct注解的方法
2. 执行这个方法
--> 初始化 (实现 InitializingBean接口 重写afterPropertiesSet方法)
> 具体底层实现就是 强转为InitializingBean,然后调用afterPropertiesSet方法
--> 初始化后(AOP)
> 动态代理,产生了代理对象
代理对象没有进行依赖注入,执行了切面逻辑之后,再回到切面的方法去执行该方法
cglib: 产生一个代理类,子类会继承之前的那个类。产生一个代理对象, 然后给Target赋值
UserServiceProxy对象 extends UserService代理对象 -->UserServiceProxy代理对象.target = 普通对象
代理对象.test()。

class UserServiceProxy extends UserService {
                        // 这里必须要继承,不然需要强制转换,不太好呀
    UserService target;

   public void test() {
      1. 执行切面逻辑// @Before("execution(public void com.zbz.service.UserService.tes())")
      2. target.test(); 就是执行UserService普通对象的test(),这里是经过依赖注入的。
   }
}

---> 放入map(单例Bean)
> 如果发生了AOP,那么放入单例池的就是代理对象
> 如果没有发送AOP,那么放入单例池的就是普通对象
-->> Bean对象


  1. 依赖注入: 给前面生成出来的对象,给对象当中的加了autowired的属性去赋值

    1. 找到加了autowired注解的属性
    2. 给加了autowired注解的属性赋值
    1. 用无参的构造方法弄出一个对象
    2. 拿到这个对象的所有的fields
    3. 遍历这些fields, 判断是否有autowired.class的注解
    4. 给当前对象的这个属性去赋值
    for (Field field: xxx.getClass().getDeclaredFields()) {
        if (filed.isAnnotationPresent(Autowired.class)) {
            field.set(xxx, ???)
        }
    }
    
  2. 单例池底层原理

    存着一个又一个的单例bean, 从map拿到的对象叫做bean对象。
    只要这个对象放入了单例池,就可以说这个对象是bean对象。

  3. PostConstruct底层原理
    假设某个Bean,当中有个属性叫做User user, 但是他没有加autowired注解,但是我们希望在我们拿到这个bean的时候
    ,这个user属性值是有值的(我们要从数据库里面去查找呀,所以只有我们程序员自己才知道),那该怎么办呢?
    那么这个a方法就是特殊的,那么如何在执行初始化之前 去执行 a方法呢,我们去给a方法戴个帽子。

    去找这个对象的所有方法,看哪个方法上面加了@PostConstruct注解的。

    
    public class UserService {
        
        @Autowired
        private OrderService orderService;
    
        private User admin;
        
        @PostConstruct
        public void a() {
            // mysql --> 查询管理员信息 --> User对象对象 --> setUser
        }
        
        public void test() {
            System.out.println(orderService);
        }
    
    }
    
    for (Method method: xxx.getClass().getDeclaredMethods()) {
        if (method.isAnnotaionPresent(PostConstruct.class)) {
            method.invoke(xxx, null);
        }
    }
    
  4. 初始化底层原理代码实现

     public class UserService 1. implements InitializingBean{
            
            @Autowired
            private OrderService orderService;
        
            private User admin;
           
           2. 
           @Override
           public void afterPropertiesSet() throws Exception {
               // mysql --> 查询管理员信息 --> User对象对象 --> setUser
           }
    
            public void test() {
                System.out.println(orderService);
            }
        
        }  
    
  5. 推断构造方法底层原理, 可能出现循环依赖问题
    如果一个java类,里面有多个构造方法,那么会看有没有无参构造方法:
    有, 调用无参构造方法
    没有,会报错!
    如果只有一个构造方法,那么就使用这个构造方法呀!
    如果想让spring知道调用那个构造方法。那么给方法加上@Autowired即可!

    @Component
    public class UserService {
       private OrderService orderService;
       
       public UserService(OrderService xxxService) { 
           this.orderService = xxxService;
       }
    }
    

    问题来了,getBean("userService")的时候,orderService有没有值?
    答案是有的!
    Spring 会给我们去找 那个OrderService对象,然后放进这个方法里面!!!
    如果是单例Bean去单例池去找,看有没有orderService的Bean对象:
    前提: 是一个Bean呀!
    有, 直接传
    没有,去创建 --- 这里可能会出现循环依赖问题!!!
    ```java
    @Component
    public class OrderService {
    private UserService userService;

         public OrderService(UserService userService) {
             this.userService = userService;
         }
     }
     ```
    

    如果是多例Bean, 那么直接创建!

    那么应该如何去找?

    1. 根据名字去找不是那么合适, (OrderService xxxService)
      因为是形参,我随便扔一个名字,找不到不就白找了,所以要根据类型去找
      Map<beanName, Bean对象>
    2. 根据类型去找可能会出现问题
      <k1, v1> <k2, v2> 这里v1和v2两个Bean对象是一个类型!
      1. 先找同类型的,找到了一堆,然后看个数,如果只有1个,那么就是这个了,否则
      2. 在这一堆同类型的去找名字,哪个名字相同就用哪个!
        先ByType再ByName!!!!!!!!!!!!!!!!!!!!!!!!!!!
        名字也找不到就会抛出异常啦!
      @Bean 
      public OrderService orderService1() {
          return new OrderService();
       }
      @Bean  
      public OrderService orderService2() {
          return new OrderService();
      }
      
      @Component
        public class UserService {
           private OrderService orderService;
           public UserService() {}
           public UserService(OrderService orderService) {
               this.orderService = orderService;
           }
        }
    

    问题来了,getBean("userService")的时候,orderService有没有值?
    答案是没有的! spring使用了无参数的构造方法!

            @Component
            public class UserService {
               private OrderService orderService;
               public UserService(OrderService orderService) {
                   this.orderService = orderService;
               }
               public UserService(OrderService orderService, OrderService orderService2) {
                   this.orderService = orderService;
               }
            }
    

    问题来了,getBean("userService")的时候,orderService有没有值?
    这个时候会报错啦!!!Spring也不知道用哪一个构造方法了!

  6. AOP底层实现原理
    应用:

@ComponentScan("com.zbz")
@EnableAspectJAutoProxy
public class AppConfig {

}
@Aspect
@Component
public class XXXAspect {
  @Before("execution(public void com.zbz.service.UserService.test()")
  public void xxxBefore(JoinPoint joinPoint) {
                          joinPoint.getTarget()可以拿到普通对象
                          joinpPoint.getThis()可以拿到代理对象
      System.out.println("before");
  }
}

底层原理:

  1. 首先是Bean的生命周期:
    推断构造方法->产生普通对象->执行依赖注入->初始化前->初始化->初始化后(AOP)->放入单例池->getBean对象去使用
  2. AOP是怎么做的呢?
1. UserServiceProxy产生一个代理类,继承普通对象的类(UserService)。
2. 产生一个代理类对象UserService代理对象
3. 给UserService代理对象的target属性赋值
    this.target = 普通对象 (前面产生的普通对象)
  1. Spring事务底层实现原理
@ComponentScan("com.zbz")
@EnableAspectJAutoProxy
//4. 开启事务
@Configure
@EnableTransactionManagement
public class AppConfig {
  // 1. 配置mybatis
  @Bean
  public JdbcTemplate JdbcTemplate() {
      return new JdbcTemplate(dataSource());
  }
  // 2. 配置一个事务管理器
  @Bean 
  public PlatformTransactionManager transactionManager() {
    DataSourceTransactionManager transactionManager = new DataSourceTranscationManager();
    transactionManager.setDataSource(dataSource());
    return transactionManager;
  }
  // 3. 配置一个dataSource
  @Bean 
  public DataSource dataSource() {
    DriveManagerDataSource dataSource = new DriveManagerDataSource();
    dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/xxx?characterEncoding=utf-8&&useSSL=false");
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    return dataSource;
  }
}
@Component
public class UserService {
  @Autowired
  private JdbcTemplate JdbcTemplate;

  @Transcational
  public void test() {
    JdbcTemplate.execute("insert into t1 values(1,1,1,1,'1'");
    throw new NullPointerException();
  }
}

加了@transactional标签,会产生一个代理对象去执行这个test()方法,但是这里没有回滚,因为少了一个注解@Configure。
加了@Configure注解后,执行失败进行回滚了。
也产生了一个cglib代理对象。

class UserServiceProxy extends UserService {
                        // 这里必须要继承,不然需要强制转换,不太好呀
    UserService target;

   public void test() {
      Spring事务切面逻辑
      执行的方法上面有@Trancational 注解才能够开启事务

      开启事务
      1. 事务管理器新建一个数据库连接conn
         为什么不用本来类持有的jdbcTemplate数据库连接? 
         因为默认的conn.autocommit = true
         所以spring只有自己去建立一个数据库连接
      2. 修改conn.autocommit = false


      3. target.test(); 就是执行UserService普通对象的test(),这里是经过依赖注入的。
      4. 这里jdcbTemplate 需要拿到之前建立的数据库连接 | sql1 sql2

      5. 如果没有抛异常 : conn.commit()
         如果抛出异常 : conn.rollback() 
   }
}
  1. Spring 事务失效原理
@Component
public class UserService {
  @Autowired
  private JdbcTemplate JdbcTemplate;

  @Transcational
  public void test() {
    jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1'");
    a(); 执行这个方法,是UserService普通对象来执行的,
  }


  @Transcational(propagation = Propagation.NEVER) NEVER: 如果有一个事务存在,那么就抛出一个异常
  public void a() {
     JdbcTemplate.execute("insert into t1 values(2,1,1,1,'1'");
  }
}

为什么这里没有抛出异常? 相当于@Transcational(propagation = Propagation.NEVER失效)
test()方法的@Transactional()注解是有用的,因为getBean("UserService")拿到的是UserService的代理对象,切面过程执行完以后
让target(指向普通对象)去执行普通对象的target方法,普通对象是不会开启事务的。

关键是要弄清楚: 执行这个方法的对象是 代理对象! 还是 普通对象! 所以下面的@Transcational没用了

那么如果想让下面的@Transcation有用,怎么办呢?

  1. 弄一个新类,把这个方法弄到新的类当中去,然后autowire一个代理对象过来执行就可以了
@Autowired 
private UserServiceBase userServiceBase; // 这里弄过来的是代理方法啊!

public void test() {
    jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1'");
    userServiceBase.a();
}
  1. 不拆类,自己注入自己
    这里也相当于产生了一个循环依赖! 但是spring帮我们进行处理了!
@Autowired 
private UserService userService; // 这里弄过来的是代理方法啊!

public void test() {
    jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1'");
    userService.a();
}
  1. @Configuration 底层原理
    配置事务@EnableTransactionManagement的时候,不加@Configuration注解,那么执行方法出错的时候不会进行回滚,为什么?
    通过ThreadLocal进行实现的,因为这些操作都是在一个线程中实现的。

回到事务的实现

开启事务
1. 事务管理器新建一个数据库连接conn 
2. conn.autocommit = false ThreadLocal<Map<DataSource, conn>>

target.test();  普通对象.test() jdbcTemplate 不加@Configuration 就拿不到上面的conn sql1 | sql2

conn.commit() conn.rollback()

ThreadLocal<Map<DataSource, conn>>存放的是map key:DataSource, value: conn
因为jdbcTemplate和transactionManager都会依赖于dataSource()

jdbcTemplate 和transactionManager所持有的dataSource是不同的

如果jdbcTemplate从ThreadLocal里面拿不到数据库连接,那么它就会自己去创建数据库连接,这里的commit是true的。

@Configuration AOP @Lazy 都是使用了动态代理技术,是平级的

@Configuration: 让jdbcTemplate和transactionManager所持有的 dataSource()对象是同一个对象
这里又和代理对象相关
@Configuration -> 产生了代理对象
这里使用了super机制
AppConfig 代理对象执行 jdbcTemplate 和transactionManager方法

对dataSource()方法 增加了切面 代理逻辑:
看Spring容器中是否有DataSource bean,有就返回, 没有就创建

 class AppConfigProxy extends AppConfig {
    public void jdbcTemplate() {

      // 代理逻辑
      super.jdbcTemplate(); 
    }


       // 2. 配置一个事务管理器
    @Bean 
    public PlatformTransactionManager transactionManager() {
      DataSourceTransactionManager transactionManager = new DataSourceTranscationManager();
      transactionManager.setDataSource(dataSource());
      return transactionManager;
    }
    // 3. 配置一个dataSource
    @Bean 
    public DataSource dataSource() {

      先看Spring容器中是否有DataSource Bean


      DriveManagerDataSource dataSource = new DriveManagerDataSource();
      dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/xxx?characterEncoding=utf-8&&useSSL=false");
      dataSource.setUsername("root");
      dataSource.setPassword("root");
      return dataSource;
    }
}

参考:https://www.bilibili.com/video/BV1tR4y1F75R?p=21&spm_id_from=pageDriver

posted @   CrazyShanShan  阅读(232)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示