Spring进阶(容器实现)

BeanFactory实现:

1.创建Config类

java 复制代码
@Configuration
static class Config {
     @Bean
     public Bean1 bean1() {
         return new Bean1();
     }
   
     @Bean
     public Bean2 bean2() {
         return new Bean2();
     }
}

2.创建Bean1类:

java 复制代码
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;
        }
}

3.创建Bean2:

java 复制代码
  static class Bean2 {
        private static final Logger log = LoggerFactory.getLogger(Bean2.class);

        public Bean2() {
            log.debug("构造 Bean2()");
        }
    }

4.主类中创建BeanFactory的具体实现类:

java 复制代码
  DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

5.定义Bean:

java 复制代码
   // bean 的定义(class, scope, 初始化, 销毁)
        AbstractBeanDefinition beanDefinition =
                BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
       

6.注册BeanDefinition:

java 复制代码
beanFactory.registerBeanDefinition("config", beanDefinition);

7.验证BeanFactory中有哪些BeanDefinition:

java 复制代码
for (String name : beanFactory.getBeanDefinitionNames()) {
      System.out.println(name);
}

发现:工厂中只有名字为config的Bean,该类中注解并未进行解析并注册!

添加处理器,对注解进行解析:

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

加了之后输出结果:

config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor

处理Configuration注解
org.springframework.context.annotation.internalAutowiredAnnotationProcessor

处理Autowired注解
org.springframework.context.annotation.internalCommonAnnotationProcessor

解析Resource注解
org.springframework.context.event.internalEventListenerProcessor

org.springframework.context.event.internalEventListenerFactory

发现多了5个BeanDefinition,当然这5个的作用就是充当DeanFactory与Bean的后置处理器

注:DeanFactory与Bean的后置处理器作用域是不同的!!!!

发现注解还是未被解析:

此时需要将BeanFactory后处理器(BeanFactoryPostProcessor)全部拿出来,然后按照添加到BeanFactory中的后处理器执行:

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

此时发现被Bean注解标记的Bean1与Bean2被注册进BeanFactory!!

此时测试BeanDefinition是否被注册完整,由于Bean1中Autowired了Bean2,想通过Bean1将Bean2拿出来,测试代码如下:

java 复制代码
System.out.println(beanFactory.getBean(Bean1.class).getBean2());

结果为:null

此时Autowired并未被解析处理

此时需要让Bean的后处理器发挥作用,同理代码如下:

java 复制代码
        // Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...
        beanFactory.getBeansOfType(BeanPostProcessor.class).values()
                .forEach(beanFactory::addBeanPostProcessor);

此时运行代码,输出正常!!

此时通过输出结果可知,Bean的实例化、初始化的时机是第一次调用到对应的Bean方法时才会创建!

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

bean3

bean4

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DEBUG 12:41:24.422 main c.itheima.a02.TestBeanFactoryBean1 - 构造 Bean1() \[DEBUG\] 12:41:24.441 \[main\] c.itheima.a02.TestBeanFactoryBean2 - 构造 Bean2()
com.itheima.a02.TestBeanFactory$Bean2@76b1e9b8

希望对于单例对象,在注册Bean对象时就进行真正的对象创建初始化:

java 复制代码
        beanFactory.preInstantiateSingletons(); // 准备好所有单例

总结:

复制代码
beanFactory 不会做的事
       1. 不会主动调用 BeanFactory 后处理器
       2. 不会主动添加 Bean 后处理器
       3. 不会主动初始化单例
       4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }
bean 后处理器会有排序的逻辑

缺少很多的拓展功能!!

Autowired与Resource的生效顺序:

如果在自己的项目中,对于一个类的依赖注入既使用了Autowired又使用了Resource究竟那个先生效??

java 复制代码
    interface Inter {

    }

    static class Bean3 implements Inter {

    }

    static class Bean4 implements Inter {

    }
java 复制代码
 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;
        }

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

        public Inter getInter() {
            return bean3;
        }
    }

测试:

java 复制代码
System.out.println(beanFactory.getBean(Bean1.class).getInter());

输出结果:

TestBeanFactory$Bean3@74d1dc36

此时通过测试发现,注入的是Bean3,Resource注入并没有生效!!

应该是与BeanPostProcessor中Bean的顺序有关:

java 复制代码
        beanFactory.getBeansOfType(BeanPostProcessor.class).values()
                .forEach(beanPostProcessor->{
                    System.out.println(">>>>"+beanPostProcessor);
                    beanFactory.addBeanPostProcessor(beanPostProcessor);
                });

结果如下:

>>>>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@c88a337

>>>>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@5d0a1059

可以通过比较器对顺序进行控制:

java 复制代码
        beanFactory.getBeansOfType(BeanPostProcessor.class).values()
                .stream().sorted(beanFactory.getDependencyComparator())
                .forEach(beanPostProcessor->{
                    System.out.println(">>>>"+beanPostProcessor);
                    beanFactory.addBeanPostProcessor(beanPostProcessor);
                });

>>>>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@54bff557

>>>>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@593aaf41

此时再次执行

java 复制代码
System.out.println(beanFactory.getBean(Bean1.class).getInter());

结果如下:

com.itheima.a02.TestBeanFactory$Bean4@7161d8d1

排序是根据Comparator进行排序,各自的各小在构造函数时已经设置:

java 复制代码
//数字越大优先级越高

public class AutowiredAnnotationBeanPostProcessor{
    private int order = 2147483645;
}


public CommonAnnotationBeanPostProcessor() {
        this.setOrder(2147483644);
}

ApplicationContext实现:

1.基于 classpath 下 xml 格式的配置文件来创建

java 复制代码
<?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">

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

    <!-- 控制反转, 让 bean2 被 Spring 容器管理 -->
    <bean id="bean2" class="com.itheima.a02.A02.Bean2">
        <!-- 依赖注入, 建立与 bean1 的依赖关系 -->
        <property name="bean1" ref="bean1"/>
    </bean>
</beans>
java 复制代码
    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());
    }

用代码模仿整个Bean创建过程:

java 复制代码
        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 FileSystemResource("src\\main\\resources\\a02.xml"));
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }

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

java 复制代码
    private static void testFileSystemXmlApplicationContext() {
        FileSystemXmlApplicationContext context =
                new FileSystemXmlApplicationContext(
                        "src\\main\\resources\\a02.xml");
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

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

3.️较为经典的容器, 基于 java 配置类来创建

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

4.基于 java 配置类来创建, 用于 web 环境

1.创建Web环境:

java 复制代码
  @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 controller1() {
            return (request, response) -> {
                response.getWriter().print("hello");
                return null;
            };
        }
    }

2.调用并注册Bean

java 复制代码
    private static void testAnnotationConfigServletWebServerApplicationContext() {
        AnnotationConfigServletWebServerApplicationContext context =
                new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }
相关推荐
泡海椒35 分钟前
告别 iText 繁杂配置:jquick-pdf 极简 PDF 生成实战(零基础上手)
java·开发语言·pdf
JaguarJack37 分钟前
PHP 8.6 只是个小版本,三十项弃用却已在为 PHP 9.0 铺路
后端·php·服务端
BingoGo38 分钟前
PHP 8.6 只是个小版本,三十项弃用却已在为 PHP 9.0 铺路
后端·php
步行cgn1 小时前
Spring 注入 List 集合详解
windows·spring·list
Bs_MoneyMagnet4 小时前
基于springboot+vue的滑雪场票务与装备租赁系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring·毕业设计·计算机毕业设计
AlienZHOU8 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
野生技术架构师9 小时前
2026 Java 面试全套总结,八股 + 场景 + AI 相关面试考点
java·人工智能·面试
香吧香10 小时前
java服务异常日志只打印异常类型,没有堆栈定位分析
java·jvm·异常
qq_25183645710 小时前
AI 疾病自查功能SpringbootAI项目实战 · 技术实现文档
java·ai·ai编程
狗头大军之江苏分军10 小时前
《潮水漫过十七岁》开学了
后端