import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import java.lang.reflect.Method;
import java.util.Collections;
/
* 测试Spring Boot执行流程
*
* @author lzlg
* 2023/4/5 19:05
*/
@Configuration
public class TestSpringBoot {
public static void main(String[] args) throws Exception {
System.out.println("1.获取Bean Definition 源");
// 创建Spring应用对象
SpringApplication app = new SpringApplication(TestSpringBoot.class);
// 添加Bean定义的xml文件
app.setSources(Collections.singleton("classpath:bean02.xml"));
System.out.println("2.推断应用类型");
// 进行反射调用
Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
deduceFromClasspath.setAccessible(true);
System.out.println("当前应用类型为: " + deduceFromClasspath.invoke(null));
System.out.println("3.添加一些初始化器");
// 这些初始化器在创建ApplicationContext之后,在调用ApplicationContext.refresh()方法之前进行扩展
app.setInitializers(Collections.singleton(
(ApplicationContextInitializer<GenericApplicationContext>) context -> {
// 向容器中添加Bean3
context.registerBean("bean3", Bean3.class);
}));
System.out.println("4.添加事件监听器");
app.setListeners(Collections.singleton(
(ApplicationListener<ApplicationEvent>) event -> {
System.out.println("事件类型为: " + event.getClass());
}));
System.out.println("5.启动类推断");
// 进行反射调用
Method deduceMainApplicationClass = SpringApplication.class.getDeclaredMethod("deduceMainApplicationClass");
deduceMainApplicationClass.setAccessible(true);
System.out.println("启动类为: " + deduceMainApplicationClass.invoke(app));
// 调用run方法,启动Spring应用
ConfigurableApplicationContext context = app.run(args);
// 打印容器中的Bean名称
for (String name : context.getBeanDefinitionNames()) {
// 根据Bean名称获取来源描述信息
String description = context.getBeanFactory().getBeanDefinition(name)
.getResourceDescription();
System.out.println("Bean名称是: " + name + ", 来源: " + description);
}
context.close();
}
static class Bean1 {
}
static class Bean2 {
}
static class Bean3 {
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.boot.context.event.EventPublishingRunListener;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.support.SpringFactoriesLoader;
import java.lang.reflect.Constructor;
import java.util.List;
/
* 测试Spring Boot启动流程中的事件发布
*
* @author lzlg
* 2023/4/5 19:37
*/
public class TestSpringBootEvent {
public static void main(String[] args) throws Exception {
// 创建Spring应用对象
SpringApplication app = new SpringApplication();
GenericApplicationContext genericContext = new GenericApplicationContext();
// 打印其中的事件发布类名称
app.addListeners(event -> System.out.println(event.getClass()));
// 从META-INF中的spring.factories配置文件中获取事件发布器
List<String> listeners = SpringFactoriesLoader.loadFactoryNames(
SpringApplicationRunListener.class,
TestSpringBootEvent.class.getClassLoader());
// EventPublishingRunListener是接口SpringApplicationRunListener的实现
// 在META-INF中的spring.factories配置文件中配置
for (String listener : listeners) {
System.out.println(listener);
// 创建EventPublishingRunListener实例
Constructor<EventPublishingRunListener> constructor = EventPublishingRunListener
.class.getConstructor(SpringApplication.class, String[].class);
EventPublishingRunListener runListener = constructor.newInstance(app, args);
// 测试用的Context
DefaultBootstrapContext context = new DefaultBootstrapContext();
// 1.Spring Boot开始启动时事件发布
runListener.starting(context);
// 3.Spring Boot环境信息准备完毕事件发布
runListener.environmentPrepared(context, new StandardEnvironment());
// 4.Spring Boot容器创建完毕,调用初始化器后事件发布
runListener.contextPrepared(genericContext);
// 5.Spring Boot容器中所有Bean定义加载完成事件发布
runListener.contextLoaded(genericContext);
// 刷新容器
genericContext.refresh();
// 6.容器启动后事件发布,容器初始化完成,refresh方法调用完毕
runListener.started(genericContext);
// 7.Spring Boot启动完毕事件发布
runListener.running(genericContext);
// 8.Spring Boot启动失败事件发布
runListener.failed(genericContext, new Exception("启动应用出现异常"));
}
}
}
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.*;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ClassPathResource;
import java.util.Arrays;
/
* 测试Spring Boot启动流程
*
* @author lzlg
* 2023/4/5 20:13
*/
public class TestSpringBootRunStep {
@SuppressWarnings("all")
public static void main(String[] args) throws Exception {
// 创建Spring Boot应用
SpringApplication app = new SpringApplication();
// 添加初始化器
app.addInitializers(context ->
System.out.println("创建容器后,在调用ApplicationContext.refresh()方法之前,执行初始化器..."));
System.out.println(">>>>>>>>>>2.封装启动的参数args<<<<<<<<<<<<");
DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);
System.out.println(">>>>>>>>>>8.创建容器<<<<<<<<<<<<");
// 获取应用类型
WebApplicationType webApplicationType = app.getWebApplicationType();
// 容器获取
GenericApplicationContext context;
// 根据应用类型返回对应的容器类型
switch (webApplicationType) {
case SERVLET:
context = new AnnotationConfigServletWebServerApplicationContext();
break;
case REACTIVE:
context = new AnnotationConfigReactiveWebServerApplicationContext();
break;
default:
context = new AnnotationConfigApplicationContext();
}
System.out.println(">>>>>>>>>>9.准备容器<<<<<<<<<<<<");
// 执行初始化器逻辑
for (ApplicationContextInitializer initializer : app.getInitializers()) {
initializer.initialize(context);
}
System.out.println(">>>>>>>>>>10.加载Bean定义<<<<<<<<<<<<");
// Bean工厂
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
// 从配置类中加载
AnnotatedBeanDefinitionReader annotatedReader = new AnnotatedBeanDefinitionReader(beanFactory);
annotatedReader.register(Config.class);
// 从配置文件xml中加载
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(beanFactory);
xmlReader.loadBeanDefinitions(new ClassPathResource("bean03.xml"));
// 从扫描包中进行加载
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan("com.lzlg.study.spring.boot.bean");
System.out.println(">>>>>>>>>>11.refresh容器<<<<<<<<<<<<");
context.refresh();
// 打印容器中的Bean名称和来源
for (String name : context.getBeanDefinitionNames()) {
System.out.println("name:" + name + " 来源: " + beanFactory.getBeanDefinition(name)
.getResourceDescription());
}
System.out.println(">>>>>>>>>>12.执行命令行Runner中的方法<<<<<<<<<<<<");
// 命令行参数
for (CommandLineRunner runner : context.getBeansOfType(CommandLineRunner.class).values()) {
runner.run(args);
}
// 应用运行参数
for (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class).values()) {
runner.run(arguments);
}
}
static class Bean4 {
}
static class Bean5 {
}
static class Bean6 {
}
@Configuration
static class Config {
@Bean
public Bean5 bean5() {
return new Bean5();
}
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
public CommandLineRunner commandLineRunner() {
return args -> System.out.println("命令行参数: " + Arrays.toString(args));
}
@Bean
public ApplicationRunner applicationRunner() {
return args -> {
System.out.println("命令行所有参数: " + Arrays.toString(args.getSourceArgs()));
System.out.println("命令行选项参数(比如--server.port=8080): " + args.getOptionNames());
System.out.println("命令行非选项参数: " + args.getNonOptionArgs());
};
}
}
}
启动过程源码解读:
/
* @author lzlg
* 2023/4/6 18:57
*/
public class SpringBootRunStepDesc {
public static void main(String[] args) {
}
/
public ConfigurableApplicationContext run(String... args) {
// 计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 创建Bootstrap根容器
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// Headless 模式是系统的一种配置模式,在系统可能缺少显示设备,键盘或鼠标这些输入输出外设的情况下可以使用该模式
// 设置Headless默认为true
configureHeadlessProperty();
// 获取Spring应用监听器,从META-INF中的Spring.Factories文件中获取
// 核心方法: SpringFactoriesLoader.loadFactoryNames(type, classLoader);
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布应用启动事件
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 将参数封装为ApplicationArguments(分为选项参数:前缀是--的属性,其他属性)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境配置信息
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 配置spring.beaninfo.ignore顺序(默认为true)
configureIgnoreBeanInfo(environment);
// 打印Spring应用的Banner信息
Banner printedBanner = printBanner(environment);
// 创建应用容器,根据应用的类别创建对应的ApplicationContext
context = createApplicationContext();
// 设置应用启动步骤信息
context.setApplicationStartup(this.applicationStartup);
// 准备Spring Boot应用需要的配置
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新Spring容器,完成Bean的创建,即调用context.refresh()方法
refreshContext(context);
// 留给子类扩展的刷新后方法
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 发布容器启动的事件
listeners.started(context);
// 调用实现CommandLineRunner和ApplicationRunner接口的对象的run方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 处理容器启动失败
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
// 发布容器启动中的事件
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
*/
/
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 根据应用类型获取环境对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 从applicationArguments中读取信息配置环境属性,配置属性来源,配置属性文件
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 将不规范的命名统一规范
ConfigurationPropertySources.attach(environment);
// 发布环境准备好的事件
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
// 把spring.main前缀的属性信息绑定到environment中指定的字段中
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
// 配置完成后,进行移除
ConfigurationPropertySources.attach(environment);
return environment;
}
*/
/
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 给容器添加环境信息
context.setEnvironment(environment);
// 添加应用需要的Bean名称生成器,数据类型转换器等...
postProcessApplicationContext(context);
// 应用初始化器,调用初始化器的初始化方法
applyInitializers(context);
// 发布容器准备好的事件
listeners.contextPrepared(context);
// 发布bootstrapContext关闭事件
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 如果是懒加载,则添加懒加载的Bean工厂后置处理器
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 获取所有的资源对象信息
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 加载所有的资源对象信息
load(context, sources.toArray(new Object[0]));
// 发布容器加载完毕的事件
listeners.contextLoaded(context);
}
*/
}
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.http11.Http11Nio2Protocol;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.File;
import java.nio.file.Files;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/
* 测试Spring和Tomcat的整合
*
* @author lzlg
* 2023/4/6 19:54
*/
public class TestSpringTomcat {
/*
Server
└───Service
├───Connector (协议, 端口)
└───Engine
└───Host(虚拟主机 localhost)
├───Context1 (应用1, 可以设置虚拟路径, / 即 url 起始路径; 项目磁盘路径, 即 docBase )
│ │ index.html
│ └───WEB-INF
│ │ web.xml (servlet, filter, listener) 3.0
│ ├───classes (servlet, controller, service ...)
│ ├───jsp
│ └───lib (第三方 jar 包)
└───Context2 (应用2)
│ index.html
└───WEB-INF
web.xml
*/
public static void main(String[] args) throws Exception {
// ============在context.refresh()方法中的onFresh()步骤中执行
// 1.创建Tomcat容器
Tomcat tomcat = new Tomcat();
// 设置基础目录,存放tomcat相关的文件
tomcat.setBaseDir("tomcat");
// 2.创建项目文件夹,项目磁盘路径, 即 docBase
// 这里创建临时文件夹
File docBase = Files.createTempDirectory("boot.").toFile();
// 3.创建Tomcat项目,在Tomcat中称为Context
Context context = tomcat.addContext("", docBase.getAbsolutePath());
// Spring的web容器
WebApplicationContext springContext = webApplicationContext();
// 4.编程添加Servlet
// 在Servlet容器初始化器中添加Servlet
context.addServletContainerInitializer(new ServletContainerInitializer() {
@Override
@SuppressWarnings("all")
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
// 添加Servlet
ctx.addServlet("helloServlet", new HelloServlet()).addMapping("/hello1");
// 通过ServletRegistrationBean添加
for (ServletRegistrationBean registrationBean : springContext.getBeansOfType(
ServletRegistrationBean.class).values()) {
registrationBean.onStartup(ctx);
}
}
}, Collections.emptySet());
// ================在context.refresh()方法中的finishRefresh()步骤中执行
// 5.启动Tomcat,启动时回调ServletContainerInitializer中的方法,将Servlet添加
tomcat.start();
// 6.保留端口,接受服务
Connector connector = new Connector(new Http11Nio2Protocol());
connector.setPort(8080);
tomcat.setConnector(connector);
}
/
* 获取web应用容器
*/
private static WebApplicationContext webApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
// 注册配置类
context.register(MyConfig.class);
// 刷新容器,生成Bean
context.refresh();
return context;
}
@Configuration
static class MyConfig {
/
* DispatcherServlet
*/
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
/
* 注册Servlet容器
*/
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
/
* 请求路径处理器适配器
*/
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
handlerAdapter.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
return handlerAdapter;
}
@RestController
static class MyController {
@GetMapping("/hello2")
public Map<String, Object> hello2() {
Map<String, Object> map = new HashMap<>(1);
map.put("hello", "Spring + Tomcat !");
return map;
}
}
}
}
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/
* @author lzlg
* 2023/4/6 20:08
*/
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("<h3>Hello HelloServlet</h3>");
}
}
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.type.AnnotationMetadata;
/
* 测试Spring Boot Aop自动代理原理
*
* @author lzlg
* 2023/4/8 19:28
*/
public class TestSpringAopAutoConfig {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
StandardEnvironment env = new StandardEnvironment();
// 控制AopAutoConfiguration自动装配是否生效的属性(命令行)
env.getPropertySources().addLast(new SimpleCommandLinePropertySource("--spring.aop.auto=true"));
env.getPropertySources().addLast(new SimpleCommandLinePropertySource(
"--spring.datasource.url=jdbc:mysql://localhost:3306/test",
"--spring.datasource.username=root",
"--spring.datasource.password=root"));
context.setEnvironment(env);
// 添加常用的注解配置后置处理器,如ConfigurationClassPostProcessor
// AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
// 注册自定义的配置类
context.registerBean(MyConfig.class);
// 设置当前类扫描的包路径名称
String packageName = TestSpringAopAutoConfig.class.getPackage().getName();
System.out.println("当前包名: " + packageName);
// 将自动装配的包名置为: 当前类扫描的包,这样可以看到通过@Mapper注解扫描到的Mapper接口
AutoConfigurationPackages.register(context, packageName);
// 注解@ConditionalOnSingleCandidate(DataSource.class),当只有一个DataSource时才生效
// @AutoConfigureAfter注解,可以在那些自动装配之后进行自动装配
context.refresh();
// 通过MyImportSelector注入的AopAutoConfiguration配置类,通过条件装配注解
// 向容器中添加了AspectJAutoProxyingConfiguration和CglibAutoProxyConfiguration
// 注解@EnableAspectJAutoProxy(proxyTargetClass = true)中
// proxyTargetClass为true,无论如何都使用CGLIB进行代理,为false,则优先使用JDK动态代理
// @EnableAspectJAutoProxy注解实际上是复合注解,通过@Import(AspectJAutoProxyRegistrar.class)
// 导入AspectJAutoProxyRegistrar这个来导入Aop注解需要的后置处理器AnnotationAwareAspectJAutoProxyCreator
for (String name : context.getBeanDefinitionNames()) {
String resourceDescription = context.getBeanDefinition(name).getResourceDescription();
if (resourceDescription != null) {
System.out.println(name + "\t 来源: " + resourceDescription);
}
}
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(
AnnotationAwareAspectJAutoProxyCreator.class);
System.out.println(">>>>>>>>>>>>>ProxyTargetClass配置: ");
System.out.println(creator.isProxyTargetClass());
System.out.println(">>>>>>>>>>>>>DataSourceProperties配置: ");
DataSourceProperties dataSourceProperties = context.getBean(DataSourceProperties.class);
System.out.println(dataSourceProperties.getUrl());
System.out.println(dataSourceProperties.getUsername());
System.out.println(dataSourceProperties.getPassword());
}
@Configuration
@Import({MyImportSelector.class})
static class MyConfig {
}
static class MyImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{
// Aop注解配置类
AopAutoConfiguration.class.getName(),
// 数据源自动配置类
DataSourceAutoConfiguration.class.getName(),
// MyBatis自动配置类
MybatisAutoConfiguration.class.getName(),
// 数据源事务管理器自动配置类
DataSourceTransactionManagerAutoConfiguration.class.getName(),
// 事务自定配置类,声明事务管理,这里有@EnableTransactionManagement注解
TransactionAutoConfiguration.class.getName(),
// ServletWebServerFactory自动配置类
ServletWebServerFactoryAutoConfiguration.class.getName(),
// DispatcherServlet自动配置类
DispatcherServletAutoConfiguration.class.getName(),
// WebMvc自动配置类
WebMvcAutoConfiguration.class.getName(),
// 对错误页面进行处理的自动配置类
ErrorMvcAutoConfiguration.class.getName(),
};
}
}
}
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
import java.util.List;
/
* 测试Spring Boot中自动装配
*
* @author lzlg
* 2023/4/8 18:47
*/
public class TestSpringAutoConfig {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
// 设置允许Bean定义覆盖,如果给false,出现相同的Bean则会抛出异常
context.setAllowBeanDefinitionOverriding(false);
// context.registerBean(MyConfig.class);
// 注册配置类
context.registerBean(ImportSelectorConfig.class);
// 配置类Bean工厂后置处理器,解析@Configuration注解的,@Bean注解,@Import注解
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 1.通过@Import注解导入第三方的配置类
// 缺点: 1) 硬编码方式,不灵活; 2) 本项目配置的同类型的Bean会覆盖第三方的Bean
Bean1 bean = context.getBean(Bean1.class);
System.out.println(bean.name);
// 2.通过注解@Import注解导入实现ImportSelector接口或DeferredImportSelector接口(延迟)
// 注解@ConditionalOnMissingBean当没有此Bean时,此Bean才生效
// 实现DeferredImportSelector接口,则本项目的Bean优先级较高(先生效),第三方的Bean优先级低(后生效)
// 实现ImportSelector接口,导入的配置类中定义的Bean优先级高
}
/
* 本项目的配置类
*/
@Configuration
@Import({MyImportSelector.class})
static class ImportSelectorConfig {
@Bean
@ConditionalOnMissingBean
public Bean1 bean1() {
return new Bean1("本项目");
}
}
/
* 自定义导入选择器
*/
static class MyImportSelector implements DeferredImportSelector {
/
* @return 返回值是导入的类的全名称(字符串)
*/
@Override
public String[] selectImports(AnnotationMetadata metadata) {
// 通过读取类路径下META-INF下的spring.factories文件
List<String> factoryNames = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
for (String factoryName : factoryNames) {
System.out.println(factoryName);
}
return factoryNames.toArray(new String[0]);
}
}
/
* 本项目的配置类
*/
@Configuration
@Import({OneAutoConfig.class, TwoAutoConfig.class})
static class MyConfig {
@Bean
public Bean1 bean1() {
return new Bean1("本项目");
}
}
/
* 第三方的配置类1
*/
@Configuration
static class OneAutoConfig {
@Bean
public Bean1 bean1() {
return new Bean1("第三方");
}
}
static class Bean1 {
private String name;
public Bean1() {
}
public Bean1(String name) {
this.name = name;
}
@Override
public String toString() {
return "Bean1{" +
"name='" + name + '\'' +
'}';
}
}
/
* 第三方的配置类2
*/
@Configuration
static class TwoAutoConfig {
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean2 {
}
}
import org.apache.ibatis.annotations.Mapper;
/
* @author lzlg
* 2023/4/8 20:12
*/
@Mapper
public interface Mapper1 {
}
import org.apache.ibatis.annotations.Mapper;
/
* @author lzlg
* 2023/4/8 20:12
*/
@Mapper
public interface Mapper2 {
}
/
* @author lzlg
* 2023/4/8 20:12
*/
public interface Mapper3 {
}
自动装配配置文件: 类路径下的META-INF中的spring.factories
com.lzlg.study.spring.autoconfig.TestSpringAutoConfig$MyImportSelector=\
com.lzlg.study.spring.autoconfig.TestSpringAutoConfig.OneAutoConfig,\
com.lzlg.study.spring.autoconfig.TestSpringAutoConfig.TwoAutoConfig
Spring Boot 是利用了自动配置类来简化了 aop 相关配置
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
spring.aop.auto=false
禁用 aop 自动配置@EnableAspectJAutoProxy
来开启了自动代理,如果在引导类上自己添加了 @EnableAspectJAutoProxy
那么以自己添加的为准@EnableAspectJAutoProxy
的本质是向容器中添加了 AnnotationAwareAspectJAutoProxyCreator
这个 bean 后处理器,它能够找到容器中所有切面,并为匹配切点的目标类创建代理,创建代理的工作一般是在 bean 的初始化阶段完成的简单说明一下,Spring Boot 支持两大类数据源:
PooledDataSource 又支持如下数据源
如果知道数据源的实现类类型,即指定了 spring.datasource.type
,理论上可以支持所有数据源,但这样做的一个最大问题是无法订制每种数据源的详细配置(如最大、最小连接数等)
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
mybatis.
前缀的配置项进行定制配置@MapperScan 注解的作用与 MybatisAutoConfiguration 类似,会注册 MapperScannerConfigurer 有如下区别
这里有同学有疑问,之前介绍的都是将具体类交给 Spring 管理,怎么到了 MyBatis 这儿,接口就可以被管理呢?
事务自动配置类有两个:
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
前者配置了 DataSourceTransactionManager 用来执行事务的提交、回滚操作
后者功能上对标 @EnableTransactionManagement,包含以下三个 bean
如果自己配置了 DataSourceTransactionManager 或是在引导类加了 @EnableTransactionManagement,则以自己配置的为准
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
import java.util.Objects;
/
* 测试Spring Boot中条件装配原理
*
* @author lzlg
* 2023/4/8 21:01
*/
public class TestSpringConditional {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
// 注册常用的注解后置处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
// 注册配置类
context.registerBean(MyConfig.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 判断到底是哪个配置生效了
MyBean bean = context.getBean(MyBean.class);
System.out.println(bean);
}
@Configuration
@Import({OneAutoConfig.class, TwoAutoConfig.class})
static class MyConfig {
}
/
* 自定义Condition条件装配判断
*/
static class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取注解上的数据信息
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnClazz.class.getName());
if (Objects.nonNull(attributes)) {
// 获取exists属性字段的值
boolean exists = (boolean) attributes.getOrDefault("exists", false);
// 获取clzArray属性字段的值
Object clzArrayObj = attributes.get("clzArray");
if (Objects.nonNull(clzArrayObj)) {
Class<?>[] clzArray = (Class<?>[]) clzArrayObj;
for (Class<?> clz : clzArray) {
boolean present = ClassUtils.isPresent(clz.getName(), null);
// 如果需要存在指定的类
if (exists) {
// 但不存在,则返回false
if (!present) {
return false;
}
} else {
// 如果不需要存在指定的类,但存在了,则返回false
if (present) {
return false;
}
}
}
// 符合匹配条件,返回true
return true;
}
}
return false;
}
}
@Retention(RetentionPolicy.RUNTIME) // 运行期间生效
@Target({ElementType.METHOD, ElementType.TYPE}) // 在方法和类上使用
// 组合注解,指明条件装配的条件,需实现Condition接口
@Conditional(MyCondition.class)
@interface ConditionalOnClazz {
// 判断类是否存在
boolean exists() default false;
// 需要判断的类集合
Class<?>[] clzArray();
}
@Configuration
// 如果存在DruidDataSource类则此配置生效
@ConditionalOnClazz(exists = false, clzArray = {DruidDataSource.class})
static class OneAutoConfig {
@Bean
public MyBean bean1() {
return new MyBean("one");
}
}
@Configuration
// 如果不存在DruidDataSource类则此配置生效
@ConditionalOnClazz(exists = true, clzArray = {DruidDataSource.class})
static class TwoAutoConfig {
@Bean
public MyBean bean2() {
return new MyBean("two");
}
}
static class MyBean {
private String name;
public MyBean() {
}
public MyBean(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MyBean{" +
"name='" + name + '\'' +
'}';
}
}
}
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
/
* 测试Spring中@Indexed注解优化启动原理
* 注解@Component上的@Indexed注解在编译的时候
* 会在META-INF下生成spring.components文件,缓存容器中的Bean
* 组件扫描时
* 1.如果发现 META-INF/spring.components存在,以它为准加载bean definition
* 2.否则,会遍历包下所有class资源(包括jar内的)
*
* 可通过添加依赖自动实现
* <dependency>
* <groupId>org.springframework</groupId>
* <artifactId>spring-context-indexer</artifactId>
* <optional>true</optional>
* </dependency>
*
* @author lzlg
* 2023/4/8 22:00
*/
public class TestSpringIndexed {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan(TestSpringIndexed.class.getPackage().getName());
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
/
* 测试Spring中@Value注解的解析过程
*
* @author lzlg
* 2023/4/9 20:26
*/
@Configuration
public class TestSpringValueResolver {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
TestSpringValueResolver.class);
// Spring通过ContextAnnotationAutowireCandidateResolver来解析@Value注解
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
resolver.setBeanFactory(context);
// 1.测试解析@Value注解中的${}属性
// testValue$Resolver(context, resolver);
// 2.测试解析@Value的类型转换
// testValueTypeConverter(context, resolver);
// 3.测试解析@Value中Spring EL表达式的解析
// testValueSpringELResolver(context, resolver);
// 4.测试解析@Value综合测试
testValueAllResolver(context, resolver);
context.close();
}
/
* 测试解析@Value综合测试
*/
private static void testValueAllResolver(AnnotationConfigApplicationContext context,
ContextAnnotationAutowireCandidateResolver resolver)
throws NoSuchFieldException {
// 通过DependencyDescriptor来获取需要解析的字段的信息
Field field = Bean4.class.getDeclaredField("hello");
// 第一个参数是要解析的字段信息,第二个是否必须,如果为false,则可以返回null
DependencyDescriptor descriptor = new DependencyDescriptor(field, false);
// resolver中获取到的是@Value中的字符串信息
String content = resolver.getSuggestedValue(descriptor).toString();
System.out.println(content);
// 通过环境对象来解析属性值即${}
String value = context.getEnvironment().resolvePlaceholders(content);
System.out.println(value + "\t" + value.getClass());
// 通过容器中的ExpressionResolver来解析#{}即Spring EL表达式
Object evaluate = context.getBeanFactory().getBeanExpressionResolver().evaluate(value,
new BeanExpressionContext(context.getBeanFactory(), null));
// 类型转换
Object result = context.getBeanFactory().getTypeConverter()
.convertIfNecessary(evaluate, descriptor.getDependencyType());
System.out.println(result);
}
/
* 测试解析@Value中Spring EL表达式的解析
*/
private static void testValueSpringELResolver(AnnotationConfigApplicationContext context,
ContextAnnotationAutowireCandidateResolver resolver)
throws NoSuchFieldException {
// 通过DependencyDescriptor来获取需要解析的字段的信息
Field field = Bean2.class.getDeclaredField("bean3");
// 第一个参数是要解析的字段信息,第二个是否必须,如果为false,则可以返回null
DependencyDescriptor descriptor = new DependencyDescriptor(field, false);
// resolver中获取到的是@Value中的字符串信息
String content = resolver.getSuggestedValue(descriptor).toString();
System.out.println(content);
// 通过环境对象来解析属性值即${}
String value = context.getEnvironment().resolvePlaceholders(content);
System.out.println(value + "\t" + value.getClass());
// 通过容器中的ExpressionResolver来解析#{}即Spring EL表达式
Object evaluate = context.getBeanFactory().getBeanExpressionResolver().evaluate(value,
new BeanExpressionContext(context.getBeanFactory(), null));
// 类型转换
Object result = context.getBeanFactory().getTypeConverter()
.convertIfNecessary(evaluate, descriptor.getDependencyType());
System.out.println(result);
}
/
* 测试解析@Value的类型转换
*/
private static void testValueTypeConverter(AnnotationConfigApplicationContext context,
ContextAnnotationAutowireCandidateResolver resolver)
throws NoSuchFieldException {
// 通过DependencyDescriptor来获取需要解析的字段的信息
Field field = Bean1.class.getDeclaredField("age");
// 第一个参数是要解析的字段信息,第二个是否必须,如果为false,则可以返回null
DependencyDescriptor descriptor = new DependencyDescriptor(field, false);
// resolver中获取到的是@Value中的字符串信息
String content = resolver.getSuggestedValue(descriptor).toString();
System.out.println(content);
// 通过环境对象来解析属性值即${}
String value = context.getEnvironment().resolvePlaceholders(content);
System.out.println(value + "\t" + value.getClass());
// 通过类型转换器来进行类型转换,可以看到age已经转换为Integer类型
Object result = context.getBeanFactory().getTypeConverter()
.convertIfNecessary(value, descriptor.getDependencyType());
System.out.println(result + "\t" + result.getClass());
}
/
* 测试解析@Value注解中的${}属性
*/
private static void testValue$Resolver(AnnotationConfigApplicationContext context,
ContextAnnotationAutowireCandidateResolver resolver)
throws NoSuchFieldException {
// 通过DependencyDescriptor来获取需要解析的字段的信息
Field field = Bean1.class.getDeclaredField("home");
// 第一个参数是要解析的字段信息,第二个是否必须,如果为false,则可以返回null
DependencyDescriptor descriptor = new DependencyDescriptor(field, false);
// resolver中获取到的是@Value中的字符串信息
String content = resolver.getSuggestedValue(descriptor).toString();
System.out.println(content);
// 通过环境对象来解析属性值即${}
String value = context.getEnvironment().resolvePlaceholders(content);
System.out.println(value);
}
static class Bean1 {
@Value("${JAVA_HOME}")
private String home;
@Value("18")
private int age;
}
static class Bean2 {
@Value("#{@bean3}")
private Bean3 bean3;
}
static class Bean4 {
@Value("#{'hello' + ' ${JAVA_HOME}'}")
private String hello;
}
@Component("bean3")
static class Bean3 {
}
}
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import java.util.Optional;
/
* 测试Spring中依赖注入
*
* @author lzlg
* 2023/4/9 21:02
*/
@Configuration
public class TestSpringDependencyInject {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
TestSpringDependencyInject.class);
// 获取Bean工厂
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
// 1.字段依赖注入: 通过resolveDependency方法进行依赖解析并注入
DependencyDescriptor dd1 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);
Object bean1 = beanFactory.resolveDependency(dd1, "bean1", null, null);
System.out.println(bean1);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 2.setter方法依赖注入: 通过MethodParameter
DependencyDescriptor dd2 = new DependencyDescriptor(
new MethodParameter(Bean1.class.getMethod("setBean2", Bean2.class), 0),
false);
Object bean2 = beanFactory.resolveDependency(dd2, "bean1", null, null);
System.out.println(bean2);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 3.Optional依赖注入
DependencyDescriptor dd3 = new DependencyDescriptor(Bean1.class.getDeclaredField("optional"), false);
// 如果是Optional类,则提示一级内嵌解析
if (dd3.getDependencyType() == Optional.class) {
dd3.increaseNestingLevel();
}
// 如果直接调用则返回的是Optional
Object bean3 = beanFactory.resolveDependency(dd3, "bean1", null, null);
System.out.println(bean3);
Optional<Object> optional = Optional.ofNullable(bean3);
System.out.println(optional);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 4.ObjectFactory依赖注入
DependencyDescriptor dd4 = new DependencyDescriptor(Bean1.class.getDeclaredField("objectFactory"), false);
// 如果是ObjectFactory类,则提示一级内嵌解析
if (dd4.getDependencyType() == ObjectFactory.class) {
dd4.increaseNestingLevel();
}
// 如果直接调用则返回的是ObjectFactory,可封装成ObjectFactory,在调用Object方法时才进行依赖解析
ObjectFactory objectFactory = new ObjectFactory() {
@Override
public Object getObject() throws BeansException {
return beanFactory.resolveDependency(dd4, "bean1", null, null);
}
};
System.out.println(objectFactory.getObject());
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 5.@Lazy注解会产生代理对象
DependencyDescriptor dd5 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean3"), false);
// 需要通过ContextAnnotationAutowireCandidateResolver来进行代理对象的处理
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
resolver.setBeanFactory(beanFactory);
// 如果加了@Lazy注解则会返回代理对象
Object proxy = resolver.getLazyResolutionProxyIfNecessary(dd5, "bean1");
System.out.println(proxy);
// 可以看到已经被CGLIB增强了
System.out.println(proxy.getClass());
context.close();
}
static class Bean1 {
@Autowired
private Bean2 bean2;
@Autowired
@Lazy
private Bean2 bean3;
@Autowired
public void setBean2(Bean2 bean2) {
this.bean2 = bean2;
}
@Autowired
private Optional<Bean2> optional;
@Autowired
private ObjectFactory<Bean2> objectFactory;
}
@Component
static class Bean2 {
}
}
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/
* 测试Spring中依赖注入选择原理
*
* @author lzlg
* 2023/4/9 21:48
*/
@Configuration
public class TestSpringDependencyInjectSelect {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
TestSpringDependencyInjectSelect.class);
// Bean工厂
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
System.out.println("1.数组类型的依赖注入>>>>>>>>>>>>>>>>>>>>>>>>>");
testArray(beanFactory);
System.out.println("2.List类型的依赖注入>>>>>>>>>>>>>>>>>>>>>>>>>");
testList(beanFactory);
System.out.println("3.ApplicationContext类型的依赖注入>>>>>>>>>>>>>>>>>>>>>>>>>");
// 在容器refresh方法中的prepareBeanFactory阶段就把ApplicationContext注入了
// beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
// beanFactory.registerResolvableDependency(ResourceLoader.class, this);
// beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
// beanFactory.registerResolvableDependency(ApplicationContext.class, this);
testApplicationContext(beanFactory);
System.out.println("4.Dao泛型类型的依赖注入>>>>>>>>>>>>>>>>>>>>>>>>>");
testDaoGeneric(beanFactory);
System.out.println("5.@Qualifier注解的依赖注入>>>>>>>>>>>>>>>>>>>>>>>>>");
testQualifier(beanFactory);
context.close();
}
/
* 注解@Qualifier的依赖注入
*/
private static void testQualifier(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
// 获取依赖描述信息
DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("service"), true);
// 获取需要注入的类型
Class<?> type = dd.getDependencyType();
// 需要借助ContextAnnotationAutowireCandidateResolver
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
resolver.setBeanFactory(beanFactory);
// 根据类型获取Bean名称,注意此时有多个
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
// 根据名称获取Bean定义信息,注意使用getMergedBeanDefinition
BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);
// isAutowireCandidate里会根据注解Qualifier判断是否是需要注入的类型
if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd, name), dd)) {
System.out.println(name);
// 然后获取Bean
Object bean = dd.resolveCandidate(name, type, beanFactory);
System.out.println(bean);
}
}
}
/
* Dao泛型类型的依赖注入
*/
private static void testDaoGeneric(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
// 获取依赖描述信息
DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("dao"), true);
// 这里dd.getDependencyType()返回的只是Dao接口的类型
Class<?> type = dd.getDependencyType();
// 需要借助ContextAnnotationAutowireCandidateResolver
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
resolver.setBeanFactory(beanFactory);
// 根据Dao接口类型获取Bean名称,注意此时有多个
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
// 根据名称获取Bean定义信息
BeanDefinition bd = beanFactory.getBeanDefinition(name);
// 判断是否是需要注入的泛型类型
if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd, name), dd)) {
System.out.println(name);
// 然后获取Bean
Object bean = dd.resolveCandidate(name, type, beanFactory);
System.out.println(bean);
}
}
}
/
* ApplicationContext类型的依赖注入
*/
@SuppressWarnings("unchecked")
private static void testApplicationContext(DefaultListableBeanFactory beanFactory) throws Exception {
// 获取依赖描述信息
DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("applicationContext"), true);
// 通过反射调用获取ApplicationContext存放的Map集合:resolvableDependencies
Field field = DefaultListableBeanFactory.class.getDeclaredField("resolvableDependencies");
field.setAccessible(true);
Map<Class<?>, Object> dependencies = (Map<Class<?>, Object>) field.get(beanFactory);
// 遍历集合
for (Map.Entry<Class<?>, Object> entry : dependencies.entrySet()) {
System.out.println(entry.getKey());
// 判断dd的类型是否能够是entry.getKey()类型的衍生类型
if (entry.getKey().isAssignableFrom(dd.getDependencyType())) {
// 如果是则value就是ApplicationContext
System.out.println(entry.getValue());
break;
}
}
}
/
* List类型的依赖注入
*/
private static void testList(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
// 获取依赖描述信息
DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("serviceList"), true);
// 判断是否是List类型
if (dd.getDependencyType() == List.class) {
// 获取List中泛型的类型信息
Class<?> type = dd.getResolvableType().getGeneric().resolve();
// 获取该类型下的,容器中的Bean名称列表
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type);
// 通过resolveCandidate从容器中获取Bean,本质上还是调用beanFactory.getBean(beanName)
List<Object> list = Arrays.stream(beanNames)
.map(name -> dd.resolveCandidate(name, type, beanFactory))
.collect(Collectors.toList());
// 进行类型转换,返回结果
Object result = beanFactory.getTypeConverter().convertIfNecessary(list, dd.getDependencyType());
System.out.println(result);
}
}
/
* 数组类型的依赖注入
*/
private static void testArray(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
// 获取依赖描述信息
DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("serviceArray"), true);
// 判断是否是数组类型
if (dd.getDependencyType().isArray()) {
// 获取数组中元素的类型
Class<?> type = dd.getDependencyType().getComponentType();
// 获取该类型下的,容器中的Bean名称列表
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type);
// 根据名称从容器中获取Bean
Object[] beans = new Object[beanNames.length];
for (int i = 0; i < beanNames.length; i++) {
// 通过resolveCandidate从容器中获取Bean,本质上还是调用beanFactory.getBean(beanName)
beans[i] = dd.resolveCandidate(beanNames[i], type, beanFactory);
}
// 进行类型转换,返回结果
Object result = beanFactory.getTypeConverter().convertIfNecessary(beans, dd.getDependencyType());
System.out.println(result);
}
}
static class Target {
@Autowired
private Service[] serviceArray;
@Autowired
private List<Service> serviceList;
@Autowired
private ConfigurableApplicationContext applicationContext;
@Autowired
private Dao<Teacher> dao;
@Autowired
@Qualifier("service2")
private Service service;
}
interface Dao<T> {
}
@Component("dao1")
static class Dao1 implements Dao<Student> {
}
@Component("dao2")
static class Dao2 implements Dao<Teacher> {
}
static class Student {
}
static class Teacher {
}
interface Service {
}
@Component("service1")
static class Service1 implements Service {
}
@Component("service2")
static class Service2 implements Service {
}
@Component("service3")
static class Service3 implements Service {
}
}
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
/
* 测试Spring中依赖注入的其余方式
* 1.通过注解@Primary来确定需要注入的bean
* 2.通过字段的名称和bean的名称匹配来注入
*
* @author lzlg
* 2023/4/9 21:50
*/
@Configuration
public class TestSpringIOCElse {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestSpringIOCElse.class);
// Bean工厂
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
// 1.通过@Primary注解选定需要注解的类型
testPrimary(beanFactory);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 2.通过字段的名称来确定注入的Bean
testDefaultByFieldName(beanFactory);
context.close();
}
/
* 通过字段的名称来确定注入的Bean
*/
private static void testDefaultByFieldName(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
// 2.通过字段的名称来确定注入的Bean
DependencyDescriptor dd = new DependencyDescriptor(Target2.class.getDeclaredField("service3"), true);
// 获取类型信息
Class<?> type = dd.getDependencyType();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
// 通过比对Bean名称和依赖项字段的名称来确定
if (name.equals(dd.getDependencyName())) {
System.out.println(name);
// 获取Bean
System.out.println(dd.resolveCandidate(name, type, beanFactory));
}
}
}
/
* 注解@Primary确定注入的bean
*/
private static void testPrimary(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
// 1.通过@Primary注解选定需要注解的类型
DependencyDescriptor dd = new DependencyDescriptor(Target1.class.getDeclaredField("service"), true);
// 获取类型信息
Class<?> type = dd.getDependencyType();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
// 通过bean定义信息中primary属性判断加了@Primary的类
if (beanFactory.getMergedBeanDefinition(name).isPrimary()) {
System.out.println(name);
// 获取Bean
System.out.println(dd.resolveCandidate(name, type, beanFactory));
}
}
}
static class Target1 {
@Autowired
private Service service;
}
static class Target2 {
@Autowired
private Service service3;
}
interface Service {
}
@Component("service1")
static class Service1 implements Service {
}
@Primary
@Component("service2")
static class Service2 implements Service {
}
@Component("service3")
static class Service3 implements Service {
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/
* 测试Spring事件监听器原理
* 通过两种方式实现事件发布
* 1.实现ApplicationListener接口,并加入到context中
* 2.使用注解@EventListener
*
* @author lzlg
* 2023/4/10 20:04
*/
@Configuration
public class TestSpringEventListener {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
TestSpringEventListener.class);
MyService myService = context.getBean(MyService.class);
myService.doBusiness();
context.close();
}
/
* 自定义事件
*/
static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
// source是事件消息
super(source);
}
}
@Component
static class MyService {
@Autowired
// 容器refresh()方法中的prepareBeanFactory方法已经加入到容器中,故可注入
private ApplicationEventPublisher publisher;
/
* 业务方法
*/
public void doBusiness() {
System.out.println("执行业务方法: doBusiness()...");
publisher.publishEvent(new MyEvent("开始执行发送短信/邮件的业务..."));
// System.out.println("发送短信...");
// System.out.println("发送邮件...");
}
}
/
* 发送短信的监听器
*/
@Component
static class SmsListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println(event.getSource());
System.out.println("发送短信...");
}
}
/
* 发送邮件的监听器
*/
@Component
static class EmailListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println(event.getSource());
System.out.println("发送邮件...");
}
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.util.concurrent.ThreadPoolExecutor;
/
* 测试Spring事件监听器原理
* 通过两种方式实现事件发布
* 1.实现ApplicationListener接口,并加入到context中
* 2.使用注解@EventListener
*
* @author lzlg
* 2023/4/10 20:04
*/
@Configuration
public class TestSpringAnnotationEventListener {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
TestSpringAnnotationEventListener.class);
MyService myService = context.getBean(MyService.class);
myService.doBusiness();
context.close();
}
/
* 自定义线程池
*/
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
// 自定义线程池
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(20);
// 任务超出极限的处理策略,调用线程池的线程处理
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
/
* Spring Boot中的默认事件发布器,方法名需为applicationEventMulticaster
*/
@Bean
public SimpleApplicationEventMulticaster applicationEventMulticaster(ThreadPoolTaskExecutor taskExecutor) {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
// 配置线程池
multicaster.setTaskExecutor(taskExecutor);
return multicaster;
}
/
* 自定义事件
*/
static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
// source是事件消息
super(source);
}
}
@Component
static class MyService {
@Autowired
// 容器refresh()方法中的prepareBeanFactory方法已经加入到容器中,故可注入
private ApplicationEventPublisher publisher;
/
* 业务方法
*/
public void doBusiness() {
System.out.println("执行业务方法: doBusiness()...");
publisher.publishEvent(new MyEvent("开始执行发送短信/邮件的业务..."));
// System.out.println("发送短信...");
// System.out.println("发送邮件...");
}
}
/
* 发送短信的监听器
*/
@Component
static class SmsService {
@EventListener
public void sms(MyEvent myEvent) {
System.out.println(Thread.currentThread().getName() + "\t" + myEvent.getSource());
System.out.println(Thread.currentThread().getName() + "\t" + "发送短信...");
}
}
/
* 发送邮件的监听器
*/
@Component
static class EmailService {
@EventListener
public void email(MyEvent myEvent) {
System.out.println(Thread.currentThread().getName() + "\t" + myEvent.getSource());
System.out.println(Thread.currentThread().getName() + "\t" + "发送邮件...");
}
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.ResolvableType;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/
* 测试Spring中的事件发布器
*
* @author lzlg
* 2023/4/10 20:47
*/
@Configuration
public class TestSpringEventPublisher {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
TestSpringEventPublisher.class);
// 调用业务方法
MyService myService = context.getBean(MyService.class);
myService.doBusiness();
context.close();
}
/
* 注入自己定义的事件发布器
*/
@Bean
public ApplicationEventMulticaster applicationEventMulticaster(ConfigurableApplicationContext context,
ThreadPoolTaskExecutor taskExecutor) {
return new AbstractApplicationEventMulticaster() {
// 监听器的集合
private List<GenericApplicationListener> listeners = new ArrayList<>();
// 添加监听器的方法
@Override
public void addApplicationListenerBean(String listenerBeanName) {
// Spring会把所有实现ApplicationListener接口的监听器的名称返回
System.out.println(listenerBeanName);
// 根据Bean名称获取监听器
ApplicationListener listener = context.getBean(listenerBeanName, ApplicationListener.class);
// 获取Bean实例的接口中的泛型(事件类型)
ResolvableType type = ResolvableType.forClass(listener.getClass())
.getInterfaces()[0].getGeneric();
System.out.println(type);
// 将ApplicationListener封装为GenericApplicationListener,更好通过事件类型进行调用
GenericApplicationListener genericListener = new GenericApplicationListener() {
/
* 判断是否是支持的事件类型
*/
@Override
public boolean supportsEventType(ResolvableType eventType) {
// 如果右边的类型可赋给监听器中的泛型事件类型,则此监听器生效
return type.isAssignableFrom(eventType);
}
/
* 事件处理方法
*/
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 异步执行调用
taskExecutor.execute(() -> listener.onApplicationEvent(event));
}
};
listeners.add(genericListener);
}
// 调用监听器的方法
@Override
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
// 遍历所有的监听器
for (GenericApplicationListener listener : listeners) {
// 判断是否支持的事件类型,通过事件event的类型
if (listener.supportsEventType(ResolvableType.forClass(event.getClass()))) {
// 是支持的事件类型才进行回调
listener.onApplicationEvent(event);
}
}
}
};
}
@Component
static class MyService {
@Autowired
private ApplicationEventPublisher publisher;
public void doBusiness() {
System.out.println(Thread.currentThread().getName() + "\t" + "执行主线业务");
// 发布事件
publisher.publishEvent(new MyEvent("开始执行发送短信/邮件的业务..."));
}
}
@Component
static class SmsApplicationListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println(Thread.currentThread().getName() + "\t" + event.getSource());
System.out.println(Thread.currentThread().getName() + "\t" + "发送短信...");
}
}
@Component
static class EmailApplicationListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println(Thread.currentThread().getName() + "\t" + event.getSource());
System.out.println(Thread.currentThread().getName() + "\t" + "发送邮件...");
}
}
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
return executor;
}
static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
}
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
/
* 实现自定义的事件监听器
*
* @author lzlg
* 2023/4/10 20:27
*/
@Configuration
public class TestSpringMyEventListener {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
TestSpringMyEventListener.class);
// 测试只在一个监听类上的监听方法
// testSmsListener(context);
// 测试所有的监听器
// testAllEventListener(context);
// 调用业务方法
MyService myService = context.getBean(MyService.class);
myService.doBusiness();
context.close();
}
/
* 将监听器的逻辑封装在SmartInitializingSingleton中,这样通过后置处理器可调用到
* 是EventListenerMethodProcessor
* 在bean初始化完成后进行调用的
*/
@Bean
public SmartInitializingSingleton smartInitializingSingleton(ConfigurableApplicationContext context) {
return () -> {
// 遍历所有的Bean
for (String beanName : context.getBeanDefinitionNames()) {
// 获取Bean对象
Object bean = context.getBean(beanName);
// 遍历Bean上的所有方法
for (Method method : bean.getClass().getMethods()) {
// 看方法上是否有注解@MyEventListener
if (method.isAnnotationPresent(MyEventListener.class)) {
// 如果有,则创建监听器,并加入到Spring容器中
ApplicationListener listener = event -> {
// 需要分辨事件是否是需要监听的事件
Class<?> parameterClass = method.getParameterTypes()[0];
// 如果参数上的类能和event中的类匹配,才进行反射调用
if (parameterClass.isAssignableFrom(event.getClass())) {
// 反射调用监听方法
try {
method.invoke(bean, event);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
context.addApplicationListener(listener);
}
}
}
};
}
/
* 测试所有的监听器
*/
private static void testAllEventListener(AnnotationConfigApplicationContext context) {
// 遍历所有的Bean
for (String beanName : context.getBeanDefinitionNames()) {
// 获取Bean对象
Object bean = context.getBean(beanName);
// 遍历Bean上的所有方法
for (Method method : bean.getClass().getMethods()) {
// 看方法上是否有注解@MyEventListener
if (method.isAnnotationPresent(MyEventListener.class)) {
// 如果有,则创建监听器,并加入到Spring容器中
ApplicationListener listener = event -> {
// 需要分辨事件是否是需要监听的事件
Class<?> parameterClass = method.getParameterTypes()[0];
// 如果参数上的类能和event中的类匹配,才进行反射调用
if (parameterClass.isAssignableFrom(event.getClass())) {
// 反射调用监听方法
try {
method.invoke(bean, event);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
context.addApplicationListener(listener);
}
}
}
}
/
* 测试只在一个监听类上的监听方法
*/
private static void testSmsListener(AnnotationConfigApplicationContext context) {
// 遍历SmsService上的所有方法
for (Method method : SmsService.class.getMethods()) {
// 看方法上是否有注解@MyEventListener
if (method.isAnnotationPresent(MyEventListener.class)) {
SmsService service = context.getBean(SmsService.class);
// 如果有,则创建监听器,并加入到Spring容器中
ApplicationListener listener = new ApplicationListener() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 需要分辨事件是否是需要监听的事件
Class<?> parameterClass = method.getParameterTypes()[0];
System.out.println(parameterClass);
// 如果参数上的类能和event中的类匹配,才进行反射调用
if (parameterClass.isAssignableFrom(event.getClass())) {
// 反射调用监听方法
try {
method.invoke(service, event);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
};
context.addApplicationListener(listener);
}
}
}
@Component
static class MyService {
@Autowired
private ApplicationEventPublisher publisher;
public void doBusiness() {
System.out.println(Thread.currentThread().getName() + "\t" + "执行业务方法...");
publisher.publishEvent(new MyEvent("开始执行发送短信/邮件的业务..."));
}
}
@Component
static class SmsService {
@MyEventListener
public void sms(MyEvent myEvent) {
System.out.println(Thread.currentThread().getName() + "\t" + myEvent.getSource());
System.out.println(Thread.currentThread().getName() + "\t" + "发送短信...");
}
}
@Component
static class EmailService {
@MyEventListener
public void email(MyEvent myEvent) {
System.out.println(Thread.currentThread().getName() + "\t" + myEvent.getSource());
System.out.println(Thread.currentThread().getName() + "\t" + "发送邮件...");
}
}
/
* 自定义事件监听器注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyEventListener {
}
/
* 自定义事件
*/
static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
}
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.core.ResolvableType;
import java.util.function.Predicate;
/
* 抽象的事件发布器
*
* @author lzlg
* 2023/4/10 20:58
*/
public class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster {
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
}
@Override
public void addApplicationListenerBean(String listenerBeanName) {
}
@Override
public void removeApplicationListener(ApplicationListener<?> listener) {
}
@Override
public void removeApplicationListenerBean(String listenerBeanName) {
}
@Override
public void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate) {
}
@Override
public void removeApplicationListenerBeans(Predicate<String> predicate) {
}
@Override
public void removeAllListeners() {
}
@Override
public void multicastEvent(ApplicationEvent event) {
}
@Override
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
}
}