天天用 Spring,bean 实例化原理你懂吗?

简介: 本次主要想写spring bean的实例化相关的内容。创建spring bean 实例是spring bean 生命周期的第一阶段。bean 的生命周期主要有如下几个步骤:

本次主要想写spring bean的实例化相关的内容。创建spring bean 实例是spring bean 生命周期的第一阶段。


bean 的生命周期主要有如下几个步骤:


创建bean的实例


给实例化出来的bean填充属性


初始化bean


通过IOC容器使用bean


容器关闭时销毁bean


在实例化bean之前在BeanDefinition里头已经有了所有需要实例化时用到的元数据,接下来spring 只需要选择合适的实例化方法以及策略即可。实例化方法有两大类分别是工厂方法和构造方法实例化,后者是最常见的。


spring默认的实例化方法就是无参构造函数实例化。


如我们在xml里定义的  以及用注解标识的bean都是通过默认实例化方法实例化的。


两种实例化方法(构造函数 和 工厂方法)


源码阅读


实例化策略(cglib or 反射)


两种实例化方

使用适当的实例化方法为指定的bean创建新实例:工厂方法,构造函数实例化。


代码演示


启动容器时会实例化所有注册的bean(lazy-init懒加载的bean除外),对于所有单例非懒加载的bean来说当从容器里获取bean(getBean(String name))的时候不会触发,实例化阶段,而是直接从缓存获取已准备好的bean,而生成bean的时机则是下面这行代码运行时触发的。

@Test  
public void testBeanInstance(){          
    // 启动容器  
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");  
}   

一 使用工厂方法实例化(很少用)

1.静态工厂方法
public class FactoryInstance {      
    public FactoryInstance() {  
        System.out.println("instance by FactoryInstance");  
    }  
}  
public class MyBeanFactory {    public static FactoryInstance getInstanceStatic(){        return new FactoryInstance();  
    }  
}   
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">  
    <bean id="factoryInstance" class="spring.service.instance.MyBeanFactory" factory-method="getInstanceStatic"/>  
</beans>  

输出结果为:

instance by FactoryInstance

2.实例工厂方法
public class MyBeanFactory {      
    /**  
     * 实例工厂创建bean实例  
     *  
     * @return  
     */  
    public FactoryInstance getInstance() {        return new FactoryInstance();  
    }  
}  
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">  
    <!-- 工厂实例 -- >     
    <bean id="myBeanFactory" class="MyBeanFactory"/>  
    <bean id="factoryInstance" factory-bean="myBeanFactory" factory-method="getInstance"/>  
</beans>   

输出结果为:

instance by FactoryInstance

二 使用构造函数实例化(无参构造函数 & 有参构造函数)

1.无参构造函数实例化(默认的)
public class ConstructorInstance {      
    public ConstructorInstance() {  
        System.out.println("ConstructorInstance none args");  
    }   
}   
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">  
    <bean id="constructorInstance" class="spring.service.instance.ConstructorInstance"/>  
</beans>  

输出结果为:

ConstructorInstance none args

1.有参构造函数实例化
public class ConstructorInstance {      
    private String name;      
    public ConstructorInstance(String name) {  
        System.out.println("ConstructorInstance with args");          
        this.name = name;  
    }      
    public String getName() {          
        return name;  
    }      
    public void setName(String name) {          
        this.name = name;  
    }  
}  

我们这里只需关注第一步创建bean实例的流程即可

instanceWrapper = createBeanInstance(beanName, mbd, args);

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {  
  // Make sure bean class is actually resolved at this point.  
  Class<?> beanClass = resolveBeanClass(mbd, beanName);  
        // 使用工厂方法进行实例化  
  if (mbd.getFactoryMethodName() != null)  {  
   return instantiateUsingFactoryMethod(beanName, mbd, args);  
  }  
  // Need to determine the constructor...  
  Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);  
  // 使用带参构造函数初始化  
  if (ctors != null ||  
    mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||  
    mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {  
   return autowireConstructor(beanName, mbd, ctors, args);  
  }  
  // 默认实例化方式 无参构造实例化  
  return instantiateBean(beanName, mbd);  
}   

面代码就是spring 实现bean实例创建的核心代码。这一步主要根据BeanDefinition里的元数据定义决定使用哪种实例化方法,主要有下面三种:


instantiateUsingFactoryMethod 工厂方法实例化的具体实现


autowireConstructor 有参构造函数实例化的具体实现


instantiateBean 默认实例化具体实现(无参构造函数)


实例化策略(cglib or 反射)

工厂方法的实例化手段没有选择策略直接用了发射实现的

实例化策略都是对于构造函数实例化而言的

上面说到的两构造函数实例化方法不管是哪一种都会选一个实例化策略进行,到底选哪一种策略也是根据BeanDefinition里的定义决定的。

beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);   

上面这一行代码就是选择实例化策略的代码,进入到上面两种方法的实现之后发现都有这段代码。

下面选一个instantiateBean的实现来介绍

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {  
  try {  
   Object beanInstance;  
   final BeanFactory parent = this;  
   if (System.getSecurityManager() != null) {  
    beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {  
     @Override  
     public Object run() {  
      return getInstantiationStrategy().instantiate(mbd, beanName, parent);  
     }  
    }, getAccessControlContext());  
   }  
   else {  
       // 在这里选择一种策略进行实例化  
    beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);  
   }  
   BeanWrapper bw = new BeanWrapperImpl(beanInstance);  
   initBeanWrapper(bw);  
   return bw;  
  }  
  catch (Throwable ex) {  
   throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);  
  }  
}   

选择使用反射还是cglib


先判断如果beanDefinition.getMethodOverrides()为空也就是用户没有使用replace或者lookup的配置方法,那么直接使用反射的方式,简单快捷,但是如果使用了这两个特性,在直接使用反射的方式创建实例就不妥了,因为需要将这两个配置提供的功能切入进去,所以就必须要使用动态代理的方式将包含两个特性所对应的逻辑的拦截增强器设置进去,这样才可以保证在调用方法的时候会被相应的拦截器增强,返回值为包含拦截器的代理实例。---引用自《spring 源码深度剖析》这本书

<bean id="constructorInstance" class="spring.service.instance.ConstructorInstance" >        <lookup-method name="getName" bean="xxx"/>  
    <replaced-method name="getName" replacer="yyy"/>  
</bean>   

如果使用了lookup或者replaced的配置的话会使用cglib,否则直接使用反射。

具体lookup-methodreplaced-method的用法可以查阅相关资料。

public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {  
    // Don't override the class with CGLIB if no overrides.  
    if (bd.getMethodOverrides().isEmpty()) {  
      constructorToUse = clazz.getDeclaredConstructor((Class[]) null);  
      return BeanUtils.instantiateClass(constructorToUse);  
    }else {  
      // Must generate CGLIB subclass.  
      return instantiateWithMethodInjection(bd, beanName, owner);  
    }  
}


由于篇幅省略了部分代码。

相关文章
|
3月前
|
缓存 Java 开发者
【Spring】原理:Bean的作用域与生命周期
本文将围绕 Spring Bean 的作用域与生命周期展开深度剖析,系统梳理作用域的类型与应用场景、生命周期的关键阶段与扩展点,并结合实际案例揭示其底层实现原理,为开发者提供从理论到实践的完整指导。
519 22
|
3月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
1401 0
|
2月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
2月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
444 2
|
4月前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
|
5月前
|
缓存 安全 Java
Spring 框架核心原理与实践解析
本文详解 Spring 框架核心知识,包括 IOC(容器管理对象)与 DI(容器注入依赖),以及通过注解(如 @Service、@Autowired)声明 Bean 和注入依赖的方式。阐述了 Bean 的线程安全(默认单例可能有安全问题,需业务避免共享状态或设为 prototype)、作用域(@Scope 注解,常用 singleton、prototype 等)及完整生命周期(实例化、依赖注入、初始化、销毁等步骤)。 解析了循环依赖的解决机制(三级缓存)、AOP 的概念(公共逻辑抽为切面)、底层动态代理(JDK 与 Cglib 的区别)及项目应用(如日志记录)。介绍了事务的实现(基于 AOP
197 0
|
5月前
|
监控 架构师 NoSQL
spring 状态机 的使用 + 原理 + 源码学习 (图解+秒懂+史上最全)
spring 状态机 的使用 + 原理 + 源码学习 (图解+秒懂+史上最全)
|
5月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
968 0
|
6月前
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
723 0
|
2月前
|
JavaScript Java Maven
【SpringBoot(二)】带你认识Yaml配置文件类型、SpringMVC的资源访问路径 和 静态资源配置的原理!
SpringBoot专栏第二章,从本章开始正式进入SpringBoot的WEB阶段开发,本章先带你认识yaml配置文件和资源的路径配置原理,以方便在后面的文章中打下基础
337 3

热门文章

最新文章