美文网首页spring
Spring原理分析-BeanFactory与Applicati

Spring原理分析-BeanFactory与Applicati

作者: 石头耳东 | 来源:发表于2022-04-11 22:12 被阅读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对象,如下:

@SpringBootApplication
public class SpringOriginDemoApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringOriginDemoApplication.class, args);
    }

}
BeanFactory接口.png

BeanFactory是:

Ⅰ ApplicationContext 的父接口;
Ⅱ 是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能。

2、BeanFactory接口

BeanFactory接口,最基础的待实现方法是getBean();方法。

另外,像控制反转基本的依赖注入、直至 Bean 的生命周期的各种功能,都是依赖于BeanFactory接口实现的。

  • ① DefaultListableBeanFactory类

是Spring项目中BeanFactory功能最完善的实现类,关系图如下:

DefaultListableBeanFactory类.png
  • ② 通过反射获取singletonObjects成员变量案例

DefaultSingletonBeanRegistry类,如下:

DefaultSingletonBeanRegistry类.png

通过反射获取beanFactory中的singletonObjects,如下:

// 通过反射获取 Field 对象singletonObjects
Field 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()));
反射输出结果.png

3、ApplicationContext接口

  • ① 实现MessageSource接口

Ⅰ 在/resources目录下编写配置文件

语言代码表:Language Code Table (lingoes.cn),此处设置配置文件地区可以省略不写,一般写成zh、ja、en就可以(推荐)。

在/resources目录下编写配置文件.png 逐个配置配置文件.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));
输出.png
  • ② 实现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");
获取jar包其他资源.png
  • ③ 实现EnvironmentCapable接口

Ⅰ 获取变量键值

该方法可以获取配置文件(如:application.properties)内的键值,也可以拿到环境变量的键值,如下:

System.out.println(context.getEnvironment().getProperty("java_home")); // 输出 C:\Program Files\Java\jdk1.8.0_311
System.out.println(context.getEnvironment().getProperty("server.port")); // 输出 8080
  • ④ 实现ApplicationEventPublisher接口

Ⅰ 编写事件类(实现ApplicationEvent )

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

Ⅱ 编写事件发布类

@Component
public 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));
    }
}

Ⅲ 编写事件监听类

@Component
public 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();
实现ApplicationEventPublisher接口.png

实际使用场景中的作用:解耦合。

  • ⑤ 总结

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没有输出,如下:

控制台输出.png

至此,我们可以看出基础的DefaultListableBeanFactory类并不直接支持此类注解扫描。

  • ② 注册各类后置处理器
// 给 beanFactory 添加常用的后置处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
添加常用的后置处理器.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);
});
获取beanFactory的后置处理器.png

至此,我们知道BeanFactoryPostProcessor是对BeanDifinition的一些补充。另外,我们尝试获取一下bean1、bean2,如下:

System.out.println(beanFactory.getBean(Bean1.class).getBean2());
尝试获取bean1.png

Creating shared instance of singleton bean 'bean1'可以看到Bean对象在我们获取的时候才去创建,并且bean1对象内部并没有注入构造好的bean2。此时我们就需要用到Bean后置处理器。

  • ④ bean后置处理器

其作用是对bean生命周期的各个阶段提供拓展,例如:@Autowired...

beanFactory.getBeansOfType(BeanPostProcessor.class).values()
        .forEach(beanFactory::addBeanPostProcessor);
bean后置处理器.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());
preInstantiateSingletons初始化.png
  • ⑥ 总结

Ⅰ 不会主动调用 BeanFactory 后处理器;
Ⅱ 不会主动添加 Bean 后处理器;
Ⅲ 不会主动初始化单例;
Ⅳ 不会解析beanFactory 还不会解析 ${ } 与 #{ }(如:@Value注解内使用)。

补充:AnnotationConfigUtils类

  • ① 基础准备

Ⅰ Config类添加:

@Bean
public Bean3 bean3(){
    return new Bean3();
}
@Bean
public Bean4 bean4(){
    return new Bean4();
}

Ⅱ 编写接口&内部类:

interface Inter{}
static class Bean3 implements Inter{}
static class Bean4 implements Inter{}

Ⅲ Bean1内注入:

a、同一接口不同实现,@Autowired按照字段名注入,如下:

@Autowired
private Inter bean3;
@Autowired按照字段名注入.png

b、同一接口不同实现,@Resource(name = "bean4")指定注入,如下:

@Resource(name = "bean4")指定注入.png

c、同时开启,按照@Autowired注入,如下:

同时开启按照@Autowired注入.png

d、通过比较器修改注入,如下:

Ⅰ 修改前

beanFactory.getBeansOfType(BeanPostProcessor.class).values()
        .forEach(beanPostProcessor -> {
            System.out.println("[!!!LOOK HERE!!!] Added BeanPostProcessor is ---> " + beanPostProcessor);
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        });
修改前.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);
        });
修改后.png

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" class="com.stone.demo02.Demo02.Bean1"/>
        <bean name="bean2" class="com.stone.demo02.Demo02.Bean2">
            <property name="bean1" ref="bean1"/>
        </bean>
</beans>

Ⅲ 编写测试方法

public static void testClassPathXmlApplicationContext(){
    ClassPathXmlApplicationContext context = 
            new ClassPathXmlApplicationContext("b01.xml");
    for (String definitionName : context.getBeanDefinitionNames()) {
        System.out.println(definitionName);
    }
    System.out.println(context.getBean(Bean2.class).getBean1());
}
ClassPathXmlApplicationContext类.png
  • ② 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());
}
FileSystemXmlApplicationContext类.png

补充: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);
}
XmlBeanDefinitionReader类.png
  • ③ AnnotationConfigApplicationContext类

Ⅰ 编写Config类

@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;
    }
}

Ⅱ 编写测试方法

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());
}
AnnotationConfigApplicationContext类.png

补充:<context:annotation-config/>

可以看到AnnotationConfigApplicationContext类自动添加了后置处理器,其作用类似于配置<context:annotation-config/>。

<!--添加注解驱动-->
<context:annotation-config/>
注解驱动在上.png 注解驱动在下.png
  • ④ AnnotationConfigServletWebServerApplicationContext类

Ⅰ 编写WebConfig类

@Configuration
static 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);
    }
}
AnnotationConfigServletWebServerApplicationContext类.png

三、结尾

以上即为Spring原理分析-容器&Bean(一)的全部内容,感谢阅读。

相关文章

网友评论

    本文标题:Spring原理分析-BeanFactory与Applicati

    本文链接:https://www.haomeiwen.com/subject/ufaisrtx.html