SpringBoot自动配置
项目内部配置第三方bean
-
第三方的bean通过配置类的@bean注入,项目内部引用使用@Import配置类的class
-
解耦引入的第三方配置类, 新建一个缓冲类加载需要引入的配置类
-
将需要引入第三方配置类加入Mate-inf中的factories中
-
加载返回可以引入第三方的bean
-
本项目bean和第三方bean冲突,以本项目为准(import先解析被覆盖,默认不可以覆盖)
-
DeferredImportSelector推迟导入,先解析本项目的
-
@ConditionalOnMissingBean本项目没有,则加载第三方的
-
package com.butch.a41;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class A41_1 {
@SuppressWarnings("all")
public static void main(String[] args) throws IOException {
GenericApplicationContext context = new GenericApplicationContext();
context.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(false);
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println(context.getBean(Bean1.class));
}
@Configuration // 本项目的配置类
@Import(MyImportSelector.class)
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1("本项目");
}
}
static class MyImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
//EnableAutoConfiguration为key的类名
// for (String name : SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, null)) {
// System.out.println(name);
// }
// System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
//获取配置文件中值 也会扫描第三方jar包的factories
List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
return names.toArray(new String[0]);
}
}
@Configuration // 第三方的配置类
static class AutoConfiguration1 {
@Bean
@ConditionalOnMissingBean
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 {
}
}
自动配置实现类
AopAutoConfiguration(AOP)
-
AOP 自动配置类为
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
-
可以通过
spring.aop.auto=false
禁用 aop 自动配置 -
AOP 自动配置的本质是通过
@EnableAspectJAutoProxy
来开启了自动代理,如果在引导类上自己添加了@EnableAspectJAutoProxy
那么以自己添加的为准 -
@EnableAspectJAutoProxy
的本质是向容器中添加了AnnotationAwareAspectJAutoProxyCreator
这个 bean 后处理器,它能够找到容器中所有切面,并为匹配切点的目标类创建代理,创建代理的工作一般是在 bean 的初始化阶段完成的
多层嵌套的静态内部类,最后一层EnableAspectJAutoProxy 导入代理创建器
-
spring.aop.auto!= false才加载
-
@ConditionalOnClass(Advice.class)存在Advice
-
@ConditionalOnMissingClass("org.aspectj.weaver.Advice") 缺失条件
-
最终@EnableAspectJAutoProxy 本质是import,proxyTargetClass创建代理的模式,为false会看看有没有实现接口有接口jdk代理
-
最终注册AnnotationAwareAspectJAutoProxyCreator的bean后处理器,高层转底层创建jdk或者cglib代理
package com.butch.a41;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.type.AnnotationMetadata;
public class TestAopAuto {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
StandardEnvironment env = new StandardEnvironment();
env.getPropertySources().addLast(new SimpleCommandLinePropertySource("--spring.aop.auto=true"));
context.setEnvironment(env);
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
context.registerBean(Config.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
//org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$AspectJAutoProxyingConfiguration$CglibAutoProxyConfiguration
//org.springframework.aop.config.internalAutoProxyCreator
//org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$AspectJAutoProxyingConfiguration
//org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
System.out.println(">>>>>>>>>>>>>>>");
//查看代理实现类型
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(
"org.springframework.aop.config.internalAutoProxyCreator", AnnotationAwareAspectJAutoProxyCreator.class);
System.out.println(creator.isProxyTargetClass());
}
@Configuration
@Import(MyImportSelector.class)
static class Config {
}
static class MyImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{AopAutoConfiguration.class.getName()};
}
}
}
Datasource
-
对应的自动配置类为: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
,理论上可以支持所有数据源,但这样做的一个最大问题是无法订制每种数据源的详细配置(如最大、最小连接数等)
dataSource
-
@Conditional(PooledDataSourceCondition.class)进入带有连接池的数据源
-
集成mybaits依赖jdbc进入@ConditionalOnClass(HikariDataSource.class)
-
@EnableConfigurationProperties(DataSourceProperties.class)封装绑定键值信息,spring.datasourcexxxxx
-
创建datasource绑定键值信息
-
mybits
-
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 管理,而是每个接口会对应一个 MapperFactoryBean,是后者被 Spring 所管理,接口只是作为 MapperFactoryBean 的一个属性来配置
package com.butch.a41;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
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.context.ApplicationContext;
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;
public class TestDataSourceAuto {
@SuppressWarnings("all")
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
StandardEnvironment env = new StandardEnvironment();
//添加配置源
env.getPropertySources().addLast(new SimpleCommandLinePropertySource(
"--spring.datasource.url=jdbc:mysql://localhost:3306/test",
"--spring.datasource.username=root",
"--spring.datasource.password=123456"
));
context.setEnvironment(env);
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
context.registerBean(Config.class);
String packageName = TestDataSourceAuto.class.getPackage().getName();
System.out.println("当前包名:" + packageName);
AutoConfigurationPackages.register(context.getDefaultListableBeanFactory(),
packageName);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
String resourceDescription = context.getBeanDefinition(name).getResourceDescription();
if (resourceDescription != null)
System.out.println(name + " 来源:" + resourceDescription);
//dataSource 来源:class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]
//hikariPoolDataSourceMetadataProvider 来源:class path resource [org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration.class]
//sqlSessionFactory 来源:class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]
//sqlSessionTemplate 来源:class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]
//transactionManager 来源:class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfiguration$DataSourceTransactionManagerConfiguration.class]
//org.springframework.transaction.config.internalTransactionAdvisor 来源:class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]
//transactionAttributeSource 来源:class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]
//transactionInterceptor 来源:class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]
//org.springframework.transaction.config.internalTransactionalEventListenerFactory 来源:class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]
//transactionTemplate 来源:class path resource [org/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration$TransactionTemplateConfiguration.class]
//platformTransactionManagerCustomizers 来源:class path resource [org/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration.class]
}
}
@Configuration
@Import(MyImportSelector.class)
static class Config {
}
static class MyImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{
//配置数据源 必选
DataSourceAutoConfiguration.class.getName(),
//mybit的sqlsession工程mapper扫描
MybatisAutoConfiguration.class.getName(),
//事务管理
DataSourceTransactionManagerAutoConfiguration.class.getName(),
//声明式事务管理
TransactionAutoConfiguration.class.getName()
};
}
}
}
TransactionAutoConfiguration
-
事务自动配置类有两个:
-
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
-
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
-
-
前者配置了 DataSourceTransactionManager 用来执行事务的提交、回滚操作
-
后者功能上对标 @EnableTransactionManagement,包含以下三个 bean
-
BeanFactoryTransactionAttributeSourceAdvisor 事务切面类,包含通知和切点
-
TransactionInterceptor 事务通知类,由它在目标方法调用前后加入事务操作
-
AnnotationTransactionAttributeSource 会解析 @Transactional 及事务属性,也包含了切点功能
-
-
如果自己配置了 DataSourceTransactionManager 或是在引导类加了 @EnableTransactionManagement,则以自己配置的为准
WebMvcAutoConfiguration
-
配置 DispatcherServlet 的各项组件,提供的 bean 见过的有
-
多项 HandlerMapping
-
多项 HandlerAdapter
-
HandlerExceptionResolver
-
自定义自动配置类
-
自动配置类本质上就是一个配置类而已,只是用 META-INF/spring.factories 管理,与应用配置类解耦
-
@Enable 打头的注解本质是利用了 @Import
-
@Import 配合 DeferredImportSelector 即可实现导入,selectImports 方法的返回值即为要导入的配置类名
-
DeferredImportSelector 的导入会在最后执行,为的是让其它配置优先解析
package com.butch.a41;
import org.springframework.boot.autoconfigure.AutoConfigurationImportSelector;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
import java.io.IOException;
import java.util.List;
public class A41_2 {
@SuppressWarnings("all")
public static void main(String[] args) throws IOException {
GenericApplicationContext context = new GenericApplicationContext();
StandardEnvironment env = new StandardEnvironment();
env.getPropertySources().addLast(new SimpleCommandLinePropertySource(
"--spring.datasource.url=jdbc:mysql://localhost:3306/test",
"--spring.datasource.username=root",
"--spring.datasource.password=root"
));
context.setEnvironment(env);
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println(context.getBean(Bean1.class));
}
//spring识别的import
// @Import(AutoConfigurationImportSelector.class)
// @Import(MyImportSelector.class)
@EnableAutoConfiguration
@Configuration // 本项目的配置类
static class Config {
}
static class MyImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
//EnableAutoConfiguration为key的类名
// for (String name : SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, null)) {
// System.out.println(name);
// }
// System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
//获取配置文件中值
List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
return names.toArray(new String[0]);
}
}
@Configuration // 第三方的配置类
static class AutoConfiguration1 {
@Bean
public Bean1 bean1() {
return new Bean1();
}
}
@Configuration // 第三方的配置类
static class AutoConfiguration2 {
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean2 {
}
static class Bean1 {
}
}
条件装配底层
当引入第三方的bean ,增加条件装配
底层使用conditional 配合实现了condition的接口进行检查
本质上是if-else语句
package com.butch.a42;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
public class A42_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.out.println(name);
}
}
@Configuration // 本项目的配置类
@Import(MyImportSelector.class)
static class Config {
}
static class MyImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()};
}
}
static class Mycondition1 implements Condition{
//检测德鲁伊存在
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
System.out.println("ClassUtils.isPresent(\"DruidDataSource\", null)" + ClassUtils.isPresent("DruidDataSource", null));
return ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource" +
"", null);
}
}
static class Mycondition2 implements Condition{
//检测德鲁伊不存在
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return !ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource", null);
}
}
// @ConditionalOnClass(className = "com.alibaba.druid.pool.DruidDataSource", exists = false) //有德鲁伊bean1 无德鲁伊bean2
@Configuration // 第三方的配置类
@Conditional(Mycondition1.class)
static class AutoConfiguration1 {
@Bean
public Bean1 bean1() {
return new Bean1();
}
}
@Configuration // 第三方的配置类
// @ConditionalOnClass(className = "com.alibaba.druid.pool.DruidDataSource", exists = true)
@Conditional(Mycondition2.class)
static class AutoConfiguration2 {
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean1 {
}
static class Bean2 {
}
}
原文地址:https://blog.csdn.net/weixin_48254383/article/details/140129761
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!