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.TestBeanFactory$Bean1 - 构造 Bean1() \[DEBUG\] 12:41:24.441 \[main\] c.itheima.a02.TestBeanFactory$Bean2 - 构造 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);
        }
    }
相关推荐
可视之道1 小时前
工业物联网前端技术栈选型与性能优化实战
后端
星辰_mya1 小时前
彩云之上——[特殊字符]的架构师
java·后端·微服务·面试·架构
小新同学^O^1 小时前
简单学习 --> 模型微调
开发语言·人工智能·python·模型微淘
水云桐程序员1 小时前
C++变量的概念及用法
开发语言·c++
phltxy2 小时前
Redis 主从复制
java·数据库·redis
Full Stack Developme2 小时前
Spring-Core 解析
java·spring·rpc
LucianaiB2 小时前
参加高德 AI 发布会的一点感受:地图,正在变成 AI 的行动入口
后端
属于自己的天空2 小时前
一个文件让 Claude Code 理解你的项目:CLAUDE.md 从入门到精通
后端
知识汲取者2 小时前
巨量引擎营销 API 完整文档
开发语言·数据库·python