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

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

前言


咋一看标题,小伙伴们是否还有点小激动呢?觉得这怎么可能呢?


可能我们(大众)都是这样认为:自从用上了Spring这个优秀的框架,一般小伙伴们都是一言不合就把对象塞进Spring的IOC容器里面,交给它来替我们管理。


不可否认的是,把Bean交给Spring管理,确实极其的方便,优点一大把,并且还几乎没有啥缺点。这也就是为何咱们一言不合就把Bean扔给Spring的原因。(在Spring的技术栈里这么做,完全没有问题)


然而,就Spring框架本身而言。它的强大的依赖注入,不仅仅能给自家的Bean使用,还能赋能给容器之外的Bean,快速的把需要注入的对象给它装配好。


本来我也一直以为你想用Spring的依赖注入功能,就得交给Spring容器进行管理。直到我上周看AutowireCapableBeanFactory源码的时候,上面的JavaDoc就清晰的写到了,它还可以为非容器内的Bean服务~


注意:原对象可以不在Spring的IOC容器里,但是需要被依赖注入的成员,就必须是Spring容器管辖的Bean

本篇文章实际用处可能较少(可能在继承某些特殊的第三方框架的时候需要),但是掌握了本篇文章的内容,能让你更加清晰的了解到Spring依赖注入的原理(一般应用开发者不会使用这个接口,但如果你是框架设计者,你有必要了解这个接口)


我们使用的应用上下文ApplicationContext它就提供了

AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

虽然它不直接继承,但是它允许你拿这个工具去做你需要的事。~~~


Spring源码基于的Spring版本为:5.0.6.RELEASE(下同)

Spring源码基于的Spring版本为:5.0.6.RELEASE(下同)

Spring源码基于的Spring版本为:5.0.6.RELEASE(下同)


AutowireCapableBeanFactory 的解释


 * Extension of the {@link org.springframework.beans.factory.BeanFactory}
 * interface to be implemented by bean factories that are capable of
 * autowiring, provided that they want to expose this functionality for
 * existing bean instances.


AutowireCapableBeanFactory在BeanFactory基础上实现了对存在实例的管理.可以使用这个接口集成其它框架,捆绑并填充并不由Spring管理生命周期并已存在的实例.像集成WebWork的Actions 和Tapestry Page就很实用.


public interface AutowireCapableBeanFactory extends BeanFactory {
  int AUTOWIRE_NO = 0;
  int AUTOWIRE_BY_NAME = 1;
  int AUTOWIRE_BY_TYPE = 2;
  int AUTOWIRE_CONSTRUCTOR = 3;
  @Deprecated
  int AUTOWIRE_AUTODETECT = 4;
  // 注意这个CreateBean和下面的CrateBean的不同
  //JavaDoc:It does <i>not</> imply traditional by-name or by-type autowiring of properties;
  // 也就是说它只管给你创建Bean,但是不管给你根据Name或者Type进行注入哦
  // 当然,你可以显示在对应属性上指定@Autowired注解,让他也可以达到相同的效果
  <T> T createBean(Class<T> beanClass) throws BeansException;
  void autowireBean(Object existingBean) throws BeansException;
  Object configureBean(Object existingBean, String beanName) throws BeansException;
  Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
  Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
  void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
      throws BeansException;
  void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;
  Object initializeBean(Object existingBean, String beanName) throws BeansException;
  Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
      throws BeansException;
  Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
      throws BeansException;
  void destroyBean(Object existingBean);
  <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;
  @Nullable
  Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;
  @Nullable
  Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;
}


这里就不解释各个方法了,具体参考:

【小家Spring】BeanFactory体系和ApplicationContext体系,两大体系各接口分析、区别和联系


Demo Show


说得可能还是一头雾水,上个例子先看看效果吧:


以createBean() 方法为例
// 准备一个Child类
@Getter
@Setter
public class Child {
  // 注意:这里并没有@Autowired注解的
    private HelloService helloService;
    private String name;
    private Integer age;
}
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
        // ApplicationContext里面是持久AutowireCapableBeanFactory这个工具的,它真实的实现类一般都是:DefaultListableBeanFactory
        AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
        // 我们吧Child的创建过程都交给Bean工厂去帮我们处理,自己连new都不需要了 (createBean方法执行多次,就会创建多个child实例)
        Child child = (Child) autowireCapableBeanFactory.createBean(Child.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
        //简直残暴,没有@Autowired注解都给注入进来了~~~  至于为什么,看看下面的分析,你就知道了
        System.out.println(child.getHelloService()); //com.fsx.service.HelloServiceImpl@6a78afa0
        // 抛出异常 No qualifying bean of type 'com.fsx.bean.Child' available 
        // 能佐证:我们的Bean并没交给Spring容器管理,它只是帮我们创建好了,并把对应属性注入进去了
        Child bean = applicationContext.getBean(Child.class);
        System.out.println(bean);
    }


看到这个现象有没有很惊喜,我们哪怕不是Spring去管理的对象,都能够依赖注入进来容器内的对象,并且,并且连@Autowired注解都不需要。所以更别谈Spring内部的容器,并且还标注了注解的,那就应该更容易去实现了


所以,了解了本文后,再回过头去看看Spring内部的自动化的依赖注入,就会说一句:也就那样嘛,哈哈~


以createBean() 方法为例的源码分析



在上面Demo Show的基础上,我们来看看源码,到底是怎么做到的。

来到AbstractAutowireCapableBeanFactory#createBean:


  @Override
  public Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException {
    // Use non-singleton bean definition, to avoid registering bean as dependent bean.
    RootBeanDefinition bd = new RootBeanDefinition(beanClass,  autowireMode, dependencyCheck);
    // 这里看到了,采用的不是单例,而是prototype
    bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
    // For the nullability warning, see the elaboration in AbstractBeanFactory.doGetBean;
    // in short: This is never going to be null unless user-declared code enforces null.
    // doc说得很明白,这里返回值永远不可能为null。除非调用者强制return null
    // 注意的是:这里BeanName就是beanClass.getName()
    return createBean(beanClass.getName(), bd, null);
  }
// 最终都调用到了下面这个createBean方法。它也是AbstractBeanFactory提供的一个抽象方法
// 最终也由AbstractAutowireCapableBeanFactory去实现的。 我们熟悉的doGetBean()方法,最终也是调用它来创建实例对象  只是doGetBean()把单例对象都缓存起来了
// 这个方法很单纯:创建一个实例,然后初始化他(给属性们赋值),然后return出去即可
  @Override
  protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
    if (logger.isDebugEnabled()) {
      logger.debug("Creating instance of bean '" + beanName + "'");
    }
    RootBeanDefinition mbdToUse = mbd;
    // Make sure bean class is actually resolved at this point, and
    // clone the bean definition in case of a dynamically resolved Class
    // which cannot be stored in the shared merged bean definition.
    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
    if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
    }
    // Prepare method overrides.
    // 解析一些@lookup注解之类的  忽略
    try {
      mbdToUse.prepareMethodOverrides();
    }
    catch (BeanDefinitionValidationException ex) {
      throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
          beanName, "Validation of method overrides failed", ex);
    }
    try {
      // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
      // 这个之前都解释过了,就不解释了。若BeanPostProcessors 产生了一个代理对象,就不需要我去创建了,就不继续往下走了(AOP都走这里)
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      if (bean != null) {
        return bean;
      }
    }
    catch (Throwable ex) {
      throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
          "BeanPostProcessor before instantiation of bean failed", ex);
    }
    try {
      // 重点来了。它是本类的一个protected方法,专门用于处理创建Bean的过程(包括属性赋值之类的)
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isDebugEnabled()) {
        logger.debug("Finished creating instance of bean '" + beanName + "'");
      }
      return beanInstance;
    }
    catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
      // A previously detected exception with proper bean creation context already,
      // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
      throw ex;
    }
    catch (Throwable ex) {
      throw new BeanCreationException(
          mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
    }
  }


这里再看看doCreateBean吧,因为之前有讲过这个方法,这里就看看止看看核心步骤。


  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args){
  BeanWrapper instanceWrapper = null;
  ...
  //createBeanInstance 是重点(都是返回一个BeanWrapper)  它也是本类的一个protected方法
  instanceWrapper = createBeanInstance(beanName, mbd, args);
  // Bean已经实例化好了,准备初始化吧
  ...
  // 执行MergedBeanDefinitionPostProcessor
  // 处理循环引用,现在若我们Bean不在容器里,肯定是不存在循环引用的(但是我依赖的Bean可能还没创建是真的,也是这里来处理的)
  ...
  // 给Bean实例的各个属性进行赋值 比如调用InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation、给属性注入值(并不依赖于@Autowired注解)
  // 执行InstantiationAwareBeanPostProcessor#postProcessPropertyValues等等
  populateBean(beanName, mbd, instanceWrapper);
  // 初始化Bean 执行一些初始化方法init @PostContruct方法等等 
  //BeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization等等
  exposedObject = initializeBean(beanName, exposedObject, mbd);
}

综上可以看出,目前最重要的三个步骤为doCreateBean里面的:createBeanInstancepopulateBeaninitializeBean,都在AbstractAutowireCapableBeanFactory这里


相关文章
|
15天前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
98 26
|
2天前
|
XML Java 数据格式
Spring容器的本质
本文主要讨论Spring容器最核心的机制,用最少的代码讲清楚Spring容器的本质。
|
2月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
2月前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
2月前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
73 6
|
2月前
|
Java 数据库 数据安全/隐私保护
轻松掌握Spring依赖注入:打造你的登录验证系统
本文以轻松活泼的风格,带领读者走进Spring框架中的依赖注入和登录验证的世界。通过详细的步骤和代码示例,我们从DAO层的创建到Service层的实现,再到Spring配置文件的编写,最后通过测试类验证功能,一步步构建了一个简单的登录验证系统。文章不仅提供了实用的技术指导,还以口语化和生动的语言,让学习变得不再枯燥。
55 2
|
2月前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
50 1
|
3月前
|
前端开发 Java Docker
使用Docker容器化部署Spring Boot应用程序
使用Docker容器化部署Spring Boot应用程序
|
3月前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
71 0
|
5天前
|
Ubuntu API 网络虚拟化
ubuntu22 编译安装docker,和docker容器方式安装 deepseek
本脚本适用于Ubuntu 22.04,主要功能包括编译安装Docker和安装DeepSeek模型。首先通过Apt源配置安装Docker,确保网络稳定(建议使用VPN)。接着下载并配置Docker二进制文件,创建Docker用户组并设置守护进程。随后拉取Debian 12镜像,安装系统必备工具,配置Ollama模型管理器,并最终部署和运行DeepSeek模型,提供API接口进行交互测试。
93 15

相关产品

  • 容器服务Kubernetes版