Spring那些容易忽视但非常有用的注解(史上最强整理)

Java技术迷

共 7429字,需浏览 15分钟

 · 2022-04-17

点击关注公众号,Java干货及时送达

作者 | 汪伟俊 
出品 | Java技术迷(ID:JavaFans1024)


 InitializingBean DisposableBean


先介绍组件的生命周期方法,在前面,我们了解到组件的生命周期方法是initMethod和destroyMethod,其实,组件的生命周期方法有很多,我们一一来看。

InitializingBean接口提供了一个方法afterPropertiesSet,该方法会在对象属性被设置后,即:调用了setter方法之后被调用,事实上, 在该方法调用之前,还有生命周期方法会被调用,比如Bean的前置处理器,这些我们后面再聊,首先来实现InitializingBean接口:

public class User implements InitializingBean {

private String name;

public String getName() {
return name;
}

public void setName(String name) {
System.out.println("setName...");
this.name = name;
}

@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet...");
}
}

尝试获取User对象,控制台输出结果如下:

setName...
afterPropertiesSet...

与之对应的是DisposableBean接口,该接口提供了destroy方法,Spring会在组件被销毁时调用它,但它的调用是在destroyMethod方法之前的,一定要注意,使用方法和InitializingBean一致:

public class User implements DisposableBean {

private String name;

public String getName() {
return name;
}

public void setName(String name) {
System.out.println("setName...");
this.name = name;
}

@Override
public void destroy() throws Exception {
System.out.println("destroy...");
}
}


BeanPostProcessor


BeanPostProcessor,意为Bean的后置处理器,这是Spring提供的一个接口,该接口有两个生命周期方法,用法非常简单:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("前置处理...");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理...");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}

一定要把后置处理器注册到容器中才会生效,而且后置处理器会对被注册到容器中的每个组件生效,也就是说,每个组件的生命周期中都需要经历这两个方法。

感知接口


在某些情况下,我们需要获取Spring提供的一些组件,比如经常使用的ApplicationContext,但是有些场景下始终无法得到,这个时候我们可以使用Spring的感知接口,如下:

1.ResourceLoaderAware:资源加载器感知接口2.BeanNameAware:Bean配置的的名字感知接口3.ApplicationContextAware:应用上下文感知接口4.BeanFactoryAware:Bean工厂感知接口5.MessageSourceAware:MessageSource感知接口6.ApplicationEventPublisherAware:ApplicationEventPublisher感知接口

感知接口均提供了对应的方法将组件提供给开发者,比如:

public class User implements ApplicationContextAware {

private ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

感知接口的方法也处在组件的生命周期中,到这里,所有的组件生命周期方法就介绍完了,我们先对生命周期进行一个总结,写一个例子来感受一下每个生命周期方法的调用时机:

public class User implements ApplicationContextAware, InitializingBean, DisposableBean {

private String name;
private Integer age;

public User() {
System.out.println("1--》创建User实例");
}

public void setName(String name) {
this.name = name;
System.out.println("2--》设置User的name属性");
}

public void setAge(Integer age) {
this.age = age;
System.out.println("2--》设置User的age属性");
}

public void init() {
System.out.println("6--》调用init-method属性指定的方法");
}

public void myDestroy() {
System.out.println("9--》调用destroy-method属性指定的方法");
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("3--》调用对应Aware接口的方法");
}

@Override
public void afterPropertiesSet() throws Exception {
System.out.println("5--》调用InitializingBean接口的afterPropertiesSet方法");
}

@Override
public void destroy() throws Exception {
System.out.println("8--》调用DisposableBean接口的destroy方法");
}
}

编写一个Bean的后置处理器:

public class MyBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("7--》调用MyBeanPostProcessor的postProcessBeforeInitialization方法");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("4--》调用MyBeanPostProcessor的postProcessAfterInitialization方法");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
}

运行结果如下:

1--》创建User实例
2--》设置User的name属性
2--》设置User的age属性
3--》调用对应Aware接口的方法
4--》调用MyBeanPostProcessor的postProcessAfterInitialization方法
5--》调用InitializingBean接口的afterPropertiesSet方法
6--》调用init-method属性指定的方法
7--》调用MyBeanPostProcessor的postProcessBeforeInitialization方法
8--》调用DisposableBean接口的destroy方法
9--》调用destroy-method属性指定的方法

@PropertySource


该注解用于绑定外部的配置文件,通常与@Value注解配合使用,比如我们有如下的一段关于数据源的配置:

jdbc.url=jdbc:mysql:///test
jdbc.driver=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=root

将其绑定到组件中:

@PropertySource("classpath:jdbc.properties")
@Component
public class MyDataSource {

@Value("${jdbc.url}")
private String url;
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
}

测试代码:

public static void main(String[] args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyDataSource myDataSource = context.getBean("myDataSource", MyDataSource.class);
System.out.println(myDataSource);
}

运行结果如下:

MyDataSource(url=jdbc:mysql:///test, driver=com.mysql.jdbc.Driver, username=root, password=root)

@Inject

我们来回顾一下关于自动装配的方式:

1.@Autowired2.@Resource

@Autowired注解默认按类型装配,若是有两个相同类型的组件,则会装配失败,可以配合@Qualifier注解一起使用;而@Resource注解既可以按类型装配,也可以按名字装配,这里我们介绍第三种装配方式:@Inject

@Component
public class User {

@Inject
private Pet pet;
}

@Inject注解默认也是按照类型装配的,所以当要装配的组件有多个相同类型可选择时,仍然会抛出熟悉的异常:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.wwj.spring.demo.entity.Pet' available: expected single matching bean but found 2: cat,dog

可以配合@Named注解指定具体装配哪个组件:

@Component
public class User {

@Inject
@Named("cat")
private Pet pet;
}

需要注意的是@Inject注解是没有required属性的,也就是说,@Inject无法做到像@Autowired注解那样,当组件不存在时也不会抛出异常。

@Inject注解还需要引入javax扩展包才能够使用,坐标如下:


<groupId>javax.injectgroupId>
<artifactId>javax.injectartifactId>
<version>1version>


@Profile


与@Conditional注解类似,@Profile注解也可以用于指定条件注册组件,不过,@Profile一般用来限制环境条件,比如在测试环境下注册哪些组件,在开发环境注册哪些组件和在生产环境注册哪些组件,看下面的一个例子:

@Configuration
public class MyConfiguration {

@Profile("dev")
@Bean
public User devUser() {
return new User("开发");
}

@Profile("test")
@Bean
public User testUser() {
return new User("测试");
}

@Profile("prod")
@Bean
public User prodUser() {
return new User("生产");
}
}

在配置类中配置了三个组件,它们分别对应不同的环境,此时添加虚拟机参数:-Dspring.profiles.active=dev,则被注册到容器中的组件为:

devUser

若将环境修改为test,则被注册的组件为testUser,prod环境同理。

我们也可以使用代码设置环境信息,从而决定需要注册的组件:

public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//设置需要激活的环境
context.getEnvironment().setActiveProfiles("test");
//注册主配制类
context.register(MyConfiguration.class);
//启动刷新容器
context.refresh();
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
}
汪伟俊 为Java技术迷专栏作者 投稿,未经允许请勿转载

    

1SQL

2 Chrome

3SpringBoot44Java

4QQ线

5SpringBoot 

浏览 11
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报