Spring程序启动的代码如下
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
启动类标上@SpringBootApplication注解就可以使用Spring的各种功能,@SpringBootApplication如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
@SpringBootApplication有三个注解:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。@SpringBootConfiguration等同于一个@Configuration,表明启动类是一个配置类,@EnableAutoConfiguration启用自动配置,@ComponentScan会扫描指定包下面的@Component。分别来看
@SpringBootConfiguration
@SpringBootConfiguration实际上就等同于一个@Configuration,如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
/*
* 注释同下,删去
*/
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
@Configuration里定义了两个字段
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
/**
* Specify whether {@code @Bean} methods should get proxied in order to enforce
* bean lifecycle behavior, e.g. to return shared singleton bean instances even
* in case of direct {@code @Bean} method calls in user code. This feature
* requires method interception, implemented through a runtime-generated CGLIB
* subclass which comes with limitations such as the configuration class and
* its methods not being allowed to declare {@code final}.
* <p>The default is {@code true}, allowing for 'inter-bean references' via direct
* method calls within the configuration class as well as for external calls to
* this configuration's {@code @Bean} methods, e.g. from another configuration class.
* If this is not needed since each of this particular configuration's {@code @Bean}
* methods is self-contained and designed as a plain factory method for container use,
* switch this flag to {@code false} in order to avoid CGLIB subclass processing.
* <p>Turning off bean method interception effectively processes {@code @Bean}
* methods individually like when declared on non-{@code @Configuration} classes,
* a.k.a. "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore
* behaviorally equivalent to removing the {@code @Configuration} stereotype.
* @since 5.2
*/
boolean proxyBeanMethods() default true;
}
翻译一下注释重要部分
指定bean方法是否会被cglib代理,使得bean方法具有spring的生命周期,也就是:即便是在代码中直接调用标bean方法也会返回单例bean。这种特性需要通过cglib代理生成子类,拦截bean方法,这也就要求@Configuration类和bean方法不能是final的。
配置类有Lite和Full两种模式,标了@Configuration并且proxyBeanMethods=true就是Full模式,否则就是Lite模式,在Full模式下会进行上述的cglib代理。看下面这个配置类,fooService方法直接调用了fooRepository方法,而这两个方法定义的都是单例bean。如果fooService直接调用fooRepository的话,那么fooRepository这个bean将不具有bean的生命周期,spring中的fooRepository bean和fooService内部的fooRepository将不是同一个。Full模式通过cglib代理生成子类,让这种直接的方法调用就跟getBean()获取的bean一样,具备spring的生命周期
@Configuration
public class AppConfig {
@Bean
public FooService fooService() {
return new FooService(fooRepository());
}
@Bean
public FooRepository fooRepository() {
return new JdbcFooRepository(dataSource());
}
}
@Bean可以定义在没有@Configuration注解的类里面,这种类就是Lite模式的配置类,不会进行cglib代理。此时对bean方法的调用就是java方法调用,不会具有spring生命周期
那么@Configuration注解是在哪里处理的,上面这种机制优势怎么支持的呢?
@Configuration处理过程
@Configuration是在ConfigurationClassPostProcessor处理的,这个类实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor接口的父接口是BeanFactoryPostProcessor。BeanFactoryPostProcessor#postProcessBeanFactory接收一个beanFactory,能修改beanFactory里面的bean。BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry接受一个registry,能向registry里面注册bean
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
这两个接口方法在PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors方法里被调用,这个方法会按一定优先级调用BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor先调用、BeanFactoryPostProcessor后调用。所以ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法先调用、postProcessBeanFactory方法后调用,看这两个方法分别做了什么
postProcessBeanDefinitionRegistry
顺着调用流程找到ConfigurationClassPostProcessor#processConfigBeanDefinitions方法,这个类做了几件事情:
1、将registry里面所有配置类找出来
2、排序
3、通过ConfigurationClassParser和ConfigurationClassBeanDefinitionReader处理每一个配置类(本文不涉及)
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 排序&处理
}
具体看下ConfigurationClassUtils#checkConfigurationClassCandidate方法是怎么确定一个类是不是配置类的
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
省略...
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// It's a full or lite configuration candidate... Let's determine the order value, if any.
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
if (config != null && !Boolean.FALSE.equals(config.get(“proxyBeanMethods”)))
如果这个类有@Configuration注解并且proxyBeanMethods不是false,那就是Full模式配置类
else if (config != null || isConfigurationCandidate(metadata))
如果这个类有@Configuration注解或者isConfigurationCandidate(metadata),那就是Lite模式配置了
否则不是配置类
看下isConfigurationCandidate方法
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
return hasBeanMethods(metadata);
}
private static final Set<String> candidateIndicators = new HashSet<>(8);
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
isConfigurationCandidate检查了下这个类是否有@Component、@ComponentScan、@Import、@ImportResource任何一个注解,如果有的话那就是Lite模式配置类,没有的话进一步看有没有Bean方法,有的话也是Lite模式配置类,总结一下ConfigurationClassUtils#checkConfigurationClassCandidate方法怎么判断是不是配置类的:
1、如果有@Configuration注解并且proxyBeanMethods不是false,那就是Full模式配置类
2、有@Configuration注解,或者有@Component、@ComponentScan、@Import、@ImportResource注解,或者有@Bean方法,那就是Lite模式配置类
3、否则不是配置类
postProcessBeanFactory
postProcessBeanFactory调用ConfigurationClassPostProcessor#enhanceConfigurationClasses对Full模式配置类进行cglib代理。删去无关代码,首先for循环把Full模式的bean definition拿出来,放到configBeanDefs;然后通过ConfigurationClassEnhancer生成cglib代理类,替换掉bean definition里原来的类
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty() || NativeDetector.inNativeImage()) {
// nothing to enhance -> return immediately
enhanceConfigClasses.end();
return;
}
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified bean class
Class<?> configClass = beanDef.getBeanClass();
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
beanDef.setBeanClass(enhancedClass);
}
}
enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();
}
@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
/**
* Environment property that can be used to override when auto-configuration is
* enabled.
*/
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
@EnableAutoConfiguration注解Import了AutoConfigurationImportSelector
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered
AutoConfigurationImportSelector实现了DeferredImportSelector,所以@EnableAutoConfiguration什么时候处理已经清楚了:
ConfigurationClassParser解析启动类这个配置类过程中处理了该注解,由于AutoConfigurationImportSelector是一个DeferredImportSelector,所以会将AutoConfigurationImportSelector暂存在DeferredImportSelectorHandler#deferredImportSelectors一个List中,等当前一轮配置类都解析完了才会处理AutoConfigurationImportSelector
DeferredImportSelector
public interface DeferredImportSelector extends ImportSelector {
@Nullable
default Class<? extends Group> getImportGroup() {
return null;
}
interface Group {
void process(AnnotationMetadata metadata, DeferredImportSelector selector);
Iterable<Entry> selectImports();
class Entry {
private final AnnotationMetadata metadata;
private final String importClassName;
// constructor, getters and setters
}
}
}
ImportSelector比较简单,就是一个selectImports方法,就是基于当前配置类的AnnotationMetadata返回一些需要import的类名。DeferredImportSelector扩展了ImportSelector,增加了getImportGroup方法,作用是什么?
由于DeferredImportSelector是先暂存,延迟到一批配置类处理完后会统一处理这些DeferredImportSelector,此时这些不同的DeferredImportSelector导入哪些类可以放在一起做一定的逻辑处理。每个DeferredImportSelector的会属于一个Group,属于同一个Group的DeferredImportSelector会统一处理,Group为null则默认为DefaultDeferredImportSelectorGroup。Entry保存了需要import的类名和配置类的AnnotationMetadata的映射关系,因为多个DeferredImportSelector可能是由多个配置类导入的,最后返回的时候要知道import进来的类是由哪个配置类import进来的
DeferredImportSelector调用上与ImportSelector不同,对于ImportSelector,会直接调用selectImports;而对于DeferredImportSelector,外部先按照Group对所有DeferredImportSelector进行分组,属于同一组的DeferredImportSelector,调用这个组的Group#process方法处理每个DeferredImportSelector,Group#process里面一般会调用DeferredImportSelector#selectImports将导入进来的类暂存一下,然后调用一次Group#selectImports方法,返回这一组DeferredImportSelector最重要import的类
DefaultDeferredImportSelectorGroup
先看一下默认的DefaultDeferredImportSelectorGroup实现细节
private static class DefaultDeferredImportSelectorGroup implements Group {
private final List<Entry> imports = new ArrayList<>();
@Override
public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
for (String importClassName : selector.selectImports(metadata)) {
this.imports.add(new Entry(metadata, importClassName));
}
}
@Override
public Iterable<Entry> selectImports() {
return this.imports;
}
}
imports保存了导入类的Entry。process方法将DeferredImportSelector导入了的类缓存一下;然后selectImports返回每个DeferredImportSelector导入的类
既然多个DeferredImportSelector属于一个Group,那么哪些DeferredImportSelector属于AutoConfigurationGroup?
AutoConfigurationImportSelector和ImportAutoConfigurationImportSelector都属于AutoConfigurationGroup,分别对应@EnableAutoConfiguration注解和@ImportAutoConfiguration注解
AutoConfigurationGroup
private static class AutoConfigurationGroup
implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
}
AutoConfigurationGroup#process过程
AutoConfigurationGroup#process方法调用了AutoConfigurationImportSelector或者ImportAutoConfigurationImportSelector的selectImports方法,拿到AutoConfigurationEntry,暂存在autoConfigurationEntries里,并且每个导入进来的类都暂存在entries里
两个ImportSelector的selectImports做了什么?
AutoConfigurationImportSelector#selectImports
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
AutoConfigurationImportSelector#selectImports实际上就是调用了SpringFactoriesLoader从spring.factories里面去读取配置的好的AutoConfiguration类
ImportAutoConfigurationImportSelector#selectImports
ImportAutoConfigurationImportSelector里面覆盖了getCandidateConfigurations方法。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> candidates = new ArrayList<>();
Map<Class<?>, List<Annotation>> annotations = getAnnotations(metadata);
annotations.forEach(
(source, sourceAnnotations) -> collectCandidateConfigurations(source, sourceAnnotations, candidates));
return candidates;
}
private void collectCandidateConfigurations(Class<?> source, List<Annotation> annotations,
List<String> candidates) {
for (Annotation annotation : annotations) {
candidates.addAll(getConfigurationsForAnnotation(source, annotation));
}
}
private Collection<String> getConfigurationsForAnnotation(Class<?> source, Annotation annotation) {
String[] classes = (String[]) AnnotationUtils.getAnnotationAttributes(annotation, true).get("classes");
if (classes.length > 0) {
return Arrays.asList(classes);
}
return loadFactoryNames(source);
}
protected Collection<String> loadFactoryNames(Class<?> source) {
return SpringFactoriesLoader.loadFactoryNames(source, getBeanClassLoader());
}
ImportAutoConfigurationImportSelector#selectImports就是从注解上去获取导入的类,getAnnotations不是获取所有注解,而是限制了ImportAutoConfiguration注解。getConfigurationsForAnnotation判断ImportAutoConfiguration,如果classes字段有值,那导入的就是这些类,否则还是通过SpringFactoriesLoader从spring.factories里读取要导入的类,只不过此时key就是标了ImportAutoConfiguration的类
AutoConfigurationGroup#selectImports过程
AutoConfigurationGroup#selectImports方法主要做了两个事情
1、导进来的类排掉exclusion
2、排序,返回
private static class AutoConfigurationGroup
implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();
@Override
public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
}
主要看一下配置类的排序逻辑
private List<String> sortAutoConfigurations(Set<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata) {
return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata)
.getInPriorityOrder(configurations);
}
其实还是通过AutoConfigurationSorter来排序的,看一下AutoConfigurationSorter#getInPriorityOrder方法
List<String> getInPriorityOrder(Collection<String> classNames) {
AutoConfigurationClasses classes = new AutoConfigurationClasses(this.metadataReaderFactory,
this.autoConfigurationMetadata, classNames);
List<String> orderedClassNames = new ArrayList<>(classNames);
// Initially sort alphabetically
Collections.sort(orderedClassNames);
// Then sort by order
orderedClassNames.sort((o1, o2) -> {
int i1 = classes.get(o1).getOrder();
int i2 = classes.get(o2).getOrder();
return Integer.compare(i1, i2);
});
// Then respect @AutoConfigureBefore @AutoConfigureAfter
orderedClassNames = sortByAnnotation(classes, orderedClassNames);
return orderedClassNames;
}
注释上:先按字母顺序排序,再按order排序,最后按@AutoConfigureBefore @AutoConfigureAfter两个注解排序,由于排序是稳定排序,所有三种排序方法中:注解定义的顺序优先;注解没定义顺序则按order来;order相同则按字母顺序
order排序怎么排?
classes.get(o1)获取到了AutoConfigurationClass,AutoConfigurationClass#getOrder实际上获取的是AutoConfigureOrder定义的order,跟ordered接口、@Order注解都没关系,主要是为了不影响bean注册的顺序
private int getOrder() {
if (wasProcessed()) {
return this.autoConfigurationMetadata.getInteger(this.className, "AutoConfigureOrder",
AutoConfigureOrder.DEFAULT_ORDER);
}
Map<String, Object> attributes = getAnnotationMetadata()
.getAnnotationAttributes(AutoConfigureOrder.class.getName());
return (attributes != null) ? (Integer) attributes.get("value") : AutoConfigureOrder.DEFAULT_ORDER;
}