注解补充
挑一些常用,但是深入不多的总结一下。
Bean的生命周期
在@Bean注解中,添加init属性
和destroy属性
@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
public User user() {
return new User();
}
使用@PostContructor
和@PreDestory
官网描述:
官网说这 JSR-250
的@PostConstruct
和@PreDestroy
注解通常被认为是在现代Spring应用程序中接收生命周期回调的最佳实践。
代码实现:
自己定义初始化和结束的方法,分别加上两个注解。
@PostConstruct
public void initJSR250(){
System.out.println("User.initJSR250");
}
@PreDestroy
public void destroyJSR250(){
System.out.println("User.destroyJSR250");
}
实现InitializingBean
以及DisposableBean
接口
代码实现:
public class User implements InitializingBean, DisposableBean {
......
@Override
public void destroy() throws Exception {
System.out.println("User.destroy ---- bean");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("User.afterPropertiesSet ----- bean");
}
}
这些声明周期的注解、方法或者属性有着明显的先后顺序。
简单测试:
/*
* Copyright (c) 2006, 2023, wuyahan 编写
*
*/
package cn.lele.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* <p>Project: ssm - User
* <p>Powered by wuyahan On 2023-02-24 09:36:11
*
* @author wuyahan [tianwenle2000@163.com]
* @version 1.0
* @since 17
*/
@Data
@AllArgsConstructor
public class User implements InitializingBean, DisposableBean {
private int id;
private String name;
private boolean sex;
public User(){
System.out.println("User类的构造方法执行");
}
public void save(){
System.out.println("User.save方法执行了");
}
@PostConstruct
public void initJSR250(){
System.out.println("User.initJSR250---------->@PostConstruct");
}
@PreDestroy
public void destroyJSR250(){
System.out.println("User.destroyJSR250----------->@PreDestroy");
}
public void initMethod(){
System.out.println("User.initMethod------->@Bean属性");
}
public void destroyMethod(){
System.out.println("User.destroyMethod-------->@Bean属性");
}
@Override
public void destroy() throws Exception {
System.out.println("User.destroy ----> 实现Spring提供的接口");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("User.afterPropertiesSet ----> 实现Spring提供的接口");
}
}
运行测试:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
User user = context.getBean(User.class);
user.save();
context.close();
}
}
运行结果:
可以发现执行顺序: JSR250的两个注解
> 实现Spring提供的接口
> 在@Bean中的属性
简单查看源码
自己构建Spring源码项目真的很好用,阅读起来也方便,还能自己加注释。
当前版本: Spring 5.3.x
将断点打在设置的Bean属性的初始化方法上,通过Debug的调用栈可以发现:
继续把Debug调试在1800行:
当beanName为user时,JSR250
已经执行。
执行下一步:
可以发现 实现Spring的初始化和关闭的接口
以及给Bean添加初始化方法和关闭的方法
是在一起执行的。
继续跟进1800行的代码,给这两行代码打上断点:
继续Debug:
执行下一步:
继续执行下一步:
可以发现Spring提供的两个接口执行顺序在自己给Bean添加属性的上面,所以先执行实现那两个接口的方法。
继续给User的JSR250
注解的初始化方法加上断点:
继续执行下一步:
可以发现在1796行的代码就是JSR250注解的方法的入口。
所以 对比三个初始化方法的源码位置,可以发现:JSR250
> 实现Spring的两个接口
> Bean属性添加方法
。
额外补充:在1796行代码继续跟进,发现里面是对BeanPostProcessor的处理。这个后面再继续补充。
@Import导入
基本导入
注入一个类:
public class User {
}
在配置类上导入:
@ComponentScan("cn.lele")
@Import(User.class)
public class AppConfig {
}
输出容器中Bean的名字:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name = " + name);
}
}
}
实现ImportSelector接口
在配置类上导入:
@ComponentScan("cn.lele")
@Import({User.class, MySelector.class})
public class AppConfig {
}
查看导入Bean的名字:
实现MyImportBeanDefinitionRegistar接口
我们可以在ioc容器创建时,管理并且注册bean。通过实现MyImportBeanDefinitionRegistar
接口。
在配置类上导入:
@ComponentScan("cn.lele")
@Import({MyImportBeanDefinitionRegistrar.class, MySelector.class})
public class AppConfig {
}
查看容器中所有Bean的名字:
@CompnentScan包扫描
这个注解还是有一些小坑的......
包含include小坑
CompnentScan注解
默认开启自动检测带有@Component
、@Repository
、@Service
、@Controller
。所以就会有一个坑,当我们默认去include的时候,如果不关闭默认过滤,添加将不会发挥作用。
当我们只想加载Service注解和Controller注解的时候,如果不取消默认的扫描,包含将会失效:
取消默认扫描:
排除不受影响
自定义扫描CUSTOM
自定义扫描需要实现TypeFilter
接口
public class MyTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取Bean的名字
String className = metadataReader.getClassMetadata().getClassName();
// 包含Dao的允许加载
if (className.toUpperCase().contains("DAO")){
return true; //允许加载
}
return false; // 不允许加载
}
}
type选择为FilterType.CUSTOM
@Configuration
@ComponentScan(
value = "cn.lele" ,
includeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class}),
useDefaultFilters = false // 包含都需要关闭这个默认过滤
)
public class AppConfig {
}
运行结果: