SpringIOC常用注解和接口


IOC Bean注入相关

前提了解

BeanDefinition知识点

组件型注解

使用@Component作为元注解的注解可理解为组件型注解,如常见的@Configuration、@Controller、@Service等

依赖注入注解

@Required(已弃用)

用于setter方法注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class ExampleBean3 {

private String name;

public String getName() {
return name;
}

@Required
public void setName(@Value("abc") String name) {
this.name = name;
}
}

@Autowired

  • 根据类型注入,从容器中寻找与该属性class相同的bean进行匹配并注入,如果找到多个则抛出异常,找不到则该属性为null;

  • 提供required属性,表示非必须注入,即容器中找不到bean进行注入则拉倒

  • 支持@Primary

    @Primary使用

    有两个同类型不同名称的bean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Configuration
    public class ExampleConfig3 {
    @Primary
    @Bean("bean3")
    public ExampleBean3 exampleBean3() {
    return new ExampleBean3();
    }
    @Bean("bean32")
    public ExampleBean3 exampleBean3(@Value("abc") String a) {
    return new ExampleBean3();
    }
    }

    由于@Autowired是根据class进行注入的,没有@Primary注解的情况下,此处注入会抛出异常,有了@Primary注解使该bean成为@Autowired的首选。

    1
    2
    3
    4
    5
    6
    @Service
    public class ExampleService {
    @Autowired
    private ExampleBean3 bean;
    // ...
    }

@Qualifier

指定名称注入,搭配@Autowired使用,解决@Autowired多个匹配的选择问题

@Resource(JSR250规范提供)

javax的注解,定义了资源相关的信息。在IOC中根据bean名称注入,不支持@Primary,不提供required属性

@Inject(JSR330规范提供)

拥有@Autowired的功能,支持@Primary,不提供required属性


>>> 开始进阶

@ComponentScan自定义过滤方式

@ComponentScan如果什么都不写,默认 basePackages 为当前类所在包,过滤类型为上述组件型注解的类。

@ComponentScan的其它过滤方式很常见了,不再赘述,使用@ComponentScan的过滤类型中的 CUSTOM 类型自定义过滤方式

1
2
3
4
5
6
7
8
public enum FilterType {
ANNOTATION, // 注解
ASSIGNABLE_TYPE,// 类
ASPECTJ, // 切点表达式
REGEX, // 正则
CUSTOM;// 自定义
//* 构造方法... */
}

自定义FilterType类型过滤器,实现 org.springframework.core.type.filter.TypeFilter 接口

1
2
3
public class ExampleBean {
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyTypeFilter implements TypeFilter {
/**
* @param metadataReader 当前类信息
* @param metadataReaderFactory 其它类信息
* @return
* @throws IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 这里简单处理,根据className选择可匹配通过的类
String className = metadataReader.getClassMetadata().getClassName();
boolean b = ExampleBean.class.getName().equals(className);
return b;
}
}

在@Configuration上使用

1
2
3
4
5
6
7
8
9
@ComponentScan(
basePackages = "ws.spring.insight.bean.injection",
useDefaultFilters = false,// 默认规则不生效,即仅扫描 basePackages包下符合includeFilters 规则下的bean
includeFilters = {
// 自定义扫描规则
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})})
@Configuration
public class ExampleConfig {
}

spring会扫描 basePackages 指定包下的所有类,符合过滤规则的都会加入IOC中

@Conditional 条件注入

简单了解:@Conditional在SpringBoot中大量使用,因为SpringBoot父工程集成了很多maven依赖,也就是集成了很多组件,但是子工程不一定能用得到,所以在IOC启动时,通过@Conditional注解进行条件注入,把需要的组件加入IOC中,子工程引入对应的starter时,对应的组件注入条件也就为true,即激活,也符合了SpringBoot特点,开关式的配置(不然你以为application配置文件有个开关图标是干嘛的)。

实现org.springframework.context.annotation.Condition接口

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
public class MyConditional implements Condition {

/**
*
* @param conditionContext 判断条件能使用的上下文环境
* @param annotatedTypeMetadata 注解信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {

// 可获取ioc bean工厂
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
// 获取目标类的加载信息
ClassLoader classLoader = conditionContext.getClassLoader();
// 获取环境信息
Environment environment = conditionContext.getEnvironment();
// 获取bean定义器,如查看是否有某个类已经注册到容器中
BeanDefinitionRegistry registry = conditionContext.getRegistry();

// 简单处理,获取操作系统名称
String osName = environment.getProperty("os.name");
return osName != null;// true
}
}

在组件型注解或@Bean上使用

1
2
3
4
5
6
7
8
9
10
11
@Conditional(MyConditional.class)// 使用自定义条件注入器,决定bean是否可以注入IOC中,搭配组件型注解使用
@Configuration
public class ExampleConfig {
// @Conditional也可用于方法上,搭配@Bean
// @Conditional(MyConditional.class)
@Bean("beanA2")
public BeanA beanA2() {

return new BeanA();
}
}

@Import 引入组件

支持类型

@Import 支持的value为class,支持三种类型:

  • 普通bean,被@Configuration注解作用的类也暂且叫做普通bean,Spring会根据该bean注解及接口等信息进行对应的操作,如遇到@Configuration则进行对应配置,遇到@Aspect时进行AOP代理等等
  • ImportSelector接口实现类
  • ImportBeanDefinitionRegistrar接口实现类
1
2
3
4
5
6
7
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}

ImportSelector接口实现类

1
2
3
4
5
6
7
8
9
10
11
12
public class MyImportSelect implements ImportSelector {

/**
*
* @param annotationMetadata 标注使用@Import注解的类的注解信息
* @return 禁止返回null
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"ws.spring.insight.bean.ExampleBeanB"};
}
}

ImportBeanDefinitionRegistrar接口实现类

1
2
3
4
5
6
7
8
9
10
11
12
public class MyImportRegister implements ImportBeanDefinitionRegistrar {
/**
*
* @param importingClassMetadata 标注使用@Import注解的类的注解信息
* @param registry bean定义器,可进行bean定义相关操作
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

registry.registerBeanDefinition("testBean2",new RootBeanDefinition(BeanB.class));
}
}

使用方式

搭配@Configuration使用

1
2
3
4
5
6
7
8
@Import({
BeanA.class,// 1、普通类,通过bean的class导入,beanName为全类名
MyImportSelect.class,// 2、通过选择器导入
MyImportRegister.class// 3、通过注册器导入
})
@Configuration
public class ExampleConfig2 {
}

@PropertySource与@PropertySources

value-properties.properties的配置文件:properties配置文件的方式仅支持spel表达式

1
2
3
admin.name=admin
admin.age=18
admin.email=email

使用配置文件装载一个bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// @PropertySources // 指定多个配置文件
@PropertySource("classpath:value-properties.properties")// 指定配置文件
@Component
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Admin {
// @Value支持spel表达式
@Value("${admin.name}")
private String name;
@Value("${admin.age}")
private Integer age;
@Value("${admin.email}")
private String email;
@Value("#{12*2}")
private int number;
}

@ConfigurationProperties(SpringBoot特有)

yml配置文件:yml支持松散绑定、支持JSR303数据校验、支持复杂类型填充、不支持spel表达式

1
2
3
4
5
6
7
8
9
10
11
12
# 松散绑定 root-admin、root_admin <=> rootAdmin,故yml的属性的键不可大写
root-admin:
username: rootadmin
password: pwd123456
age: 10
email: 1245689668@qq.com
birth: 2020/10/1
map: {k1: v1, k2: v2} # 支持map
list: # 支持 list集合 数组等
- abd
- 123
- CDF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@ConfigurationProperties(prefix = "root-admin") // prefix不能有大写字母,RootAdmin对应 root-admin 属性 => 松散绑定
@AllArgsConstructor
@NoArgsConstructor
@Data
@Validated // 开启JSR303 数据检验
@Component
public class RootAdmin {

private String username;
private String password;
private Integer age;
// 此注解因JSR303检验开启而生效
// 在创建这个bean的时候,填充到email的值不是邮箱地址格式则抛出异常,异常的message为注解指定的message
// 其它JSR303的注解以此类推
@Email(message = "配置参数必须为邮箱")
private String email;
private Date birth;
private Map<String,String> map;
private List<String> list;
}

Spring在创建带有@Validated注解(见下文)的bean的时候,就会对其属性进行JSR303数据校验。

FactoryBean

实现FactoryBean接口成为工厂bean,最终加入Spring IOC容器的bean有两个:

  • bean名称 &myFactoryBean,实例是 MyFactoryBean 工厂bean
  • bean名称 myFactoryBean,但实例是 ExampleBeanD 对象的实例,调用了getObject()方法。可通过其它方式修改bean名称,如@Component的value属性

工厂bean的名称 = 目标bean名称前 + &,通过bean名称获取工厂bean时,只需在bean名称前加“&”;

获取bean时,先获取工厂Bean再调用getObject()方法;bean工厂与bean的作用域相同

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
@Component
public class MyFactoryBean implements FactoryBean<ExampleBeanD> {

// 注册时,注册了两个bean的信息
// bean名称 myFactoryBean,但实例是 ExampleBeanD 对象的实例
// bean名称 &myFactoryBean,实例是 MyFactoryBean 工厂bean

/**
* spring 调用此方法将返回的对象会加入到容器中
* @return
* @throws Exception
*/
@Override
public ExampleBeanD getObject() throws Exception {

return new ExampleBeanD();
}

@Override
public Class<?> getObjectType() {
return ExampleBeanD.class;
}

@Override
public boolean isSingleton() {

return true;
}
}

Bean初始化

  • 实现InitializingBean, DisposableBean接口(官方不建议,与Spring框架强耦合)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @Component
    public class ExampleInitBean implements InitializingBean, DisposableBean {

    /**
    * 初始化方法
    * @throws Exception
    */
    @Override
    public void afterPropertiesSet() throws Exception {

    }

    /**
    * 销毁方法
    * @throws Exception
    */
    @Override
    public void destroy() throws Exception {

    }
    }
  • @Bean中指定初始化方法与销毁方法(适用于导入第三方库的bean时)

    1
    2
    3
    4
    5
    6
    7
    8
    @Configuration
    public class AtBean {

    @Bean(value = "exampleBean",initMethod = "init",destroyMethod = "disposable")
    public ExampleBeanA exampleBeanA() {
    return new ExampleBeanA();
    }
    }
  • 使用@PostConstruct和@PreDestroy标注初始化和销毁方法(推荐)

    1
    2
    3
    4
    5
    6
    7
    @Component
    public class ExampleBeanE {
    @PostConstruct
    public void init() {}
    @PreDestroy
    public void disposable() {}
    }

初始化与销毁优先级

当一个bean中同时存在多个初始化方法时,如在@Bean中标注初始化方法与销毁方法、该bean又实现InitializingBean, DisposableBean接口、同时该bean使用了@PostConstruct和@PreDestroy注解标注方法。执行顺序如下:

@PostConstruct -> InitializingBean接口 -> @Bean(initMethod=””) -> @PreDestroy -> DisposableBean接口 -> @Bean(destroyMethod=””)

即:注解 > 接口 > @Bean标注

Bean实现xxxAware接口

bean实现Spring中xxxAware接口可以拿到对应的Spring组件,如图(仅展示部分)

aware

BeanFactoryAware

BeanFactory是顶层接口,功能少,面向Spring框架,不建议使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class ExampleBeanFactoryBean implements BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
// 瓷锤获取bean工厂
// beanFactory.getBean(""); // 获取bean
// beanFactory.containsBean(""); // 是否包含指定bean

// 转成 DefaultListableBeanFactory 可添加bean
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory)beanFactory;
// 注册单例bean
defaultListableBeanFactory.registerSingleton("exampleBean",new ExampleBeanG());
}
}

ApplicationContextAware

ApplicationContext扩展了BeanFactory接口,功能丰富,面向开发者,建议使用;

setApplicationContext()方法调用时机:ApplicationContextAware是在spring初始化完bean后才注入上下文的

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class ExampleBeanApplicationContextAware implements ApplicationContextAware {

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 此处获取 applicationContext
// 获取环境
Environment environment = applicationContext.getEnvironment();
environment.getProperty("os.name");// 获取操作系统名称
// applicationContext.getBean("beanName"); // 获取bean
}
}

EmbeddedValueResolverAware

提供String 解析器

1
2
3
4
5
6
7
8
9
10
11
@Component
public class ExampleBeanEmbeddedValueResolverAware implements EmbeddedValueResolverAware {
@Override
public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
// String 解析器,支持spel表达式,可获取配置文件信息
// os.name 操作系统名称 Windows 7
// logging.level.root 配置文件中的值:info
String str = stringValueResolver.resolveStringValue("aaa${os.name}===#{12*2}+${logging.level.root}");
System.out.println(str);// aaaWindows 7===24+info
}
}

接口方法调用时机

xxxAware接口方法的调用时机:每个Aware接口一般都用对应的xxxProcessor来处理调用该接口bean的方法,如ApplicationContextAware对应ApplicationContextAwareProcessor。可以看到ApplicationContextAwareProcessor实现了BeanPostProcessor接口,即bean后置处理器,也就是在bean即将可用之前,处理一波。其它Aware接口类似。

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
class ApplicationContextAwareProcessor implements BeanPostProcessor {
// ...
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment());
}

if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}

if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext);
}

if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext);
}

if (bean instanceof MessageSourceAware) {
((MessageSourceAware)bean).setMessageSource(this.applicationContext);
}

if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
}

}
}

Spring 钩子

BeanFactoryPostProcessor和BeanPostProcessor这两个接口都是初始化bean时对外暴露的入口之一;

BeanFactoryPostProcessor

bean工厂的bean属性后置处理容器,也就是说,Spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其它的bean之前读取配置元数据,并有可能修改它。

1
2
3
4
5
6
7
8
9
10
11
@Component
public class ExampleBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

// 获取BeanDefinition 可对其进行任何操作
// BeanDefinition beanDefinition = beanFactory.getBeanDefinition("");
// 获取单例bean数量
// int singleSize = beanFactory.getSingletonCount();
}
}

BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口,BeanFactoryPostProcessor的作用是在Spring Bean的定义信息已经加载但还没有初始化的时候执行postProcessBeanFactory()来处理一些额外的逻辑,而BeanDefinitionRegistryPostProcessor的作用是在BeanFactoryPostProcessor增加了一个前置处理,当一个Bean实现了该接口后,始化前先执行该接口的postProcessBeanDefinitionRegistry()方法,然后再执行其父类的方法postProcessBeanFactory()。这样就把一个Spring Bean的初始化周期更加细化,让我们在各个阶段有定制它的可能。

1
2
3
4
5
6
7
8
9
10
11
@Component
public class ExampleBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 先执行
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 后执行
}
}

BeanPostProcessor

bean属性后置处理器,方法也是见名知意,实现BeanPostProcessor接口可以在Bean(实例化之后)初始化的前后做一些自定义的操作,但是拿到的参数只有BeanDefinition实例和BeanDefinition的名称,也就是无法修改BeanDefinition元数据,这里说的Bean的初始化是:

1)bean实现了InitializingBean接口,对应的方法为afterPropertiesSet

2)在bean定义的时候,通过init-method设置的方法

PS:BeanFactoryPostProcessor回调会先于BeanPostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class ExampleBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// bean初始化前执行
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// bean初始化后执行
return bean;
}
}