3. Spring Bean
3.1 Bean的配置
Spring可以看作一个大型工厂,用于生产和管理Spring容器中的Bean。如果要使用这个工厂生产和管理bean,需要开发者将Bean配置在Spring的配置文件中。Spring框架支持XML和Properties两种格式的配置文件,在实际开发中常用XML格式的配置文件。
3.1.1 <bean>
元素的常用属性及其子元素
3.1.2 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">
<!-- 使用id属性定义myTestDIDao,其对应的实现类为dao.TestDIDaoImpl-->
<bean id="myTestDIDao" class="dao.TestDIDaoImpl" />
<!-- 使用构造方法注入 -->
<bean id="testDIService" class="service.TestDIServiceImpl">
<!-- 给构造方法传引用类型的参数值myTestDIDao -->
<constructor-arg index="0" ref="myTestDIDao"/>
</bean>
</beans>
3.2 Bean的实例化
在Spring框架中,如果想使用Spring容器中的Bean,需要实例化Bean。Spring框架实例化Bean有3种方式,即构造方法实例化、静态工厂实例化、和实例工厂实例化,其中最常见的是构造方法实例化。
3.2.1 构造方法实例化
在Spring框架中,Spring容器可以调用Bean对应类中的无参数构造方法来实例化Bean,这种方式称为构造方法实例化。
实例演示:
-
创建Web应用,在src目录下创建instance包,在包中创建BeanClass类
package instance; public class BeanClass { public String message; public BeanClass() { message = "构造方法实例化Bean"; } public BeanClass(String s) { message = s; } }
-
创建配置文件
<?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 id="constructorInstance" class="instance.BeanClass" /> </beans>
-
创建测试类
package test; import instance.BeanClass; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestInstance { public static void main(String[] args) { // 初始化Spring容器ApplicationContext,加载配置文件 ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml"); // 测试构造方法实例化Bean BeanClass b1 = (BeanClass) appCon.getBean("constructorInstance"); System.out.println(b1+b1.message); } }
-
运行结果
3.2.2 静态工厂实例化
在使用静态工厂实例化Bean时要求开发者在工厂类中创建一个静态方法来创建Bean的实例。在配置Bean时,class属性指定静态工厂类,同时还需要使用factory-method属性指定工厂类中的静态方法。
实例演示:
-
创建工厂类BeanStaticFactory,在instance包中创建工厂类BeanStaticFactory
package instance; public class BeanStaticFactory { private static BeanClass beanInstance = new BeanClass("调用静态工厂方法实例化Bean"); public static BeanClass createInstance() { return beanInstance; } }
-
编辑配置文件
<?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,createInstance为静态工厂类BeanStaticFactory中的静态方法--> <bean id="staticFactoryInstance" class="instance.BeanStaticFactory" factory-method="createInstance" /> </beans>
-
添加测试代码
package test; import instance.BeanClass; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestInstance { public static void main(String[] args) { // 初始化Spring容器ApplicationContext,加载配置文件 ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml"); // 测试静态工厂方法实例化Bean BeanClass b2 = (BeanClass) appCon.getBean("staticFactoryInstance"); System.out.println(b2+b2.message); } }
-
运行效果
3.2.3 实例工厂实例化
在使用实例工厂实例化Bean时要求开发者在工厂类中创建一个实例方法来创建Bean的实例。在配置Bean时需要使用factory-method属性指定配置的实例工厂,同时还需要使用factory-method属性指定实例工厂中的实例方法。
-
创建工厂类BeanStaticFactory
在instance包中创建工厂类BeanInstanceFactory,该类中有一个实例方法类实例化对象
package instance; public class BeanInstanceFactory { public BeanClass createBeanClassInstance() { return new BeanClass("调用实例工厂方法实例化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 id="myFactory" class="instance.BeanInstanceFactory" /> <!-- 使用factory-bean属性指定配置工厂,使用factory-method属性指定使用工厂中的哪个方法实例化Bean--> <bean id="instanceFactoryInstance" factory-bean="myFactory" factory-method="createBeanClassInstance" /> </beans>
-
添加测试代码
package test; import instance.BeanClass; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestInstance { public static void main(String[] args) { // 初始化Spring容器ApplicationContext,加载配置文件 ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml"); // 测试实例工厂方法实例化Bean BeanClass b3 = (BeanClass) appCon.getBean("instanceFactoryInstance"); System.out.println(b3+b3.message); } }
-
运行效果
3.3 Bean的作用域
3.3.1 在Spring 5.0中为Bean的实例定义的作用域
singleton和prototype是最常用的两种,后面4中作用域尽在Web Spring应用程序上下文中使用。
3.3.2 singleton作用域
当bean的scope设置为singleton时,Spring IoC容器仅生成和管理一个Bean实例。在使用id或name获取Bean实例时,IoC容器将会返回共享的Bean实例。
singleton时scope的默认方式,有两种方式将bean的scope设置为singleton。示例如下:
<bean id="constructorInstance" class="instance.BeanClass" />
或
<bean id="constructorInstance" class="instance.BeanClass" scope="singleton"/>
在使用id或name获取Bean实例时,IoC容器仅返回同一个Bean实例。
3.3.3 prototype作用域
当bean的scope设置为prototype时,Spring IoC容器将为每次请求创建一个新的实例。示例如下:
<bean id="constructorInstance" class="instance.BeanClass" scope="prototype"/>
3.4 Bean的生命周期
- Bean的生命周期过程:
- 步骤文字描述:
- Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化。
- Bean实例化后对将Bean的引入和值注入到Bean的属性中。
- 如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法。
- 如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入。
- 如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
- 如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
- 如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
- 如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
- 此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
- 如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。
实例演示Bean的生命周期:
-
创建Bean的实现类
在src目录中创建life包,在life包下创建BeanLife类。在BeanLife类中有两个方法,一个演示初始化方法,一个演示销毁过程。
package life; public class BeanLife { public void initMyself() { System.out.println(this.getClass().getName()+"执行自定义的初始化方法"); } public void destoryMyself() { System.out.println(this.getClass().getName()+"执行自定义的销毁方法"); } }
-
配置Bean
在Spring配置文件中使用实现类BeanLife配置一个id为beanLife的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,使用iniit-method属性指定初始化方法,使用destroy-method属性指定销毁方法--> <bean id="beanLife" class="life.BeanLife" init-method="initMyself" destroy-method="destoryMyself" /> </beans>
-
测试声明周期
在test包中创建测试类TestLife。
package test; import life.BeanLife; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestLife { public static void main(String[] args) { // 初始化Spring容器,加载配置文件 // 为了方便演示销毁方法的执行,这里使用ClassPathXmlApplicationContext // 实现类声明容器 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println("获得对象前"); BeanLife blife = (BeanLife)ctx.getBean("beanLife"); System.out.println("获得对象后" + blife); ctx.close(); //关闭容器,销毁Bean对象 } }
-
运行效果
3.5 Bean的装配方式
Bean的装配可以理解为将bean依赖注入到Spring容器中,Bean的装配方式即Bean依赖注入的方式。
3.5.1 基于XML配置的装配
在使用构造方法注入方式装配Bean时,Bean的实现类需要提供带参数的构造方法,并在配置文件中使用
实例演示:
-
创建Bean的实现类
在src目录中创建assemble包,在assemble包下创建ComplexUser类。在ComplexUser类中分别使用构造方法注入和使用属性的setter方法注入。
package assemble; import java.util.List; import java.util.Map; import java.util.Set; public class ComplexUser { private String uname; private List<String> hobbyList; private Map<String,String> residenceMap; private Set<String> aliasSet; private String[] array; /* 使用构造方法注入,需要提供带参数的构造方法 */ public ComplexUser(String uname, List<String> hobbyList, Map<String, String> residenceMap, Set<String> aliasSet, String[] array) { super(); this.uname = uname; this.hobbyList = hobbyList; this.residenceMap = residenceMap; this.aliasSet = aliasSet; this.array = array; } /** * 使用属性的setter方法注入,提供默认无参数的构造方法,并为注入的属性提供setter方法 */ public ComplexUser() { super(); } @Override public String toString() { return "uname=" + uname + ";hobbyList=" + hobbyList + ";residenceMap=" + residenceMap + ";aliasSet=" + aliasSet +";array=" + array; } // 此处为所有属性的setter方法 public void setUname(String uname) { this.uname = uname; } public void setHobbyList(List hobbyList) { this.hobbyList = hobbyList; } public void setResidenceMap(Map residenceMap) { this.residenceMap = residenceMap; } public void setAliasSet(Set aliasSet) { this.aliasSet = aliasSet; } public void setArray(String[] array) { this.array = array; } }
-
配置Bean
在Spring配置文件中使用实现类ComplexUser配置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"> <!-- 使用构造方法注入方式装配ComplexUser实例user1--> <bean id="user1" class="assemble.ComplexUser"> <constructor-arg index="0" value="yihang1" /> <constructor-arg index="1"> <list> <value>唱歌</value> <value>跳舞</value> <value>爬山</value> </list> </constructor-arg> <constructor-arg index="2"> <map> <entry key="dalian" value="大连" /> <entry key="beijing" value="北京" /> <entry key="shanghai" value="上海" /> </map> </constructor-arg> <constructor-arg index="3"> <set> <value>逸航01</value> <value>逸航02</value> <value>逸航03</value> </set> </constructor-arg> <constructor-arg index="4"> <array> <value>aaaaa</value> <value>bbbbb</value> </array> </constructor-arg> </bean> <!-- 使用属性的setter方法注入方式装配 ComplexUser实例user2--> <bean id="user2" class="assemble.ComplexUser"> <property name="uname" value="yihang2" /> <property name="hobbyList"> <list> <value>看书</value> <value>学习Spring</value> </list> </property> <property name="residenceMap" > <map> <entry key="shenzhen" value="深圳" /> <entry key="guangzhou" value="广州" /> <entry key="tianjin" value="天津" /> </map> </property> <property name="aliasSet"> <set> <value>逸航04</value> <value>逸航05</value> <value>逸航06</value> </set> </property> <property name="array"> <array> <value>ccccc</value> <value>ddddd</value> </array> </property> </bean> </beans>
-
测试基于XML配置的装配方式
在test包中创建测试类TestAssemble。
package test; import assemble.ComplexUser; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAssemble { public static void main(String[] args) { ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml"); // 使用构造方法装配测试 ComplexUser u1 = (ComplexUser) appCon.getBean("user1"); System.out.println(u1); System.out.println("-------------------------"); // 使用setter方法装配测试 ComplexUser u2 = (ComplexUser) appCon.getBean("user2"); System.out.println(u2); } }
-
运行效果
3.5.2 基于注解的装配
大量的Bean装配会导致XML配置文件过于庞大,不方便以后的升级与维护,因此更多的时候推荐开发者使用注解(annotation)的方式去装配Bean。
3.5.1.1 @Component
该注解是一个泛化的概念,仅仅表示一个组件对象(Bean),可以作用在任何层次上。
3.5.2.2 @Repository
该注解用于将数据访问层(DAO)的类标识为Bean,即注解数据访问层Bean,其功能与@Component相同。
3.5.2.3 @Service
该注解用于标注一个业务逻辑组件类(Service层),其功能与@Component相同。
3.5.2.4 @Controller
该注解用于标注一个控制器组件类(Spring MVC的Controller),其功能与@Component相同。
3.5.2.5 @Autowired
该注解可以对类成员变量、方法及构造方法进行标注,完成自动装配的工作。通过使用@Autowired来消除setter和getter方法。默认按照Bean的类型进行装配。
3.5.2.6 @Resource
该注解与@Autowired的功能一样,区别在于该注解默认是按照名称来装配注入的,只有当找不到与名称匹配的Bean时才会按照类型来装配注入;而@Autowired默认按照Bean的类型进行装配,如果想按照名称来装配注入,则需要和@Qualifier注解一起使用。
3.5.3.7 @Qualifier
该注解与@Aitowired注解配合使用。当@Autowired注解需要按照名称来装配注入时需要和该注解一起使用,Bean的实例名称由@Qualifier注解的参数制定。
实例演示:
-
创建DAO层
在src中创建annotation.dao包,在该包下创建TestDao接口和TestDaoImpl实现类,并将实现类TestDaoImpl使用@Repository注解标注为数据访问层。
TestDao的代码如下:
package annotation.dao; public interface TestDao { public void save(); }
TestDaoImpl的代码如下:
package annotation.dao; import org.springframework.stereotype.Repository; @Repository("testDao") /** * 相当于@Repository,但如果在service层中使用@Resource(name="testDao"), * testDao不能省略 */ public class TestDaoImpl implements TestDao { @Override public void save() { System.out.println("testDao save"); } }
-
创建Service层
在src中创建annotation.service包,在该包下创建TestService接口和TestServiceImpl实现类,并将实现类TestServiceImpl使用@service注解标注为业务逻辑层。
Testservice的代码如下:
package annotation.service; public interface TestService { public void save(); }
TestServiceImpl的代码如下:
package annotation.service; import annotation.dao.TestDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service("testService") //相当于@Service public class TestSerivceImpl implements TestService { // @Resource(name="testDao") @Autowired // 相当于@Autowired,@Autowired默认按照Bean状态装配 private TestDao testDao; @Override public void save() { testDao.save(); System.out.println("testService save"); } }
-
创建Controller层
在src中创建annotation.controller包,在该包下创建TestController类,并将TestController类使用@Controller注解标注为控制器层。
TestController的代码如下:
package annotation.controller; import annotation.service.TestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class TestController { @Autowired private TestService testService; public void save() { testService.save(); System.out.println("testController save"); } }
-
配置注解
由于annotation.dao、annotation.service和annotation.controller包都属于annotation包的子包,因此不要在配置文件annotationContext.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" xmlns:context="http://www.springframework.org/schema/context" 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"> <context:component-scan base-package="annotation" /> </beans>
-
创建测试类
package test; import annotation.controller.TestController; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestMoreAnnotation { public static void main(String[] args) { ApplicationContext appCon = new ClassPathXmlApplicationContext("annotationContext.xml"); TestController testcon = (TestController) appCon.getBean("testController"); testcon.save(); } }
-
运行效果