轻松理解Spring中Bean的配置

SpingBean

Bean对象由IoC容器根据配置信息创建,是构成应用程序的基础。配置信息称为元数据,Spring框架规定了一系列可设置的元数据属性,Spring官方文档说明了所有可配置的属性1

PropertyExplained in…DESC
ClassInstantiating Beans必选项,用来设定Bean的类型
NameNaming BeansBean的唯一标识,xml文件中可以使用id/name
ScopeBean Scopes设置Bean的作用域
Constructor argumentsDependency Injection设定构造函数注入的参数
PropertiesDependency Injection设定Bean对象的属性
Autowiring modeAutowiring Collaborators设定注入模式
Lazy initialization modeLazy-initialized Beans是否懒加载
Initialization methodInitialization Callbacks设定对象初始化方法
Destruction methodDestruction Callbacks设定对象销毁方法
表1:Bean对象可配置的属性
Bean和IoC容器的关系

程序启动时IoC容器会通过配置文件读取元数据,保存在 BeanFactory 对象的 beanDefinitionMap 属性中, beanDefinitionMap 是一个 ConcurrentHashMap 类对象,容器根据其中保存的元数据信息,创建相应的Bean对象。创建出来的Bean对象根据配置不同保存到不同的位置,默认状态的Bean都是单例对象,保存在 singletonObjects 属性中。下图是对这个过程的简单描述:2
在这里插入图片描述

图2:IoC容器创建Bean的过程

XML配置文件中定义Bean对象的方式如下:

<?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"
>
		<!-- 创建 Bean 对象,所有属性使用默认值 -->
    <bean name="Bean" class="cn.sunyog.entity.XMLBean">
      	<!-- 设置对象的属性值 -->
        <property name="message" value="默认设置的 Bean"/>
    </bean>
</beans>

Bean的类定义如下:

public class XMLBean {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

执行测试代码:

public static void main(String[] args){
    ApplicationContext context = new ClassPathXmlApplicationContext("/config/beans.xml");
    XMLBean bean = (XMLBean) context.getBean("Bean");//断点
    System.out.println(bean);
    System.out.println(bean.getMessage());
}

打印结果:

cn.sunyog.entity.XMLBean@45752059
默认设置的 Bean

使用以上代码,通过debug的方式进入断点,查看Bean的定义信息和对象保存位置如下:

在这里插入图片描述

图3:元数据信息保存位置

在这里插入图片描述

图4:Bean对象保存位置
Bean的作用域

Spring官方文档中规定了Bean的作用域所有可选值,包括:singleton、prototype、request、session、application、websocket六种。具体描述如下3

ScopeDescription
singleton默认作用域,单例模式,整个容器中只有一个Bean对象
prototype每次使用时创建新对象,Bean使用完成后销毁
request每个HTTP请求包含一个自己专属的Bean对象。只能在web程序中使用。只支持ApplicationContext容器
session每个 HTTP Session包含一个自己专属的Bean对象。只能在web程序中使用。只支持ApplicationContext容器
application针对ServletContext创建的Bean对象。只能在web程序中使用。只支持ApplicationContext容器
websocket针对WebSocket创建的Bean对象。只能在web程序中使用。只支持ApplicationContext容器
图2:Bean的作用域说明表
1.XML文件方式配置Bean

类定义代码:

public class XMLBean{
    private String message;
		//无参构造
  	public XMLBean() {
        System.out.println("构造:"+this);
    }
  
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
		//配置文件中说明的初始化方法
    public void initFunc(){
        System.out.println("Bean 初始化方法执行");
    }
		//配置文件中说明的销毁方法
    public void destroyFund(){
        System.out.println("Bean 销毁方法执行");
    }
}

XML文件配置信息:

<?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"
>
		<bean id="Singleton-Bean" class="cn.sunyog.bean.XMLBean"
          lazy-init="true" scope="singleton" 
          init-method="initFunc" destroy-method="destroyFund">
        <property name="message" value="XML Bean,懒加载,singleton模式,已设定初始化方法,已设定销毁方法" />
    </bean>
</beans>

通过 junit 测试框架测试各各个Bean的创建顺序

public class XMLBeanFactoryTest {
    private ClassPathXmlApplicationContext context = null;
  
    @Test
    public void singletonBeanTest() {
      	//获取两次 单例懒加载bean
        XMLBean bean = (XMLBean) this.context.getBean("Singleton-Bean");
        XMLBean bean2 = (XMLBean) this.context.getBean("Singleton-Bean");
      	//获取一次默认Bean
        XMLBean bean3 = (XMLBean) this.context.getBean("Bean");
				//打印Bean对象内存地址和message属性
        printBeanInfo(bean, bean2, bean3);
    }

    private void printBeanInfo(XMLBean... bean) {
        for (XMLBean item : bean) {
            System.out.println(item);
        }
        for (XMLBean item : bean) {
            System.out.println(item.getMessage());
        }
    }

    @Before
    public void doBefore() {
        this.context = new ClassPathXmlApplicationContext("/config/beans.xml");
    }

    @After
    public void doAfter() {
        //销毁所有bean
        this.context.registerShutdownHook();
    }
}

结果打印及说明:

构造:cn.sunyog.bean.XMLBean@2758fe70
构造:cn.sunyog.bean.XMLBean@2db7a79b
Bean 初始化方法执行
cn.sunyog.bean.XMLBean@2db7a79b
cn.sunyog.bean.XMLBean@2db7a79b
cn.sunyog.bean.XMLBean@2758fe70
XML Bean,懒加载,singleton模式,已设定初始化方法,已设定销毁方法
XML Bean,懒加载,singleton模式,已设定初始化方法,已设定销毁方法
默认设置的 Bean


Bean 销毁方法执行

测试代码中调用三次 getBean() 方法,但打印结果结果显示只创建了两个对象。由于名称为 Singleton-Bean 的Bean配置了单例作用域,所以两次获取的Bean其实是同一个对象,因此分别只调用了一次 initFunc() 方法和 destroyFunc() 方法。这里需要注意prototype模式下,Bean对象使用完成后会自动回收,而不是通过IoC容器回收,所以设定的销毁方法会失效。

另外,测试代码中先获取的 Singleton-Bean 对象,后获取的 Bean 对象,但创建顺序是先创建 Bean 对象,后创建 Singleton-Bean 对象,这是由于 Singleton-Bean 设置了lazy-init属性为true,只有调用 get Bean() 方法时才创建这个Bean。

2.注解方式配置Bean
@Configuration
public class AnnoBeanConfig{
    @Bean(name = "Anno-Bean",initMethod = "initFunc",destroyMethod = "destroyFunc")
    @Scope("singleton") //作用域
    @Lazy   //懒加载
    public AnnotationBean getAnnotationBean(){
        AnnotationBean bean = new AnnotationBean();
        bean.setMessage("基于注解的配置");
        return bean;
    }
}

测试代码:

public class AnnoBeanFactoryTest {
    AnnotationConfigApplicationContext context=null;

    @Before
    public void doBefore(){
        this.context=new AnnotationConfigApplicationContext(AnnoBeanConfig.class);
    }

    @After
    public void doAfter(){
        this.context.registerShutdownHook();
    }

    @Test
    public void test(){
        AnnotationBean bean = (AnnotationBean) context.getBean("Anno-Bean");
        this.printBeanInfo(bean);
    }

    private void printBeanInfo(AnnotationBean... bean) {
        for (AnnotationBean item : bean) {
            System.out.println(item);
        }
        for (AnnotationBean item : bean) {
            System.out.println(item.getMessage());
        }
    }
}

打印结果:

构造:cn.sunyog.bean.AnnotationBean@21be3395
注解配置的初始化方法
cn.sunyog.bean.AnnotationBean@21be3395
基于注解的配置


注解配置的销毁方法
Bean的生命周期

另一种设定Bean的初始化和销毁回调方法的方式。在定义Bean时,实现 InitializingBean 接口的 afterPropertiesSet() 方法来设定初始化回调;实现 DisposableBean接口的 destroy() 方法来设定销毁回调方法。

配置代码:

@Bean(name="Simple-Bean",initMethod = "initFunc",destroyMethod = "destroyFunc")
public SimpleBean getSimpleBean(){
    SimpleBean bean = new SimpleBean();
    bean.setMessage("实现初始化回调和销毁回调方法");
    return bean;
}

Bean代码:

public class SimpleBean extends AnnotationBean implements InitializingBean, DisposableBean {
    public void destroy() throws Exception {
        System.out.println("实现 DisposableBean 接口的 销毁回调 方法");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("实现 InitializingBean 接口的 初始化回调 方法");
    }
}

测试代码:

@Test
public void testSimpleBean(){
    SimpleBean bean = (SimpleBean) context.getBean("Simple-Bean");
    this.printBeanInfo(bean);
}

打印结果:

构造:cn.sunyog.bean.SimpleBean@16e7dcfd
实现 InitializingBean 接口的 初始化回调 方法
注解配置的初始化方法
cn.sunyog.bean.SimpleBean@16e7dcfd
实现初始化回调和销毁回调方法


实现 DisposableBean 接口的 销毁回调 方法
注解配置的销毁方法

也可以通过配置后置处理器的方式设定初始化前、后处理方法,这种方式需要实现 BeanPostProcessor 接口的 postProcessBeforeInitialization()postProcessAfterInitialization() 方法,后置处理器类代码如下:

public class PostProcessorBean implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      	//只处理SimpleBean类型
        if (bean instanceof SimpleBean){
            System.out.println(beanName+": 前置处理方法 执行");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof SimpleBean){
            System.out.println(beanName+": 后置处理方法 执行");
        }
        return bean;
    }
}

配置类代码:

@Bean
public BeanPostProcessor getPostProcessorBean(){
    return new PostProcessorBean();
}

执行 SimpleBean 类的测试代码,得到打印结果(和SimpleBean的测试打印结果略有不同):

构造:cn.sunyog.bean.SimpleBean@79efed2d
Simple-Bean: 前置处理方法 执行
实现 InitializingBean 接口的 初始化回调 方法
注解配置的初始化方法
Simple-Bean: 后置处理方法 执行
cn.sunyog.bean.SimpleBean@79efed2d
实现初始化回调和销毁回调方法


实现 DisposableBean 接口的 销毁回调 方法
注解配置的销毁方法

Bean的整个生命周期的方法调用顺序如下图,图中直角方框表示应用程序调用,圆角方框表示容器调用:

在这里插入图片描述

图5:Bean的生命周期方法调用顺序
依赖注入DI

依赖注入是Spring框架的核心功能,通过DI来管理Bean之间的依赖关系。有构造函数注入和set方法注入两种方式实现,构造函数注入使用含参构造函数创建Bean对象,set方法注入调用无参构造函数创建Bean对象或使用无参的工厂方法构造Bean对象,并使用setter方法赋值。

构造方法注入

Bean类代码:

public class AutowireBean {
    private XMLBean child;

    public AutowireBean(XMLBean child) {
        this.child = child;
        System.out.println("AutowireBean类 含参构造函数执行");
    }

    public AutowireBean() {
        System.out.println("AutowireBean类 无参构造函数执行");
    }

    public XMLBean getChild() {
        return child;
    }

    public void setChild(XMLBean child) {
        this.child = child;
        System.out.println("AutowireBean类 setter函数执行");
    }
}

XML配置文件代码:

<bean name="Bean" class="cn.sunyog.bean.XMLBean">
    <property name="message" value="默认设置的 Bean"/>
</bean>

<bean id="cons-autowire-bean" class="cn.sunyog.bean.AutowireBean" lazy-init="true">
    <constructor-arg name="child" ref="Bean" />
</bean>

测试代码:

@Test
public void consAutowireBeanTest(){
    AutowireBean bean = (AutowireBean) this.context.getBean("cons-autowire-bean");
    PrintTool.printBeanInfo(bean);
}

/**
 * 打印工具类
 */
public class PrintTool {
    public static void printBeanInfo(XMLBean... bean) {
        for (XMLBean item : bean) {
            System.out.println(item);
        }
        for (XMLBean item : bean) {
            System.out.println(item.getMessage());
        }
    }

    public static void printBeanInfo(AnnotationBean... bean) {
        for (AnnotationBean item : bean) {
            System.out.println(item);
        }
        for (AnnotationBean item : bean) {
            System.out.println(item.getMessage());
        }
    }

    public static void printBeanInfo(AutowireBean... bean){
        for (AutowireBean item : bean) {
            XMLBean child = item.getChild();
            printBeanInfo(child);
        }
    }
}

结果打印:

构造:cn.sunyog.bean.XMLBean@79be0360
AutowireBean类 含参构造函数执行
cn.sunyog.bean.XMLBean@79be0360
默认设置的 Bean

set注入

XML配置代码:

<bean id="set-autowire-bean" class="cn.sunyog.bean.AutowireBean" lazy-init="true">
    <property name="child" ref="Bean" />
</bean>

测试代码:

@Test
public void setAutowireBeanTest(){
    AutowireBean bean = (AutowireBean) this.context.getBean("set-autowire-bean");
    PrintTool.printBeanInfo(bean);
}

结果打印:

构造:cn.sunyog.bean.XMLBean@79be0360
AutowireBean类 无参构造函数执行
AutowireBean类 setter函数执行
cn.sunyog.bean.XMLBean@79be0360
默认设置的 Bean

Spring的自动装配

Spring的自动装配(autowire)在配置文件中设置了bean的autowire属性后生效,auto-wire属性有三种模式,分别是byName(按名称匹配),byType(按类型匹配),constructor(构造函数自动装配)。其中,前两种分别按名称和类型从容器中查找符合条件的Bean,找到后通过set方法入,查找失败会抛出异常;constructor模式则通过构造方法注入。

注意,在byType模式下要保证这种类型的Bean只进行了一次配置。

三种模式xml配置如下:

<bean name="child" class="cn.sunyog.bean.XMLBean">
    <property name="message" value="默认设置的 Bean"/>
</bean>
<bean id="autowire-name-bean" class="cn.sunyog.bean.AutowireBean" lazy-init="true" autowire="byName" />
<bean id="autowire-type-bean" class="cn.sunyog.bean.AutowireBean" lazy-init="true" autowire="byType" />
<bean id="autowire-cons-bean" class="cn.sunyog.bean.AutowireBean" lazy-init="true" autowire="constructor" />

测试代码:

@Test
public void autowireTest(){
    AutowireBean bean1 = (AutowireBean) this.context.getBean("autowire-name-bean");
    AutowireBean bean2 = (AutowireBean) this.context.getBean("autowire-type-bean");
    AutowireBean bean3 = (AutowireBean) this.context.getBean("autowire-cons-bean");
}

结果打印:

构造:cn.sunyog.bean.XMLBean@1f36e637
AutowireBean类 无参构造函数执行
AutowireBean类 setter函数执行
AutowireBean类 无参构造函数执行
AutowireBean类 setter函数执行
AutowireBean类 含参构造函数执行

【注】原创文章,欢迎点赞、转发、评论。转载请先与作者联系


  1. Spring Core官方文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-class ↩︎

  2. 图片来源:https://www.w3cschool.cn/wkspring/8kei1icc.html ↩︎

  3. 表格来源:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-scopes ↩︎

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李奇技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值