1. Bean的创建
1.1 xml配置
<bean id="xxxx" class="xxxx.xxxx"/>
1.2 注解
@Component
@Service
@Controler
@Repository
1.3 其他注解
@Bean 第三方Bean
@Configuration 声明配置类
1.4 @Import
@Import(User.class)
public class SpringConfig {
}
2
3
4
public class App {
public static void main(String[] args) {
// ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
User bean = context.getBean(User.class);
System.out.println(bean);
}
2
3
4
5
6
7
8
1.5 ImportSelector
使用ImportSelector或者ImportBeanDefinitionRegistrar接口,配合@Import实现
package com.mszlu.domain;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{User.class.getName()};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.mszlu.config;
import com.mszlu.domain.MyImportSelector;
import com.mszlu.domain.User;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan("com.mszlu")
//@Import(User.class)
@Import(MyImportSelector.class)
public class SpringConfig {
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Im
package com.mszlu.domain;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
BeanDefinition beanDefinition = new RootBeanDefinition(User.class.getName());
beanDefinitionRegistry.registerBeanDefinition(User.class.getName(),beanDefinition);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.mszlu.config;
import com.mszlu.domain.MyImportBeanDefinitionRegistrar;
import com.mszlu.domain.MyImportSelector;
import com.mszlu.domain.User;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan("com.mszlu")
//@Import(User.class)
//@Import(MyImportSelector.class)
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringConfig {
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1.6 手动注入
有些场景下需要代码动态注入,以上方式都不适用。这时就需要创建 对象手动注入。
通过DefaultListableBeanFactory注入。
registerSingleton(String beanName,Object object);
示例:
package com.mszlu.domain;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
public class UserRegistrar implements BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory)beanFactory;
//方式一
// BeanDefinition beanDefinition = new RootBeanDefinition(User.class);
// listableBeanFactory.registerBeanDefinition(User.class.getName(),beanDefinition);
//方式二
User user = new User();
listableBeanFactory.registerSingleton(User.class.getName(),user);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.mszlu.config;
import com.mszlu.domain.MyImportBeanDefinitionRegistrar;
import com.mszlu.domain.MyImportSelector;
import com.mszlu.domain.User;
import com.mszlu.domain.UserRegistrar;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan("com.mszlu")
//@Import(User.class)
//@Import(MyImportSelector.class)
//@Import(MyImportBeanDefinitionRegistrar.class)
@Import(UserRegistrar.class)
public class SpringConfig {
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2. Bean的作用域
在Bean容器启动会读取bean的xml配置文件,然后将xml中每个bean元素分别转换成BeanDefinition对象。在BeanDefinition对象中有scope 属性,就是它控制着bean的作用域。
Spring框架支持5种作用域,有三种作用域是当开发者使用基于web的ApplicationContext的时候才生效的。
作用域 | 描述 |
---|---|
单例(singleton) | (默认)每一个Spring IoC容器都拥有唯一的一个实例对象 |
原型(prototype) | 一个Bean定义,任意多个对象 |
请求(request) | 一个HTTP请求会产生一个Bean对象,也就是说,每一个HTTP请求都有自己的Bean实例。只在基于web的Spring ApplicationContext中可用 |
会话(session) | 限定一个Bean的作用域为HTTPsession的生命周期。同样,只有基于web的Spring ApplicationContext才能使用 |
全局会话(global session) | 限定一个Bean的作用域为全局HTTPSession的生命周期。通常用于门户网站场景,同样,只有基于web的Spring ApplicationContext可用 |
3. Bean的生命周期管理
ApplicationContext容器中,Bean的生命周期流程如上图所示,流程大致如下:
- 首先容器启动后,会对scope为singleton且非懒加载的bean进行实例化
- 按照Bean定义信息配置信息,注入所有的属性
- 如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该Bean的id,此时该Bean就获得了自己在配置文件中的id
- 如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的BeanFactory,这样该Bean就获得了自己所在的BeanFactory
- 如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入该Bean的ApplicationContext,这样该Bean就获得了自己所在的ApplicationContext
- 如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation()方法
- 如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法
- 如果Bean配置了init-method方法,则会执行init-method配置的方法
- 如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessAfterInitialization()方法
- 经过流程9之后,就可以正式使用该Bean了,对于scope为singleton的Bean,Spring的ioc容器中会缓存一份该bean的实例,而对于scope为prototype的Bean,每次被调用都会new一个新的对象,生命周期就交给调用方管理了,不再是Spring容器进行管理了
- 容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法,
- 如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法,至此,整个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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.mszlu" />
<!-- <bean id="userService" class="com.mszlu.service.UserService">-->
<!-- <property name="mailService" ref="mailService" />-->
<!-- </bean>-->
<!-- <bean id="mailService" name="mailService2" class="com.mszlu.service.MailService" scope="singleton"/>-->
<bean id="person1" destroy-method="myDestroy"
init-method="myInit" class="com.mszlu.domain.Person">
<property name="name">
<value>jack</value>
</property>
</bean>
<!-- 配置自定义的后置处理器 -->
<bean id="postProcessor" class="com.mszlu.domain.MyBeanPostProcessor" />
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.mszlu.domain;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class Person implements BeanNameAware, BeanFactoryAware,
ApplicationContextAware, InitializingBean, DisposableBean {
private String name;
public Person() {
System.out.println("Person类构造方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("set方法被调用");
}
//自定义的初始化函数
public void myInit() {
System.out.println("myInit被调用");
}
//自定义的销毁方法
public void myDestroy() {
System.out.println("myDestroy被调用");
}
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("destory被调用");
}
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("afterPropertiesSet被调用");
}
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("setApplicationContext被调用");
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
System.out.println("setBeanFactory被调用,beanFactory");
}
public void setBeanName(String beanName) {
// TODO Auto-generated method stub
System.out.println("setBeanName被调用,beanName:" + beanName);
}
public String toString() {
return "name is :" + name;
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.mszlu.domain;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean,
String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessBeforeInitialization被调用");
return bean;
}
public Object postProcessAfterInitialization(Object bean,
String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessAfterInitialization被调用");
return bean;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.mszlu;
import com.mszlu.config.SpringConfig;
import com.mszlu.domain.Person;
import com.mszlu.domain.User;
import com.mszlu.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Person bean = context.getBean(Person.class);
System.out.println(bean);
((ClassPathXmlApplicationContext)context).close();
// ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
// User bean = context.getBean(User.class);
// System.out.println(bean);
// UserService userService = (UserService) context.getBean("userService");
// userService.registerUser("ddd@mszlu.com","123456","ddd");
}
}
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
输出:
postProcessBeforeInitialization被调用
postProcessAfterInitialization被调用
postProcessBeforeInitialization被调用
postProcessAfterInitialization被调用
postProcessBeforeInitialization被调用
postProcessAfterInitialization被调用
Person类构造方法
set方法被调用
setBeanName被调用,beanName:person1
setBeanFactory被调用,beanFactory
setApplicationContext被调用
postProcessBeforeInitialization被调用
afterPropertiesSet被调用
myInit被调用
postProcessAfterInitialization被调用
postProcessBeforeInitialization被调用
postProcessAfterInitialization被调用
name is :jack
destory被调用
myDestroy被调用
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
4. List注入
有些时候,我们会有一系列相同的接口,不同实现类的Bean。例如,注册用户时,我们要对email、password和name这3个变量进行验证。
先定义接口:
public interface Validator {
void validate(String email, String password, String name);
}
2
3
分别使用3个Validator
对用户参数进行验证:
@Component
public class EmailValidator implements Validator {
public void validate(String email, String password, String name) {
if (!email.matches("^[a-z0-9]+\\@[a-z0-9]+\\.[a-z]{2,10}quot;)) {
throw new IllegalArgumentException("invalid email: " + email);
}
}
}
@Component
public class PasswordValidator implements Validator {
public void validate(String email, String password, String name) {
if (!password.matches("^.{6,20}quot;)) {
throw new IllegalArgumentException("invalid password");
}
}
}
@Component
public class NameValidator implements Validator {
public void validate(String email, String password, String name) {
if (name == null || name.isBlank() || name.length() > 20) {
throw new IllegalArgumentException("invalid name: " + name);
}
}
}
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
最后,我们通过一个Validators
作为入口进行验证
@Component
public class Validators {
@Autowired
List<Validator> validators;
public void validate(String email, String password, String name) {
for (var validator : this.validators) {
validator.validate(email, password, name);
}
}
}
2
3
4
5
6
7
8
9
10
11
注意到Validators
被注入了一个List<Validator>
,Spring会自动把所有类型为Validator
的Bean装配为一个List
注入进来,这样一来,我们每新增一个Validator
类型,就自动被Spring装配到Validators
中了,非常方便。
因为Spring是通过扫描classpath获取到所有的Bean,而List
是有序的,要指定List
中Bean的顺序,可以加上@Order
注解:
@Component
@Order(1)
public class EmailValidator implements Validator {
...
}
@Component
@Order(2)
public class PasswordValidator implements Validator {
...
}
@Component
@Order(3)
public class NameValidator implements Validator {
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
5. 可选注入
默认情况下,当我们标记了一个
@Autowired
后,Spring如果没有找到对应类型的Bean,它会抛出NoSuchBeanDefinitionException
异常。
可以给@Autowired
增加一个required = false
的参数:
@Component
public class MailService {
@Autowired(required = false)
ZoneId zoneId = ZoneId.systemDefault();
...
}
2
3
4
5
6
这个参数告诉Spring容器,如果找到一个类型为ZoneId
的Bean,就注入,如果找不到,就忽略。
这种方式非常适合有定义就使用定义,没有就使用默认值的情况。