Spring那些容易忽视但非常有用的注解(史上最强整理)
共 7429字,需浏览 15分钟
·
2022-04-17 09:32
点击关注公众号,Java干货及时送达
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技术迷专栏作者 投稿,未经允许请勿转载