一丶疑问
大家时否有这样的疑问,写spring项目时需要使用xml或者注解来
- 扫描包扫标注了@Controller、@Service、@Repository,@Component 到spring容器
- 开启事务,来保证sql操作的原子性
但是到了springboot我们没有写过如下的
@ComponentScan("com.jh")
@EnableTransactionManagement
@Configuration
public class Config {
....
}
<context:component-scan base-package="com.jh"/>
<tx:annotation-driven/>
这是为什么呢?原因就在于springboot的自动装配
二丶快速加入容器注解与条件注解
快速加入容器注解:
- 包扫描(@ComponentScan)+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
- @Bean[导入的第三方包里面的组件,但这需要知道构造方法]
- @Import[快速给容器中导入一个组件]
- @Import(类.class);容器中就会自动注册这个组件,id默认是全类名
- @Import(ImportSelector.class):返回需要导入的组件的全类名数组;
- @Import(ImportBeanDefinitionRegistrar.class):手动注册bean到容器中
条件注解,满足条件才会加入容器:
- @Conditional(MyCondition.class)
// 包扫描(@ComponentScan)+组件标注注解
@ComponentScan("com.jh")
// @Import[快速给容器中导入一个组件]
@Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
@Configuration
public class MainConfig2 {
//bean方式加入
@Bean("person")
public Person person(){
System.out.println("给容器中添加Person....");
return new Person("张三", 25);
}
// 条件注解,如果满足条件,即返回true才加入容器管理
@Conditional(MyCondition.class)
@Bean("windows")
public Person person02(){
return new Person("windows", 48);
}
}
// 条件类,判断如果是windows系统加入容器
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if(property.contains("Windows")){
return true;
}
return false;
}
}
class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if(property.contains("Windows")){
return true;
}
return false;
}
}
class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"org.hhh.Red"};
}
}
class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean def=registry.containsBeanDefinition("org.hhh.Red");
if (def) {
BeanDefinition beanDefinition=new RootBeanDefinition(Color.class);
registry.registerBeanDefinition("color",beanDefinition);
}
}
}
class Red { }
class Blue { }
class Color { }
class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Person() { }
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
}
class Main {
public static void main(String[] args) {
ApplicationContext applicationContext1= new AnnotationConfigApplicationContext(MainConfig.class);
String[] strings=applicationContext1.getBeanDefinitionNames();
for (String str:strings){
System.out.println(str);
}
}
}
结果如下:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig # 由于@Configuration
org.hhh.Blue #@Import({Blue.class})
org.hhh.Red #@Import({MyImportSelector.class})
person # @bean
windows # @bean + @conditonal 我的是windows
color #@Import({MyImportBeanDefinitionRegistrar.class})
三丶SpringBoot自动装配
@SpringBootApplication
├─ @SpringBootConfiguration #配置类
├─@Configuration
├─ @EnableAutoConfiguration #
├─@AutoConfigurationPackage
├─@Import({Registrar.class}) # 扫描根包及其子包注册到容器
├─@Import({AutoConfigurationImportSelector.class}) # 将第三方自动配置类加入容器
├─ @ComponentScan #注解扫描
整个过程是@SpringBootApplication有着至关重要的作用
1. @ComponentScan
可以进行注解扫描
2. @Configuration
@SpringBootConfiguration 下面的@Configuration使得 启动类本身就是配置类
@SpringBootApplication
public class CartApp {
public static void main(String[] args) {
SpringApplication.run(CartApp.class,args);
}
@Bean
...
}
3. @AutoConfigurationPackage的@Import({Registrar.class})
你会发现这个Registrar.class与我们之前写得MyImportBeanDefinitionRegistrar一样就是往里面注入组件, 作用扫描根包及其子包批量注册到容器
// 自己写的
class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean def=registry.containsBeanDefinition("org.hhh.Red");
if (def) {
BeanDefinition beanDefinition=new RootBeanDefinition(Color.class);
registry.registerBeanDefinition("color",beanDefinition);
}
}
}
// Registrar
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));
}
}
扫描根包及其子包批量注册到容器,因为AutoConfigurationPackages.register就是拿到 引导类所在的根包,从而扫描工程下根包及其子包的所有组件,可以看到下面断点程序拿到了根包目录
4. @Import({AutoConfigurationImportSelector.class})
作用第三方依赖的自动配置加入容器
AutoConfigurationImportSelector 也是与MyImportBeanDefinitionRegistrar 一致作用,进入下面断点的getAutoConfigurationEntry方法
发现了下面的127个自动配置类,这是如何获取的呢?
其实是由下面步骤依次得到,进去getCandidateConfigurations ,loadFactoryNames ,getOrDefault,META-INF/spring.factories 如下图
最后我们发现由类加载去META-INF/spring.factories下面找资源
我们先拿springboot的autoconfiguration看看,里面都是全类限定名
但是并不会添加所有的,这就是conditional作用,比如redis和mq的自动配置类,必须满足规则才会引入这个自动配置类,由于redis和mq的conditional没有其他的严格,所以没有配置数据源等等系统能正常启动,但是一旦遇到redis操作和mq操作就会报错
但是像druid这种严格,一旦数据源没有配置,就会无法启动
四丶结论
@ComponentScan @EnableTransactionManagement都去哪了?
都在@SpringBootApplication里面,所以之前spring工程需要写很多的配置类,也会被springboot看是否符合需要自动加载,不需要人为介入
springboot帮了我们很多,默认大于配置
五丶进阶
ConfigurationClassPostProcessor
1. 判断之前加入的bd是否被某些注解修饰了,有注释的添加到configCandidates
for (String beanName : candidateNames) {
// 获取指定名称的BeanDefinition对象
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 如果beanDefinition中的configurationClass属性不等于空,那么意味着已经处理过,输出日志信息
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// 判断当前BeanDefinition是否是一个配置类,并为BeanDefinition设置属性为lite或者full,此处设置属性值是为了后续进行调用
// 如果Configuration配置proxyBeanMethods代理为true则为full
// 如果加了@Bean、@Component、@ComponentScan、@Import、@ImportResource注解,则设置为lite
// 如果配置类上被@Order注解标注,则设置BeanDefinition的order属性值
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
// 添加到对应的集合对象中
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
分类
checkConfigurationClassCandidate 获取注解元数据信息
metadataReader.getAnnotationMetadata()
检测 Configuration
检测
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
检测
metadata.hasAnnotatedMethods(Bean.class.getName());
2. 生成名字
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
3. 配置解析类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
4. 要处理的处理过的集合
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
5. 解析
parser.parse(candidates);
processConfigurationClass
6. 解析细节 6.1~6.3前置,6.4 核心处理
6.1 根据条件注解查看是否跳过解析
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
shouldSkip 遍历条件是否符合,根据matches方法
6.2 防止多次处理,进行合并等处理
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext();) {
if (configClass.equals(it.next())) {
it.remove();
}
}
}
}
6.3 递归处理父类,变成sourceClass解析…
SourceClass sourceClass = asSourceClass(configClass);
6.4 核心处理 doProcessConfigurationClass
6.4.1 递归处理内部类
processMemberClasses(configClass, sourceClass)
有的话回到解析过程第五步5. 解析processConfigurationClass,递归
6.4.2 解析@PropertySource
@PropertySource({“classPath:db.properties”})
@Value("#{jdbc.url}")
String url
最终会把这些属性加载到beanFactory 的MutablePropertySource
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
6.4.3 解析@ComponentScan
最终会把这些属性加载到bd,这里也会递归处理parse
核心的是findCandidateComponents,找出来后进行解析处理
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(
holder.getBeanDefinition(), this.metadataReaderFactory)) {
parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
}
6.4.4 解析 @Import
processImports 下面仍然会有递归processImports
参数getImports也是递归,递归收集所有的生命的{@code @Import} values
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
6.4.5 解析 @ImportResource
加载某些spring的配置文件
// Process any @ImportResource annotations
if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
6.4.6 解析 @Bean
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
6.4.7 解析接口@Bean
jdk8接口可以由默认实现,可以在接口方法加@Bean
6.4.8 解析父类
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
六丶进阶理解
总体:执行BFPP有个核心的ccpp解析注解解析,包含@…等等的解析,解析@Import,会从启动类开始解析,会发现识别到@ACIS,有个延迟加载的实现,getImports的方法可以获取autoConfigurationEntry对象,获取entry对象会调用getCandidateConfiguration方法可以把配置文件中对应属性值加载来完成我们的自动加载回来
springboot主要就是两个Import,也就是解析@Import
用这个方法找出所有的Import,下面是主要代码
processImports(configClass, sourceClass, getImports(sourceClass), true);
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
collectImports(sourceClass, imports, visited);
return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
collectImport会将SpringBootApplication上的所有注解进行递归遍历,发现有两个Registrar.class和AutoConfigurationImportSelector.class
@SpringBootApplication
├─ @SpringBootConfiguration #配置类
├─@Configuration
├─ @EnableAutoConfiguration #
├─@AutoConfigurationPackage
├─@Import({Registrar.class}) # 扫描根包及其子包注册到容器
├─@Import({AutoConfigurationImportSelector.class}) # 将第三方自动配置类加入容器
├─ @ComponentScan #注解扫描
processImports 导入Selector额外的配置类同时完成具体类的实例化工作
就像我们上面说的三种导入 ImportSelector,ImportBeanDefinitionRegistrar,直接导入
- ImportSelector:继承DeferredImportSelector这个接口可以延迟处理,暂先加入deferredImportSelectors,不然直接处理。处理是递归的
- ImportBeanDefinitionRegistrar:实例化导入类
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
deferredImportSelectors这个会在parse的最后执行 this.deferredImportSelectorHandler.process();
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
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);
}
}
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;
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
待完善补充