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做一些处理,比如说,返回该对象的代理对象

扫描二维码关注公众号,回复: 9895765 查看本文章

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每一天
不定时推送相关文章,期待和大家一起成长!!
在这里插入图片描述


发布了246 篇原创文章 · 获赞 29 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/baidu_21349635/article/details/104766775