Spring註解補充(一)

情韻發表於2023-02-25

註解補充

挑一些常用,但是深入不多的總結一下。

Bean的生命週期

在@Bean註解中,新增init屬性destroy屬性

@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
public User user() {
    return new User();
}

使用@PostContructor@PreDestory

官網描述:

官網說這 JSR-250@PostConstruct@PreDestroy註解通常被認為是在現代Spring應用程式中接收生命週期回撥的最佳實踐。

image-20230225092910663

image-20230225092658826

程式碼實現:

自己定義初始化和結束的方法,分別加上兩個註解。

@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();
    }
}

執行結果:

image-20230225093954895

可以發現執行順序: JSR250的兩個註解 > 實現Spring提供的介面 > 在@Bean中的屬性

簡單檢視原始碼

自己構建Spring原始碼專案真的很好用,閱讀起來也方便,還能自己加註釋。

當前版本: Spring 5.3.x

將斷點打在設定的Bean屬性的初始化方法上,透過Debug的呼叫棧可以發現:

image-20230225144345960

image-20230225144304923

繼續把Debug除錯在1800行:

image-20230225144602503

當beanName為user時,JSR250已經執行。

image-20230225144709326

執行下一步:

image-20230225144754714

可以發現 實現Spring的初始化和關閉的介面以及給Bean新增初始化方法和關閉的方法是在一起執行的。

繼續跟進1800行的程式碼,給這兩行程式碼打上斷點:

image-20230225145022711

繼續Debug:

image-20230225145127038

執行下一步:

image-20230225145145882

繼續執行下一步:

image-20230225145209004

可以發現Spring提供的兩個介面執行順序在自己給Bean新增屬性的上面,所以先執行實現那兩個介面的方法。


繼續給User的JSR250註解的初始化方法加上斷點:

image-20230225145617029

繼續執行下一步:

image-20230225145643990

image-20230225145654755

image-20230225145704960

可以發現在1796行的程式碼就是JSR250註解的方法的入口。

image-20230225145824787

所以 對比三個初始化方法的原始碼位置,可以發現:JSR250 > 實現Spring的兩個介面 > Bean屬性新增方法

額外補充:在1796行程式碼繼續跟進,發現裡面是對BeanPostProcessor的處理。這個後面再繼續補充。

image-20230225151202957

@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);
        }

    }

}

image-20230225101619018

實現ImportSelector介面

image-20230225102210549

在配置類上匯入:

 @ComponentScan("cn.lele")
 @Import({User.class, MySelector.class})
 public class AppConfig {


 }

檢視匯入Bean的名字:

image-20230225102141193

實現MyImportBeanDefinitionRegistar介面

我們可以在ioc容器建立時,管理並且註冊bean。透過實現MyImportBeanDefinitionRegistar介面。

image-20230225103115989

在配置類上匯入:

 @ComponentScan("cn.lele")
 @Import({MyImportBeanDefinitionRegistrar.class, MySelector.class})
 public class AppConfig {


 }

檢視容器中所有Bean的名字:

image-20230225103215116

@CompnentScan包掃描

這個註解還是有一些小坑的......

包含include小坑

image-20230225152400035

CompnentScan註解預設開啟自動檢測帶有@Component@Repository@Service@Controller。所以就會有一個坑,當我們預設去include的時候,如果不關閉預設過濾,新增將不會發揮作用。

當我們只想載入Service註解和Controller註解的時候,如果不取消預設的掃描,包含將會失效:

image-20230225153416297

取消預設掃描:

image-20230225153606111

排除不受影響

自定義掃描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 {



}

執行結果:

image-20230225154920468

相關文章