【小家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容器管理也不是必须的,所以建议能够了解到每段代码、注解、处理器的具体执行时机,很多问题就迎刃而解了。

相关文章
|
14天前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
96 26
|
2月前
|
XML Java 数据格式
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
116 69
|
2天前
|
XML Java 数据格式
Spring容器的本质
本文主要讨论Spring容器最核心的机制,用最少的代码讲清楚Spring容器的本质。
|
2月前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
60 21
|
4天前
|
Ubuntu API 网络虚拟化
ubuntu22 编译安装docker,和docker容器方式安装 deepseek
本脚本适用于Ubuntu 22.04,主要功能包括编译安装Docker和安装DeepSeek模型。首先通过Apt源配置安装Docker,确保网络稳定(建议使用VPN)。接着下载并配置Docker二进制文件,创建Docker用户组并设置守护进程。随后拉取Debian 12镜像,安装系统必备工具,配置Ollama模型管理器,并最终部署和运行DeepSeek模型,提供API接口进行交互测试。
90 15
|
2月前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
299 78
|
1月前
|
Ubuntu NoSQL Linux
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
157 6
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
|
2月前
|
监控 Docker 容器
在Docker容器中运行打包好的应用程序
在Docker容器中运行打包好的应用程序
|
2月前
|
Ubuntu Linux 开发工具
docker 是什么?docker初认识之如何部署docker-优雅草后续将会把产品发布部署至docker容器中-因此会出相关系列文章-优雅草央千澈
Docker 是一个开源的容器化平台,允许开发者将应用程序及其依赖项打包成标准化单元(容器),确保在任何支持 Docker 的操作系统上一致运行。容器共享主机内核,提供轻量级、高效的执行环境。本文介绍如何在 Ubuntu 上安装 Docker,并通过简单步骤验证安装成功。后续文章将探讨使用 Docker 部署开源项目。优雅草央千澈 源、安装 Docker 包、验证安装 - 适用场景:开发、测试、生产环境 通过以上步骤,您可以在 Ubuntu 系统上成功安装并运行 Docker,为后续的应用部署打下基础。
96 8
docker 是什么?docker初认识之如何部署docker-优雅草后续将会把产品发布部署至docker容器中-因此会出相关系列文章-优雅草央千澈
|
2月前
|
存储 Kubernetes 开发者
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
Docker 是一种开源的应用容器引擎,允许开发者将应用程序及其依赖打包成可移植的镜像,并在任何支持 Docker 的平台上运行。其核心概念包括镜像、容器和仓库。镜像是只读的文件系统,容器是镜像的运行实例,仓库用于存储和分发镜像。Kubernetes(k8s)则是容器集群管理系统,提供自动化部署、扩展和维护等功能,支持服务发现、负载均衡、自动伸缩等特性。两者结合使用,可以实现高效的容器化应用管理和运维。Docker 主要用于单主机上的容器管理,而 Kubernetes 则专注于跨多主机的容器编排与调度。尽管 k8s 逐渐减少了对 Docker 作为容器运行时的支持,但 Doc
172 5
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档

相关产品

  • 容器服务Kubernetes版