springboot原理實戰(2)-- applicationContext獲取的3種方式和原理

目錄

先看下腦圖
在這裏插入圖片描述

方式1:@Autowire

@Component
public class Demo1 {
    @Autowired
    private ApplicationContext applicationContext;
    public void show() {
        System.out.println("Demo1: " + applicationContext);
    }
}

測試

public class AppDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2.test");
        context.getBean(Demo1.class).show();
    }
}

運行結果,顯示已經拿到ApplicationContext 了:
在這裏插入圖片描述

方式2:構造方法

spring4.3新特性:構造方法,注入 只能有一個構造函數,此時,如如果有多個spring會使用無參的構造函數

@Component
public class Demo2 {
    private ApplicationContext applicationContext;

    //4.3+的新特性
    //只能有一個構造函數
    public Demo2(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public void show() {
        System.out.println("Demo2: " + applicationContext.getClass());
    }
}

測試:

public class AppDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2.test");
//        context.getBean(Demo1.class).show();
        context.getBean(Demo2.class).show();
    }
}

運行結果,顯示已經拿到ApplicationContext 了:
在這裏插入圖片描述

方式3:實現ApplicationContextAware

1.獲取方式demo

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class Demo3 implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public void show() {
        System.out.println("Demo3: " + applicationContext);
    }
}

測試:

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AppDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2.test");
//        context.getBean(Demo1.class).show();
//        context.getBean(Demo2.class).show();
        context.getBean(Demo3.class).show();
    }
}

運行結果,顯示已經拿到ApplicationContext 了:
在這裏插入圖片描述

2.獲取原理實現BeanPostProcessor接口

①BeanPostProcessor是啥?有啥用?

作用就是可以在spring裝配和初始bean的過程中,搞點小動作。
在這裏插入圖片描述
他有2個方法:
postProcessBeforeInitialization:在bean依賴裝配(屬性設置完)完成之後觸發,這裏可以指定的bean做一些處理,比如說,返回該對象的代理對象

postProcessAfterInitialization:bean init方法執行之後觸發。
用代碼說明:
我們自己實現這個BeanPostProcessor接口:

/**
 * EchoBeanPostProcessor 會在每個bean初始化的時候,調用一次
 * 兩個方法不能返回null,否則,從容器中獲取不到
 */
@Component
public class EchoBeanPostProcessor implements BeanPostProcessor {


    /**
     * 實在bean依賴裝配(屬性設置完)完成之後觸發
     *
     *  這裏可以指定的bean做一些處理,比如說,返回該對象的代理對象
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {       System.out.println("=========postProcessBeforeInitialization======before======" + bean.getClass());
        return bean;
    }


    /**
     * 是在bean init方法執行之後觸發
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("=========postProcessAfterInitialization====post========" + bean.getClass());
        return bean;
    }
}

這個類被spring容器管理後,會影響每個bean的裝配加載初始化,就像過濾器filter對應於每個請求一樣,可以理解爲spring裝配完bean後加了個bean的後置處理器。
測試一波:

public class App {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2");
        context.getBean(Book.class).show();
        context.getBean(Bank.class).show();
    }
}

看見這2個bean裝載後,調用自己的方法前(使用前),調用了我們自己的邏輯。

在這裏插入圖片描述

②利用BeanPostProcessor做個代理對象的小demo

我們來實現一個功能: 當User對象裝載時,加入我們的日誌信息:
User

@Component
public class User {


     @PostConstruct
    public void init() {
        System.out.println("user init2");
    }

    private ApplicationContext applicationContext;

    public void show() {
        System.out.println("User: " + applicationContext);
    }

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        System.out.println("========set===========");
    }
}

子類LoginUser

public class LogUser extends User{
    public void show() {
        System.out.println("log start ..");
        super.show();
        System.out.println("log end ..");
    }
}

添加我們的邏輯判斷,如果是User裝載,就調用LoginUser的方法:

@Component
public class EchoBeanPostProcessor implements BeanPostProcessor {


    /**
     * 實在bean依賴裝配(屬性設置完)完成之後觸發
     *
     *  這裏可以指定的bean做一些處理,比如說,返回該對象的代理對象
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof User) {
            return new LogUser();
        }
        System.out.println("=========postProcessBeforeInitialization======before======" + bean.getClass());
        return bean;
    }


    /**
     * 是在bean init方法執行之後觸發
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("=========postProcessAfterInitialization====post========" + bean.getClass());
        return bean;
    }
}

測試:

public class App {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2");
        context.getBean(User.class).show();
    }
}

結果顯示:
①在bean設置完屬性後開始調用postProcessBeforeInitialization
②在這個方法中,直接返回了loginUser,然後User替換成LoginUser後,
③執行loginUser初始化後,
④調用postProcessAfterInitialization
⑤ 調用了LoginUser的show方法。
在這裏插入圖片描述

②方式3通過BeanPostProcessor就獲取applcationContext具體做法:

這個得讀源碼:
方式3:從外觀上看是實現了ApplicationContextAware就可以獲取applcationContext了。
那麼我們debug起來:
在AnnotationConfigApplicationContext源碼中
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述
進入這個類:
ApplicationContextAwareProcessor
發現1,實現了beanPostProcessor接口,在這裏插入圖片描述
發現2:這個類下面的代碼就是最終答案:在這裏判斷如果實現了ApplicationContextWare,就注入了ApplicationContext這個屬性。
在這裏插入圖片描述

方式4:自己實現獲取

我們瞭解了這個原理,就可自己實現個ApplicationWare的功能,然後我們也可以獲取ApplicationContext對象。

import org.springframework.context.ApplicationContext;
public interface SpringContentAware {
    public void setApplicationContext(ApplicationContext applicationContext);
}

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class ContextBeanPostProcessor implements BeanPostProcessor {
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof  SpringContentAware){
            SpringContentAware sca = (SpringContentAware) bean;
            sca.setApplicationContext(applicationContext);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

測試下我們寫的這個SpringContentAware 能否拿到applicationContext:

@Component
public class Dog implements SpringContentAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public void show() {
        System.out.println("Dog: " + applicationContext);
    }
}

test類測試:

public class App {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springboot.demo2");
        context.getBean(Dog.class).show();
    }
}

運行結果,顯示已經拿到了:
在這裏插入圖片描述


個人微信公號:
搜索: 怒放de每一天
不定時推送相關文章,期待和大家一起成長!!
在這裏插入圖片描述


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章