# 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。
```java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
<1.>@SpringBootConfiguration
<2.>@ComponentScan
<3.>@EnableAutoConfiguration
public @interface SpringBootApplication {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //实际上它也是一个配置类
public @interface SpringBootConfiguration {
}
```
## 2.2 @EnableAutoConfiguration
EnableAutoConfiguration 只是一个简单地注解,自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector类。
```java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //作用:将main包下的所欲组件注册到容器中
@Import({AutoConfigurationImportSelector.class}) //加载自动装配类 xxxAutoconfiguration
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
```
## 2.3 AutoConfigurationImportSelector
继承体系如下:
```java
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()这个方法主要负责加载自动配置类的。
```java
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的注解:
```java
@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
```java
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装配
*/
@Configuration
public 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文件
引入自己配置的自动装配
```java
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.yanhua.fast_build_starter.configuration.ThreadPoolAutoConfiguration
```
## 第三步
测试程序能改运行
```java
@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仓库
```java
mvn clean install
```
## 第五步
其他项目中引入这个pom starter即可
```java
<dependency>
<groupId>com.yanhua</groupId>
<artifactId>fast_build_starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
```

SpringBoot 自动装配