註解補充
挑一些常用,但是深入不多的總結一下。
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 {
}
執行結果: