写在前面的碎碎念,不知不觉来公司干开发已经三年多了,一直没有重视文章写作的相关积累。这次打算从基础入手,培养下写作习惯。
一、Bean的实例化
Spring容器支持XML和Properties两种格式的配置文件,在实际开发中,最常使用的就是XML格式的配置方式。Spring容器支持XML和Properties两种格式的配置文件,在实际开发中,最常使用的就是XML格式的配置方式。
XML配置文件的根元素是<beans>
,<beans>
中包含了多个<bean>
子元素,每一个<bean>
子元素都定义了一个bean,并描述了该bean如何被装配到Spring容器中。
实例化Bean有三种方法:1.构造器实例化 2.静态工厂方式实例化 3.实例工厂方式实例化
1.构造器实例化
构造器实例化是指Spring容器通过Bean对应类中默认的无参构造方法来实例化Bean。
package com.demo.instance.constructor;
public class Bean1 {
}
<?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="bean1" class="com.demo.instance.constructor.Bean1"/>
</beans>
2.静态工厂方式实例化
使用设值注入时,在Spring配置文件中,需要使用<bean>
元素的子元素<property>
来为每个属性注入值;而使用构造注入时,在配置文件里,需要使用<bean>
元素的子元素<constructor-arg>
来定义构造方法的参数,可以使用其value属性(或子元素)来设置该参数的值。
package com.demo.instance.static_factory;
public class Bean2 {
}
package com.demo.instance.static_factory;
public class MyBean2Factory {
public static Bean2 createBean2() {
return new Bean2();
}
}
<?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="bean2" class="com.demo.instance.static_factory.MyBean2Factory" factory-method="createBean2"/>
</beans>
3.实例工厂方式实例化
还有一种实例化Bean的方式就是采用实例工厂。此种方式的工厂类中,不再使用静态方法创建Bean实例,而是采用直接创建Bean实例的方式。同时,在配置文件中,需要实例化的Bean也不是通过class属性直接指向的实例化类,而是通过factory-bean属性指向配置的实例工厂,然后使用factory-method属性确定使用工厂中的哪个方法。
package com.demo.instance.factory;
public class Bean3 {
}
package com.demo.instance.factory;
public class MyBean3Factory {
public MyBean3Factory() {
}
public Bean3 createBean3() {
return new Bean3();
}
}
<?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="myBean3Factory"
class="com.demo.instance.factory.MyBean3Factory" />
<!-- 使用factory-bean属性指向配置的实例工厂,
使用factory-method属性确定使用工厂中的哪个方法 -->
<bean id="bean3" factory-bean="myBean3Factory"
factory-method="createBean3" />
</beans>
二、Bean的作用域
作用域名称 | 说明 |
singleton(单例) | 使用singleton定义的Bean在Spring容器中将只有一个实例,无论有多少个Bean引用它,始终将指向同一个对象,这也是Spring容器默认的作用域 |
prototype(原型) | 每次通过Spring容器获取的prototype定义的Bean时,容器都将创建一个新的Bean实例 |
request | 在一次HTTP请求中,容器会返回该Bean的同一个实例。对不同的HTTP请求则会产生一个新的Bean,而且该Bean仅在当前HTTP Request内有效 |
session | 在一次HTTP Session请求中,容器会返回该Bean的同一个实例。对不同的HTTP请求则会产生一个新的Bean,而且该Bean仅在当前HTTP Session内有效 |
globalSesson | 在一个全局的HTTP Session中,容器会返回该Bean的同一个实例。仅在使用protlet上下文时有效 |
application | 为每个ServletContex对象创建一个实例。仅在Web相关的ApplicationContext中生效 |
websocket | 为每个websocket对象创建一个实例。仅在Web相关的ApplicationContext中生效 |
<?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="bean1" class="com.demo.instance.constructor.Bean1" scope="prototype"/>
</beans>
三、Bean的生命周期
Spring容器可以管理singleton作用域的Bean的生命周期,在此作用域下,Spring能够精确地知道该Bean何时被创建,何时初始化完成以及何时被销毁。
对于prototype作用域的Bean, Spring只负责创建,当容器创建了Bean实例后,Bean的实例就交给客户端代码来管理,Spring容器将不再跟踪其生命周期。
每次客户端请求prototype作用域的Bean时,Spring容器都会创建一个新的实例,并且不会管那些被配置成prototype作用域的Bean的生命周期。
Bean的生命周期的整个执行过程描述如下:
- 根据配置情况调用
Bean
构造方法或工厂方法实例化Bean
- 利用依赖注入完成
Bean
中的所有属性值的配置注入 - 如果
Bean
实现了BeanNameAware
接口,则Spring调用Bean
的setBeanName()
方法传入当前Bean
的id值 - 如果
Bean
实现了BeanFactoryAware
接口,则Spring调用setBeanFactory()
方法传入当前工厂实例的引用 - 如果
Bean
实现了ApplicationContextAware
接口,则Spring调用setApplicationContext()
方法传入当前ApplicationContext
实例的引用 - 如果
BeanPostProcessor
和Bean
关联,则Spring将调用该接口的预初始化postProcessBeforeInitialzation()
对Bean
进行加工操作(这个非常重要,Spring的Aop就是用它实现的) - 如果
Bean
实现了IntializingBean
接口,则Spring将调用afterPropertiesSet()
方法 - 如果配置文件中通过
init-method
属性制定了初始化方法,则调用该初始化方法 - 如果有
BeanPostProcessor
和Bean
关联,则Spring将调用该接口的初始化方法postProcessAfterInitializition()
。此时,Bean
已经可以被应用系统使用了 - 如果在
<bean>
中制定了该Bean
的租用范围为scope="singleton"
,则将该Bean
放入Spring Ioc的缓存池中,将出发Spring对该Bean
的生命周期管理,如果<bean>
中指定了该Bean
的作用范围为scope="prototype"
,则将该Bean交给调用者,调用者管理该Bean
的生命周期,Spring不在管理该Bean
- 如果
Bean
实现了DisposableBean
接口,则Spring会调用destory()
方法将Spring中的Bean
销毁,如果在配置文件中通过destory-method
属性制定了Bean
的销毁方法,则Spring将调用该方法进行销毁
四、Bean的装配模式
Bean的装配模式即Bean的依赖注入方式,Spring有三种装配模式:基于XML的装配、基于注解(Annotation)的装配和自动装配。(其中最常用的是基于注解的装配)
1.基于XML的装配
Spring提供了两种基于XML的装配方式:设值注入(Setter Injection)和构造注入(Constructor Injection)
在Spring实例化Bean的过程中,Spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter方法来注入属性值。因此,设值注入要求一个Bean必须满足以下两点要求。
- Bean类必须提供一个默认的无参构造方法。
- Bean类必须为需要注入的属性提供对应的setter方法。
元素的子元素来为每个属性注入值;而使用构造注入时,在配置文件里,需要使用元素的子元素来定义构造方法的参数,可以使用其value属性(或子元素)来设置该参数的值。
package com.demo.assemble;
import java.util.List;
public class User {
private String username;
private Integer password;
private List<String> list;
/**
* 1.使用构造注入
* 1.1提供带所有参数的有参构造方法
* @param username
* @param password
* @param list
*/
public User(String username, Integer password, List<String> list) {
super();
this.username = username;
this.password = password;
this.list = list;
}
/**
* 2.使用设值注入
* 2.1提供默认空参数构造方法
* 2.2为所有属性提供setter方法
*/
public User() {
super();
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(Integer password) {
this.password = password;
}
public void setList(List<String> list) {
this.list = list;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password=" + password +
", list=" + list +
'}';
}
}
<?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="user1" class="com.demo.assemble.User">
<constructor-arg index="0" value="tom"/>
<constructor-arg index="1" value="123456"/>
<constructor-arg index="2">
<list>
<value>"constructorValue1"</value>
<value>"constructorValue2"</value>
</list>
</constructor-arg>
</bean>
<!--设值注入-->
<bean id="user2" class="com.demo.assemble.User">
<property name="username" value="jerry"/>
<property name="password" value="654321"/>
<property name="list">
<list>
<value>"setListValue1"</value>
<value>"setListValue2"</value>
</list>
</property>
</bean>
</beans>
2.基于Annotation的装配
在Spring中,尽管使用XML配置文件可以实现Bean的装配工作,但如果应用中有很多Bean时,会导致XML配置文件过于臃肿,给后续的维护和升级工作带来一定的困难。为此,Spring提供了对Annotation(注解)技术的全面支持。
Spring中定义了一系列的注解,常用的注解如下所示。
注解 | 说明 |
@Component | 可以使用此注解描述Spring中的Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。 |
@Repository | 用于将数据访问层(DAO层)的类标识为Spring中的Bean,其功能与@Component相同。 |
@Service | 通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。 |
@Controller | 通常作用在控制层(如Spring MVC的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同。 |
@Autowired | 用于对Bean的属性变量、属性的setter方法及构造方法进行标注,配合对应的注解处理器完成Bean的自动配置工作。默认按照Bean的类型进行装配。 |
@Resource | 其作用与Autowired一样。其区别在于@Autowired默认按照Bean类型装配,而@Resource默认按照Bean实例名称进行装配。 [1] |
@Qualifier | 与@Autowired注解配合使用,会将默认的按Bean类型装配修改为按Bean的实例名称装配,Bean的实例名称由@Qualifier注解的参数指定。 |
在上面几个注解中,虽然@Repository、@Service与@Controller功能与@Component注解的功能相同,但为了使标注类本身用途更加清晰,建议在实际开发中使用@Repository、@Service与@Controller分别对实现类进行标注。
package com.demo.annotation;
public interface UserDao {
public void save();
}
package com.demo.annotation;
import org.springframework.stereotype.Repository;
//@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("userDao...save...");
}
}
package com.demo.annotation;
public interface UserService {
public void save();
}
package com.demo.annotation;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
//@Service("userService")
public class UserServiceImpl implements UserService {
// @Resource(name = "userDao")
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
this.userDao.save();
System.out.println("userService...save...");
}
}
package com.demo.annotation;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
//@Controller("userController")
public class UserController {
// @Resource(name = "userService")
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void save() {
this.userService.save();
System.out.println("userController...save...");
}
}
<?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="com.demo.annotation"/>-->
<bean id="userDao" class="com.demo.annotation.UserDaoImpl"/>
<bean id="userService" class="com.demo.annotation.UserServiceImpl" autowire="byName"/>
<bean id="userController" class="com.demo.annotation.UserController" autowire="byName"/>
</beans>
演示的为自动装配写法,注释掉的写法为Annotation的装配
- @Resource中有两个重要属性:name和type。Spring将name属性解析为Bean实例名称,type属性解析为Bean实例类型。如果指定name属性,则按实例名称进行装配;如果指定type属性,则按Bean类型进行装配;如果都不指定,则先按Bean实例名称装配,如果不能匹配,再按照Bean类型进行装配;如果都无法匹配,则抛出NoSuchBeanDefinitionException异常。 ↩︎