Spring原理分析-BeanFactory与ApplicationContext

源码 2024-9-7 11:26:18 52 0 来自 中国
零、本文纲要


  • 一、容器接口
    1、BeanFactory与ApplicationContext
    2、BeanFactory接口
    ① DefaultListableBeanFactory类
    3、ApplicationContext接口
    ① 实现MessageSource接口
    ② 实现ResourcePatternResolver接口
    ③ 实现EnvironmentCapable接口
    ④ 实现ApplicationEventPublisher接口
  • 二、容器实现
    1、BeanFactory实现
    ① DefaultListableBeanFactory类
    ② 注册各类后置处置处罚器
    ③ beanFactory后置处置处罚器
    ④ bean后置处置处罚器
    ⑤ preInstantiateSingletons初始化
    增补:AnnotationConfigUtils类
    2、ApplicationContext实现
    ① ClassPathXmlApplicationContext类
    ② FileSystemXmlApplicationContext类
    增补:XmlBeanDefinitionReader类
    ③ AnnotationConfigApplicationContext类
    增补:<context:annotation-config/>
    ④ AnnotationConfigServletWebServerApplicationContext类
一、容器接口

0、底子准备
添加最底子的Spring Boot依赖,如下:
<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter</artifactId></dependency><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-web</artifactId></dependency><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-test</artifactId>    <scope>test</scope></dependency>1、BeanFactory与ApplicationContext

  • ① Spring Boot启动类
SpringApplication.run(A01.class, args);的返回值为ConfigurableApplicationContext对象,如下:
@SpringBootApplicationpublic class SpringOriginDemoApplication {    public static void main(String[] args) {        ConfigurableApplicationContext context = SpringApplication.run(SpringOriginDemoApplication.class, args);    }} 1.png BeanFactory是:
Ⅰ ApplicationContext 的父接口;
Ⅱ 是 Spring 的焦点容器, 重要的 ApplicationContext 实现都【组合】了它的功能。
2、BeanFactory接口
BeanFactory接口,最底子的待实现方法是getBean();方法。
别的,像控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都是依赖于BeanFactory接口实现的。

  • ① DefaultListableBeanFactory类
是Spring项目中BeanFactory功能最美满的实现类,关系图如下:


  • ② 通过反射获取singletonObjects成员变量案例
DefaultSingletonBeanRegistry类,如下:
通过反射获取beanFactory中的singletonObjects,如下:
// 通过反射获取 Field 对象singletonObjectsField singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");// 暴力反射singletonObjects.setAccessible(true);// 获取beanFactory中的singletonObjects,并遍历ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);map.entrySet()        .stream().filter(e -> e.getKey().startsWith("component"))        .forEach(e -> System.out.println(e.getKey() + " = " + e.getValue()));3、ApplicationContext接口

  • ① 实现MessageSource接口
Ⅰ 在/resources目次下编写设置文件
语言代码表:Language Code Table (lingoes.cn),此处设置设置文件地域可以省略不写,一样平常写成zh、ja、en就可以(保举)。
5.png Ⅱ 编写测试代码
System.out.println(context.getMessage("hi", null, Locale.ENGLISH));System.out.println(context.getMessage("hi", null, Locale.CHINA));System.out.println(context.getMessage("hi", null, Locale.JAPAN));

  • ② 实现ResourcePatternResolver接口
Ⅰ 获取类路径下指定资源classpath:
Resource[] resources = context.getResources("classpath:application.properties");for (Resource resource : resources) {    System.out.println(resource); // 输出 class path resource [application.properties]}Ⅱ 获取jar包其他资源classpath*:
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");

  • ③ 实现EnvironmentCapable接口
Ⅰ 获取变量键值
该方法可以获取设置文件(如:application.properties)内的键值,也可以拿到环境变量的键值,如下:
System.out.println(context.getEnvironment().getProperty("java_home")); // 输出 C:\Program Files\Java\jdk1.8.0_311System.out.println(context.getEnvironment().getProperty("server.port")); // 输出 8080

  • ④ 实现ApplicationEventPublisher接口
Ⅰ 编写变乱类(实现ApplicationEvent )
public class UserRegisterEvent extends ApplicationEvent {    public UserRegisterEvent(Object source) {        super(source);    }}Ⅱ 编写变乱发布类
@Componentpublic class Component1 {    private static final Logger log = LoggerFactory.getLogger(Component1.class);    @Autowired    private ApplicationEventPublisher context;    public void register() {        log.debug("用户注册");        context.publishEvent(new UserRegisteredEvent(this));    }}Ⅲ 编写变乱监听类
@Componentpublic class Component2 {    private static final Logger log = LoggerFactory.getLogger(Component2.class);    @EventListener    public void aaa(UserRegisteredEvent event) {        log.debug("{}", event);        log.debug("发送短信");    }}此处我们是debug输出的,在application.properties中自界说一下日记输出:logging.level.com.stone=debug。
Ⅳ 测试
context.getBean(Component1.class).register();实际利用场景中的作用:解耦合。

  • ⑤ 总结
ApplicationContext接口拓展功能:
Ⅰ 国际化支持(MessageSource);
Ⅱ 通配符获取资源(ResourcePatternResolver);
Ⅲ 获取环境变量(EnvironmentCapable);
Ⅳ 发送变乱(ApplicationEventPublisher)。
二、容器实现

1、BeanFactory实现

  • ① DefaultListableBeanFactory类
Ⅰ 准备测试类
public class TestBeanFactory {    @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(Bean1.class);        public Bean2(){            log.debug("构造 Bean2()");        }    }}Ⅱ 编写main方法
public static void main(String[] args) {    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();    // Bean 的界说信息 class、scope、initMethod、destroyMethod 等    AbstractBeanDefinition bd            = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();    beanFactory.registerBeanDefinition("config", bd);    for (String definitionName : beanFactory.getBeanDefinitionNames()) {        System.out.println(definitionName);    }}可以看到我们@Configuration注解的设置类并没有被扫描,以是@Bean相关的Bean没有输出,如下:
至此,我们可以看出底子的DefaultListableBeanFactory类并不直接支持此类注解扫描。

  • ② 注册各类后置处置处罚器
// 给 beanFactory 添加常用的后置处置处罚器AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); 11.png 可以看到,此时bean1和bean2仍旧没有被扫描放入容器。以为还没有调用beanFactory的后置处置处罚器。添加的后置处置处罚器:
a、ConfigurationAnnotationProcessor;(BeanFactory后置处置处罚器)
b、AutowiredAnnotationProcessor;
c、CommonAnnotationProcessor;
d、EventListenerProcessor;
e、EventListenerFactory。

  • ③ beanFactory后置处置处罚器
获取beanFactory的后置处置处罚器,并调用,如下:
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()        .forEach(beanFactoryPostProcessor -> {    beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);}); 12.png 至此,我们知道BeanFactoryPostProcessor是对BeanDifinition的一些增补。别的,我们实验获取一下bean1、bean2,如下:
System.out.println(beanFactory.getBean(Bean1.class).getBean2());Creating shared instance of singleton bean 'bean1'可以看到Bean对象在我们获取的时间才去创建,并且bean1对象内部并没有注入构造好的bean2。此时我们就须要用到Bean后置处置处罚器。

  • ④ bean后置处置处罚器
其作用是对bean生命周期的各个阶段提供拓展,比方:@Autowired...
beanFactory.getBeansOfType(BeanPostProcessor.class).values()        .forEach(beanFactory::addBeanPostProcessor); 14.png 此时可以看到,装配bean1时须要bean2,容器就去沟通bean2完成注入。但是,实际利用Spring的时间,实在bean都是在我们利用前已经创建好的,我们还需处置处罚。

  • ⑤  preInstantiateSingletons初始化
beanFactory.preInstantiateSingletons();System.out.println("==== before we get these beans ====");System.out.println(beanFactory.getBean(Bean1.class).getBean2()); 15.png

  • ⑥ 总结
Ⅰ 不会主动调用 BeanFactory 后处置处罚器;
Ⅱ 不会主动添加 Bean 后处置处罚器;
Ⅲ 不会主动初始化单例;
Ⅳ 不会分析beanFactory 还不会分析 ${ } 与 #{ }(如:@Value注解内利用)。
增补:AnnotationConfigUtils类

  • ① 底子准备
Ⅰ Config类添加:
@Beanpublic Bean3 bean3(){    return new Bean3();}@Beanpublic Bean4 bean4(){    return new Bean4();}Ⅱ 编写接口&内部类:
interface Inter{}static class Bean3 implements Inter{}static class Bean4 implements Inter{}Ⅲ Bean1内注入:
a、同一接口差别实现,@Autowired按照字段名注入,如下:
@Autowiredprivate Inter bean3;b、同一接口差别实现,@Resource(name = "bean4")指定注入,如下:
c、同时开启,按照@Autowired注入,如下:
18.png d、通过比力器修改注入,如下:
Ⅰ 修改前
beanFactory.getBeansOfType(BeanPostProcessor.class).values()        .forEach(beanPostProcessor -> {            System.out.println("[!!!LOOK HERE!!!] Added BeanPostProcessor is ---> " + beanPostProcessor);            beanFactory.addBeanPostProcessor(beanPostProcessor);        }); 19.png Ⅱ 修改后
添加了.stream().sorted(beanFactory.getDependencyComparator());
beanFactory.getBeansOfType(BeanPostProcessor.class).values()        .stream().sorted(beanFactory.getDependencyComparator())        .forEach(beanPostProcessor -> {            System.out.println("[!!!LOOK HERE!!!] Added BeanPostProcessor is ---> " + beanPostProcessor);            beanFactory.addBeanPostProcessor(beanPostProcessor);        });2、ApplicationContext实现

  • ① ClassPathXmlApplicationContext类
Ⅰ 编写内部类
public class Demo02 {    static class Bean1{}    static class Bean2{        private Bean1 bean1;        public void setBean1(Bean1 bean1) {            this.bean1 = bean1;        }        public Bean1 getBean1() {            return bean1;        }    }}Ⅱ 编写b01.xml设置文件
<?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-->        <bean name="bean1"

  • ② FileSystemXmlApplicationContext类
public static void testFileSystemXmlApplicationContext(){    FileSystemXmlApplicationContext context            // 绝对路径 D:\JavaStudy\Level1\spring_origin_demo\src\main\resources\b01.xml            // 相对路径 src/main/resources/b01.xml            = new FileSystemXmlApplicationContext("src/main/resources/b01.xml");    for (String definitionName : context.getBeanDefinitionNames()) {        System.out.println(definitionName);    }    System.out.println(context.getBean(Bean2.class).getBean1());}增补:XmlBeanDefinitionReader类
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();System.out.println("[!!!LOOK HERE!!!] Before reader works :");for (String definitionName : beanFactory.getBeanDefinitionNames()) {    System.out.println(definitionName);}XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);//        reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));reader.loadBeanDefinitions(new FileSystemResource("src/main/resources/b01.xml"));System.out.println("[!!!LOOK HERE!!!] After reader worked :");for (String definitionName : beanFactory.getBeanDefinitionNames()) {    System.out.println(definitionName);}

  • ③ AnnotationConfigApplicationContext类
Ⅰ 编写Config类
@Configurationstatic class Config{    @Bean    public Bean1 bean1(){        return new Bean1();    }    @Bean    public Bean2 bean2(Bean1 bean1){        Bean2 bean2 = new Bean2();        bean2.setBean1(bean1);        return bean2;    }}Ⅱ 编写测试方法
public static void testAnnotationConfigApplicationContext(){    AnnotationConfigApplicationContext context            = new AnnotationConfigApplicationContext(Config.class);    for (String definitionName : context.getBeanDefinitionNames()) {        System.out.println(definitionName);    }    System.out.println(context.getBean(Bean2.class).getBean1());}增补:<context:annotation-config/>
可以看到AnnotationConfigApplicationContext类主动添加了后置处置处罚器,其作用类似于设置<context:annotation-config/>。
<!--添加注解驱动--><context:annotation-config/>

  • ④ AnnotationConfigServletWebServerApplicationContext类
Ⅰ 编写WebConfig类
@Configurationstatic class WebConfig{    @Bean // 必须有    public ServletWebServerFactory servletWebServerFactory(){        return new TomcatServletWebServerFactory();    }    @Bean // 必须有    public DispatcherServlet dispatcherServlet(){        return new DispatcherServlet();    }    @Bean // 必须有    public DispatcherServletRegistrationBean registrationBean(            DispatcherServlet dispatcherServlet){        return new DispatcherServletRegistrationBean(dispatcherServlet, "/");    }    @Bean("/hello")    public Controller controller(){        // org.springframework.web.servlet.mvc.Controller        return (request, response) -> {            response.getWriter().println("Hello, context!");            return null;        };    }}Ⅱ 编写测试方法
public static void testAnnotationConfigServletWebServerApplicationContext(){    AnnotationConfigServletWebServerApplicationContext context            = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);    for (String definitionName : context.getBeanDefinitionNames()) {        System.out.println(definitionName);    }}三、末了

以上即为Spring原理分析-容器&Bean(一)的全部内容,感谢阅读。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-19 04:24, Processed in 0.149855 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表