第二讲、Spring之容器实现

文章内容根据黑马程序员Spring视频教程所写!!!

视频地址:点击这里


BeanFactory实现

/**
 * @author Becant
 * 2024-04-24-20:06
 * @version 1.0.0
 * 测试
 */
public class TestBeanFactory {
    public static void main(String[] args) {
        test1();
        test2();
    }

    @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("构造 Bean1()");}
    }
}

BeanFactory后处理器

public void test1() {
	DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    // bean 的定义 (class, scope, 初始化,销毁)
    AbstractBeanDefinition beanDefinition =
    BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
    // 注册到bean工厂
    beanFactory.registerBeanDefinition("config", beanDefinition);
    for (String name : beanFactory.getBeanDefinitionNames()) {
        System.out.println(name);
    }
}

test1()方法只是将Config类注册到bean工厂中,至于在Config类中注册的两个Bean为什么没有在控制台打印出来,原因是beanFactory没有解析注解的功能,下面我们来看test2()

控制台打印:

public void test2() {
	DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    // bean 的定义 (class, scope, 初始化,销毁)
    AbstractBeanDefinition beanDefinition =
    BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
    // 注册到BeanFactory
    beanFactory.registerBeanDefinition("config", beanDefinition);
	// 添加Spring内置的后处理器,这些后处理器也是bean
    AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
     // 执行BeanFactory后处理器	
    beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
     .forEach(beanFactoryPostProcessor -> {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        });
    for (String name : beanFactory.getBeanDefinitionNames()) {
        System.out.println(name);
    }
}

给bean工厂添加后处理器,执行后处理器,其中internalConfigurationAnnotationProcessor内处理器有解析@Configuration的功能。

小疑惑:添加内置的后处理器有5个,那么执行的有多少个呢,下面来看控制台打印

只执行了2个,BeanFactoryPostProcessor只包含了两个Spring内置的两个后处理器。

小结:通过上面的案例,我们发现了其实BeanFactory没有那么多功能,很多功能都是通过后处理器实现的。BeanFactory后处理器的主要功能,就是补充了bean的一些定义。

过渡

System.out.println(beanFactory.getBean(Bean1.class).getBean2());,打印这段代码,发现只有Bean1被注入容器,但是Bean1里面获取不到Bean2,明明Bean2被@Autowired修饰。

原因是AutowiredAnnotationBeanPostProcessor没执行。AutowiredAnnotationBeanPostProcessor是Bean后处理器,和BeanFactory后处理器不是同一个东西。

Bean后处理器

Bean后处理器,针对bean的生命周期的各个阶段提供扩展。例如@Autowired,@Resource

通过控制台打印,我们也发现了Bean后处理器有两个,AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor,AutowiredAnnotationBeanPostProcessor可以解析@Autowired;CommonAnnotationBeanPostProcessor可以解析@Resource

// 执行Bean后处理器
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);

执行了Bean后处理器,控制台的打印:

注意:当需要用到Bean时,才会去创建Bean,如果想先创建Bean,再使用则要加上beanFactory.preInstantiateSingletons();这句代码是准备好单例Bean的功能

后处理器排序

问题引出:如果同时对一个变量加了@Autowired和@Resource,会出现什么情况呢?

修改一下代码,增加了Bean3和Bean4,以及接口inter,Bean3和Bean4实现了inter

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

static interface Inter{}


static class Bean1 {
    private static final Logger log = LoggerFactory.getLogger(Bean1.class);
    public Bean1() {log.debug("构造 Bean1()");}

    @Autowired
    private Bean2 bean2;
    
    @Autowired
    @Resource(name = "bean4")
    // 说明:如果有多个bean对象,@Autowired会根据变量名优先获取bean,也就是获取bean3
    // @Resource则根据name获取,也就是获取bean4
    private Inter bean3;

    public Bean2 getBean2() {return bean2;}
    public Inter getInter() {return bean3;}

}
static class Bean2 {
    private static final Logger log = LoggerFactory.getLogger(Bean2.class);
    public Bean2() {log.debug("构造 Bean2()");}
}

static class Bean3 implements Inter{
    private static final Logger log = LoggerFactory.getLogger(Bean2.class);
    public Bean3() {log.debug("构造 Bean3()");}
}

static class Bean4 implements Inter{
    private static final Logger log = LoggerFactory.getLogger(Bean2.class);
    public Bean4() {log.debug("构造 Bean4()");}
}

在原有的代码基础上,我们执行 System.out.println(beanFactory.getBean(Bean1.class).getInter());控制台打印了:

为什么获取了Bean3对象而不是获取Bean4对象呢,这是根据Bean后处理器的执行顺序有关的,因为先执行了AutowiredAnnotationBeanPostProcessor,然后才执行CommonAnnotationBeanPostProcessor。故解析@Autowired。

现在,我们让CommonAnnotationBeanPostProcessor先执行,AutowiredAnnotationBeanPostProcessor后执行。

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

比较器

比较器怎么来的?在添加Spring内置后处理器的时候,也就是执行AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);的时候,这个方法会设置一个比较器

排序原理

数字越小,排序越优先。

ApplicationContext实现

/**
 * @author Becant
 * 2024-04-25-16:18
 * @version 1.0.0
 * ApplicationContext的实现
 */
public class A02Application {
    public static void main(String[] args) {
        testAnnotationConfigApplicationContext();
        testAnnotationConfigServletWebServerApplicationContext();

    }

    @Configuration
    static class WebConfig {
        // 注册内嵌的Tomcat服务器
        @Bean
        public ServletWebServerFactory ServletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }
        // 注册DispatcherServlet
        @Bean
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }
        // 将DispatcherServlet注册到Tomcat上
        @Bean
        public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }
        // 当web发来请求时,优先通过注册DispatcherServlet,再请求分发到控制器
        @Bean("/hello")
        public Controller controller1() {
            return (request, response) -> {
                response.getWriter().print("hello");
                return null;
            };
        }
    }
    
    @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;
        }
    }
    static class Bean1{}
    static class Bean2{
        private Bean1 bean1;
        public void setBean1(Bean1 bean1) {
            this.bean1 = bean1;
        }
        public Bean1 getBean1() {
            return this.bean1;
        }
    }
}

ClassPathXmlApplicationContext

该类已过时,日常中基本用不上。

FileSystemXmlApplicationContext

该类已过时,日常中基本用不上。

AnnotationConfigApplicationContext

测试方法:

public static void testAnnotationConfigApplicationContext() {
    AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(Config.class);
    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    }
    System.out.println(context.getBean(Bean2.class).getBean1());
}

执行结果:

对于这个类,再使用这个类的构造方法时,AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Bean1.class);Bean1这个类上没有任何注解修饰,Bean1也会被注入容器。还有,没有在Config类上添加@Configuration注解,Config,Bean1,Bean2也会被注入容器。

AnnotationConfigServletWebServerApplicationContext

测试方法:

public static void testAnnotationConfigServletWebServerApplicationContext() {
    AnnotationConfigServletWebServerApplicationContext context =
            new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    }
}

执行结果:

接下来访问一下localhost:8080/hello

访问成功

总结

BeanFactory

BeanFactory 不会做的事:

  1. 不会主动调用 Beanfactory 的后处理器
  2. 不会主动调用 Bean的后处理器
  3. 不会主动初始化单例
  4. 不会解析beanFactory
  5. 不会解析${} 与 #{}

后处理器有顺序

ApplicationContext

​ 1.常见的ApplicationContext 容器实现

​ 2.内嵌容器、DispatcherServlet 的创建方法、作用

​ 3.非web环境的SpringBoot下使用AnnotationConfigApplicationContext

​ 4.web环境下的SpringBoot使用AnnotationConfigServletWebServerApplicationContext

​ 5.web环境下的SpringBoot核心组件就是ServletWebServerFactory和DispatcherServlet


第二讲、Spring之容器实现
http://example.com/2024/04/24/第二讲、Spring之容器实现/
发布于
2024年4月24日
更新于
2024年4月25日
许可协议