spring底层自用学习笔记(二) 如何实现BeanFactory与ApplicationContext

1.BeanFactory实现

测试类起手

public class TestBeanFactory {
    public static void main(String[] args){
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //bean的定义(lass,scope单例多例,初始化、消费方法)
        //建造者模式
        AbstractBeanDefinition beanDefinition =
                BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        beanFactory.registerBeanDefinition("config",beanDefinition);

        for (String name : beanFactory.getBeanDefinitionNames()) {
            //此时只有config,没有bean1和bean2,说明@Configuration@Bean注解没有解析
            //beanFactory不具备解析注解的功能
            System.out.println(name);
        }
    }
    @Configuration
    static class Config{
        @Bean
        public Bean1 bean1(){return new Bean1();}
        @Bean
        public Bean2 bean2(){return new Bean2();}
    }

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

        public Bean1(){log.debug("构造Bean1()");}

        @Autowired
        private Bean2 bean2;

        public Bean2 getBean2(){return bean2;}
    }

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

        public Bean2(){log.debug("构造Bean2()");}
    }
}

此时的输出,只有config,不包含其他的bean,因为beanFactory不具备解析注解的功能,@Configuration@Bean注解没有被解析

1.1 BeanFactoryPostProcessor

稍作修改

        //给beanFactory添加一些常用的后处理器,也就是对beanFactory进行扩展
        //后处理器: 先把bean实例化,再去处理该Bean所依赖的关系
        //spring的源码里有大量的后置处理器,aop原理就是借助后置处理器的执行时机来进行动态代理的
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        for (String name : beanFactory.getBeanDefinitionNames()) {
            //此时再看,又多了几个bean
            System.out.println(name);
        }

输出如图:在这里插入图片描述
此时还没有bean1和bean2,原因是还没执行后处理器

		//生成beanFactory后处理器对象并执行
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().stream().forEach(beanFactoryPostProcessor -> {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        });

        for (String name : beanFactory.getBeanDefinitionNames()) {
            //此时再看,bean1和bean2有了
            System.out.println(name);
        }

输出如图:
在这里插入图片描述
注意这里只是BeanDifination 而不是Bean本身,从bean1和bean2的构造器没被调用就知道了。
bean工厂的原始功能并没有那么丰富,扩展功能是通过后处理器实现的。

1.2 BeanPostProcessor

那么此时bean1中注入的bean2可以调用吗?试一下

System.out.println(beanFactory.getBean(Bean1.class).getBean2());

输出结果:

21:38:08.903 [main] DEBUG com.example.demo.a02.TestBeanFactory$Bean1 - 构造Bean1()
null

说明此时@Autowired没被解析,依赖注入功能是没有的,刚才只执行了beanFactory的后处理器
要做依赖注入的话,还有bean的后处理器,刚才图里也有
在这里插入图片描述

@Autowired注解
org.springframework.context.annotation.internalAutowiredAnnotationProcessor

@Resource注解
org.springframework.context.annotation.internalCommonAnnotationProcessor

复习一下@Autowired和@Resource这俩注解的区别:
(1)两个注解@Autowired和@Resource功能上等价;
(2)但匹配顺序不同, @Autowired获取bean会先byType方式再byName方式, 而@Resource与前者相反;
(3)另, 作用域不同, @Autowired可作用于构造器, 字段, setter方法上, 而@Resource只可作用于字段和setter方法上

//bean后处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowired @Resource
        beanFactory.getBeansOfType(BeanPostProcessor.class)
                .values().stream().forEach(beanFactory::addBeanPostProcessor);
        System.out.println(beanFactory.getBean(Bean1.class).getBean2());

此时完成了依赖注入,输出如下

21:54:40.052 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean ‘bean1’
21:54:40.053 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean ‘config’
21:54:40.065 [main] DEBUG com.example.demo.a02.TestBeanFactory$Bean1 - 构造Bean1()

21:54:40.072 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean ‘bean2’
21:54:40.073 [main] DEBUG com.example.demo.a02.TestBeanFactory$Bean2 - 构造Bean2()

最后看看这两个添加后处理器操作的区别:

//后处理器添加到BeanFactory
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

//建立BeanFactory后处理器和BeanFactory的联系
beanFactory.getBeansOfType(BeanPostProcessor.class)
                .values().stream().forEach(beanFactory::addBeanPostProcessor);

另外@Configuration加不加Config类中的@Bean注解都会被解析,@Configuration是用于Spring类扫描的时候用的,加了这个注解的类被扫描到了就会被放进Bean工厂

假设这样一种情况, 在方法1, 3中都调用了方法2去new Bean2, 那么Bean2是否会实例化多次?
答案是如果加了@Configuration就不会, 而是会走cglib动态代理去拦截@Bean方法的调用,使其每次会从Spring容器中获取Bean实例

那BeanFactory是用到时才创建,还是一开始就创建好呢?
这里加上分隔线

//bean后处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowired @Resource
        beanFactory.getBeansOfType(BeanPostProcessor.class)
                .values().stream().forEach(beanFactory::addBeanPostProcessor);
        //用到的时候才去创建bean
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>");
        System.out.println(beanFactory.getBean(Bean1.class).getBean2());

可见是用到的时候才会创建bean
在这里插入图片描述
但对于单例对象,一般希望在getBean之前就把单例对象提前实例化,可以使用preInstantiateSingletons

//bean后处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowired @Resource
        beanFactory.getBeansOfType(BeanPostProcessor.class)
                .values().stream().forEach(beanFactory::addBeanPostProcessor);

        //对于单例对象,一般希望在getBean之前就把单例对象提前实例化
        beanFactory.preInstantiateSingletons();
        //用到的时候才去创建bean
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>");
        System.out.println(beanFactory.getBean(Bean1.class).getBean2());

此时创建bean就在分隔线(实际使用)之前了
在这里插入图片描述

1.3 后处理器排序(@Autowired @Resource谁先被解析?)

新增了接口和implements该接口的bean3 bean4,然后这时注入Inter接口类型
在这里插入图片描述
因为@Autowired获取bean会先byType方式再byName方式,此时会有报错,容器里有两个Inter类型,可以用@qualifier,也可以明确变量名

		@Autowired
        private Inter bean3;

如果换成@Resource注解,顺序就是相反的,先byName,这里获取到的就是bean4

		@Resource(name = "bean4")
        private Inter bean3;

那如果同时使用@Autowired和@Resource,会注入谁呢?答案是bean3

		@Autowired
        @Resource(name = "bean4")
        private Inter bean3;

和addBeanPostProcessor这里添加后处理器的顺序有关,Autowired后处理器先被添加进去,sout输出看一下

    beanFactory.getBeansOfType(BeanPostProcessor.class)
                .values().stream().forEach(beanPostProcessor -> {
            System.out.println("===="+beanPostProcessor);
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        });

确实如此,没排序之前,是根据getBeansOfType取出来的顺序设置的,不会考虑Order的大小,所以Autowired排到了Common前边

====org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@247d8ae
====org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@48974e45

那怎么控制后处理器的顺序呢?

其实在AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory)中,已经set了比较器,所以这里可以直接beanFactory.getDependencyComparator()
在这里插入图片描述
我们可以通过比较器来修改顺序

    beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
                //比较器
                .sorted(beanFactory.getDependencyComparator())
                .forEach(beanPostProcessor -> {
            System.out.println("===="+beanPostProcessor);
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        });

====org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@2fd1433e
====org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@29d89d5d

这样sorted就能改变顺序是因为,后处理器有order属性
AutowiredAnnotationBeanPostProcessor.order = Ordered.LOWEST_PRECEDENCE - 2 也就是2147483647-2=2147483645

CommonAnnotationBeanPostProcessor.order=Ordered.LOWEST_PRECEDENCE - 3也就是2147483647-3=2147483644

order的顺序是由小到大,那CommonAnnotationBeanPostProcessor就在前面了,这时@Resource先生效

总结:
1.beanFactory不会做的事
(1)不会主动调用BeanFactory后处理器
(2)不会主动添加Bean后处理器
(3)不会主动初始化单例对象
(4)不会解析BeanFactory 还不会解析 ${} 与 #{}
2.bean后处理器会有排序的逻辑

=>所以有了applicationContext的refresh

2.ApplicationContext实现

加载bean的定义信息,首先看xml配置bean的方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="bean1" class="com.example.demo.a02.A02Application.Bean1"/>

    <bean id="bean2" class="com.example.demo.a02.A02Application.Bean2">
        <property name="bean1" ref="bean1"/>
    </bean>

</beans>

2.1 ClassPathXmlApplicationContext:较为经典的容器,基于classpath 下xml格式的配置文件来创建

ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("b01.xml");

2.2 FileSystemXmlApplicationContext:基于磁盘路径下 xml格式的配置文件来创建, 绝对路径/相对路径

FileSystemXmlApplicationContext context =
                new FileSystemXmlApplicationContext("src/main/resources/b01.xml");

2.3 XmlBeanDefinitionReader:使用XmlBeanDefinitionReader读取xml

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
 //把xml的标签转成beanDefinition
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));

2.4 AnnotationConfigApplicationContext:较为经典的容器,基于java配置类来创建,相对前几种,多了一些后处理器:

AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(Config.class);

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

        @Bean
        public Bean2 bean2(Bean1 bean1){
            Bean2 bean2 = new Bean2();
            bean2.setBean1(bean1);
            return bean2;
        }
    }

或者xml里加一句也能加上后处理器

<context:annotation-config/>

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
a02Application.Config bean1 bean2

2.5 AnnotationConfigServletWebApplicationContext:较为经典的容器,基于java配置类来创建,实现简单的web环境

使用了内嵌tomcat容器,dispatcherServlet,和注册的DispatcherServletRegistrationBean,没有用springboot的注解

    private static void testAnnotationConfigServletWebServerApplicationContext(){
        AnnotationConfigServletWebServerApplicationContext context =
                new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);

    }

    @Configuration
    static class WebConfig{
        @Bean
        public ServletWebServerFactory servletWebServerFactory(){
            return new TomcatServletWebServerFactory();
        }

        @Bean
        public DispatcherServlet dispatcherServlet(){
            return new DispatcherServlet();
        }

        //把DispatcherServlet注册到tomcat服务器中
        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){
            return new DispatcherServletRegistrationBean(dispatcherServlet,"/");
        }

        @Bean("/hello")
        public Controller controller1(){
            return (request, response) -> {
                response.getWriter().print("hello");
                return null;
            };
        }
    }

然后浏览器访问http://localhost:8080/hello,日志如下图
在这里插入图片描述

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值