【Java】Spring5学习
基础环境与文档资料:
见黑马视频:
https://www.bilibili.com/video/BV1P44y1N7QG
依赖坐标:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.cloud9</groupId> <artifactId>Spring5</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> </dependencies> </project>
一、ApplicationContext接口
package cn.cloud9; import cn.cloud9.bean.Component1; import cn.cloud9.event.UserRegisteredEvent; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.io.Resource; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Locale; import java.util.Map; /** * @projectName: Spring5 * @author: OnCloud9 * @date: 2023年02月07日 09:13 * @version: 1.0 */ @Slf4j @SpringBootApplication public class MainApplication { @SneakyThrows public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(MainApplication.class, args); log.info("ConfigurableApplicationContext -> {}", applicationContext); /* * ApplicationContext的功能 * 1、I18N支持, MessageSource接口实现 * - 默认读取的配置文件:messages.properties * - 指定文件文件名称 中文zh 英文en 日文ja */ String cnHello = applicationContext.getMessage("hello", null, Locale.CHINA); String enHello = applicationContext.getMessage("hello", null, Locale.ENGLISH); String jpHello = applicationContext.getMessage("hello", null, Locale.JAPANESE); log.info("I18N 国际化参数读取 CN -> {}, EN -> {}, JP -> {}", cnHello, enHello, jpHello); /* * ApplicationContext的功能 * 2、读取类路径下的资源 * - classpath: * 读取当前项目下的类路径资源 * - classpath*: * 读取项目所有依赖下的类路径 */ Resource[] resources = applicationContext.getResources("classpath:*"); Arrays.stream(resources).forEach(resource -> log.info("classpath: -> {}", resource)); resources = applicationContext.getResources("classpath*:*"); Arrays.stream(resources).forEach(resource -> log.info("classpath*: -> {}", resource)); // 获取Spring的SPI配置文件 resources = applicationContext.getResources("classpath*:META-INF/spring.factories"); Arrays.stream(resources).forEach(resource -> log.info("META-INF/spring.factories: -> {}", resource)); /* * ApplicationContext的功能 * 3、读取配置文件的值 */ ConfigurableEnvironment environment = applicationContext.getEnvironment(); // 系统变量 + 配置文件 String javaHome = environment.getProperty("java_home"); String serverPort = environment.getProperty("server.port"); log.info("javaHome -> {}, serverPort -> {}", javaHome, serverPort); /* * ApplicationContext的功能 * 4、发布事件 * - 发布事件的意义是为了解耦业务逻辑 */ // 1、使用ApplicationContext直接发布事件 UserRegisteredEvent userRegisteredEvent = new UserRegisteredEvent(applicationContext); applicationContext.publishEvent(userRegisteredEvent); // 2、使用Bean对象发布事件 Component1 component1 = applicationContext.getBean("component1", Component1.class); component1.registerBusinessAction(); } }
I18N国际化配置文件:
UserRegisteredEvent事件源类:
package cn.cloud9.event; import org.springframework.context.ApplicationEvent; /** * 用户注册事件类 * @author OnCloud9 * @version 1.0 * @project Spring5 * @date 2023年02月08日 10:45 */ public class UserRegisteredEvent extends ApplicationEvent { /** * Create a new {@code ApplicationEvent}. * * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */ public UserRegisteredEvent(Object source) { super(source); } }
在Component2类的监听方法:
package cn.cloud9.bean; import cn.cloud9.event.UserRegisteredEvent; import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; /** * @projectName: Spring5 * @author: OnCloud9 * @date: 2023年02月07日 09:30 * @version: 1.0 */ @Slf4j @Component public class Component2 { /** * @author OnCloud9 * @date 2023/2/8 11:01 * @description * @params [event] * @return void */ @EventListener public void receiveEvent(UserRegisteredEvent event) { log.info("用户注册事件触发, 监听方法执行!!!!"); } }
Component1中的发布事件方法:
package cn.cloud9.bean; import cn.cloud9.event.UserRegisteredEvent; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; import javax.annotation.Resource; /** * @projectName: Spring5 * @author: OnCloud9 * @date: 2023年02月07日 09:30 * @version: 1.0 */ @Slf4j @Component public class Component1 { @Resource private ApplicationEventPublisher applicationEventPublisher; /** * @author OnCloud9 * @date 2023/2/8 11:06 * @description 用户注册业务行为 * @params [] * @return void */ public void registerBusinessAction() { log.info("用户注册业务行为开始!"); applicationEventPublisher.publishEvent(new UserRegisteredEvent(this)); } }
启动时打印信息:
2023-02-08 14:36:50.523 INFO 6548 --- [ main] cn.cloud9.bean.Component2 : 用户注册事件触发, 监听方法执行!!!! 2023-02-08 14:36:50.524 INFO 6548 --- [ main] cn.cloud9.bean.Component1 : 用户注册业务行为开始! 2023-02-08 14:36:50.524 INFO 6548 --- [ main] cn.cloud9.bean.Component2 : 用户注册事件触发, 监听方法执行!!!!
二、BeanFactory接口
/* * BeanFactory * 1、是ApplicationContext的父接口 * 2、是Spring的核心容器 * 3、ApplicationContext组合了BeanFactory的功能 * 4、获取Bean对象,控制反转,依赖注入,Bean的生命周期的各种功能,由BeanFactory的实现类完成 */ Field field = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects"); field.setAccessible(true); ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); Map<String,Object> beanTanker = (Map<String,Object>)field.get(beanFactory); beanTanker.entrySet().stream().filter(e -> e.getKey().startsWith("component")).forEach(e -> { log.info("key -> {}, value -> {}", e.getKey(), e.getValue()); });
import cn.cloud9.MainApplication; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; import java.util.Map; /** * @author OnCloud9 * @version 1.0 * @project Spring5 * @date 2023年02月08日 11:12 */ @Slf4j @RunWith(SpringRunner.class) @SpringBootTest(classes = MainApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class BeanFactoryTest { /** * @author OnCloud9 * @date 2023/2/8 13:47 * BeanFactory * - 不会主动调用BeanFactory的后置处理器 * - 不会主动调用Bean的后置处理器 * - 不会主动初始化单例Bean对象 * - 不会解析BeanFactory, 包括${} #{} 值注入 * Bean的后处理有排序逻辑 * */ @Test public void beanInitializeProcess() { /* * Bean的定义, 初始化, scope, 销毁, */ final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); /* 1、生成Bean的定义对象 */ AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition(); /* 2、Bean工厂注册定义对象 */ beanFactory.registerBeanDefinition("config", beanDefinition); /* 3、获取注册的Bean定义名称集合 */ String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) log.info("beanDefinitionName: {}", beanDefinitionName); } @Configuration static class Config { @org.springframework.context.annotation.Bean public Bean1 bean1() { return new Bean1(); } @org.springframework.context.annotation.Bean public Bean2 bean2() { return new Bean2(); } } @Data private static class Bean1 { @Autowired private Bean2 bean2; } private static class Bean2 {} }
在注册Bean定义对象之后
beanFactory.registerBeanDefinition("config", beanDefinition);
BeanFactory对象中只有当前这个config的Bean定义对象
一些自动装配注解,事件监听需要后置处理器的加入:
/* 4、添加一些常用的后处理器 */ AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); /* * 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 */ log.info("添加后置处理器后..."); beanDefinitionNames = beanFactory.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) log.info("beanDefinitionName : {}", beanDefinitionName);
打印结果:
2023-02-08 14:54:26.964 INFO 33432 --- [ main] BeanFactoryTest : 添加后置处理器后... 2023-02-08 14:54:26.964 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: config 2023-02-08 14:54:26.965 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.annotation.internalConfigurationAnnotationProcessor 2023-02-08 14:54:26.965 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.annotation.internalAutowiredAnnotationProcessor 2023-02-08 14:54:26.965 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.annotation.internalCommonAnnotationProcessor 2023-02-08 14:54:26.965 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.event.internalEventListenerProcessor 2023-02-08 14:54:26.965 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.event.internalEventListenerFactory
但是我们发现Bean1和Bean2并没有在BeanFactory中注册
BeanFactory的后置处理器只是被添加到BeanFactory,并没有调用处理方法
所以需要把每个处理器的处理方法都调用一遍才行
/* 5、获取BeanFactory处理器的容器, 用于BeanFactory的后置处理器的添加 */ Map<String, BeanFactoryPostProcessor> processorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class); processorMap.values().forEach(p -> { /* 调用每个处理器的处理方法, 扩充Bean的定义 */ p.postProcessBeanFactory(beanFactory); }); log.info("在后置处理器处理后..."); /* config下的 bean1 bean2 被注册进来 */ beanDefinitionNames = beanFactory.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) log.info("beanDefinitionName: {}", beanDefinitionName);
再次打印之后发现,Bean1和Bean2加入进来了:
2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : 在后置处理器处理后... 2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: config 2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.annotation.internalConfigurationAnnotationProcessor 2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.annotation.internalAutowiredAnnotationProcessor 2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.annotation.internalCommonAnnotationProcessor 2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.event.internalEventListenerProcessor 2023-02-08 14:54:26.972 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: org.springframework.context.event.internalEventListenerFactory 2023-02-08 14:54:26.973 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: bean1 2023-02-08 14:54:26.973 INFO 33432 --- [ main] BeanFactoryTest : beanDefinitionName: bean2
在Bean1中我们设置了Bean2成员属性,并且标注自动装配
然后在BeanFactory获取Bean1内的Bean2实例时,发现Bean2对象并没有注入:
/* 在没有扫描@Autowired注解驱动时不能装填Bean实例 */ log.info("打印bean2对象:{}", beanFactory.getBean(Bean1.class).getBean2());
打印结果:
2023-02-08 15:09:38.299 INFO 3852 --- [ main] BeanFactoryTest : 打印bean2对象:null
因为之前的处理只针对BeanFactory的后置处理,对于一个配置Bean来说
配置Bean中要注册的Bean并不归于BeanFactory的后置处理,而是Bean的后置处理
所以这里需要做Bean的后置处理:
/* 添加Bean的后置处理器 */ beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor); /* 在Bean的处理器添加之后,Bean2的对象装配了 */ log.info("打印bean2对象:{}", beanFactory.getBean(Bean1.class).getBean2());
打印结果:
2023-02-08 15:15:54.219 INFO 23160 --- [ main] BeanFactoryTest : 打印bean2对象:BeanFactoryTest$Bean2@2c6aed22
Bean不会被BeanFactory主动初始化,在首次获取这个Bean时初始化
如果需要的情况,可以调用预先实例化方法,提前创建
/* Bean对象一般在调用getBean获取时才创建, 也可以调用预先实例化方法提前创建 (只针对scope为单例的对象) */ beanFactory.preInstantiateSingletons();
2、自动装配的执行顺序
新增一个InterFaceType接口,Bean3和Bean4同时实现这个接口
在Bean1注册这个接口,Spring如何装配这个成员属性?
@Data private static class Bean1 { @Autowired private Bean2 bean2; // 1、指定声明bean名称 // @Autowired @Qualifier("bean4") // private InterFaceType interFaceType; // 2、或者变量名称也可以匹配bean名称 // @Autowired // private InterFaceType bean4; // 3、使用@Resource声明了名称,则优先级高于变量名称 // @Resource(name = "bean3") // private InterFaceType bean4; // 4、又加 @Autowired 又加 @Resource, 交给后处理器的顺序决定先由哪个注解解析器处理 @Autowired @Qualifier("bean4") @Resource(name = "bean3") private InterFaceType interFaceType; } private static class Bean2 {} private interface InterFaceType {} private static class Bean3 implements InterFaceType {} private static class Bean4 implements InterFaceType {}
我们打印这个接口类型看看:
/* * 可以看到默认是自动装配注解处理器先执行,所以bean4先被装填 * org.springframework.context.annotation.internalAutowiredAnnotationProcessor * org.springframework.context.annotation.internalCommonAnnotationProcessor */ log.info("InterFaceType Spring实际装填对象 -> {}", beanFactory.getBean(Bean1.class).getInterFaceType());
打印结果:
2023-02-08 15:22:30.444 INFO 23516 --- [ main] BeanFactoryTest : InterFaceType Spring实际装填对象 -> BeanFactoryTest$Bean4@3c46dcbe
若要改变注解的处理顺序,就改变Bean后置处理器类的处理顺序:
/* 更改后置处理器的添加顺序 将@Resource优先被添加执行 @Autowired滞后 */ beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream() .sorted(beanFactory.getDependencyComparator()) .forEach(beanFactory::addBeanPostProcessor); /* * 默认是自动装配注解处理器先执行 * org.springframework.context.annotation.internalAutowiredAnnotationProcessor * org.springframework.context.annotation.internalCommonAnnotationProcessor */ log.info("InterFaceType Spring实际装填对象 -> {}", beanFactory.getBean(Bean1.class).getInterFaceType());
打印结果:
2023-02-08 15:29:52.865 INFO 32756 --- [ main] BeanFactoryTest : InterFaceType Spring实际装填对象 -> BeanFactoryTest$Bean3@3c46dcbe
3、排序比较器对象
基于这个对象来决定处理器的先后顺序
beanFactory.getDependencyComparator()
在一开始的BeanFactory注册中就已经放置了
比较器基于处理器的顺序属性决定先后:
这里在源码可以看到两个顺序值:
我们写个测试类比较便知:
@Test public void processorOrderCompare() { int orderForResource = Ordered.LOWEST_PRECEDENCE - 3; int orderForAutowired = 2147483645; log.info("@Resource -> {}, @Autowired -> {}", orderForResource, orderForAutowired); log.info("@Resource > @Autowired ? {}", orderForResource > orderForAutowired); }
打印结果:
2023-02-08 15:57:31.929 INFO 17840 --- [ main] BeanFactoryTest : @Resource -> 2147483644, @Autowired -> 2147483645 2023-02-08 15:57:31.931 INFO 17840 --- [ main] BeanFactoryTest : @Resource > @Autowired ? false
三、ApplicationContextImpl的实现
4种上下文的实现方式配置:
package cn.cloud9.spring; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.mvc.Controller; import java.net.URL; @Slf4j public class ApplicationContextImpl { public static void main(String[] args) { classpathXmlApplicationContext(); systemXmlApplicationContext(); annotationConfigApplicationContext(); webServletApplicationContext(); } /** * @author OnCloud9 * @date 2023/2/8 16:30 * @description xml方式配置bean * @params [] * @return void */ private static void classpathXmlApplicationContext() { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("context1.xml"); for (String beanDefinitionName : classPathXmlApplicationContext.getBeanDefinitionNames()) { log.info("xmlRegisteredBean: {}", beanDefinitionName); } log.info("inside bean2 is {}", classPathXmlApplicationContext.getBean(Bean1.class).getBean2()); } /** * @author OnCloud9 * @date 2023/2/8 16:35 * @description 文件系统方式读取xml配置bean * @params [] * @return void */ private static void systemXmlApplicationContext() { URL resource = ApplicationContextImpl.class.getClassLoader().getResource("context1.xml"); String path = resource.getPath(); FileSystemXmlApplicationContext fsXmlApplicationContext = new FileSystemXmlApplicationContext(path); for (String beanDefinitionName : fsXmlApplicationContext.getBeanDefinitionNames()) { log.info("systemXmlRegisteredBean: {}", beanDefinitionName); } log.info("inside bean2 is {}", fsXmlApplicationContext.getBean(Bean1.class).getBean2()); } /** * @author OnCloud9 * @date 2023/2/8 16:35 * @description 当前配置类方式实现Bean配置 * @params [] * @return void */ private static void annotationConfigApplicationContext() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfigurationClass.class); for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) { log.info("javaAnnotationBean: {}", beanDefinitionName); } log.info("inside bean2 is {}", applicationContext.getBean(Bean1.class).getBean2()); } /** * @author OnCloud9 * @date 2023/2/9 09:10 * @description WebServlet应用上下文对象Bean配置 * @params [] * @return void */ private static void webServletApplicationContext() { AnnotationConfigServletWebServerApplicationContext applicationContext = new AnnotationConfigServletWebServerApplicationContext(WebMvcConfig.class); } @Data static class Bean1 { private Bean2 bean2; } static class Bean2 {} @Configuration static class BeanConfigurationClass { @Bean public Bean1 bean1(Bean2 bean2) { Bean1 bean1 = new Bean1(); bean1.setBean2(bean2); return bean1; } @Bean public Bean2 bean2() { return new Bean2(); } } @Configuration static class WebMvcConfig { /* ServletWeb服务器工厂对象配置,默认按Tomcat实现 */ @Bean public ServletWebServerFactory servletWebServerFactory() { return new TomcatServletWebServerFactory(); } /* 配置前置请求分发处理器 */ @Bean public DispatcherServlet dispatcherServlet() { return new DispatcherServlet(); } /* 分发注册Bean */ @Bean public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) { return new DispatcherServletRegistrationBean(dispatcherServlet, "/"); } /* 直接注册一个控制器 */ @Bean("/controller1") public Controller controller() { return (request, response) -> { response.getWriter().write("这是配置类的Controller!"); return null; }; } } }
XML方式的配置文件(context1.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="cn.cloud9.spring.ApplicationContextImpl.Bean1" > <property name="bean2" ref="bean2" /> </bean> <bean id="bean2" class="cn.cloud9.spring.ApplicationContextImpl.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 --> <context:annotation-config /> </beans>