Spring原理-1.BeanFactory与ApplicationContext的基础介绍

本文属于关于Spring原理的深入学习,旨在帮助读者更好地理解Spring。

目录

BeanFactory和ApplicationContext

Application的几个功能介绍

Bean Factory

bean的注册

bean的依赖注入

饿汉式,懒汉式的切换创建单例

Application

1.类路径下的xml注入bean

2.绝对路径下的文件名注入bean

3.基于java配置类注入bean

4.通过内嵌tomcat实现



BeanFactory和ApplicationContext

这是一个简单的启动类

@SpringBootApplication
public class SpringCodeHhApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(SpringCodeHhApplication.class, args);
        System.out.println(run);
    }

}

点击run方法可以看到返回值是ConfigurableApplicationContext

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class[]{primarySource}, args);
}

进入类图可以看到ConfigurableApplicationContext是实现自BeanFactory。

执行: 

  ConfigurableApplicationContext run = SpringApplication.run(SpringCodeHhApplication.class, args);
        System.out.println(run);

可以看到context中包含了beanFactory中的singletonObjects,

得出了结论:在ConfigurableApplicationContext 加载完成,对应的beanFactory里面的singletionObjects也加载完成了。这个就是ApplicationContext的一个很大的优势,实现了自动加载。所以得出结论,BeanFactory是 ApplicationContext 的父接口并且它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能

来到BeanFactory接口中:

BeanFactory支持的方法里有很多getBean方法,虽然表面上只有getBean,但实际上控制反转、基本的依赖注入,直至Bean的生命周期的各种功能,其实都由其实现类提供。

Application的几个功能介绍

MessageSource,ResourcePatternResolver,EnvironmentCapable,ApplicationEventPublisher四个接口是Application组合并扩展了BeanFactory 的功能,MessageSource用来处理国际化资源,ResourcePatternResolver能够通过磁盘路径找文件,EnvironmentCapable是用来读取spring中配置文件的功能,ApplicationEventPublisher用来发布事件对象。

用代码完成:

@SpringBootApplication
public class SpringCodeHhApplication {

    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext run = SpringApplication.run(SpringCodeHhApplication.class, args);
        System.out.println(run);


        //MessageResource
        System.out.println(run.getMessage("hi", null, Locale.CHINA));
        System.out.println(run.getMessage("hi", null, Locale.ENGLISH));
        System.out.println(run.getMessage("hi", null, Locale.JAPANESE));

        System.out.println(run.getEnvironment().getProperty("java_home"));
        System.out.println(run.getEnvironment().getProperty("server.port"));



        Resource[] resources = run.getResources("classpath*:META-INF/spring.factories");
        for (Resource resource : resources) {
            System.out.println(resource);
        }

        System.out.println(run.getEnvironment().getProperty("java_home"));
        System.out.println(run.getEnvironment().getProperty("server.port"));

//        context.publishEvent(new UserRegisteredEvent(context));
        run.getBean(Component1.class).register();
    }

}

Bean Factory

由以下的类图可得,DefaultListableBeanFactory是Bean Factory的最重要的实现类

使用DefaultListableBeanFactory就可以创建一个核心的Spring容器,不过刚创建好的beanFactory没有任何bean。

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

bean的注册

加入bean的定义:class,scope(单例,多例),初始化,销毁等描述信息。我们在这加入一组配置,并打印已经注册好的bean。

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();
        beanFactory.registerBeanDefinition("config", beanDefinition);

        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被注册进去了,但通常来说加了@Configuration注解和@Bean注解的情况下,Bean1和Bean2不也应该被注册吗? 所以此处的BeanFactory缺少了解析注解的能力,那这些能力是谁提供的呢?

我们需要给Bean Factory加入一些常用的后处理器,这些处理器具备Bean Factory的扩展功能,可以来解析@Bean注解等

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

// BeanFactory 后处理器主要功能,补充了一些 bean 定义
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
    beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);

});

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

可以看到我们添加了@Bean注解的bean已经被注册了,我们可以通俗的把beanFactory后处理器理解为他们也是一个bean,我们要先执个这些bean,才能够解析@configuration的注解,再把@Bean定义的bean加入 到容器中。

bean的依赖注入

在Bean1中用@Autowired进行了依赖注入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;
    }


}

但是通过以下getBean的方法却获取到Bean2,证明了Bean Factory没有进行依赖注入的功能,依赖注入对Bean Factory来说也是一个扩展功能

beanFactory.getBean(Bean1.class).getBean2();

其实,我们可以用Bean的后处理器进行依赖注入的处理,下图的internalConfigurationAnnotationProcessor就是用来处理@Autowired或者@Value注解的。而internalCommonAnnotationProcessor是用来处理@Resource注解的。Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...

进行Bean后处理器的注册,类似于BenFactory。

beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
beanFactory.getBean(Bean1.class).getBean2();

现在可以看到Bean2顺利地实现了依赖注入。

饿汉式,懒汉式的切换创建单例

默认情况是延迟创建单例,即当我们要用到的时候才去创建

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> "); //用这个来观察单例创建的时机。
beanFactory.getBean(Bean1.class).getBean2();

当加入beanFactory.preInstantiateSingletons(); 就代表一开始就创建单例,不要等到要用到的时候才去创建。

beanFactory.preInstantiateSingletons(); // 准备好所有单例
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ");
beanFactory.getBean(Bean1.class).getBean2();

从以下的执行结果来看,就可以看到已经提前示例好了这些单例对象。

可以得到结论:BeanFactory 不会主动调用 BeanFactory 后处理器,不会主动添加 Bean 后处理器,不会主动初始化单例。

Application

本节讲解几个Application中常见的方法实现注册Bean。

1.类路径下的xml注入bean

private static void testClassPathXmlApplicationContext() {
    ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("a02.xml");

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

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

//xml文件
 <!-- 控制反转, 让 bean1 被 Spring 容器管理 -->
    <bean id="bean1" class="com.wyw.springcodehh.a02.A02.Bean1"/>

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


2.绝对路径下的文件名注入bean

private static void testFileSystemXmlApplicationContext() {
    FileSystemXmlApplicationContext context =
            new FileSystemXmlApplicationContext(
                    "D:\\BaiduNetdiskDownload\\SpringCode\\SpringCodeHH\\src\\main\\resources\\a02.xml");
    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    }

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

以上两种方式是怎么执行的呢,我们用下述代码探究

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("a02.xml")); 类路径
        reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\b01.xml"));
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }

其实就是用 XmlBeanDefinitionReader来进行注入。

3.基于java配置类注入bean

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

 static class Bean1 {
    }

    static class Bean2 {

        private Bean1 bean1;

        public void setBean1(Bean1 bean1) {
            this.bean1 = bean1;
        }

        public Bean1 getBean1() {
            return bean1;
        }
    }

从执行结果来看,bean1和bean2都执行成功,还有bean2依赖注入的bean1也成功。

4.通过内嵌tomcat实现

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值