自学内容网 自学内容网

SpringBoot自动配置

项目内部配置第三方bean

  1. 第三方的bean通过配置类的@bean注入,项目内部引用使用@Import配置类的class

  2. 解耦引入的第三方配置类, 新建一个缓冲类加载需要引入的配置类

  3. 将需要引入第三方配置类加入Mate-inf中的factories中

  4. 加载返回可以引入第三方的bean

  5. 本项目bean和第三方bean冲突,以本项目为准(import先解析被覆盖,默认不可以覆盖)

    1. DeferredImportSelector推迟导入,先解析本项目的

    2. @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 导入代理创建器

  1. spring.aop.auto!= false才加载

  2. @ConditionalOnClass(Advice.class)存在Advice

  3. @ConditionalOnMissingClass("org.aspectj.weaver.Advice") 缺失条件

  4. 最终@EnableAspectJAutoProxy 本质是import,proxyTargetClass创建代理的模式,为false会看看有没有实现接口有接口jdk代理

  5. 最终注册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

  1. @Conditional(PooledDataSourceCondition.class)进入带有连接池的数据源

  2. 集成mybaits依赖jdbc进入@ConditionalOnClass(HikariDataSource.class)

  3. @EnableConfigurationProperties(DataSourceProperties.class)封装绑定键值信息,spring.datasourcexxxxx

    1. 创建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

自定义自动配置类

  1. 自动配置类本质上就是一个配置类而已,只是用 META-INF/spring.factories 管理,与应用配置类解耦

  2. @Enable 打头的注解本质是利用了 @Import

  3. @Import 配合 DeferredImportSelector 即可实现导入,selectImports 方法的返回值即为要导入的配置类名

  4. 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)!