刚刚看视频,雷老师亲手写了一个Starter,我想着也自定义一个Starter,并看源码解析一下AutoConfiguration类何时运行,其中组件何时被加载到容器。
第一步,建立空工程,分别加入两个模块
第一个项目工程,也就是给外界引用的启动器,主要引用了自动配置包
pom.xml文件依赖如下:
<dependencies>
<dependency>
<groupId>com.yanxin</groupId>
<artifactId>myhello-spring-boot-autoconfigurer</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
第二个项目工程,自动配置包 pom.xml文件依赖如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
第二步,为自动配置包加入自动配置
先考虑这样一个组件,这个组件中的方法需要调用另一个组件的某些属性,我们想springboot运行的时候会自动配置这个组件。利用组件来绑定属性,我们可以先写出这个类:
@ConfigurationProperties(prefix = "hello")
public class HelloProperties {
private String prefix;
private String suffix;
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
}
其中@ConfigurationProperties
注解用来将配置文件的配置和类绑定起来。
我们需要的功能组件类如下:
public class HelloService {
HelloProperties hello;
public HelloProperties getHello() {
return hello;
}
public void setHello(HelloProperties hello) {
this.hello = hello;
}
public String sayHello(String name){
String reply = hello.getPrefix() + name + hello.getSuffix();
System.out.println(reply);
return reply;
}
}
我们给HelloProperties属性加上setter和getter方法就是为了可以给功能组件注入HelloProperties属性值。
自动配置类如下:
@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {
@Autowired
HelloProperties hello;
@Bean
public HelloService getService(){
HelloService ser = new HelloService();
ser.setHello(hello);
return ser;
}
}
自动配置类有@Configuration
注解,可以自动加载该类到容器中(那么,springboot程序何时加载HelloAutoConfiguration类呢?)
在/META-INF/spring.factories
中配置好,以便读取自动配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.yanxin.myhellospringbootautoconfigurer.HelloAutoConfiguration
将其导入maven仓库中,分别对两个module进行maven,install。
第三步,新建一个web项目,引用自定义starter
新建test-starter module,pom.xml文件引入
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.yanxin</groupId>
<artifactId>myhello-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
在application.properties中配置
hello.prefix=zy
hello.suffix=yz
新建Controller类HelloController
@RestController
public class HelloController {
@Autowired
HelloService service;
@GetMapping("/hello")
public String getCon(String name){
return service.sayHello(name);
}
}
运行springboot项目,访问/hello
,得到
可见返回的是zy
--前缀,yz
--后缀,zys
--name。
至此自动配置类配置成功。那么,自动配置类是合适加载的呢?我们跟进源码。
自动配置原理
我们点开@SpringBootApplication
注解,发现注解@SpringBootConfiguration
和@EnableAutoConfiguration
,顾名思义,EnableAutoConfiguration
负责开启自动配置,那到底是怎样开启的呢?我们继续点开:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
我们注意到两个注解@AutoConfigurationPackage
和@Import({AutoConfigurationImportSelector.class})
,点开@AutoConfigurationPackage
知道这个类有两个属性:basePackages和basePackageClasses
,且导入了组件Registrar.class
点开Registrar.class
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
在此处断点看看到底registerBeanDefinitions方法干了什么
进入方法,
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
} else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(AutoConfigurationPackages.BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(2);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
知道这似乎是将注解所在的包名信息注入到一个bean中,然后使得该报名下的配置默认扫描。
总结一下就是,@AutoConfigurationPackage
就是使得自动配置包,使得默认扫描主配置类所在包。
下面再看看,导入的组件AutoConfigurationImportSelector.class
点开能看到
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
这个方法用于导入某些配置。我们点进getAutoConfigurationEntry()
方法,
this.getCandidateConfigurations(annotationMetadata, attributes)
就是通过注解信息获取配置类,点开看到,
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
再看loadFactoryNames
方法,
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
再看loadSpringFactories
方法,
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
得知在加载自动配置类的地方再这里,
我们的自动配置类在这里被加入缓存result中
然后返回到上级
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList(names.size());
Iterator var7 = names.iterator();
while(var7.hasNext()) {
String name = (String)var7.next();
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable var12) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
}
}
return instances;
}
这一步获取5个ContextInitializer
进行初始化容器。从而将/META-INF/spring.factories
下的AutoConfigration类加载到容器中并进行了初始化。