目录
4、TransactionAutoConfiguration
5、ServletWebServerFactoryAutoConfiguration
6、DispatcherServletAutoConfiguration
10、HttpEncodingAutoConfiguration
1、构建boot项目
如果是 linux 环境,用以下命令即可获取 spring boot 的骨架 pom.xml
//将需要的依赖写入到 pom.xml当中
curl -G https://start.spring.io/pom.xml -d dependencies=web,mysql,mybatis -o pom.xml
也可以使用 Postman 等工具实现
若想获取更多用法,请参考
curl https://start.spring.io
- 必须添加如下依赖,因为此时用的还是内嵌 tomcat,而内嵌 tomcat 默认不带 jasper(用来解析 jsp)
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
也可以使用 Idea 配置 tomcat 来测试,此时用的是外置 tomcat
- 骨架生成的代码中,多了一个 ServletInitializer,它的作用就是配置外置 Tomcat 使用的,在外置 Tomcat 启动后,去调用它创建和运行 SpringApplication
2、Boot 启动过程
1、SpringApplication 构造
public class Day39_1 {
public static void main(String[] args) throws Exception {
//1.获取 Bean Definition 源"
SpringApplication application = new SpringApplication(Day39_1.class);
application.setSources(Set.of("classpath:b01.xml"));
//2. 推断应用类型(web的servlet、web的reactive、非web)
Method method = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
method.setAccessible(true);
System.err.println("应用类型:"+method.invoke(null));
//3.ApplicationContext 初始化器
application.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
if (configurableApplicationContext instanceof GenericApplicationContext) {
GenericApplicationContext gac = (GenericApplicationContext) configurableApplicationContext;
gac.registerBean("bean3",Bean3.class);
}
}
});
//4.监听器与事件
application.addListeners(applicationEvent -> {
System.err.println("事件:"+applicationEvent.getClass());
});
//5.主类推断(main所在的类)
Method clazz = SpringApplication.class.getDeclaredMethod("deduceMainApplicationClass");
clazz.setAccessible(true);
System.err.println("主类是:"+clazz.invoke(application));
ConfigurableApplicationContext context = application.run(args);
System.err.println();
for (String name : context.getBeanDefinitionNames()) {
String s = context.getBeanFactory().getBeanDefinition(name).getResourceDescription();
System.err.println(name+" 来源:【"+s+"】");
}
context.close();
}
static class Bean1 {
}
static class Bean2 {
}
static class Bean3 {
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
@Bean
public TomcatServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
2、执行 run 方法
第 1 步:
public class Day39_2 {
public static void main(String[] args) throws Exception {
SpringApplication application = new SpringApplication(Day39_2.class);
// 添加 app 监听器
application.addListeners(applicationEvent -> {
System.err.println("监听器:"+applicationEvent.getClass());
});
// 获取事件发送器实现类名
List<String> names = SpringFactoriesLoader.loadFactoryNames(SpringApplicationRunListener.class, Day39_2.class.getClassLoader());
for (String name : names) {
System.err.println("发送器:"+name);
Class<?> clazz = Class.forName(name);
Constructor<?> constructor = clazz.getConstructor(SpringApplication.class, String[].class);
SpringApplicationRunListener listener = (SpringApplicationRunListener) constructor.newInstance(application, args);
// 发布事件
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
listener.starting(bootstrapContext);
listener.environmentPrepared(bootstrapContext,new StandardEnvironment());
GenericApplicationContext context = new GenericApplicationContext();
listener.contextPrepared(context);
listener.contextLoaded(context);
context.refresh();
listener.started(context);
listener.running(context);
listener.failed(context,new Exception("出错了!"));
}
}
}
第 2、8、9、11、12 步
public class Day39_3 {
public static void main(String[] args) throws Exception {
SpringApplication application = new SpringApplication();
application.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.err.println("执行初始化器增强...");
}
});
System.err.println(">>>>>>>>>>>>>>>>>>>>>>>> 2. 封装启动 args");
DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);
System.err.println(">>>>>>>>>>>>>>>>>>>>>>>> 8. 创建容器");
GenericApplicationContext context = createApplicationContext(WebApplicationType.SERVLET);
System.err.println(">>>>>>>>>>>>>>>>>>>>>>>> 9. 准备容器");
for (ApplicationContextInitializer initializer : application.getInitializers()) {
initializer.initialize(context);
}
System.err.println(">>>>>>>>>>>>>>>>>>>>>>>> 10. 加载 bean 定义");
DefaultListableBeanFactory factory = context.getDefaultListableBeanFactory();
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(factory);
reader.register(Config.class);
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(factory);
xmlReader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(factory);
scanner.scan("com.springboot.springbootmybatis.day39.sub");
System.err.println(">>>>>>>>>>>>>>>>>>>>>>>> 11. refresh 容器");
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.err.println(name+" 来源:【"+factory.getBeanDefinition(name).getResourceDescription()+"】");
}
System.err.println(">>>>>>>>>>>>>>>>>>>>>>>> 12. 执行 runner");
for (CommandLineRunner runner : context.getBeansOfType(CommandLineRunner.class).values()) {
//main方法的 args
runner.run(args);
}
for (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class).values()) {
//第二步封装的参数
runner.run(arguments);
}
}
private static GenericApplicationContext createApplicationContext(WebApplicationType type) {
GenericApplicationContext context = null;
switch (type) {
case SERVLET :
context = new AnnotationConfigServletWebServerApplicationContext();
break;
case REACTIVE :
context = new AnnotationConfigReactiveWebServerApplicationContext();
break;
case NONE :
context = new AnnotationConfigApplicationContext();
break;
}
return context;
}
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 new CommandLineRunner() {
@Override
public void run(String... args) throws Exception {
System.err.println("commandLineRunner()..." + Arrays.toString(args));
}
};
}
@Bean
public ApplicationRunner applicationRunner() {
return new ApplicationRunner() {
@Override
public void run(ApplicationArguments args) throws Exception {
System.err.println("applicationRunner()..." + Arrays.toString(args.getSourceArgs()));
System.err.println(args.getOptionNames());
System.err.println(args.getOptionValues("server.port"));
System.err.println(args.getNonOptionArgs());
}
};
}
}
}
第 3 步
public class Step3 {
public static void main(String[] args) throws IOException {
ApplicationEnvironment env = new ApplicationEnvironment(); // 系统环境变量, properties, yaml
env.getPropertySources().addLast(new ResourcePropertySource(new ClassPathResource("step3.properties")));
env.getPropertySources().addFirst(new SimpleCommandLinePropertySource(args));
for (PropertySource<?> ps : env.getPropertySources()) {
System.out.println(ps);
}
// System.out.println(env.getProperty("JAVA_HOME"));
System.out.println(env.getProperty("server.port"));
}
}
第 4 步
public class Step4 {
public static void main(String[] args) throws IOException {
ApplicationEnvironment environment = new ApplicationEnvironment();
environment.getPropertySources().addLast(
new ResourcePropertySource("step4", new ClassPathResource("step4.properties"))
);
ConfigurationPropertySources.attach(environment);
for (PropertySource<?> source : environment.getPropertySources()) {
System.err.println(">>>"+source);
}
System.err.println();
System.err.println(environment.getProperty("user.first-name"));
System.err.println(environment.getProperty("user.middle-name"));
System.err.println(environment.getProperty("user.last-name"));
}
}
user.first-name=George
user.middle_name=Walker
user.lastName=Bush
第 5 步
public class Step5 {
public static void main(String[] args) {
SpringApplication application = new SpringApplication();
ApplicationEnvironment environment = new ApplicationEnvironment();
System.err.println("---------------- 增强前");
for (PropertySource<?> source : environment.getPropertySources()) {
System.err.println(">>>"+source);
}
System.err.println("\n---------------- 增强后");
ConfigDataEnvironmentPostProcessor processor1
= new ConfigDataEnvironmentPostProcessor(new DeferredLogs(),new DefaultBootstrapContext());
processor1.postProcessEnvironment(environment,application);
for (PropertySource<?> source : environment.getPropertySources()) {
System.err.println(">>>"+source);
}
System.err.println("\n---------------- 增强后");
RandomValuePropertySourceEnvironmentPostProcessor processor2
= new RandomValuePropertySourceEnvironmentPostProcessor(new DeferredLog());
processor2.postProcessEnvironment(environment,application);
for (PropertySource<?> source : environment.getPropertySources()) {
System.err.println(">>>"+source);
}
System.err.println(environment.getProperty("server.port"));
//固定写法,获取随机值
System.err.println(environment.getProperty("random.int"));
System.err.println(environment.getProperty("random.uuid"));
}
}
public static void main(String[] args) {
SpringApplication application = new SpringApplication();
application.addListeners(new EnvironmentPostProcessorApplicationListener());
// List<String> names = SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, Step5.class.getClassLoader());
// for (String name : names) {
// System.out.println(name);
// }
EventPublishingRunListener listener = new EventPublishingRunListener(application,args);
ApplicationEnvironment e = new ApplicationEnvironment();
System.err.println("-------------- 增强前");
for (PropertySource<?> source : e.getPropertySources()) {
System.err.println(">>>"+source);
}
listener.environmentPrepared(new DefaultBootstrapContext(),e);
System.err.println("-------------- 增强后");
for (PropertySource<?> source : e.getPropertySources()) {
System.err.println(">>>"+source);
}
}
第 6 步
public class Step6 {
// 绑定 spring.main 前缀的 key value 至 SpringApplication, 请通过 debug 查看
public static void main(String[] args) throws IOException {
SpringApplication application = new SpringApplication();
ApplicationEnvironment e = new ApplicationEnvironment();
e.getPropertySources().addLast(new ResourcePropertySource("step4", new ClassPathResource("step4.properties")));
// e.getPropertySources().addLast(new ResourcePropertySource("step6", new ClassPathResource("step6.properties")));
User user = Binder.get(e).bind("user", User.class).get();
System.err.println(user);
User u = new User();
Binder.get(e).bind("user", Bindable.ofInstance(u));
System.err.println(u);
System.out.println(application);
Binder.get(e).bind("spring.main",Bindable.ofInstance(application));
System.out.println(application);
}
static class User {
private String firstName;
private String middleName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "User{" +
"firstName='" + firstName + '\'' +
", middleName='" + middleName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
}
第 7 步
public class Step7 {
public static void main(String[] args) {
ApplicationEnvironment env = new ApplicationEnvironment();
SpringApplicationBannerPrinter printer = new SpringApplicationBannerPrinter(
new DefaultResourceLoader(),
new SpringBootBanner()
);
// 测试文字 banner
env.getPropertySources().addLast(new MapPropertySource("custom", Map.of("spring.banner.location","banner1.txt")));
// 测试图片 banner
env.getPropertySources().addLast(new MapPropertySource("custom", Map.of("spring.banner.image.location","banner2.png")));
// 版本号的获取
System.out.println(SpringBootVersion.getVersion());
printer.print(env, Step7.class, System.out);
}
}
-
得到 SpringApplicationRunListeners,名字取得不好,实际是事件发布器
-
发布 application starting 事件1️⃣
-
-
封装启动 args
-
准备 Environment 添加命令行参数(*)
-
ConfigurationPropertySources 处理(*)
-
发布 application environment 已准备事件2️⃣
-
-
通过 EnvironmentPostProcessorApplicationListener 进行 env 后处理(*)
-
application.properties,由 StandardConfigDataLocationResolver 解析
-
spring.application.json
-
-
绑定 spring.main 到 SpringApplication 对象(*)
-
打印 banner(*)
-
创建容器
-
准备容器
-
发布 application context 已初始化事件3️⃣
-
-
加载 bean 定义
-
发布 application prepared 事件4️⃣
-
-
refresh 容器
-
发布 application started 事件5️⃣
-
-
执行 runner
-
发布 application ready 事件6️⃣
-
这其中有异常,发布 application failed 事件7️⃣
-
3、Tomcat 内嵌容器
public class MyTomcat {
public static void main(String[] args) throws IOException, LifecycleException {
// 1.创建 Tomcat 对象
Tomcat tomcat = new Tomcat();
tomcat.setBaseDir("mytomcat");
// 2.创建项目文件夹, 即 docBase 文件夹
File docBase = Files.createTempDirectory("boot.").toFile();
docBase.deleteOnExit();
// 3.创建 Tomcat 项目, 在 Tomcat 中称为 Context
Context context = tomcat.addContext("", docBase.getAbsolutePath());
//集成spring容器
WebApplicationContext springContext = getWebApplicationContext();
// 4.编程添加 Servlet
context.addServletContainerInitializer(new ServletContainerInitializer() {
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
HelloServlet helloServlet = new HelloServlet();
servletContext.addServlet("aaa",helloServlet).addMapping("/hello");
// DispatcherServlet dispatcherServlet = springContext.getBean(DispatcherServlet.class);
// servletContext.addServlet("dispatcherServlet",dispatcherServlet).addMapping("/");
for (ServletRegistrationBean bean : springContext.getBeansOfType(ServletRegistrationBean.class).values()) {
System.err.println(bean);
bean.onStartup(servletContext);
}
}
}, Collections.emptySet());
// 5.启动 Tomcat
tomcat.start();
// 6.创建连接器, 设置监听端口
Connector connector = new Connector(new Http11Nio2Protocol());
connector.setPort(8090);
tomcat.setConnector(connector);
}
public static WebApplicationContext getWebApplicationContext(){
//内嵌了tomcat
// AnnotationConfigServletWebServerApplicationContext
//没有内嵌tomcat
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(Config.class);
context.refresh();
return context;
}
@Configuration
static class Config {
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean
// 这个例子中必须为 DispatcherServlet 提供 AnnotationConfigWebApplicationContext, 否则会选择 XmlWebApplicationContext 实现
public DispatcherServlet dispatcherServlet(WebApplicationContext applicationContext) {
return new DispatcherServlet(applicationContext);
}
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
handlerAdapter.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter()));
return handlerAdapter;
}
@RestController
static class MyController {
@GetMapping("hello2")
public Map<String,Object> hello() {
return Map.of("hello2", "hello2, spring!");
}
}
}
4、Boot 自动配置
1、AopAutoConfiguration
public class Test1 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
// StandardEnvironment env = new StandardEnvironment();
// env.getPropertySources().addLast(new SimpleCommandLinePropertySource("--spring.aop.auto=false"));
// context.setEnvironment(env);
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
context.registerBean(Config.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.err.println(name);
}
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
System.err.println(">>>"+creator.isProxyTargetClass());
context.close();
}
@Configuration
@Import(MyImportSelector.class)
static class Config {
}
static class MyImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{AopAutoConfiguration.class.getName()};
}
}
}
Spring Boot 是利用了自动配置类来简化了 aop 相关配置
-
AOP 自动配置类为
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
-
可以通过
spring.aop.auto=false
禁用 aop 自动配置 -
AOP 自动配置的本质是通过
@EnableAspectJAutoProxy
来开启了自动代理,如果在引导类上自己添加了@EnableAspectJAutoProxy
那么以自己添加的为准 -
@EnableAspectJAutoProxy
的本质是向容器中添加了AnnotationAwareAspectJAutoProxyCreator
这个 bean 后处理器,它能够找到容器中所有切面,并为匹配切点的目标类创建代理,创建代理的工作一般是在 bean 的初始化阶段完成的
2、DataSourceAutoConfiguration
-
对应的自动配置类为:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
-
它内部采用了条件装配,通过检查容器的 bean,以及类路径下的 class,来决定该 @Bean 是否生效
简单说明一下,Spring Boot 支持两大类数据源:
-
EmbeddedDatabase - 内嵌数据库连接池
-
PooledDataSource - 非内嵌数据库连接池
PooledDataSource 又支持如下数据源
-
hikari 提供的 HikariDataSource
-
tomcat-jdbc 提供的 DataSource
-
dbcp2 提供的 BasicDataSource
-
oracle 提供的 PoolDataSourceImpl
如果知道数据源的实现类类型,即指定了 spring.datasource.type
,理论上可以支持所有数据源,但这样做的一个最大问题是无法订制每种数据源的详细配置(如最大、最小连接数等)
3、MybatisAutoConfiguration
-
MyBatis 自动配置类为
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
-
它主要配置了两个 bean
-
SqlSessionFactory - MyBatis 核心对象,用来创建 SqlSession
-
SqlSessionTemplate - SqlSession 的实现,此实现会与当前线程绑定
-
用 ImportBeanDefinitionRegistrar 的方式扫描所有标注了 @Mapper 注解的接口
-
用 AutoConfigurationPackages 来确定扫描的包
-
-
还有一个相关的 bean:MybatisProperties,它会读取配置文件中带
mybatis.
前缀的配置项进行定制配置
@MapperScan 注解的作用与 MybatisAutoConfiguration 类似,会注册 MapperScannerConfigurer 有如下区别
-
@MapperScan 扫描具体包(当然也可以配置关注哪个注解)
-
@MapperScan 如果不指定扫描具体包,则会把引导类范围内,所有接口当做 Mapper 接口
-
MybatisAutoConfiguration 关注的是所有标注 @Mapper 注解的接口,会忽略掉非 @Mapper 标注的接口
这里有同学有疑问,之前介绍的都是将具体类交给 Spring 管理,怎么到了 MyBatis 这儿,接口就可以被管理呢?
-
其实并非将接口交给 Spring 管理,而是每个接口会对应一个 MapperFactoryBean,是后者被 Spring 所管理,接口只是作为 MapperFactoryBean 的一个属性来配置
4、TransactionAutoConfiguration
-
事务自动配置类有两个:
-
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
-
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
-
-
前者配置了 DataSourceTransactionManager 用来执行事务的提交、回滚操作
-
后者功能上对标 @EnableTransactionM anagement,包含以下三个 bean
-
BeanFactoryTransactionAttributeSourceAdvisor 事务切面类,包含通知和切点
-
TransactionInterceptor 事务通知类,由它在目标方法调用前后加入事务操作
-
AnnotationTransactionAttributeSource 会解析 @Transactional 及事务属性,也包含了切点功能
-
-
如果自己配置了 DataSourceTransactionManager 或是在引导类加了 @EnableTransactionManagement,则以自己配置的为准
5、ServletWebServerFactoryAutoConfiguration
-
提供 ServletWebServerFactory
6、DispatcherServletAutoConfiguration
-
提供 DispatcherServlet
-
提供 DispatcherServletRegistrationBean
7、WebMvcAutoConfiguration
-
配置 DispatcherServlet 的各项组件,提供的 bean 见过的有
-
多项 HandlerMapping
-
多项 HandlerAdapter
-
HandlerExceptionResolver
-
8、ErrorMvcAutoConfiguration
-
提供的 bean 有 BasicErrorController
9、MultipartAutoConfiguration
-
它提供了 org.springframework.web.multipart.support.StandardServletMultipartResolver
-
该 bean 用来解析 multipart/form-data 格式的数据
10、HttpEncodingAutoConfiguration
-
POST 请求参数如果有中文,无需特殊设置,这是因为 Spring Boot 已经配置了 org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter
-
对应配置 server.servlet.encoding.charset=UTF-8,默认就是 UTF-8
-
当然,它只影响非 json 格式的数据
public class Day41_1 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(false);//不允许同名bean覆盖(报错提示),默认true
context.registerBean("config",Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.err.println(name);
}
System.err.println(context.getBean("bean1"));
context.close();
}
@Configuration
//第一种方式
// @Import({AutoConfiguration1.class,AutoConfiguration2.class})
@Import(MyImportSelector.class)
static class Config{
@Bean
public Bean1 bean1() {
return new Bean1("本项目");
}
}
// static class MyImportSelector implements ImportSelector{
//推迟导入
static class MyImportSelector implements DeferredImportSelector{
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//第二种方式
// return new String[]{AutoConfiguration1.class.getName(),AutoConfiguration2.class.getName()};
//第三种方式
List<String> list = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
return list.toArray(new String[0]);
}
}
@Configuration // 第三方的配置类
static class AutoConfiguration1 {
@Bean
@ConditionalOnMissingBean //缺失某个bean时,才会使用该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 + '\'' +
'}';
}
}
@Configuration // 第三方的配置类
static class AutoConfiguration2 {
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean2 {
}
}
#内部类用 $
com.springboot.springbootmybatis.day41.Day41_1$MyImportSelector=\
com.springboot.springbootmybatis.day41.Day41_1.AutoConfiguration1,\
com.springboot.springbootmybatis.day41.Day41_1.AutoConfiguration2
-
自动配置类本质上就是一个配置类而已,只是用 META-INF/spring.factories 管理,与应用配置类解耦
-
@Enable 打头的注解本质是利用了 @Import
-
@Import 配合 DeferredImportSelector 即可实现导入,selectImports 方法的返回值即为要导入的配置类名
-
DeferredImportSelector 的导入会在最后执行,为的是让其它配置优先解析
5、条件装配底层
条件装配的底层是本质上是 @Conditional 与 Condition,这两个注解。引入自动配置类时,期望满足一定条件才能被 Spring 管理,不满足则不管理。
- 首先编写条件判断类,它实现 Condition 接口,编写条件判断逻辑
- 其次,在要导入的自动配置类上添加
@Conditional(MyCondition1.class)
,将来此类被导入时就会做条件检查
public class Day41_2 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config",Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.err.println(name);
}
context.close();
}
@Configuration
@Import(MyImportSelector.class)
static class Config{
}
static class MyImportSelector implements DeferredImportSelector{
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
List<String> list = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
return list.toArray(new String[0]);
}
}
static class MyCondition1 implements Condition{
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
//存在 druid
Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
String name = map.get("className").toString();
boolean exists = (boolean) map.get("exists");
boolean flag = ClassUtils.isPresent(name,null);
return exists ? flag: !flag;
}
}
// static class MyCondition2 implements Condition{
//
// @Override
// public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// return !ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource",null);
// }
// }
//自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
@Conditional(MyCondition1.class)
@interface ConditionOnClass{
boolean exists(); //true 存在,false 不存在
String className();
}
@Configuration
// @Conditional(MyCondition1.class)
@ConditionOnClass(className = "com.alibaba.druid.pool.DruidDataSource",exists = false)
static class AutoConfiguration1 {
@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 + '\'' +
'}';
}
}
@Configuration
// @Conditional(MyCondition2.class)
@ConditionOnClass(className = "com.alibaba.druid.pool.DruidDataSource",exists = true)
static class AutoConfiguration2 {
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean2 {
}
}