【小家Spring】为脱离Spring IOC容器管理的Bean赋能【依赖注入】的能力,并分析原理(借助AutowireCapableBeanFactory赋能)(下)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 【小家Spring】为脱离Spring IOC容器管理的Bean赋能【依赖注入】的能力,并分析原理(借助AutowireCapableBeanFactory赋能)(下)

源码这里就不再贴了,因为确实比较简单。用一些文字描述即可:


1.invokeAwareMethods:执行一些感知接口Aware的注入

2. postProcessBeforeInitialization:执行后置处理器的此方法。基本上也是执行一些Aware的注入。

—>1. InitDestroyAnnotationBeanPostProcessor在这里会执行@PostConstruct标记的方法

—>2. ApplicationContextAwareProcessor/ServletContextAwareProcessor会实现感知接口的注入

4. invokeInitMethods:先执行InitializingBean#afterPropertiesSet方法,再执行init-method。

5. postProcessAfterInitialization:执行后置处理器此方法。这里面有不少干实事的:

—>1. AbstractAdvisingBeanPostProcessor,AbstractAutoProxyCreator:会把所有的切面保存下来(AOP包下的)

—>2. ApplicationListenerDetector:会把所有的ApplicationListener子类保存起来

—>3. ScheduledAnnotationBeanPostProcessor:会把所有的标注了@Scheduled的方法保存起来

—>4. SimpleServletPostProcessor:会调用Servlet#init的init方法


至于对应的@EventListener注解是什么时候解析的。是它:EventListenerMethodProcessor,它是一个SmartInitializingSingleton,所以它是在preInstantiateSingletons()方法的最后一步去调用的(比如getBean()方法还晚)

由此,我们是可以得出结论的:Spring之外的Bean,SmartInitializingSingleton接口对他是不起作用的。但是,其余的处理器、注解等等都是有用的哦:@PostConstruct、@Autowired等等注解


就这样,就完成结束了我们的Bean的创建、实例化、初始化等等操作。


其它API:autowireBean、configureBean等等



autowireBean:

  @Override
  public Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException {
    // Use non-singleton bean definition, to avoid registering bean as dependent bean.
    final RootBeanDefinition bd = new RootBeanDefinition(beanClass, autowireMode, dependencyCheck);
    bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
    if (bd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR) {
      return autowireConstructor(beanClass.getName(), bd, null, null).getWrappedInstance();
    }
    else {
      Object bean;
      final BeanFactory parent = this;
      if (System.getSecurityManager() != null) {
        bean = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
            getInstantiationStrategy().instantiate(bd, null, parent),
            getAccessControlContext());
      }
      else {
        bean = getInstantiationStrategy().instantiate(bd, null, parent);
      }
      populateBean(beanClass.getName(), bd, new BeanWrapperImpl(bean));
      return bean;
    }
  }


和createBean的区别:createBean是全量的。什么各种后置处理器都不执行。而autowire它直接调用策略器实例化了,什么有关实例化的处理器就都不会执行了(所以不能生成代理对象了嘛)(使用较少,但可以绕过一些处理器)


autowireBeanProperties: 相当于只执行了给属性赋值populateBean()(以及相关的处理器)

  @Override
  public void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
      throws BeansException {
    // 构造器注入的模式,这里就不能支持了
    if (autowireMode == AUTOWIRE_CONSTRUCTOR) {
      throw new IllegalArgumentException("AUTOWIRE_CONSTRUCTOR not supported for existing bean instance");
    }
    // Use non-singleton bean definition, to avoid registering bean as dependent bean.
    RootBeanDefinition bd =
        new RootBeanDefinition(ClassUtils.getUserClass(existingBean), autowireMode, dependencyCheck);
    bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
    BeanWrapper bw = new BeanWrapperImpl(existingBean);
    initBeanWrapper(bw);
    populateBean(bd.getBeanClass().getName(), bd, bw);
  }


    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
        // ApplicationContext里面是持久AutowireCapableBeanFactory这个工具的,它真实的实现类一般都是:DefaultListableBeanFactory
        AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
        //我们也可以自己手动创建一个实例  然后让Spring帮我们自动装配就行  也能正常注入
        Child child = new Child();
        autowireCapableBeanFactory.autowireBeanProperties(child, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
        System.out.println(child.getHelloService()); //com.fsx.service.HelloServiceImpl@175c2241
    }


initializeBean:相当于只执行了初始化Bean的一些操作。。。


  @Override
  public Object initializeBean(Object existingBean, String beanName) {
    return initializeBean(beanName, existingBean, null);
  }


后面的就不一一介绍了,因为有了上面的源码分析,了解其它的就不难了。


AutowireCapableBeanFactory的使用场景


1、首先最大的使用场景就是Spring内部

2、Spring Boot集成Quartz的时候。很好的使用到了此方案。我们在Job类里面使用@Autowired注入的时候,若出现注入不进去的现象。可以考虑使用AutowireCapableBeanFactory进行协助

3、前面说到的继承特定的第三方框架,他们的Bean并不需要交给Spring管理,但是又想用到Spring容器里面的Bean完成一些功能的时候,使用它就特别的方便了。


InstantiationStrategy


本来该策略接口不准备放在此处,但是正所谓说过的话,跪着也要走完啊(上面说过)。因此,那就来吧~

上面分析我们发现,Bean大都最终的实例化操作是交由这个策略器来搞定的,那它是什么呢?


// Interface responsible for creating instances corresponding to a root bean definition.
// 简而言之,就是根据RootBeanDefinition,去实例化一个实例(相当于new了一个对象而已,bean的具体的属性在此时并未赋值)
public interface InstantiationStrategy {
  // 下面是他的三个重载方法
  // owner:这个Bean定义所属的BeanFactory工厂
  // args:构造函数的参数(大多数情况下都用无参构造)
  // factoryBean:factoryMethod  也支持工厂方法方式创建实例(注意此factoryBean非我们所常指的接口:FactoryBean哦~)
  Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner)
      throws BeansException;
  Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
      Constructor<?> ctor, @Nullable Object... args) throws BeansException;
  Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
      @Nullable Object factoryBean, Method factoryMethod, @Nullable Object... args)
      throws BeansException;
}

它的继承结构很简单:


image.png


SimpleInstantiationStrategy


简单实现类,采用实现部分,抽象部分的策略(但是它可不是抽象类,因为我们是可以直接使用它的(如果没有方法注入MethodInjection的去求的话)。只是我们一般都不会直接使用它,而是子类


它的源码逻辑比较简单,伪代码如下:


  if (!bd.hasMethodOverrides()) {
    ...
    // 最后:借助BeanUtils.instantiateClass(ctor, args) 利用构造函数创建实例
  } else {
    return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
  }


CglibSubclassingInstantiationStrategy

留下了包含MethodOverride对象的bd对象定义未做实现,那么CglibSubclassingInstantiationStrategy就是它的一个实现:它采用采用cglib生成之类方式,实例化对象。(当然若不需要生成代理对象,就直接使用父类的功能即可的)


它的源码相对来说比较复杂:主要是静态内部类CglibSubclassCreator它生成一个子类对象去实现方法注入的功能,它内部有cglib动态代理的内容,这里确实就不太适合展开了,AOP再相见吧~


关于方法注入(MethodInjection):


当我们在一个bean中依赖其他的bean时,我们可以注入其他依赖的bean通过set()或者构造器方法。 这样调用get方法的时候返回在bean中注入的实例。但是如果我们希望在每次调用get方法的时候返回新的实例,怎么办呢?


比如单例的A,希望每次使用B的时候都是一个新的对象~

有的伙伴可能就会这么写了:

@Component
public class A {
    @Autowired
    private B b;
    public B getB() {
        return b;
    }
}
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 这里scope指定不是单例
public class B {
}
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
        A a = applicationContext.getBean(A.class);
        // 我们发现,虽然我们给B设置成`prototype`,但是从a里面get出来的每次都还是同一个对象
        System.out.println(a.getB() == a.getB()); //true
        //证明B不是单例了:
        B b1 = applicationContext.getBean(B.class);
        B b2 = applicationContext.getBean(B.class);
        //证明B并不是单例的
        System.out.println(b1 == b2); //false
    }


现在,这个没达到我们的需求嘛。因为每次A类里面使用B的时候,还是同一个实例~


原因:因为A是单例,它只会被实例化一次,因此对应的属性也会被注入一次。所以即使你get()调用了多次,返回的还是第一次赋值的那个属性值


怎么办呢?、这个时候方法注入闪亮登场,我们只需要在A的get方法上加上@Lookup注解,在看看效果:

@Component
public class A {
    @Autowired
    private B b;
    @Lookup // 采用方法注入
    public B getB() {
        return b;
    }
}
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
        A a = applicationContext.getBean(A.class);
        // 不仅仅每次getB都不一样了,我们还发现a已经变成了一个CGLIB的代理对象
        System.out.println(a); //com.fsx.bean.A$$EnhancerBySpringCGLIB$$558725dc@6a6cb05c
        System.out.println(a.getB() == a.getB()); //false
    }


一下子就解决问题了。在上面分析的时候我们看到AbstractAutowireCapableBeanFactory默认采用的创建Bean策略器为


private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();



因此默认就是支持方法注入的,所以当我们方法标注了@Lookup注解,就能达到我们上诉的效果了~


了解了此处,对后续AOP原理讲解的时候,也有非常大的帮助~~~ 看来提前讲解还是不亏的,哈哈


总结


AutowireCapableBeanFactory接口给我们最大的感觉就是:交给Spring管理的Bean,关于Bean的创建、实例化、初始化等都是自动挡。而我们使用此接口可以拆解成手动档,自己来控制Bean的一些行为。


能够有如此的灵活性,还是得益于Spring的分层设计、组件化的、可插拔等等一些设计特针。再次向Spring的设计者致敬,给这个行业做出的巨大贡献~


另外本文分析中,还有些小细节是需要注意的:比如@Autowired注解不是必须的,交给Spring容器管理也不是必须的,所以建议能够了解到每段代码、注解、处理器的具体执行时机,很多问题就迎刃而解了。

相关文章
|
5月前
|
XML Java 数据格式
Spring IoC容器的设计与实现
Spring 是一个功能强大且模块化的 Java 开发框架,其核心架构围绕 IoC 容器、AOP、数据访问与集成、Web 层支持等展开。其中,`BeanFactory` 和 `ApplicationContext` 是 Spring 容器的核心组件,分别定位为基础容器和高级容器,前者提供轻量级的 Bean 管理,后者扩展了事件发布、国际化等功能。
|
4月前
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
499 0
|
6月前
|
SQL 前端开发 Java
深入分析 Spring Boot 项目开发中的常见问题与解决方案
本文深入分析了Spring Boot项目开发中的常见问题与解决方案,涵盖视图路径冲突(Circular View Path)、ECharts图表数据异常及SQL唯一约束冲突等典型场景。通过实际案例剖析问题成因,并提供具体解决方法,如优化视图解析器配置、改进数据查询逻辑以及合理使用外键约束。同时复习了Spring MVC视图解析原理与数据库完整性知识,强调细节处理和数据验证的重要性,为开发者提供实用参考。
259 0
|
10月前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
269 6
|
10月前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
147 14
|
10月前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
196 1
|
12月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
2566 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
11月前
|
运维 持续交付 虚拟化
深入解析Docker容器化技术的核心原理
深入解析Docker容器化技术的核心原理
231 1
|
12月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
12月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
1061 2

相关产品

  • 容器服务Kubernetes版
  • 推荐镜像

    更多