細說Spring——IoC詳解(@Conditional注解和生命周期)

一、前言

最近發現自己太喪了,因為是考試周,但是感覺考試及格就好,所以也無心復習,又因為馬上要放暑假了,歸心似箭啊,感覺有點厭學。這幾天都在看《士兵突擊》,挺勵志的一個電視劇,感覺還是不能就這樣喪下去,希望接下來的幾天加油吧。

二、@Conditional

@Conditional注解的作用是:按照一定的條件進行判斷,滿足條件后才在中容器中注入該組件。這個注解在SpringBoot的底層實現中大量的使用,學習一下有助于將來學習SpringBoot,我們先來看一下配置類:

@Configuration
public class MainConfig2 {
    @Conditional({WindowsCondition.class})
    @Bean("windows")
    public Person person01() {

        return new Person("windows", 22);
    }

    @Conditional({LinuxCondition.class})
    @Bean("linux")
    public Person person02() {

        return new Person("linux", 26);
    }
}

我用@Conditional注解標注了兩個方法,這兩個方法都是向容器中注入組件的方法,我們看一下@Conditional注解中的參數:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

    /**
     * All {@link Condition}s that must {@linkplain Condition#matches match}
     * in order for the component to be registered.
     */
    Class<? extends Condition>[] value();

}

我們可以看到參數是Condition的子類,上面已經實現了兩個,分別是:
WindowsCondition

public class WindowsCondition implements Condition {
    /**
     *
     * @param context   判斷條件能使用的上下文環境
     * @param metadata   注解信息
     * @return
     */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        //1、獲取ioc使用的BeanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

        //2、獲取類的加載器
        ClassLoader classLoader = context.getClassLoader();

        //3、獲取當前的運行時環境,不如當前的操作系統
        Environment environment = context.getEnvironment();

        //4、獲取bean定義的注冊類
        BeanDefinitionRegistry registry = context.getRegistry();

        //獲取當前的運行環境
        String property = environment.getProperty("os.name");
        if(property.contains("Windows")) {
            return true;
        }
        return false;
    }
}

LinuxCondition

public class LinuxCondition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //3、獲取當前的運行時環境,不如當前的操作系統
        Environment environment = context.getEnvironment();

        String property = environment.getProperty("os.name");
        if(property.contains("linux")) {
            return true;
        }
        return false;
    }
}

這兩個類是判斷當前的操作系統的,綜合配置類來看,就是如果當前操作系統為windows,就把名字為“windows”的組件放入容器,如果是linux系統,就把名字為“linux”的組件放入容器。然后我們就來看一下測試的結果吧:

這里寫圖片描述

我們可以看到容器中有windows組件,我們也可以同過虛擬機參數:-Dos.name=linux來測試一下linux的情況。這里只是簡單的@Conditional注解的用法,這個注解也可以作用與類上,說明只有滿足條件,這個類中的所有的組件注冊才能生效,在SpringBoot的自動配置類中大量的使用了這個注解,有興趣的可以自己研究一下。

三、生命周期

組件的生命周期是指:組件的創建,組件初始化,組件的銷毀。
這里組件的銷毀僅指在容器中單例的的組件,如果是多例的組件那么銷毀和容器是沒有關系的。
我在 細說Spring——IoC詳解(Bean的生命周期中講解過組件的生命周期,我們可以在XML中指定init-method和destory-method的方法來控制組件的生命周期,我們現在來學習一下使用注解的方法怎么控制組件的生命周期。
1、我們先來看第一種方法:
組件Car類

@Component
public class Car {
    public Car() {
        System.out.println("car constructor.....");
    }

    public void init() {
        System.out.println("car init......");
    }

    public void destory() {
        System.out.println("car destory......");
    }
}

配置類:

@Configuration
@ComponentScan("com.jiayifan.bean")
//@Import(value = Cat.class)
public class MainConfigOfLifeCycle {

    @Bean(initMethod = "init", destroyMethod = "destory")
    public Car car() {
        return new Car();
    }

}

我們可以在組件類中寫一個初始化方法和銷毀方法,然后在配置類中使用@BeaninitMethoddestroyMethod來指定相應的方法。
然后我們測試一下:

public class IOCTest_lifeCycle {

    @Test
    public void test01() {
        //1、創建IOC容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
        System.out.println("容器創建完成");
        //關閉容器
        applicationContext.close();

    }
}

這里寫圖片描述

我們可以看到先調用了Car的構造器,然后調用了Car的初始化方法,最后在容器關閉前調用了Car的銷毀方法。
2、通過讓組件實現InitializingBean接口實現初始化,通過實現DisposableBean接口實現銷毀
組件Cat類

@Component
public class Cat implements InitializingBean, DisposableBean {
    public Cat() {
        System.out.println("cat constructor.....");
    }

    //銷毀方法
    public void destroy() throws Exception {
        System.out.println("cat destory......");
    }

    //初始化方法
    public void afterPropertiesSet() throws Exception {
        System.out.println("cat afterPropertiesSet......");
    }
}

配置類不變,測試類不變,看一下測試結果:


這里寫圖片描述

3、可以使用JSR250中定義的兩個注解:@PostConstruct 實現初始化、@PreDestroy 實現銷毀
組件Dog類

public class Dog {
    public Dog() {
        System.out.println("dog constructor.....");
    }

    //在Construct之后執行
    @PostConstruct
    public void init() {
        System.out.println("dog @PostConstruct .......");
    }

    //在destory執行之前
    @PreDestroy
    private void destoyr() {
        System.out.println("dog @PreDestroy.......");
    }
}

配置類和測試類不變,測試結果:


這里寫圖片描述

4、BeanPostProcessor接口
還記得我在:細說Spring——IoC詳解(深入IoC實現)中說過的BeanPostProcessor嗎,其實它也可以用來控制生命周期。不過這個接口并只涉及到組件初始化,我們可以通過實現這個接口來在組件初始化前對組件做一些增強,下面我們來看一下怎么使用:
組件MyBeanPostProcessor類

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization...." + beanName + "=>" + bean);
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization...." + beanName + "=>" + bean);
        return bean;
    }
}

在所有組件的初始化前后進行一些處理工作,包含兩個方法:

  • postProcessBeforeInitialization:在初始化之前工作
  • postProcessAfterInitialization:在初始化之后工作

配置類不變
測試類:

public class IOCTest_lifeCycle {

    @Test
    public void test01() {
        //1、創建IOC容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
        System.out.println("容器創建完成");
        printBeans();
        //關閉容器
        applicationContext.close();

    }
    private void printBeans() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }
    }
}

測試結果:


這里寫圖片描述

我們可以看到,我們的Car組件在初始化前調用了我們的MyBeanPostProcessor中的postProcessBeforeInitialization方法,在初始化后調用了postProcessAfterInitialization方法。我們可以在Spring中發現很多BeanPostProcessor的實現類,這些類大多是給組件做了某種增強。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1.1 spring IoC容器和beans的簡介 Spring 框架的最核心基礎的功能是IoC(控制反轉)容器,...
    simoscode閱讀 6,749評論 2 22
  • 本來是準備看一看Spring源碼的。然后在知乎上看到來一個帖子,說有一群**自己連Spring官方文檔都沒有完全讀...
    此魚不得水閱讀 6,952評論 4 21
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,868評論 18 139
  • (二)我遇到你的時間遲到了 5. 成長 日子輕輕淺淺的走著,似乎不著痕跡,豆蔻年華的我們,也似乎不懂得時間的...
    慵懶的小蟲子閱讀 356評論 0 2
  • 前半部分我一度想放棄不看了,前半部分描述的就是久木和凜子兩人的婚外情約會,和兩人的內心的變化。而兩人之間的情色描述...
    張_凱閱讀 4,775評論 0 1