抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

摘要:本文学习了Spring配置对象的三种方式。

环境

Windows 10 企业版 LTSC 21H2
Java 1.8
Tomcat 8.5.50
Maven 3.6.3
Spring 5.2.25.RELEASE

1 XML配置

XML配置是Spring框架最早的配置方式,通过XML文件来定义对象和它们之间的依赖关系。

1.1 基本配置

1.1.1 创建对象

使用bean标签创建对象。

常用属性:

属性名 作用 取值
id 指定对象的唯一标识 唯一标识
class 指定对象的全类名 全类名

示例:

xml
1
2
<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User"/>

1.1.2 设置属性

1.1.2.1 通过Set方法设置属性

使用property标签设置属性。

常用属性:

属性名 作用 取值
name 指定属性名 属性名
value 指定属性值 属性值
ref 指定引用其他对象的ID 对象的ID

注意:

  • 需要属性有Set方法,并且方法名按照驼峰命名。
  • 必须有无参构造器,默认的无参构造器可以省略。
  • 如果属性是基本类型,使用value属性设置值;如果属性是引用类型,使用ref属性引用其他对象。

示例:

xml
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 创建Address对象 -->
<bean id="address" class="com.example.bean.Address">
<property name="province" value="北京市"/>
<property name="detail" value="北京市海淀区"/>
</bean>

<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User">
<property name="id" value="1"/>
<property name="name" value="张三"/>
<property name="age" value="18"/>
<property name="address" ref="address"/>
</bean>
1.1.2.2 通过构造器设置属性

使用constructor-arg标签设置属性,需要属性有对应的构造器。

常用属性:

属性名 作用 取值
name 指定属性名 属性名
value 指定属性值 属性值
ref 指定引用其他对象的ID 对象的ID

示例:

xml
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 创建Address对象 -->
<bean id="address" class="com.example.bean.Address">
<constructor-arg value="北京市"/>
<constructor-arg value="北京市海淀区"/>
</bean>

<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User">
<constructor-arg value="1"/>
<constructor-arg value="张三"/>
<constructor-arg value="18"/>
<constructor-arg ref="address"/>
</bean>

1.2 特殊属性

1.2.1 特殊符号

使用value标签设置特殊符号,特殊符号需要使用CDATA包裹,示例:

xml
1
2
3
4
5
6
7
<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User">
<property name="id" value="1"/>
<property name="name">
<value><![CDATA[张三]]></value>
</property>
</bean>

1.2.2 级联属性

使用bean标签设置级联属性,示例:

xml
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User">
<property name="id" value="1"/>
<property name="name" value="张三"/>
<property name="age" value="18"/>
<property name="address">
<bean class="com.example.bean.Address">
<property name="province" value="北京市"/>
<property name="detail" value="北京市海淀区"/>
</bean>
</property>
</bean>

1.2.3 对象属性

使用bean标签设置对象属性,示例:

xml
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 创建Address对象 -->
<bean id="address" class="com.example.bean.Address">
<constructor-arg value="北京市"/>
<constructor-arg value="北京市海淀区"/>
</bean>

<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User" autowire="byName">
<property name="id" value="1"/>
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>

使用autowire属性自动装配:

  • byName:根据属性名自动装配,如果匹配多个则报错,如果没有匹配则设置为null值。
  • byType:根据属性类型自动装配,如果匹配多个则报错,如果没有匹配则设置为null值。
  • constructor:根据构造器参数自动装配,不常用。

1.2.4 数组属性

使用array标签设置数组属性。

1.2.4.1 基本类型

使用value标签设置基本类型数据,示例:

xml
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User">
<property name="id" value="1"/>
<property name="name" value="张三"/>
<property name="age" value="18"/>
<property name="hobbies">
<array>
<value>篮球</value>
<value>足球</value>
</array>
</property>
</bean>
1.2.4.2 引用类型

使用bean标签设置引用类型数据,示例:

xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User">
<property name="id" value="1"/>
<property name="name" value="张三"/>
<property name="age" value="18"/>
<property name="addresses">
<array>
<bean class="com.example.bean.Address">
<property name="province" value="北京市"/>
<property name="detail" value="北京市海淀区"/>
</bean>
<bean class="com.example.bean.Address">
<property name="province" value="上海市"/>
<property name="detail" value="上海市静安区"/>
</bean>
</array>
</property>
</bean>

使用ref标签引用其他对象,示例:

xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 创建Address对象 -->
<bean id="address1" class="com.example.bean.Address">
<property name="province" value="北京市"/>
<property name="detail" value="北京市海淀区"/>
</bean>
<bean id="address2" class="com.example.bean.Address">
<property name="province" value="上海市"/>
<property name="detail" value="上海市静安区"/>
</bean>

<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User">
<property name="id" value="1"/>
<property name="name" value="张三"/>
<property name="age" value="18"/>
<property name="addresses">
<array>
<ref bean="address1"/>
<ref bean="address2"/>
</array>
</property>
</bean>

1.2.5 集合属性

使用list标签设置List集合属性,支持基本类型和引用类型,示例:

xml
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User">
<property name="id" value="1"/>
<property name="name" value="张三"/>
<property name="age" value="18"/>
<property name="pets">
<list>
<value>小猫</value>
<value>小狗</value>
</list>
</property>
</bean>

使用set标签设置Set集合属性,支持基本类型和引用类型,示例:

xml
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User">
<property name="id" value="1"/>
<property name="name" value="张三"/>
<property name="age" value="18"/>
<property name="pets">
<list>
<value>小猫</value>
<value>小狗</value>
</list>
</property>
</bean>

使用map标签设置Map集合属性,支持基本类型和引用类型,示例:

xml
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User">
<property name="id" value="1"/>
<property name="name" value="张三"/>
<property name="age" value="18"/>
<property name="pets">
<map>
<entry key="cat" value="小猫"/>
<entry key="dog" value="小狗"/>
</map>
</property>
</bean>

1.3 工厂对象

1.3.1 静态工厂方法

创建静态工厂实体类,工厂方法需要是静态方法:

java
1
2
3
4
5
public class UserFactory {
public static User getUser() {
return new User(1, "张三", 18);
}
}

使用factory-method属性指定工厂方法,示例:

xml
1
2
<!-- 创建User对象 -->
<bean id="user" class="com.example.factory.UserFactory" factory-method="getUser"/>

1.3.2 实例工厂方法

创建实例工厂实体类,工厂方法不能是静态方法:

java
1
2
3
4
5
public class UserFactory {
public User getUser() {
return new User(1, "张三", 18);
}
}

使用factory-bean属性指定实例工厂,使用factory-method属性指定工厂方法,示例:

xml
1
2
3
4
<!-- 创建UserFactory对象 -->
<bean id="userFactory" class="com.example.factory.UserFactory"/>
<!-- 使用实例工厂方法创建User对象 -->
<bean id="user" factory-bean="userFactory" factory-method="getUser"/>

1.3.3 使用工厂接口

创建FactoryBean实体类,实现FactoryBean接口:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User(1, "张三", 18);
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}

示例:

xml
1
2
<!-- 创建UserFactoryBean对象 -->
<bean id="user" class="com.example.factory.UserFactoryBean"/>

1.4 设置作用域

使用scope属性设置作用域,常用取值:

  • singleton:单例模式,容器初始化时创建对象,整个容器中只有一个实例(默认)
  • prototype:原型模式,每次获取对象时都会创建一个新的实例
  • request:每次HTTP请求都会创建一个新的实例,仅适用于Web应用
  • session:每个HTTP会话创建一个新的实例,仅适用于Web应用

示例:

xml
1
2
<!-- 创建User对象,原型模式 -->
<bean id="user" class="com.example.bean.User" scope="prototype"/>

1.5 管理生命周期

对象的生命周期:

  • 执行构造方法创建对象。
  • 执行设置方法设置对象属性。
  • 执行初始化方法。
  • 执行使用方法。
  • 执行销毁方法,在容器关闭时自动调用。

1.5.1 使用配置文件

修改实体类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class User {
private Integer id;
public User() {
System.out.println("执行构造方法");
}
public Integer getId() {
System.out.println("执行Get使用方法");
return id;
}
public void setId(Integer id) {
System.out.println("执行Set设置方法");
this.id = id;
}
public void initMethod() {
System.out.println("执行自定义初始化方法");
}
public void destroyMethod() {
System.out.println("执行自定义销毁方法");
}
}

修改配置文件:

xml
1
2
3
4
<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User" init-method="initMethod" destroy-method="destroyMethod">
<property name="id" value="1"/>
</bean>

修改启动类:

java
1
2
3
4
5
6
7
8
9
10
11
12
public class DemoApplication {
public static void main(String[] args) {
// 加载配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// 获取User对象
User user = context.getBean(User.class);
// 使用User对象
System.out.println(user.getId());
// 关闭容器
context.close();
}
}

结果:

log
1
2
3
4
5
6
执行构造方法
执行Set设置方法
执行自定义初始化方法
执行Get使用方法
1
执行自定义销毁方法

1.5.2 实现接口

可以通过实现接口管理生命周期:

  • 实现InitializingBean接口可以在属性设置完成后执行初始化操作。
  • 实现DisposableBean接口可以在容器关闭前执行销毁操作。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class User implements InitializingBean, DisposableBean {
private Integer id;
public User() {
System.out.println("执行构造方法");
}
public Integer getId() {
System.out.println("执行Get使用方法");
return id;
}
public void setId(Integer id) {
System.out.println("执行Set设置方法");
this.id = id;
}
public void initMethod() {
System.out.println("执行自定义初始化方法");
}
public void destroyMethod() {
System.out.println("执行自定义销毁方法");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("执行InitializingBean的afterPropertiesSet方法");
}
@Override
public void destroy() throws Exception {
System.out.println("执行DisposableBean的destroy方法");
}
}

结果:

log
1
2
3
4
5
6
7
8
执行构造方法
执行Set设置方法
执行InitializingBean的afterPropertiesSet方法
执行自定义初始化方法
执行Get使用方法
1
执行DisposableBean的destroy方法
执行自定义销毁方法

1.5.3 后置处理器

后置处理器允许在对象的生命周期的不同阶段进行干预,主要有两种类型:

  • BeanPostProcessor:在对象初始化前后进行处理,可以修改对象的实例。
  • BeanFactoryPostProcessor:在BeanFactory初始化后,并且在对象实例化之前进行处理,可以修改对象的定义。
1.5.3.1 对象后置处理器

实现BeanPostProcessor接口需要重写两个方法:

  • postProcessBeforeInitialization:在对象初始化前调用,可以修改对象属性。
  • postProcessAfterInitialization:在对象初始化后调用,可以返回代理对象。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
public class DemoPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行BeanPostProcessor的postProcessBeforeInitialization方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行BeanPostProcessor的postProcessAfterInitialization方法");
return bean;
}
}

配置:

xml
1
2
3
4
5
6
7
<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User" init-method="initMethod" destroy-method="destroyMethod">
<property name="id" value="1"/>
</bean>

<!-- 创建DemoPostProcessor对象 -->
<bean id="demoPostProcessor" class="com.example.processor.DemoPostProcessor"/>

结果:

log
1
2
3
4
5
6
7
8
9
10
执行构造方法
执行Set设置方法
执行BeanPostProcessor的postProcessBeforeInitialization方法
执行InitializingBean的afterPropertiesSet方法
执行自定义初始化方法
执行BeanPostProcessor的postProcessAfterInitialization方法
执行Get使用方法
1
执行DisposableBean的destroy方法
执行自定义销毁方法
1.5.3.2 对象工厂后置处理器

实现BeanFactoryPostProcessor接口需要重写postProcessBeanFactory()方法,在BeanFactory初始化后调用,可以修改对象定义。

示例:

java
1
2
3
4
5
6
public class DemoFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("执行BeanFactoryPostProcessor的postProcessBeanFactory方法");
}
}

配置:

xml
1
2
3
4
5
6
7
8
9
10
<!-- 创建User对象 -->
<bean id="user" class="com.example.bean.User" init-method="initMethod" destroy-method="destroyMethod">
<property name="id" value="1"/>
</bean>

<!-- 创建DemoPostProcessor对象 -->
<bean id="demoPostProcessor" class="com.example.processor.DemoPostProcessor"/>

<!-- 创建DemoFactoryPostProcessor对象 -->
<bean id="demoFactoryPostProcessor" class="com.example.processor.DemoFactoryPostProcessor"/>

结果:

log
1
2
3
4
5
6
7
8
9
10
11
执行BeanFactoryPostProcessor的postProcessBeanFactory方法
执行构造方法
执行Set设置方法
执行BeanPostProcessor的postProcessBeforeInitialization方法
执行InitializingBean的afterPropertiesSet方法
执行自定义初始化方法
执行BeanPostProcessor的postProcessAfterInitialization方法
执行Get使用方法
1
执行DisposableBean的destroy方法
执行自定义销毁方法

2 半注解配置

从Spring的2.5版本开始,引入了基于注解的配置方式,大大简化了配置工作:

  • spring.xml配置文件中使用context:component-scan标签启用对象扫描,需要在XML文件中添加context命名空间。
  • 使用@Component注解自动创建对象。

2.1 基本配置

创建实体类:

java
1
2
3
4
5
@Component
public class User {
private Integer id;
// ...
}

创建配置文件:

spring.xml
1
2
3
4
5
6
7
8
9
10
11
<?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">
<!-- 启用对象扫描,需要在XML文件中添加context命名空间 -->
<context:component-scan base-package="com.example"/>
</beans>

创建启动类:

java
1
2
3
4
5
6
7
8
9
10
11
12
public class DemoApplication {
public static void main(String[] args) {
// 加载配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// 获取User对象
User user = context.getBean(User.class);
// 使用User对象
System.out.println(user);
// 关闭容器
context.close();
}
}

2.2 自动注入

spring.xml配置文件中使用context:annotation-config标签启用自动注入,自动对容器中的对象注入依赖。

2.3 配置扫描

spring.xml配置文件中使用context:component-scan标签启用对象扫描,将标注了特定注解的类自动注册到容器中,并且自动对容器中的对象注入依赖。

使用context:component-scan标签可以覆盖context:annotation-config标签的自动注入功能。

常用属性:

属性名 作用 取值
base-package 指定需要扫描的包路径 包路径,多个使用逗号分隔
resource-pattern 指定扫描的资源模式,按类匹配 表达式

示例:

xml
1
2
<!-- 启用对象扫描,扫描com.example包下的所有类名以Service结尾的类,包括子包下的类 -->
<context:component-scan base-package="com.example" resource-pattern="**/*Controller.class"/>

说明:

  • **表示所有层级的目录。
  • *表示所有文件。

2.4 常用注解

2.4.1 标识注解

使用注解标识对象可以简化XML配置,支持通过value属性指定对象名称:

  • 使用@Component注解标识通用的对象。
  • 使用@Repository注解标识数据访问层的对象。
  • 使用@Service注解标识服务层的对象。
  • 使用@Controller注解标识接入层的对象。

示例:

java
1
2
3
4
5
@Component
public class User {
private Integer id;
// ...
}

2.4.2 属性注解

使用@Value注解可以注入基本类型的属性值,支持字段注入、方法注入、构造器注入。

示例:

java
1
2
3
4
5
6
@Component
public class User {
@Value("1")
private Integer id;
// ...
}

2.4.3 注入注解

2.4.3.1 @Autowired

使用@Autowired注解可以根据类型注入依赖:

  • 支持字段注入、方法注入、构造器注入。
  • 如果有且仅有一个类型相同的对象,则注入该对象。
  • 如果有多个类型相同的对象,则会抛出异常。需要使用@Qualifier注解指定具体的对象。
  • 如果找不到类型相同的对象,则会抛出异常。在设置required属性为false后,找不到会设置为null值,不会抛出异常。

示例:

java
1
2
3
4
5
6
7
8
9
@Component
public class User {
@Value("1")
private Integer id;
@Autowired
@Qualifier("address")
private Address address;
// ...
}
2.4.3.2 @Inject

使用@Inject注解可以根据名称或类型注入依赖,这是由JDK提供的注解,属于JSR-330规范:

  • 支持字段注入、方法注入、构造器注入。
  • 如果有且仅有一个类型相同的对象,则注入该对象。
  • 如果有多个类型相同的对象,则会抛出异常。需要使用@Named注解指定具体的对象。
  • 如果找不到类型相同的对象,则会抛出异常。

添加依赖:

pom.xml
1
2
3
4
5
6
<!-- Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>

示例:

java
1
2
3
4
5
6
7
8
9
@Component
public class User {
@Value("1")
private Integer id;
@Inject
@Named("address")
private Address address;
// ...
}
2.4.3.3 @Resource

使用@Resource注解可以根据名称或类型注入依赖,这是由JDK提供的注解,属于JSR-250规范:

  • 支持字段注入、方法注入,不支持构造器注入。
  • 如果有且仅有一个名称相同的对象,则自动装配该对象。
  • 如果有多个名称相同的对象,则会抛出异常。
  • 如果找不到名称相同的对象,则会匹配类型相同的对象。
  • 如果有且仅有一个类型相同的对象,则自动装配该对象。
  • 如果有多个类型相同的对象,则会抛出异常。
  • 如果找不到类型相同的对象,则会抛出异常。

示例:

java
1
2
3
4
5
6
7
8
@Component
public class User {
@Value("1")
private Integer id;
@Resource
private Address address;
// ...
}

2.5 设置作用域

使用@Scope注解可以在标识对象时设置作用域。

示例:

java
1
2
3
4
5
6
7
@Component
@Scope("prototype")
public class User {
@Value("1")
private Integer id;
// ...
}

2.6 管理生命周期

2.6.1 使用注解

可以通过使用JDK自带的注解管理生命周期:

  • 使用@PostConstruct注解可以在属性设置完成后执行初始化操作。
  • 使用@PreDestroy注解可以在容器关闭前执行销毁操作。

修改实体类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Component
public class User {
private Integer id;
public User() {
System.out.println("执行构造方法");
}
public Integer getId() {
System.out.println("执行Get使用方法");
return id;
}
@Value("1")
public void setId(Integer id) {
System.out.println("执行Set设置方法");
this.id = id;
}
@PostConstruct
public void initMethod() {
System.out.println("执行自定义初始化方法");
}
@PreDestroy
public void destroyMethod() {
System.out.println("执行自定义销毁方法");
}
}

修改启动类:

java
1
2
3
4
5
6
7
8
9
10
11
12
public class DemoApplication {
public static void main(String[] args) {
// 加载配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// 获取User对象
User user = context.getBean(User.class);
// 使用User对象
System.out.println(user.getId());
// 关闭容器
context.close();
}
}

结果:

log
1
2
3
4
5
6
执行构造方法
执行Set设置方法
执行自定义初始化方法
执行Get使用方法
1
执行自定义销毁方法

2.6.2 实现接口

可以通过实现接口管理生命周期:

  • 实现InitializingBean接口可以在属性设置完成后执行初始化操作。
  • 实现DisposableBean接口可以在容器关闭前执行销毁操作。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Component
public class User implements InitializingBean, DisposableBean {
private Integer id;
public User() {
System.out.println("执行构造方法");
}
public Integer getId() {
System.out.println("执行Get使用方法");
return id;
}
@Value("1")
public void setId(Integer id) {
System.out.println("执行Set设置方法");
this.id = id;
}
@PostConstruct
public void initMethod() {
System.out.println("执行自定义初始化方法");
}
@PreDestroy
public void destroyMethod() {
System.out.println("执行自定义销毁方法");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("执行InitializingBean的afterPropertiesSet方法");
}
@Override
public void destroy() throws Exception {
System.out.println("执行DisposableBean的destroy方法");
}
}

结果:

log
1
2
3
4
5
6
7
8
执行构造方法
执行Set设置方法
执行自定义初始化方法
执行InitializingBean的afterPropertiesSet方法
执行Get使用方法
1
执行自定义销毁方法
执行DisposableBean的destroy方法

2.6.3 后置处理器

后置处理器允许在对象的生命周期的不同阶段进行干预,主要有两种类型:

  • BeanPostProcessor:在对象初始化前后进行处理,可以修改对象的实例。
  • BeanFactoryPostProcessor:在BeanFactory初始化后,并且在对象实例化之前进行处理,可以修改对象的定义。
2.6.3.1 对象后置处理器

实现BeanPostProcessor接口需要重写两个方法:

  • postProcessBeforeInitialization:在对象初始化前调用,可以修改对象属性。
  • postProcessAfterInitialization:在对象初始化后调用,可以返回代理对象。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class DemoPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行BeanPostProcessor的postProcessBeforeInitialization方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行BeanPostProcessor的postProcessAfterInitialization方法");
return bean;
}
}

结果:

log
1
2
3
4
5
6
7
8
9
10
执行构造方法
执行Set设置方法
执行BeanPostProcessor的postProcessBeforeInitialization方法
执行自定义初始化方法
执行InitializingBean的afterPropertiesSet方法
执行BeanPostProcessor的postProcessAfterInitialization方法
执行Get使用方法
1
执行自定义销毁方法
执行DisposableBean的destroy方法
2.6.3.2 对象工厂后置处理器

实现BeanFactoryPostProcessor接口需要重写postProcessBeanFactory()方法,在BeanFactory初始化后调用,可以修改对象定义。

示例:

java
1
2
3
4
5
6
7
@Component
public class DemoFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("执行BeanFactoryPostProcessor的postProcessBeanFactory方法");
}
}

结果:

log
1
2
3
4
5
6
7
8
9
10
11
执行BeanFactoryPostProcessor的postProcessBeanFactory方法
执行构造方法
执行Set设置方法
执行BeanPostProcessor的postProcessBeforeInitialization方法
执行自定义初始化方法
执行InitializingBean的afterPropertiesSet方法
执行BeanPostProcessor的postProcessAfterInitialization方法
执行Get使用方法
1
执行自定义销毁方法
执行DisposableBean的destroy方法

3 全注解配置

从Spring的3.0版本开始,引入了全注解配置方式:

  • 使用@Configuration注解替代spring.xml配置文件。
  • 使用@ComponentScan注解替代context:component-scan标签启用对象扫描。
  • 使用@Bean注解替代bean标签创建对象实例。

需要将容器改为AnnotationConfigApplicationContext类型,并且传入配置类,使用注解的方式获取配置。

3.1 基本配置

创建实体类:

java
1
2
3
4
5
@Component
public class User {
private Integer id;
// ...
}

创建配置类:

java
1
2
3
4
@Configuration
@ComponentScan("com.example")
public class DemoConfig {
}

创建启动类:

java
1
2
3
4
5
6
7
8
9
10
11
12
public class DemoApplication {
public static void main(String[] args) {
// 加载配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class);
// 获取User对象
User user = context.getBean(User.class);
// 使用User对象
System.out.println(user);
// 关闭容器
context.close();
}
}

3.2 常用注解

3.2.1 配置注解

使用@Configuration注解可以标记配置类,本质上是@Component注解的特例,所以配置类会被注册为对象。

常用属性:

属性名 作用 取值
value 指定对象名称 对象名称,默认使用类名
proxyBeanMethods 设置通过@Bean注解创建的对象是否使用CGLIB代理,使用代理创建单例对象 默认为true表示使用代理,设置为false表示不使用代理td>

示例:

java
1
2
3
@Configuration(value = "demoConfig", proxyBeanMethods = false)
public class DemoConfig {
}

3.2.2 扫描注解

使用@ComponentScan注解可以启用对象扫描,代替在spring.xml配置文件中使用context:component-scan标签。

常用属性:

属性名 作用 取值
value或basePackages 指定要扫描的包路径 包路径,多个使用逗号分隔
basePackageClasses 指定要扫描的类路径 类路径,多个使用逗号分隔
includeFilters 指定包含过滤器,满足这些过滤器的类会被注册为对象 表达式
excludeFilters 指定排除过滤器,满足这些过滤器的类不会被注册为对象 表达式
useDefaultFilters 是否使用默认过滤器,默认过滤器会自动注册带有四种标识注解的类 默认为true表示使用默认过滤器,设置为false禁用后只注册用户自定义的过滤器

过滤器类型:

  • Filter.TYPE_ANNOTATION:注解过滤器
  • Filter.TYPE_ASSIGNABLE:类型过滤器
  • Filter.TYPE_ASPECTJ:AspectJ表达式过滤器
  • Filter.TYPE_REGEX:正则表达式过滤器

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
@ComponentScan(
basePackages = "com.example",
useDefaultFilters = false,
includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Service.class)
},
excludeFilters = {
@ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Test")
}
)
public class DemoConfig {
}

3.2.3 对象注解

使用@Bean注解可以注册对象,需要在配置类中使用。

使用@Bean注解修饰的方法可以传入参数,支持的参数类型:

  • 容器中的对象:支持注入容器中的其他对象,包括使用@Component注解或者@Bean注解创建的对象。
  • ApplicationContext:支持注入应用上下文对象,可以获取容器中的其他对象。
  • ResourceLoader:支持注入资源加载器对象,可以加载资源文件。
  • @Value:支持注入配置属性值,可以获取配置文件中的配置。
  • Environment:支持注入环境对象,可以获取配置文件中的配置。

常用属性:

属性名 作用 取值
value或name 指定对象名称 对象名称,默认使用方法名
initMethod 指定初始化方法 方法名
destroyMethod 指定销毁方法 方法名

示例:

java
1
2
3
4
5
6
7
@Configuration
public class DemoConfig {
@Bean("user")
public User user() {
return new User();
}
}

3.2.4 依赖注解

使用@DependsOn注解可以指定依赖的对象,用于显示声明依赖关系,可以在标识注解和对象注解中使用。

示例:

java
1
2
3
4
5
6
@Component
@DependsOn("address")
public class User {
private Address address;
// ...
}

3.2.5 导入注解

使用@Import注解可以导入其他类,多个类之间使用逗号分隔。

支持:

  • 导入普通类:普通类会被注册为对象。
  • 导入配置类:可以使用配置类中的对象,还可以通过接口灵活选择。

示例:

java
1
2
3
4
@Configuration
@Import(User.class)
public class DemoConfig {
}

3.2.6 资源注解

使用@PropertySource注解可以获取配置文件,可以通过@Value注解或者注入Environment环境变量读取。

通过@Value注解读取:

java
1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@PropertySource("classpath:demo.properties")
public class DemoConfig {
@Value("${user.id}")
private Integer id;
@Bean
public User user() {
User user = new User();
user.setId(id);
return user;
}
}

通过注入Environment环境变量读取:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
@PropertySource("classpath:demo.properties")
public class DemoConfig {
private Environment env;
@Autowired
public void setEnv(Environment env) {
this.env = env;
}
@Bean
public User user() {
User user = new User();
user.setId(Integer.parseInt(env.getProperty("user.id")));
return user;
}
}

3.3 管理生命周期

3.3.1 使用属性

使用@Bean注解可以管理生命周期:

  • 通过initMethod属性指定初始化方法。
  • 通过destroyMethod属性指定销毁方法。

也可以通过使用JDK自带的注解管理生命周期:

  • 使用@PostConstruct注解可以在属性设置完成后执行初始化操作。
  • 使用@PreDestroy注解可以在容器关闭前执行销毁操作。

修改实体类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class User {
private Integer id;
public User() {
System.out.println("执行构造方法");
}
public Integer getId() {
System.out.println("执行Get使用方法");
return id;
}
@Value("1")
public void setId(Integer id) {
System.out.println("执行Set设置方法");
this.id = id;
}
public void initMethodAttr() {
System.out.println("执行属性自定义初始化方法");
}
public void destroyMethodAttr() {
System.out.println("执行属性自定义销毁方法");
}
@PostConstruct
public void initMethodFunc() {
System.out.println("执行注解自定义初始化方法");
}
@PreDestroy
public void destroyMethodFunc() {
System.out.println("执行注解自定义销毁方法");
}
}

修改配置类:

java
1
2
3
4
5
6
7
@Configuration
public class DemoConfig {
@Bean(name = "user", initMethod = "initMethodAttr", destroyMethod = "destroyMethodAttr")
public User user() {
return new User();
}
}

修改启动类:

java
1
2
3
4
5
6
7
8
9
10
11
12
public class DemoApplication {
public static void main(String[] args) {
// 加载配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class);
// 获取User对象
User user = context.getBean(User.class);
// 使用User对象
System.out.println(user.getId());
// 关闭容器
context.close();
}
}

结果:

log
1
2
3
4
5
6
7
8
执行构造方法
执行Set设置方法
执行注解自定义初始化方法
执行属性自定义初始化方法
执行Get使用方法
1
执行注解自定义销毁方法
执行属性自定义销毁方法

3.3.2 实现接口

可以通过实现接口管理生命周期:

  • 实现InitializingBean接口可以在属性设置完成后执行初始化操作。
  • 实现DisposableBean接口可以在容器关闭前执行销毁操作。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class User implements InitializingBean, DisposableBean {
private Integer id;
public User() {
System.out.println("执行构造方法");
}
public Integer getId() {
System.out.println("执行Get使用方法");
return id;
}
@Value("1")
public void setId(Integer id) {
System.out.println("执行Set设置方法");
this.id = id;
}
public void initMethodAttr() {
System.out.println("执行属性自定义初始化方法");
}
public void destroyMethodAttr() {
System.out.println("执行属性自定义销毁方法");
}
@PostConstruct
public void initMethodFunc() {
System.out.println("执行注解自定义初始化方法");
}
@PreDestroy
public void destroyMethodFunc() {
System.out.println("执行注解自定义销毁方法");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("执行InitializingBean的afterPropertiesSet方法");
}
@Override
public void destroy() throws Exception {
System.out.println("执行DisposableBean的destroy方法");
}
}

结果:

log
1
2
3
4
5
6
7
8
9
10
执行构造方法
执行Set设置方法
执行注解自定义初始化方法
执行InitializingBean的afterPropertiesSet方法
执行属性自定义初始化方法
执行Get使用方法
1
执行注解自定义销毁方法
执行DisposableBean的destroy方法
执行属性自定义销毁方法

3.3.3 后置处理器

后置处理器允许在对象的生命周期的不同阶段进行干预,主要有两种类型:

  • BeanPostProcessor:在对象初始化前后进行处理,可以修改对象的实例。
  • BeanFactoryPostProcessor:在BeanFactory初始化后,并且在对象实例化之前进行处理,可以修改对象的定义。
3.3.3.1 对象后置处理器

实现BeanPostProcessor接口需要重写两个方法:

  • postProcessBeforeInitialization:在对象初始化前调用,可以修改对象属性。
  • postProcessAfterInitialization:在对象初始化后调用,可以返回代理对象。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class DemoPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof User) {
System.out.println("执行BeanPostProcessor的postProcessBeforeInitialization方法");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof User) {
System.out.println("执行BeanPostProcessor的postProcessAfterInitialization方法");
}
return bean;
}
}

结果:

log
1
2
3
4
5
6
7
8
9
10
11
12
执行构造方法
执行Set设置方法
执行BeanPostProcessor的postProcessBeforeInitialization方法
执行注解自定义初始化方法
执行InitializingBean的afterPropertiesSet方法
执行属性自定义初始化方法
执行BeanPostProcessor的postProcessAfterInitialization方法
执行Get使用方法
1
执行注解自定义销毁方法
执行DisposableBean的destroy方法
执行属性自定义销毁方法
3.3.3.2 对象工厂后置处理器

实现BeanFactoryPostProcessor接口需要重写postProcessBeanFactory()方法,在BeanFactory初始化后调用,可以修改对象定义。

示例:

java
1
2
3
4
5
6
7
@Component
public class DemoFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("执行BeanFactoryPostProcessor的postProcessBeanFactory方法");
}
}

结果:

log
1
2
3
4
5
6
7
8
9
10
11
12
13
执行BeanFactoryPostProcessor的postProcessBeanFactory方法
执行构造方法
执行Set设置方法
执行BeanPostProcessor的postProcessBeforeInitialization方法
执行注解自定义初始化方法
执行InitializingBean的afterPropertiesSet方法
执行属性自定义初始化方法
执行BeanPostProcessor的postProcessAfterInitialization方法
执行Get使用方法
1
执行注解自定义销毁方法
执行DisposableBean的destroy方法
执行属性自定义销毁方法

评论