栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

不一样的视角来学习Spring源码之容器与Bean---上

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

不一样的视角来学习Spring源码之容器与Bean---上

Spring源码学习一之容器与Bean---上

1) 容器接口

BeanFactory 与 ApplicationContext 的区别

国际化资源获取环境事件 2) 容器实现

DefaultListableBeanFactory

BeanFactory自己为什么不能解析bean里面的@Bean和 @Configuration注解呢?BeanFactory自己为什么不能从bean定义中扫描到@Autowired注解,并进行自动依赖注入呢?BeanFactory设置预先实例化所有的Bean小结 常见 ApplicationContext 实现 3) Bean 的生命周期

bean 生命周期

小结收获 模板方法设计模式常见的Bean的后置处理器

收获 AutowiredAnnotationBeanPostProcessor后置处理器分析---负责解析autowired注解和value注解

收获


1) 容器接口

BeanFactory 接口,典型功能有:

getBean

ApplicationContext 接口,是 BeanFactory 的子接口。它扩展了 BeanFactory 接口的功能,如:

国际化通配符方式获取一组 Resource 资源整合 Environment 环境(能通过它获取各种来源的配置信息)事件发布与监听,实现组件之间的解耦


BeanFactory 与 ApplicationContext 的区别

    到底什么是 BeanFactory

    它是 ApplicationContext 的父接口它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory

    BeanFactory 能干点啥

    表面上只有 getBean实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供下面例子中通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean

BeanFactory只是一个接口,我们需要的是它的实现类,BeanFactory默认实现类为DefaultListableBeanFactory


下面我们尝试反射调用该成员变量:

@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        ConfigurableApplicationContext context = SpringApplication.run(OneMain.class, args);

        //默认单例bean的注册中心
        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        singletonObjects.setAccessible(true);
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //获取在beanFactory实例上singletonObjects属性集合
        Map map = (Map) singletonObjects.get(beanFactory);
        //对Bean名称进行过滤,不然会有很多的单例bean
        map.entrySet().stream().filter(e->e.getKey().startsWith("dhy")).forEach(e->{
            System.out.println(e.getKey()+" = "+e.getValue());
        });
    }
}


3.ApplicationContext 比 BeanFactory 多点啥

ApplicationContext 组合并扩展了 BeanFactory 的功能国际化、通配符方式获取一组 Resource 资源、整合 Environment 环境、事件发布与监听新学一种代码之间解耦途径,事件解耦 国际化

国际化文件均在 src/resources 目录下

messages.properties(空)

messages_en.properties

hi=Hello

messages_zh.properties

hi=你好

注意

ApplicationContext 中 MessageSource bean 的名字固定为 messageSource使用 SpringBoot 时,国际化文件名固定为 messages空的 messages.properties 也必须存在

@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        GenericApplicationContext context=new GenericApplicationContext();
         //自定义一个bean注册到容器中
        context.registerBean("messageSource", MessageSource.class, () -> {
            ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
            ms.setDefaultEncoding("utf-8");
            ms.setbasename("messages");
            return ms;
        });

        context.refresh();

        System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
        System.out.println(context.getMessage("hi", null, Locale.CHINESE));
    }
}

资源获取

详细参考

@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        GenericApplicationContext context=new GenericApplicationContext();
        //寻找多个资源,底层使用ResourcePatternResolver
        Resource[] resources = context.getResources("classpath*:meta-INF/spring.factories");
        for (int i = 0; i < resources.length; i++) {
            System.out.println(resources[i]);
        }
        //查找单个资源--底层使用DefaultResourceLoader
        Resource resource = context.getResource("classpath:messages.properties");
        System.out.println(resource);
    }
}


环境
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        GenericApplicationContext context=new GenericApplicationContext();
        String java_home = context.getEnvironment().getProperty("java_home");
        System.out.println(java_home);
    }
}


事件

自定义一个事件发布对象:

public class UEvent extends ApplicationEvent {
    public UEvent(Object source) {
        super(source);
    }
}

发布事件:

@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(OneMain.class, args);
        //我们只要获取到了context对象,就可以在应用程序执行期间发布事件
        applicationContext.publishEvent(new UEvent("你好,我是自定义事件"));
    }
}

监听器:

@Component
public class UListener {
    @EventListener
    public void listener(UEvent event)
    {
        System.out.println(event.getSource());
    }
}


详细参考


2) 容器实现

Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,这里出于怀旧的原因,把它们都列出来,供大家参考

DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转和依赖注入功能,都是它来实现ClassPathXmlApplicationContext,从类路径查找 XML 配置文件,创建容器(旧)FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)AnnotationConfigApplicationContext,Spring boot 中非 web 环境容器(新)AnnotationConfigServletWebServerApplicationContext,Spring boot 中 servlet web 环境容器(新)AnnotationConfigReactiveWebServerApplicationContext,Spring boot 中 reactive web 环境容器(新)

另外要注意的是,后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现,但它们是组合了 DefaultListableBeanFactory 的功能,并非继承而来


DefaultListableBeanFactory

使用演示:

@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //手动构建一个BeanDefinition
        AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope("singleton").getBeanDefinition();
       //注册到bean工厂中
        beanFactory.registerBeanDefinition("config",beanDefinition);
        //获取当前bean工厂中存在的bean的定义
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) {
            System.out.println(beanDefinitionNames[i]);
        }
    }

    @Configuration
    class Config{
        @Bean
        public Bean1 bean1(){return new Bean1();}
        @Bean
        public Bean2 bean2(){return new Bean2();}
    }
}


BeanFactory自己为什么不能解析bean里面的@Bean和 @Configuration注解呢?

为什么只有config被注册到了容器中,config里面的@Bean注解并没有被解析,显然BeanFactory并没有解析这些注解的能力,那么这些能力是who提供的呢?

public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //手动构建一个BeanDefinition
        AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope("singleton").getBeanDefinition();
       //注册到bean工厂中
        beanFactory.registerBeanDefinition("config",beanDefinition);

        //给BeanFactory添加一些常用的后置处理器---相当于增加了解析这些注解的能力
        //这一步只是往容器中注册了一些后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        //这一步才是让这些后置处理器开始工作
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
                .values().stream().forEach(
                        //执行bean工厂后置处理器
                        beanPostProcessor->{beanPostProcessor.postProcessBeanFactory(beanFactory);
                            System.out.println("Bean工厂后置处理器有: "+beanPostProcessor);
                        });

        //获取当前bean工厂中存在的bean的定义
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) {
            System.out.println(beanDefinitionNames[i]);
        }
    }

    @Configuration
    static class Config{
        @Bean
        public Bean1 bean1(){return new Bean1();}
        @Bean
        public Bean2 bean2(){return new Bean2();}
    }
}

Spring的BeanFactory原始功能并没有那么丰富,但是可以通过后置处理器来增强BeanFactory的功能


BeanFactory自己为什么不能从bean定义中扫描到@Autowired注解,并进行自动依赖注入呢?

上面添加的bean工厂的后置处理器是用来解析一些例如@Configuration,@Bean等注解


我们还需要让Bean的后置处理器开始工作,Bean的后置处理器一般工作在Bean的生命周期过程中

public class Bean2 {
    @Autowired
    private Bean1 bean1;

    public Bean2() {
        System.out.println("Bean2构造中...");
    }

    public Bean1 getBean1() {
        return bean1;
    }
}
public class Bean1 {
    public Bean1() {
        System.out.println("Bean1构造中...");
    }
}

@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //手动构建一个BeanDefinition
        AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope("singleton").getBeanDefinition();

       //注册到bean工厂中
        beanFactory.registerBeanDefinition("config",beanDefinition);

        //给BeanFactory添加一些常用的后置处理器---相当于增加了解析这些注解的能力
        //这一步只是往容器中注册了一些后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        //这一步才是让这些后置处理器开始工作
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
                .values().stream().forEach(
                        //执行bean工厂后置处理器
                        beanPostProcessor->{beanPostProcessor.postProcessBeanFactory(beanFactory);
                            System.out.println("Bean工厂后置处理器有: "+beanPostProcessor);
                        });

        //添加bean后置处理器---针对bean的生命周期各个阶段提供扩展
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        //获取当前bean工厂中存在的bean的定义
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) {
            System.out.println(beanDefinitionNames[i]);
        }

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        System.out.println(beanFactory.getBean(Bean2.class).getBean1());
    }

    @Configuration
    static class Config{
        @Bean
        public Bean1 bean1(){return new Bean1();}
        @Bean
        public Bean2 bean2(){return new Bean2();}
    }
}


使用到bean才会去创建,这显然表明BeanFactory是懒加载的


BeanFactory设置预先实例化所有的Bean
@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //手动构建一个BeanDefinition
        AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
                .setScope("singleton").getBeanDefinition();

       //注册到bean工厂中
        beanFactory.registerBeanDefinition("config",beanDefinition);

        //给BeanFactory添加一些常用的后置处理器---相当于增加了解析这些注解的能力
        //这一步只是往容器中注册了一些后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        //这一步才是让这些后置处理器开始工作
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
                .values().stream().forEach(
                        //执行bean工厂后置处理器
                        beanPostProcessor->{beanPostProcessor.postProcessBeanFactory(beanFactory);
                            System.out.println("Bean工厂后置处理器有: "+beanPostProcessor);
                        });

        //添加bean后置处理器---针对bean的生命周期各个阶段提供扩展
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        //获取当前bean工厂中存在的bean的定义
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) {
            System.out.println(beanDefinitionNames[i]);
        }
        
        //预先实例化所有的bean
        beanFactory.preInstantiateSingletons();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        System.out.println(beanFactory.getBean(Bean2.class).getBean1());
    }

    @Configuration
    static class Config{
        @Bean
        public Bean1 bean1(){return new Bean1();}
        @Bean
        public Bean2 bean2(){return new Bean2();}
    }
}


小结

beanFactory 可以通过 registerBeanDefinition 注册一个 bean definition 对象

我们平时使用的配置类、xml、组件扫描等方式都是生成 bean definition 对象注册到 beanFactory 当中bean definition 描述了这个 bean 的创建蓝图:scope 是什么、用构造还是工厂创建、初始化销毁方法是什么,等等 beanFactory 需要手动调用 beanFactory 后处理器对它做增强

例如通过解析 @Bean、@ComponentScan 等注解,来补充一些 bean definition beanFactory 需要手动添加 bean 后处理器,以便对后续 bean 的创建过程提供增强

例如 @Autowired,@Resource 等注解的解析都是 bean 后处理器完成的bean 后处理的添加顺序会对解析结果有影响


后置处理器都实现了该接口,确保可以保证不同后置处理的执行顺序


order越小,优先级越高

beanFactory 需要手动调用方法来初始化单例beanFactory 需要额外设置才能解析 ${} 与 #{}


常见 ApplicationContext 实现

从类路径下加载配置文件:

        ClassPathXmlApplicationContext xmlApplicationContext=new ClassPathXmlApplicationContext("application.xml");

从磁盘路径加载配置文件:

        FileSystemXmlApplicationContext fileSystemXmlApplicationContext=new FileSystemXmlApplicationContext("D:application.xml");

使用DefaultListableBeanFactory作为bean的注册中心,XmlBeanDefinitionReader从配置文件读取配置信息,注册到DefaultListableBeanFactory中

        DefaultListableBeanFactory listableBeanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(listableBeanFactory);
       // reader.loadBeanDefinitions(new ClassPathResource("application.xml"));
          reader.loadBeanDefinitions(new FileSystemResource("D:application.xml"));

基于注解方式的: 通过@Configuration标注的配置类替代原来的配置文件

@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        AnnotationConfigApplicationContext annotationConfigApplicationContext=new AnnotationConfigApplicationContext(Config.class);
        String[] definitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
        System.out.println("已经注册的bean定义有: ");
        for (int i = 0; i < definitionNames.length; i++) {
            System.out.println(definitionNames[i]);
        }
    }

    @Configuration
    static class Config{
        @Bean
        public Bean1 bean1(){return new Bean1();}
        @Bean
        public Bean2 bean2(){return new Bean2();}
    }
}


因为默认注册了这些后置处理器,才可以去解析这些注解


xml配置文件中注册上面的后置处理器

    

这个标签的作用就是往容器中注入上面那些后置处理器


用于web环境的容器:

@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        AnnotationConfigServletWebApplicationContext context=new AnnotationConfigServletWebApplicationContext(WebConfig.class);
    }
    
    @Configuration
    static class WebConfig{
        @Bean
        public ServletWebServerFactory webServerFactory(){
            //tomcat环境下的web服务器工厂
            return new TomcatServletWebServerFactory();
        }
        @Bean
        public DispatcherServlet dispatcherServlet(){
            //负责请求派发的servlet
            return new DispatcherServlet();
        }
        @Bean
        public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(){
            //注册上的servlet---拦截所有路径
            return new DispatcherServletRegistrationBean(dispatcherServlet(),"/");
        }

        
        @Bean("/hello")
        public Controller controller(){
            //DispatcherServlet拦截到请求后,将请求转发给对应的controller
            return new Controller() {
                @Override
                public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
                    httpServletResponse.getWriter().println("大忽悠");
                    return null;
                }
            };
        }
    }
}

大家可以顺便联系一下springboot中内嵌tomcat是如何启动的


3) Bean 的生命周期

先看下面这段代码运行结果:

@Component("lifeCycleBean")
public class TestBean {
    public TestBean() {
        System.out.println("testBean的构造方法被调用");
    }

    @Autowired
    public void autowired(@Value("${JAVA_HOME}") String home)
    {
        System.out.println("依赖注入得到的JAVA_HOME结果为: "+home);
    }

    @PostConstruct
    public void init(){
        System.out.println("初始化方法");
    }

    @PreDestroy
    public void destory()
    {
        System.out.println("销毁");
    }
}

@SpringBootApplication
public class OneMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(OneMain.class, args);
        applicationContext.close();
    }

}


ApplicationContext会在容器启动的时候自动初始化所有的单例Bean

一个受 Spring 管理的 bean,生命周期主要阶段有

    创建:根据 bean 的构造方法或者工厂方法来创建 bean 实例对象依赖注入:根据 @Autowired,@Value 或其它一些手段,为 bean 的成员变量填充值、建立关系初始化:回调各种 Aware 接口,调用对象的各种初始化方法销毁:在容器关闭时,会销毁所有单例对象(即调用它们的销毁方法)

    prototype 对象也能够销毁,不过需要容器这边主动调用

一些资料会提到,生命周期中还有一类 bean 后处理器:BeanPostProcessor,会在 bean 的初始化的前后,提供一些扩展逻辑。但这种说法是不完整的,见下面的演示1


bean 生命周期

ApplicationContext会自动识别容器中的BeanPostProcessor

@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

    private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");
    }

    @Override
    public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
        //返回null,表示不进行替换
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")) {
            log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
//            return false;
        }
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
        return pvs;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
        return bean;
    }
}


小结

创建前后的增强

postProcessBeforeInstantiation

这里返回的对象若不为 null 会替换掉原本的 bean,并且仅会走 postProcessAfterInitialization 流程 postProcessAfterInstantiation

这里如果返回 false 会跳过依赖注入阶段

依赖注入前的增强

postProcessProperties

如 @Autowired、@Value、@Resource

初始化前后的增强

postProcessBeforeInitialization

这里返回的对象会替换掉原本的 bean如 @PostConstruct、@ConfigurationProperties postProcessAfterInitialization

这里返回的对象会替换掉原本的 bean如代理增强

销毁之前的增强

postProcessBeforeDestruction

如 @PreDestroy 收获

    Spring bean 生命周期各个阶段模板设计模式, 指大流程已经固定好了, 通过接口回调(bean 后处理器)在一些关键点前后提供扩展

模板方法设计模式

看问题:

public class TestMethodTemplate {
    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.getBean();
    }

    static class MyBeanFactory {
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            System.out.println("初始化 " + bean);
            return bean;
        }
    }
}

我们通过伪代码简单模拟了一下构造,依赖注入和初始化的流程,这个流程是不变的,因此可以看做是一个模板过程

但是,如果我们在依赖注入过程中,一开始只支持了@Autowired注解的解析,现在又要增加对 @Resource注解的解析,我们可以写成下面这样子:

public class TestMethodTemplate {
    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.getBean();
    }

    static class MyBeanFactory {
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            System.out.println("对Autowired注解进行解析...");
            System.out.println("对Resource注解进行解析...");
            System.out.println("初始化 " + bean);
            return bean;
        }
    }
}

那现在我们又要增加对Inject注解的解析,该怎么办呢? 难不成写成下面这样子?

public class TestMethodTemplate {
    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.getBean();
    }

    static class MyBeanFactory {
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            System.out.println("对Autowired注解进行解析...");
            System.out.println("对Resource注解进行解析...");
            System.out.println("对Inject注解进行解析...");
            System.out.println("初始化 " + bean);
            return bean;
        }
    }
}

这不是意味着后面如果我们想要增强功能,就需要改动getBean方法来提供对新功能的支持?

这显然是非常错误的做法,那么有没有什么方法可以很好的改进这个错误做法呢? ----模板方法模式

public class TestMethodTemplate {

    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Autowired"));
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Inject"));
        beanFactory.getBean();
    }

    // 模板方法  Template Method Pattern
    static class MyBeanFactory {
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            for (BeanPostProcessor processor : processors) {
                processor.inject(bean);
            }
            System.out.println("初始化 " + bean);
            return bean;
        }

        private List processors = new ArrayList<>();

        public void addBeanPostProcessor(BeanPostProcessor processor) {
            processors.add(processor);
        }
    }
    
    static interface BeanPostProcessor {
        public void inject(Object bean); // 对依赖注入阶段的扩展
    }
}

无论功能如何增强,我们都可以通过增加后置处理器来巧妙解决这个问题,是不是很方便?

模板方法就是静中有动,即整个流程中有些操作是固定不变的,但是有些操作是不确定的,我们可以用抽象方法来或者接口中的抽象方法来提供,参考上面的代码。


常见的Bean的后置处理器
public class Bean1 {
    private static final Logger log = LoggerFactory.getLogger(Bean1.class);

    private Bean2 bean2;

    @Autowired
    public void setBean2(Bean2 bean2) {
        log.debug("@Autowired 生效: {}", bean2);
        this.bean2 = bean2;
    }

    @Autowired
    private Bean3 bean3;

    @Resource
    public void setBean3(Bean3 bean3) {
        log.debug("@Resource 生效: {}", bean3);
        this.bean3 = bean3;
    }

    private String home;

    @Autowired
    public void setHome(@Value("${JAVA_HOME}") String home) {
        log.debug("@Value 生效: {}", home);
        this.home = home;
    }

    @PostConstruct
    public void init() {
        log.debug("@PostConstruct 生效");
    }

    @PreDestroy
    public void destroy() {
        log.debug("@PreDestroy 生效");
    }

    @Override
    public String toString() {
        return "Bean1{" +
               "bean2=" + bean2 +
               ", bean3=" + bean3 +
               ", home='" + home + ''' +
               '}';
    }
}

public class Bean2 {
}
public class Bean3 {
}

@ConfigurationProperties(prefix = "java")
public class Bean4 {

    private String home;

    private String version;

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    @Override
    public String toString() {
        return "Bean4{" +
               "home='" + home + ''' +
               ", version='" + version + ''' +
               '}';
    }
}

这里属性绑定,会从配置文件和环境变量两个地方找,因为这里java.home和java.version在环境变量中都存在,因此我们不需要在配置文件中配置任何内容,这里就可以绑定

public class A04 {
    public static void main(String[] args) {
        // ⬇️GenericApplicationContext 是一个【干净】的容器
        //不会像AnnotationConfigApplicationContext一样自动给我们添加一些后置处理器
        GenericApplicationContext context = new GenericApplicationContext();

        // ⬇️用原始方法注册三个 bean
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);
        context.registerBean("bean4", Bean4.class);

        //配合autowired和value注解解析值注入类型,例如string类型的值注入
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());

        // @Autowired @Value
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);

        // @Resource @PostConstruct @PreDestroy
        context.registerBean(CommonAnnotationBeanPostProcessor.class);

        //解析ConfigurationProperties注解
        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

        // ⬇️初始化容器
        context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例

        System.out.println(context.getBean(Bean4.class));

        // ⬇️销毁容器
        context.close();

        
    }
}





注意该解析器不是用来解析{}大括号的,而是解析这种值注入类型的,否则会把String当做bean对象看待,如上所示: 这河里吗?

当然该解析器不只这个功能,目前通过我们的测试,只发现了这个功能,后续我们会专门探讨一下该解析器


收获
    @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能,这些扩展功能由 bean 后处理器来完成每个后处理器各自增强什么功能

    AutowiredAnnotationBeanPostProcessor 解析 @Autowired 与 @ValueCommonAnnotationBeanPostProcessor 解析 @Resource、@PostConstruct、@PreDestroyConfigurationPropertiesBindingPostProcessor 解析 @ConfigurationProperties 另外 ContextAnnotationAutowireCandidateResolver 负责获取 @Value 的值,解析 @Qualifier、泛型、@Lazy 等


AutowiredAnnotationBeanPostProcessor后置处理器分析—负责解析autowired注解和value注解
// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // registerSingleton方法会忽略: 创建过程,依赖注入,初始化---相当于直接提供bean的成品
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        // @Value---值解析器
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // ${} 的解析器
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 Injectionmetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        //与Bean工厂关联----需要到容器找寻找一些Bean进行依赖注入
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        //此时后置处理器没有工作
        System.out.println(bean1);

        //执行依赖注入@Autowired @Value
        //第一个参数是手动指定值,而不通过依赖注入,这里我们不需要手动指定,而是通过去容器中找,进行依赖注入
        //postProcessProperties方法会在bean生命周期中的依赖注入阶段被执行
        processor.postProcessProperties(null,bean1,"bean1");
        System.out.println(bean1);

    }
}


看一下postProcessProperties方法的源码:

    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // registerSingleton方法会忽略: 创建过程,依赖注入,初始化---相当于直接提供bean的成品
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        // @Value---值解析器
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // ${} 的解析器
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 Injectionmetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        //与Bean工厂关联----需要到容器找寻找一些Bean进行依赖注入
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        //此时后置处理器没有工作
        System.out.println(bean1);

        //执行依赖注入@Autowired @Value
        //第一个参数是手动指定值,而不通过依赖注入,这里我们不需要手动指定,而是通过去容器中找,进行依赖注入
      // processor.postProcessProperties(null,bean1,"bean1");
//        System.out.println(bean1);

        //反射调用findAutowiringmetadata方法,来获取返回值封装的Injectionmetadata信息
        Method findAutowiringmetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringmetadata", String.class,
                Class.class, PropertyValues.class);
        findAutowiringmetadata.setAccessible(true);
        // 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
        Injectionmetadata injectionmetadata = (Injectionmetadata) findAutowiringmetadata.invoke(processor, "bean1", Bean1.class, null);
        System.out.println(injectionmetadata);
    }

由于Injectionmetadata 没有重写toString方法,因此我们通过断点查看



调用Injectionmetadata的inject方法,完成依赖注入

// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // registerSingleton方法会忽略: 创建过程,依赖注入,初始化---相当于直接提供bean的成品
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        // @Value---值解析器
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // ${} 的解析器
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 Injectionmetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        //与Bean工厂关联----需要到容器找寻找一些Bean进行依赖注入
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        //此时后置处理器没有工作
        System.out.println(bean1);

        //执行依赖注入@Autowired @Value
        //第一个参数是手动指定值,而不通过依赖注入,这里我们不需要手动指定,而是通过去容器中找,进行依赖注入
      // processor.postProcessProperties(null,bean1,"bean1");
//        System.out.println(bean1);

        //反射调用findAutowiringmetadata方法,来获取返回值封装的Injectionmetadata信息
        Method findAutowiringmetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringmetadata", String.class,
                Class.class, PropertyValues.class);
        findAutowiringmetadata.setAccessible(true);
        // 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
        Injectionmetadata injectionmetadata = (Injectionmetadata) findAutowiringmetadata.invoke(processor, "bean1", Bean1.class, null);

        //调用Injectionmetadata的inject方法,完成依赖注入
        injectionmetadata.inject(bean1,"bean1",null);
        System.out.println(bean1);
    }
}


injectionmetadata中的inject方法的源码分析:

	public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
		Collection checkedElements = this.checkedElements;
		Collection elementsToIterate =
				(checkedElements != null ? checkedElements : this.injectedElements);
		if (!elementsToIterate.isEmpty()) {
			for (InjectedElement element : elementsToIterate) {
			//调用InjectedElement 的inject方法---这里实际上应该是AutowiredFieldElement
				element.inject(target, beanName, pvs);
			}
		}
	}

AutowiredFieldElement 是AutowiredAnnotationBeanPostProcessor的一个内部类,它里面的inject方法

private class AutowiredFieldElement extends Injectionmetadata.InjectedElement {

//inject方法源码:
@Override
		protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
			Field field = (Field) this.member;
			Object value;
			if (this.cached) {
				try {
					value = resolvedCachedArgument(beanName, this.cachedFieldValue);
				}
				catch (NoSuchBeanDefinitionException ex) {
					// Unexpected removal of target bean for cached argument -> re-resolve
					value = resolveFieldValue(field, bean, beanName);
				}
			}
			else {
				value = resolveFieldValue(field, bean, beanName);
			}
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}


resolveFieldValue方法中都会用到一个叫做DependencyDescriptor的依赖注入描述符号

成员变量依赖bean查找:

        //3.按照类型查找
        Field bean3 = Bean1.class.getDeclaredField("bean3");
        //封装依赖关系---->需要bean3类型的bean,并且required=false
        DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(bean3, false);
       //目标类型的bean---通过依赖关系,去容器中找到该类型的bean
        Object targetBean = beanFactory.doResolveDependency(dependencyDescriptor, null, null,null);
        System.out.println(targetBean);


方法参数依赖查找:

这里是按照方法参数为单位去容器中寻找对应的依赖关系

        Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
        //封装方法参数描述符
        DependencyDescriptor dd2 =
                new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
        //解析,获取依赖的bean
        Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);
        System.out.println(o1);


如果不像容器中放入bean2类型的bean对象:

        // registerSingleton方法会忽略: 创建过程,依赖注入,初始化---相当于直接提供bean的成品
       // beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());


值注入

        Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
        DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), false);
        Object o2 = beanFactory.doResolveDependency(dd3, null, null, null);
        System.out.println(o2);

这里是去环境变量中寻找JAVA_HOME的值


收获
    AutowiredAnnotationBeanPostProcessor.findAutowiringmetadata 用来获取某个 bean 上加了 @Value @Autowired 的成员变量,方法参数的信息,表示为 InjectionmetadataInjectionmetadata 可以完成依赖注入Injectionmetadata 内部根据成员变量,方法参数封装为 DependencyDescriptor 类型有了 DependencyDescriptor,就可以利用 beanFactory.doResolveDependency 方法进行基于类型的查找

未完待续…

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/780992.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号