在Spring Framework
框架中,各个对象都被当为Bean
来进行管理,具体由IoC
容器来进行初始化、存储及销毁等。
在IoC
容器内部,这类Bean
具体由BeanDefinition
来表示,该对象定义了如下信息:
Bean
的行为特性(如:scope范围、生命周期回调等)Bean
的依赖信息在Spring Framework
框架中,每一个Bean
都有一个或多个唯一的标识。可以使用传统的XML
的形式配置Bean
,也可以使用Java Config
的形式来配置Bean
。
在XML
的元信息配置中,可以使用id
或name
来标识Bean
。其中,name
可以设置多个值,用,
或;
或空格隔开。不指定id
或name
时,Spring
会生成一个默认的标识。
自动生成Bean的唯一标识,遵循Java标准规则,即使用
java.beans.Introspector#decapitalize
方法,若前两个字母都是大写,则不会变换,否则,将首字母小写
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-core
、spring-beans
、spring-aop
、spring-expression
等。
首先来看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:属性名的形式。
编写一个静态工厂类,如下:
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")); }
与静态工厂方法类似,也是通过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