Spring Beans初始化

Spring Framework框架中,各个对象都被当为Bean来进行管理,具体由IoC容器来进行初始化、存储及销毁等。

IoC容器内部,这类Bean具体由BeanDefinition来表示,该对象定义了如下信息:

  • 全路径类名
  • Bean的行为特性(如:scope范围、生命周期回调等)
  • Bean的依赖信息
  • 其它配置信息

1. Bean的定义

Spring Framework框架中,每一个Bean都有一个或多个唯一的标识。可以使用传统的XML的形式配置Bean,也可以使用Java Config的形式来配置Bean

XML的元信息配置中,可以使用idname来标识Bean。其中,name可以设置多个值,用,;或空格隔开。不指定idname时,Spring会生成一个默认的标识。

自动生成Bean的唯一标识,遵循Java标准规则,即使用java.beans.Introspector#decapitalize方法,若前两个字母都是大写,则不会变换,否则,将首字母小写

2. 初始化Bean

Bean的初始化主要有以下三种方式:

  • 构造方法
  • 静态工厂方法
  • 工厂方法

首先,来看构造器初始化的方式
新建maven工程,并引入Spring Framework相关依赖,如下:

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <springFramework.version>5.2.0.RELEASE</springFramework.version>
    </properties>

     <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${springFramework.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.2</version>
                <scope>test</scope>
            </dependency>
     </dependencies>

引入spring-context模块实际会引入Spring Framework好几个模块,如:spring-corespring-beansspring-aopspring-expression等。

2.1 构造器方式

首先来看XML的形式,创建一个普通的PoJo对象,如下:

package com.vielat.springframework.bean.service;

/**
 * TODO
 *
 * @author Michael
 * @since 2021/12/3
 */
public class ConstructorClientService {

}

src/main/resources目录下新建META-INF/applicationContext.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
       https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="service" class="com.vielat.springframework.bean.service.ConstructorClientService" />

</beans>

src/test/java目录下编写Junit测试类,如下:

public class XmlApplicationDemo {

    @Test
    public void testConstructorInitializing(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("META-INF/applicationContext.xml");
        System.out.println(ac.getBean("service"));
    }
}

运行Junit测试类后,执行结果如下:


com.vielat.springframework.bean.service.ConstructorClientService@77ec78b9

Process finished with exit code 0

若为该PoJo添加有参的构造函数,如下:

public class ConstructorClientService {

    public ConstructorClientService(String name){

    }
}

再次执行Junit测试类,则会抛出如下异常:

十二月 03, 2021 7:57:00 上午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'service' defined in class path resource [META-INF/applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.vielat.springframework.bean.service.ConstructorClientService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.vielat.springframework.bean.service.ConstructorClientService.<init>()

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'service' defined in class path resource [META-INF/applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.vielat.springframework.bean.service.ConstructorClientService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.vielat.springframework.bean.service.ConstructorClientService.<init>()

	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1320)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1214)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
	at com.vielat.springframework.bean.XmlApplicationDemo.testConstructorInitializing(XmlApplicationDemo.java:27)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.vielat.springframework.bean.service.ConstructorClientService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.vielat.springframework.bean.service.ConstructorClientService.<init>()
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:83)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1312)
	... 38 more
Caused by: java.lang.NoSuchMethodException: com.vielat.springframework.bean.service.ConstructorClientService.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.getDeclaredConstructor(Class.java:2178)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:78)
	... 39 more

由上述日志可以看到,在XML中的bean标签若只配置id和class属性(其中id属性可以省略),会直接使用class对应对象的无参构造器进行初始化,若该对象没有提供无参构造器,则会抛出异常。若想使用带参数的构造器进行初始化,需要在XML配置中进行相应的配置,如下:

...
  <bean id="service" class="com.vielat.springframework.bean.service.ConstructorClientService">
        <constructor-arg name="name" value="Michael" />
  </bean>

上述配置,除了使用name字段属性,还可以使用index下标的方式进行指定。
除此之外,在Spring Framework 3.1之后,还提供一种更加简化的命名空间的形式,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="service" class="com.vielat.springframework.bean.service.ConstructorClientService">
        <constructor-arg name="name" value="Michael" />
    </bean>

    <bean id="service2" class="com.vielat.springframework.bean.service.ConstructorClientService" c:name="Michael">
    </bean>

</beans>

需要在beans签名上新增xmlns:c="http://www.springframework.org/schema/c",在指定构造参数时可以直接配置为c:属性名的形式。

2.2 静态工厂方式

编写一个静态工厂类,如下:

public class ClientService {

    private static final ClientService service = new ClientService();

    public static ClientService getInstance() {
        return service;
    }

    public ClientService getService(){
        return service;
    }
}

XML配置信息如下:

 <bean name="clientService" class="com.vielat.springframework.bean.service.ClientService" factory-method="getInstance" />

测试类如下:

    @Test
    public void testStaticFactoryInitializing(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("META-INF/application.xml");
        System.out.println(ac.getBean("clientService"));
    }

2.3 工厂方法

与静态工厂方法类似,也是通过FactoryBean来初始化对象,只不过初始化方法没有static进行修饰。
工厂方法定义如下:

public class DefaultServiceLocator {
    public ClientService createClientService() {
        return new ClientService();
    }
}

XML定义如下:

  <bean id="defaultServiceLocator" class="com.vielat.springframework.bean.service.DefaultServiceLocator"/>
  <bean id="serviceLocator" factory-bean="defaultServiceLocator" factory-method="createClientService" />

测试类如下:

    @Test
    public void testFactoryInitializing() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("META-INF/applicationContext.xml");
        System.out.println(ac.getBean("serviceLocator"));
    }

无论是静态工厂还是普通工厂类,均可以初始化多个Bean,只需在工厂类中定义多个方法即可,代码略。这类工厂即被称之为FactoryBean

上一篇:prometheus全局配置解释


下一篇:HttpRunner(23):httprunner的请求证书验证