写点什么

SpringBoot 自动装配

用户头像
黄敏
关注
发布于: 刚刚

1 对比

以前: 在没有 SpringBoot 之前,我们需要通过 XML 或者 Java 中定义 @Bean 来把我们要用的 jar 装配到 Spring 中

现在: Springboot 提供了一个规范,只要按照这个规范定义好我们用的 starter,它就会为我们自动装配。

2 具体实现

2.1 Springboot 的核心注解

  • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制, 是实现自动装配的重要注解,我们以这个注解入手。

  • @Configuration:允许在上下文中注册额外的 bean 或导入其他配置类

  • @ComponentScan: 扫描被 @Component (@Service,@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如下图所示,容器中将排除 TypeExcludeFilter 和 AutoConfigurationExcludeFilter。

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited<1.>@SpringBootConfiguration<2.>@ComponentScan<3.>@EnableAutoConfigurationpublic @interface SpringBootApplication { } @Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Configuration //实际上它也是一个配置类public @interface SpringBootConfiguration {}
复制代码

2.2 @EnableAutoConfiguration

EnableAutoConfiguration 只是一个简单地注解,自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector 类。

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage //作用:将main包下的所欲组件注册到容器中@Import({AutoConfigurationImportSelector.class}) //加载自动装配类 xxxAutoconfigurationpublic @interface EnableAutoConfiguration {    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";     Class<?>[] exclude() default {};     String[] excludeName() default {};}
复制代码

2.3 AutoConfigurationImportSelector

继承体系如下:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { } public interface DeferredImportSelector extends ImportSelector { } public interface ImportSelector {    String[] selectImports(AnnotationMetadata var1);}
复制代码

2.4 selectImports

可以看出,AutoConfigurationImportSelector 类实现了 ImportSelector 接口,也就实现了这个接口中的 selectImports 方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中。

private static final String[] NO_IMPORTS = new String[0];
public String[] selectImports(AnnotationMetadata annotationMetadata) { // <1>.判断自动装配开关是否打开 if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { //<2>.获取所有需要装配的bean AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } }
复制代码

2.5 getAutoConfigurationEntry

getAutoConfigurationEntry()这个方法主要负责加载自动配置类的

private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry(); AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {        //<1>.第一步        if (!this.isEnabled(annotationMetadata)) {            return EMPTY_ENTRY;        } else {            //<2>.第二步            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);            //<3>.第三步            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);            //<4>.第四步            configurations = this.removeDuplicates(configurations);            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);            this.checkExcludedClasses(configurations, exclusions);            configurations.removeAll(exclusions);            configurations = this.filter(configurations, autoConfigurationMetadata);            this.fireAutoConfigurationImportEvents(configurations, exclusions);            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);        }    }
复制代码

第一步:

判断自动装配开关是否打开。默认 spring.boot.enableautoconfiguration=true,可在 application.properties 或 application.yml 中设置


第二步:

用于获取 EnableAutoConfiguration 注解中的 exclude 和 excludeName。


第三步:

获取需要自动装配的所有配置类,读取 META-INF/spring.factories



第四步:

会根据 @ConditionalOnXXX 进行配置的过滤

已经全部加载到


全部 ConditionalOn 的注解:

@ConditionalOnBean:当容器里有指定 Bean 的条件下@ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下@ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean@ConditionalOnClass:当类路径下有指定类的条件下@ConditionalOnMissingClass:当类路径下没有指定类的条件下@ConditionalOnProperty:指定的属性是否有指定的值@ConditionalOnResource:类路径是否有指定的值@ConditionalOnExpression:基于 SpEL 表达式作为判断条件@ConditionalOnJava:基于 Java 版本作为判断条件@ConditionalOnJndi:在 JNDI 存在的条件下差在指定的位置@ConditionalOnNotWebApplication:当前项目不是 Web 项目的条件下@ConditionalOnWebApplication:当前项目是 Web 项 目的条件下
复制代码

3 构造一个 ThreadPool Starter

第一步

创建一个 Configuration


package com.yanhua.fast_build_starter.configuration; import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration; import java.util.concurrent.*;import java.util.concurrent.atomic.AtomicInteger; /** * 自动装配线程池 * * @ConditionalOnClass 检测ThreadPoolExecutor存在,才会SpringBoot装配 */@Configurationpublic class ThreadPoolAutoConfiguration {    @Bean    @ConditionalOnClass(ThreadPoolExecutor.class)    public ThreadPoolExecutor FailFastThreadPool() {        int corePoolSize = Runtime.getRuntime().availableProcessors();        int maximumPoolSize = Runtime.getRuntime().availableProcessors() * 2 + 1;        long keepAliveTime = 10;        TimeUnit unit = TimeUnit.SECONDS;        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100);        ThreadFactory threadFactory = new FastBuildThreadFactory("FastBuild");        RejectedExecutionHandler handler = new FastBuildAbortPolicy("FastBuild");        return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);    }} /** * 1、自定义的线程工厂 * 2、一定要起线程别名 * 3、通过jstack或者authas查看线程名称 */class FastBuildThreadFactory implements ThreadFactory {    private final AtomicInteger threadNum = new AtomicInteger(1);    private String threadName;     public FastBuildThreadFactory(String threadName) {        this.threadName = threadName;    }     @Override    public Thread newThread(Runnable r) {        Thread t = new Thread(r, threadName + "_" + threadNum.getAndIncrement());        if (t.isDaemon()) {            t.setDaemon(true);        }        if (t.getPriority() != Thread.NORM_PRIORITY) {            t.setPriority(Thread.NORM_PRIORITY);        }        return t;    }} /** * 1、自定义拒绝策略 * 2、队列满后进行阻塞 * 3、默认:异常,抛弃,最老抛弃,调用线程运行 */class FastBuildAbortPolicy extends ThreadPoolExecutor.AbortPolicy {    private static final Logger LOG = LoggerFactory.getLogger(FastBuildAbortPolicy.class);     private final String threadName;     public FastBuildAbortPolicy(String threadName) {        this.threadName = threadName;    }     @Override    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {        String msg = String.format("Provider端线程池满!" +                        " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d)," +                        " Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s)",                threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(),                e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating());        LOG.warn(msg);        if (!e.isShutdown()) {            try {                LOG.info("Start get queue");                e.getQueue().put(r);                LOG.info("End get queue");            } catch (InterruptedException ee) {                LOG.error(ee.toString(), ee);                Thread.currentThread().interrupt();            }        }    }}
复制代码

第二步

工程的 resources 包下创建 META-INF/spring.factories 文件 引入自己配置的自动装配

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.yanhua.fast_build_starter.configuration.ThreadPoolAutoConfiguration
复制代码

第三步

测试程序能改运行

@Test    public void FailFastThreadPool() {        ThreadPoolAutoConfiguration configuration = new ThreadPoolAutoConfiguration();        ThreadPoolExecutor poolExecutor = configuration.FailFastThreadPool();        for (int i = 0; i < 150; i++) {            poolExecutor.submit(() -> {                try {                    System.out.println("ThreadName:" + Thread.currentThread().getName() + " Start");                    Thread.sleep(10000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            });        }    }
复制代码

第四步

把当前程序打 jar 包,放入 maven 仓库

mvn clean install
复制代码

第五步

其他项目中引入这个 pom starter 即可

<dependency>    <groupId>com.yanhua</groupId>    <artifactId>fast_build_starter</artifactId>    <version>0.0.1-SNAPSHOT</version></dependency>
复制代码


用户头像

黄敏

关注

还未添加个人签名 2019.11.30 加入

熟悉Java并发编程、数据结构和算法、FFmpeg音视频处理技术

评论

发布
暂无评论
SpringBoot 自动装配