Spring原理学习(一)容器接口与容器实现

感谢黑马满老师的讲解,通俗易懂,很适合去学习。

视频地址:Spring高级49讲-导学_哔哩哔哩_bilibili

一、容器接口 

学习目标:

  1. BeanFactory能做哪些事
  2. ApplicationContext有哪些扩展功能
  3. 事件解耦

1、BeanFactory 与 ApplicationContext 的区别

通过这个示例结合 debug 查看 ApplicationContext 对象的内部结构

@SpringBootApplication
public class A01 {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {

        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);

        context.getBean("aaa");
        System.out.println(context);
    }
}

BeanFactory 接口典型功能:getBean方法

    //AbstractApplicationContext类 
    public Object getBean(String name) throws BeansException {
        this.assertBeanFactoryActive();
        //内部调用了beanFactory的getBean方法
        return this.getBeanFactory().getBean(name);
    }

debug查看context内部结构

 ConfigurableApplicationContext继承关系: 

总结:到底什么是 BeanFactory

  • 它是 ApplicationContext 的父接口

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

2、BeanFactory 能干啥

BeanFactory接口:

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> var1);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);

    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

表面上只有 getBean,实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供。 

查看它的实现类

 我们知道BeanFactory能管理所有的bean,最为熟悉的就是单例bean,其中DefaultSingletonBeanRegistry就管理了所有的单例对象。

通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean

@SpringBootApplication
public class A01 {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {

        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);

        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        singletonObjects.setAccessible(true);
        //获取beanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //获取beanFactory对象的singletonObjects属性
        Map<String,Object> map = (Map<String,Object>) singletonObjects.get(beanFactory);
        //由于对象太多,我们只打印名称以component开头的对象
        map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
                .forEach( e -> {
                    System.out.println(e.getKey() + "=" + e.getValue());
                });
    }
}
@Component
public class Component1 {

}

@Component
public class Component2 {

}

结果:

component1=com.itheima.a01.Component1@16eedaa6
component2=com.itheima.a01.Component2@28501a4b 

3、ApplicationContext 比 BeanFactory 多点啥

查看类图,可以发现ApplicationContext多实现了四个接口,也就可以回答下面的问题了。

ApplicationContext 比 BeanFactory 多点啥:

  • ApplicationContext 组合并扩展了 BeanFactory 的功能

  • 国际化、通配符方式获取一组 Resource 资源、整合 Environment 环境、事件发布与监听

1)MessageSource接口:国际化

@SpringBootApplication
public class A01 {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {

        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);

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

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

messages.properties(空),空的 messages.properties 也必须存在。

messages_en.properties

hi=Hello

messages_ja.properties

hi=こんにちは

messages_zh.propertieshi=你好

hi=你好

结果:

你好
Hello
こんにちは

2)ResourcePatternResolver接口:通配符方式获取一组 Resource 资源

@SpringBootApplication
public class A01 {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {

        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);

        //Resource[] resources = context.getResources("classpath:application.properties");

        //classpath:META-INF/spring.factories:在类路径找,如果在jar包中是找不到的
        //classpath*:META-INF/spring.factories:这样就可以
        Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
        for (Resource resource : resources) {
            System.out.println(resource);
        }
}

结果:第一个是自己模块的,剩下的是导入的jar中的

URL [file:/E:/spring%e6%ba%90%e7%a0%81/%e4%bb%a3%e7%a0%81/show/target/classes/META-INF/spring.factories]
URL [jar:file:/D:/developer_tools/maven/repository/org/springframework/boot/spring-boot/2.5.5/spring-boot-2.5.5.jar!/META-INF/spring.factories]
URL [jar:file:/D:/developer_tools/maven/repository/org/springframework/boot/spring-boot-autoconfigure/2.5.5/spring-boot-autoconfigure-2.5.5.jar!/META-INF/spring.factories]
URL [jar:file:/D:/developer_tools/maven/repository/org/springframework/spring-beans/5.3.10/spring-beans-5.3.10.jar!/META-INF/spring.factories]
URL [jar:file:/D:/developer_tools/maven/repository/org/mybatis/spring/boot/mybatis-spring-boot-autoconfigure/2.2.0/mybatis-spring-boot-autoconfigure-2.2.0.jar!/META-INF/spring.factories]
URL [jar:file:/D:/developer_tools/maven/repository/com/alibaba/druid-spring-boot-starter/1.2.8/druid-spring-boot-starter-1.2.8.jar!/META-INF/spring.factories]
URL [jar:file:/D:/developer_tools/maven/repository/org/springframework/spring-test/5.3.10/spring-test-5.3.10.jar!/META-INF/spring.factories]

 3)EnvironmentCapable接口:整合 Environment 环境

@SpringBootApplication
public class A01 {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {

        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);

        //不区分大小写
        System.out.println(context.getEnvironment().getProperty("java_home"));
        System.out.println(context.getEnvironment().getProperty("server.port"));
}

结果:

D:\developer_tools\jdk\1.8.0_131
8082

4)ApplicationEventPublisher接口:事件发布与监听

Spring中任何一个组件都可以作为监听器,这里用Component2

事件类 

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

发送事件 

@SpringBootApplication
public class A01 {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {

        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);

        //发送事件
        context.publishEvent(new UserRegisteredEvent(context));
}

监听器 

@Component
public class Component2 {

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

    //监听器,方法名任意,参数要求:与发送的事件类型一致
    @EventListener
    public void aaa(UserRegisteredEvent event) {
        log.debug("{}", event);
    }
}

结果

[DEBUG] 10:00:42.236 [main] com.itheima.a01.Component2
-com.itheima.a01.UserRegisteredEvent
[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@747edf66, 
started on Tue Nov 01 10:00:39 CST 2022] 

 4、事件解耦

完成用户注册与发送短信之间的解耦

发送事件

@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("发送短信");
    }
}

 从容器获取Component1调用其方法

@SpringBootApplication
public class A01 {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {

        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);

        context.getBean(Component1.class).register();
}

二、容器实现

学习目标:

  1. BeanFactory实现的特点
  2. ApplicationContext的常见实现和用法
  3. 内嵌容器,注册DispatcherServlet

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 的功能,并非继承而来

1、DefaultListableBeanFactory演示

原始的DefaultListableBeanFactory

public class TestBeanFactory {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        //bean的定义(class,scope,初始化,销毁)
        AbstractBeanDefinition beanDefinition
                = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        //注册beandefinition对象
        //参数一:名字 ,参数二:具体bean
        beanFactory.registerBeanDefinition("config", beanDefinition);

        //输出容器中bean的名字
        for (String name : beanFactory.getBeanDefinitionNames()) {
            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对象,原始的beanFactory并没有解析注解的能力

config

1)给BeanFactory添加一些常用的后处理器

public class TestBeanFactory {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        //bean的定义(class,scope,初始化,销毁)
        AbstractBeanDefinition beanDefinition
                = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        //注册beandefinition对象
        //参数一:名字 ,参数二:具体bean
        beanFactory.registerBeanDefinition("config", beanDefinition);

        //给BeanFactory添加一些常用的后处理器,仅仅是注册后处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        //输出容器中bean的名字
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }

    }
}

结果:仅仅是注册了后处理器,还没有执行,此时并不具备解析注解的能力

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

2)执行BeanFactory后置处理器

public class TestBeanFactory {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        //bean的定义(class,scope,初始化,销毁)
        AbstractBeanDefinition beanDefinition
                = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        //注册beandefinition对象
        //参数一:名字 ,参数二:具体bean
        beanFactory.registerBeanDefinition("config", beanDefinition);

        //给BeanFactory添加一些常用的后处理器,仅仅是注册后处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
        
        //执行BeanFactory后置处理器
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
                .stream().forEach(beanFactoryPostProcessor -> {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        });

        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }

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

    }
}

结果: bean1,bean2成功注册进容器,但是bean1获取不到bean2对象,结果为null。

并且可以发现是懒加载,第一次用到的时候才创建对象实例

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
bean1
bean2
[DEBUG] 15:34:11.550 [main] c.itheima.a02.TestBeanFactory$Bean1 - 构造 Bean1() 
null

 原因:输出刚才执行的后处理器,发现没有执行@Autowire后处理器

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

[org.springframework.context.annotation.ConfigurationClassPostProcessor@24313fcc, 
org.springframework.context.event.EventListenerMethodProcessor@7d20d0b]

 3)执行Bean后处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowire,@Resource

public class TestBeanFactory {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        //bean的定义(class,scope,初始化,销毁)
        AbstractBeanDefinition beanDefinition
                = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        //注册beandefinition对象
        //参数一:名字 ,参数二:具体bean
        beanFactory.registerBeanDefinition("config", beanDefinition);

        //给BeanFactory添加一些常用的后处理器,仅仅是注册后处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
        
        //执行BeanFactory后置处理器
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
                .stream().forEach(beanFactoryPostProcessor -> {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        });

        //执行Bean后处理器
        beanFactory.getBeansOfType(BeanPostProcessor.class).values()
                .forEach(beanPostProcessor -> {
                    //查看Bean后处理器顺序
                    System.out.println(">>>>" + beanPostProcessor);
                    //添加Bean后处理器
                    beanFactory.addBeanPostProcessor(beanPostProcessor);
                });

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

    }
}

结果:

>>>>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@76908cc0
>>>>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@2473d930
[DEBUG] 15:50:59.132 [main] c.itheima.a02.TestBeanFactory$Bean1 - 构造 Bean1() 
[DEBUG] 15:50:59.149 [main] c.itheima.a02.TestBeanFactory$Bean2 - 构造 Bean2() 
com.itheima.a02.TestBeanFactory$Bean2@188715b5

我们可以在获取bean前调用下面的方法准备好单例,就不用到使用时才去创建实例。 

beanFactory.preInstantiateSingletons(); //准备好所有单例
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");//为了区分是在获取前创建的

结果:

>>>>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@76908cc0
>>>>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@2473d930
[DEBUG] 15:56:20.124 [main] c.itheima.a02.TestBeanFactory$Bean1 - 构造 Bean1() 
[DEBUG] 15:56:20.138 [main] c.itheima.a02.TestBeanFactory$Bean2 - 构造 Bean2() 
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
com.itheima.a02.TestBeanFactory$Bean2@43195e57

此时增加bean3,bean4

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

    interface Inter {

    }

    static class Bean3 implements Inter {

    }

    static class Bean4 implements Inter {

    }

 关于@Autowire的一些点,这里说明一下

我要在bean1里面这样注入,能成功吗?注入失败,容器中bean3,bean4都匹配

@Autowired
private Inter inter; 

 但是这样就可以,当有多个bean匹配时,通过名字进一步匹配

@Autowired
private Inter bean3

可以发现我刚刚打印了Bean后处理器的顺序,这个有什么用?看这个例子,请问注入的是bean3还是bean4呢

注入bean3,与后处理器顺序有关,先解析Autowire

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

设置Bean后处理器顺序,通过优先级排序。

//Bean后处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowire,@Resource
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
        .sorted(beanFactory.getDependencyComparator()).
        forEach(beanPostProcessor -> {
            //添加Bean后处理器
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        });

查看源码,它们都实现了 Ordered 接口,设置Ordered大小进行排序,ordered小的优先级高

//AutowiredAnnotationBeanPostProcessor类
private int order = 2147483645;

//CommonAnnotationBeanPostProcessor类
public CommonAnnotationBeanPostProcessor() {
    this.setOrder(2147483644);
    this.setInitAnnotationType(PostConstruct.class);
    this.setDestroyAnnotationType(PreDestroy.class);
    this.ignoreResourceType("javax.xml.ws.WebServiceContext");
}

public interface Ordered {
    int HIGHEST_PRECEDENCE = -2147483648;
    int LOWEST_PRECEDENCE = 2147483647;

    int getOrder();
}

beanFactory.getDependencyComparator()是什么?

查看源码:可以发现

  1. 实现了 PriorityOrdered 接口的优先级最高
  2. 实现了 Ordered 接口与加了 @Order 注解的平级, 按数字升序
  3. 其它的排在最后
public class AnnotationAwareOrderComparator extends OrderComparator {
}
public class OrderComparator implements Comparator<Object> {
    public int compare(@Nullable Object o1, @Nullable Object o2) {
        return this.doCompare(o1, o2, (OrderComparator.OrderSourceProvider)null);
    }

    private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderComparator.OrderSourceProvider sourceProvider) {
        boolean p1 = o1 instanceof PriorityOrdered;
        boolean p2 = o2 instanceof PriorityOrdered;
        if (p1 && !p2) {
            return -1;
        } else if (p2 && !p1) {
            return 1;
        } else {
            int i1 = this.getOrder(o1, sourceProvider);
            int i2 = this.getOrder(o2, sourceProvider);
            return Integer.compare(i1, i2);
        }
    }
}

 总结

beanFactory 不会做的事:

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

bean 后处理器会有排序的逻辑

2、常见 ApplicationContext 实现

1)ClassPathXmlApplicationContext

从类路径查找 XML 配置文件,创建容器

public class A02Application {
    public static void main(String[] args) {
        testClassPathXmlApplicationContext();
    }

    // 较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
    private static void testClassPathXmlApplicationContext() {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("b01.xml");
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

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


    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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

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

    <!-- 控制反转, 让 bean2 被 Spring 容器管理 -->
    <bean id="bean2" class="com.itheima.a02.A02Application.Bean2">
        <!-- 依赖注入, 建立与 bean1 的依赖关系 -->
        <property name="bean1" ref="bean1"/>
    </bean>
</beans>

结果:

bean1
bean2
com.itheima.a02.A02Application$Bean1@4b168fa9

 我们在Spring基础中都学过这个标签,作用是让系统能够识别相应的注解

<context:annotation-config/>

现在我们看看到底是为什么呢?

加上这个标签,打印容器中的bean,发现多了几个后处理器,用来解析@Configuration,@Autowire,@Resourse等注解

bean1
bean2
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

2)FileSystemXmlApplicationContext

基于磁盘路径下 xml 格式的配置文件来创建

public class A02Application {
    public static void main(String[] args) {
        testFileSystemXmlApplicationContext();
    }

    private static void testFileSystemXmlApplicationContext() {
        FileSystemXmlApplicationContext context =
                new FileSystemXmlApplicationContext("src\\main\\resources\\b01.xml");

        //相对路径(模块开始):src\main\resources\b01.xml
        //绝对路径:E:\spring源码\代码\show\src\main\resources\b01.xml
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

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

结果:

bean1
bean2
com.itheima.a02.A02Application$Bean1@4b168fa9

 测试的时候遇到了一个报错,如下

Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException:
IOException parsing XML document from file 
[D:\workspace_idea2\spring_yuanli\src\main\resources\b01.xml];
nested exception is java.io.FileNotFoundException: src\main\resources\b01.xml

原因:这个项目是导进去的,与默认的工作目录不一样导致的。

处理方法:Edit Configurations -> 修改工作目录

原理:前面说过,ApplicationContext内部还是需要beanFactory的支持

比DefaultListableBeanFactory多的功能,加载bean的定义信息

public class A02Application {
    public static void main(String[] args) {

        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        System.out.println("读取之前...");
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        System.out.println("读取之后...");
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
        //reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\b01.xml"));
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
}

结果:

读取之前...
读取之后...
bean1
bean2
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

3)AnnotationConfigApplicationContext

Spring boot 中非 web 环境容器,较为经典的容器, 基于 java 配置类来创建

    private 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());
    }

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

结果:发现该容器已经配置了解析@Configuration,@Autowire,@Resourse等注解的后处理器

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
com.itheima.a02.A02Application$Bean1@6025e1b6

4)AnnotationConfigServletWebServerApplicationContext

Spring boot 中 servlet web 环境容器,基于 java 配置类来创建, 用于 web 环境

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

    @Configuration
    static class WebConfig {
        //内嵌一个基于servlet的Web容器,即提供web服务器的组件
        @Bean
        public ServletWebServerFactory servletWebServerFactory(){
            return new TomcatServletWebServerFactory();
        }
        //前端控制器
        @Bean
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }
        //Servlet是运行在Tomcat服务器上的,需要将他们两个关联
        //将DispatcherServlet注册到Tomcat服务器
        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            //让除了jsp的所有请求都先经过DispatcherServlet
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }
        //以 /开头可以将它的名称作为访问路径
        @Bean("/hello")
        //注意:是org.springframework.web.servlet.mvc.Controller接口,而不是注解
        public Controller controller1() {
            return new Controller() {
                @Override
                public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
                    response.getWriter().print("hello");
                    return null;
                }
            };
        }
    }

 结果:发现该容器已经配置了解析@Configuration,@Autowire,@Resourse注解的后处理器,并且我们可以通过 localhost:8080/hello进行访问

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.WebConfig
servletWebServerFactory
dispatcherServlet
registrationBean
/hello

总结

学到了什么?

  • 常见的 ApplicationContext 容器实现
  • 内嵌容器、DispatcherServlet 的创建方法、作用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鲁蛋儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值