前言
前一章分析了Spring的整个启动流程,但是只是讲了基于XML的配置方式,基于注解和基于Java类的配置方式并没有讲解,本章就介绍一下这部分内容,同时也补充一些上章没有讲到的细节。
component-scan
上一章说到哪些类需要注册到IoC容器中,由容器负责创建,在基于XML的配置方式中我们通过<bean>标签来定义需要由IoC容器管理的bean。但在有很多bean需要容器管理的场景,比如100个bean,难道要在XML中定义100个<bean>标签,这显然不合理。
这时候就需要使用component-scan了,我们扫描一个或多个包,将每个包中配置了注解的类注册到IoC容器中。
<context:component-scan base-package="org.example.anno"></context:component-scan>
上面的例子就是扫描org.example.anno的所有类,我们来看下这是如何实现的。
在上一章的obtainFreshBeanFactory章节里,我们说到在这一步里会生成一个beanFactory,同时解析XML,加载beanDefinition。在BeanDefinitionParserDelegate类的parseCustomElement方法中解析自定义元素。
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
通过命名空间context找到对应的parser:ComponentScanBeanDefinitionParser
public BeanDefinition parse(Element element, ParserContext parserContext) {
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
原理就是找到basePackages下的所有类,判断是否有对应的注解,有就注册beanDefinition到容器中,扫描的默认注解如下:
- @Component
- @javax.annotation.ManagedBean,如果有的话
- @javax.inject.Named,如果有的话
在解析的最后一步registerComponents,这一步做了很重要的事,开启注解配置:annotation-config,添加了一些重要的BeanPostProcessor:
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
......
return beanDefs;
}
这里的代码只保留了重点,主要添加了以下BPP:
- AutowiredAnnotationBeanPostProcessor,用来处理Autowired注解
- CommonAnnotationBeanPostProcessor,用来处理Resource注解
- ConfigurationClassPostProcessor,用来处理Configuration和Bean注解
@Autowired
在第一章时,我们提出一个问题:类中的哪些成员需要容器自动注入,答案就是可以通过Autowired注解,注解的成员变量就会由IoC容器自动注入,我们来看下其实现原理。
Autowired注解是由AutowiredAnnotationBeanPostProcessor这个BPP来实现功能,当添加了component-scan,会开启annotation-config,就会注册这个BPP。
public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
/**
* Post-process the given merged bean definition for the specified bean.
* @param beanDefinition the merged bean definition for the bean
* @param beanType the actual type of the managed bean instance
* @param beanName the name of the bean
* @see AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
*/
void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
/**
* A notification that the bean definition for the specified name has been reset,
* and that this post-processor should clear any metadata for the affected bean.
* <p>The default implementation is empty.
* @param beanName the name of the bean
* @since 5.1
* @see DefaultListableBeanFactory#resetBeanDefinition
*/
default void resetBeanDefinition(String beanName) {
}
}
AutowiredAnnotationBeanPostProcessor实现了MergedBeanDefinitionPostProcessor接口,接口中的postProcessMergedBeanDefinition会在bean的实例创建后、属性注入前调用。
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
postProcessMergedBeanDefinition方法的主要作用就是判断当前bean、bean的成员变量、成员方法有没有相应的注解,有的话就缓存起来。主要逻辑在buildAutowiringMetadata方法中:
- 扫描所有成员变量上是否有this.autowiredAnnotationTypes,注意this.autowiredAnnotationTypes的初始化值是@Autowired、@Value、@javax.inject.Inject
- 扫描所有方法上是否有this.autowiredAnnotationTypes
- 如果扫描到,就组装元信息,缓存下来
AutowiredAnnotationBeanPostProcessor还实现了postProcessProperties方法,这个方法会在bean实例化后,属性注入(populateBean)阶段调用。
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
从缓存中获得前面缓存“注入元信息metadata”,注入
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
for (InjectedElement element : elementsToIterate) {
element.inject(target, beanName, pvs);
}
}
}
如果这个bean没有被Autowired或Value注解的成员或方法,elementsToIterate会为空,否则注入相应值。
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {
try {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
catch (NoSuchBeanDefinitionException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve
value = resolveFieldValue(field, bean, beanName);
}
}
else {
value = resolveFieldValue(field, bean, beanName);
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
if (checkPropertySkipping(pvs)) {
return;
}
Method method = (Method) this.member;
Object[] arguments;
if (this.cached) {
try {
arguments = resolveCachedArguments(beanName);
}
catch (NoSuchBeanDefinitionException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve
arguments = resolveMethodArguments(method, bean, beanName);
}
}
else {
arguments = resolveMethodArguments(method, bean, beanName);
}
if (arguments != null) {
try {
ReflectionUtils.makeAccessible(method);
method.invoke(bean, arguments);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
如果是成员变量,那就resolveFieldValue,其实就是从beanFactory中getBean,也就是第二章中介绍的流程;
如果是方法,那就resolveMethodArguments,然后调用方法。
以上就@Autowired的原理,注意@Autowired虽然是通过BPP实现,但并不是通过常用的postProcessBeforeInitialization和postProcessAfterInitialization方法实现。
不管是resolveFieldValue还是resolveMethodArguments,最后都是调用beanFactory.resolveDependency,这个方法是按类型获取bean。
@Resource
Resource注解是由CommonAnnotationBeanPostProcessor这个BPP来实现功能,同样是在开启annotation-config时注册。
CommonAnnotationBeanPostProcessor功能逻辑基本与AutowiredAnnotationBeanPostProcessor一样,只不过最终是通过调用beanFactory.resolveBeanByName来获取要注入的bean,按名字获取bean。
@Configuration
@Configuration由ConfigurationClassPostProcessor实现功能,ConfigurationClassPostProcessor可以说是Spring最重要的PostProcessor之一,它可以处理以下注解:
- @Configuration
- @Component
- @ComponentScan
- @Import
- @ImportResource
- @Bean
ConfigurationClassPostProcessor会添加新的bean,而BPP是在所有bean定义都加载并实例化后才调用,所以ConfigurationClassPostProcessor本质上是一个BFPP,在bean工厂创建并加载所有bean定义后开始作用,往bean工厂中添加新的bean定义。下图是主体逻辑:
- processConfigBeanDefinitions,遍历beanFactory中的所有beanName,判断是否候选者
- 如果类上有@Configuration且proxyBeanMethods属性为true,则是候选者,FULL模式
- 如果类上有@Configuration或者有Component、ComponentScan、Import、ImportResource、Bean中任一注解,则是候选者,LITE模式
- 解析每个候选者
- 如果有@PropertySources,processPropertySource
- 如果@ComponentScans和@ComponentScan,则扫描,并添加bean,和前面component-scan逻辑一样
- 如果有@Import,则处理Import
- 找到所有@Bean注解的方法
- this.reader.loadBeanDefinitions(configClasses),这一步会把上面找到的class全部转为beanDefinition,注册到beanFactory中
- 调用另一个BFPP方法postProcessBeanFactory
- 遍历beanFactory中所有beanName,找到FULL模式的bean,做CGLib增强,也就是说@Bean注解的方法被一个直接返回bean的方法替换
- 添加一个BPP ImportAwareBeanPostProcessor
- 在beanFactory实例化所有bean时,对于@Bean注解的方法,装配方法的参数,执行方法,获得bean的实例。再再次执行该方法时,因为第11步的CGLib增强,会直接返回bean,而不是再执行一次方法
@Configuration
public class AppConfig {
@Bean
public UserDao userDao(){
// 会被打印几次 ??
System.out.println("注入UserDao");
return new UserDao();
}
@Bean
public OrderDao orderDao(){
// 在这里调用userDao()方法
userDao();
return new OrderDao();
}
}
// 启动容器
public class MainApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
}
}
正如上面13步所说,有@Configuration注解且属性proxyBeanMethods=true(默认为true)的类是FULL模式,会做类增强。所以userDao方法会被一个直接返回UserDao的方法替换,只会在bean实例化时执行一次,后面就直接返回bean。
@Import
@Import注解让我们可以像import库一样将一些第三方类注册到IoC容器中。简单介绍一下常用的场景:
- @Import(A.class)普通类,将是把类A作为bean注册到IoC容器中
- @Import(A.class),类A实现了ImportSelector接口,那么将selectImports方法返回的类注册到IoC容器中
- @Import(A.class),类A实现了ImportBeanDefinitionRegistrar接口,那么调用接口的registerBeanDefinitions方法将类注册到IoC容器中
基于注解
前一章讲到了基于XML配置的ClassPathXmlApplicationContext启动过程,其过程简单来讲就是解析XML,从中读取beanDefinition,注册到beanFactory中,然后实例化。如果XML配置了component-scan,还会扫描指定的包,找到@Component注解的类,注册到IoC容器。
那么基于注解配置的AnnotationConfigApplicationContext,因为没有了XML,我们可以猜到它的过程少了解析XML的过程,只有后面几步。
我们来分析下源码,看是不是这样。
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
可以看到,在AnnotationConfigApplicationContext的默认构造函数中,创建AnnotatedBeanDefinitionReader实例,在AnnotatedBeanDefinitionReader的构造函数中AnnotationConfigUtils.registerAnnotationConfigProcessors,和在XML中配置component-scan一样,开启注解配置:annotation-config,注册了一些重要的BPP和BFPP。
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
如果是带参的构造函数,比如ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class):
- 调用默认构造函数,开启annotation-config
- 注册参数传递的类
- refresh,就是上一章分析过的启动流程
如果MyConfig类上有@Configuration注解,就会由BFPP ConfigurationClassPostProcessor处理。
通过以上三章的内容,基本描述了Spring的依赖注入(IoC)功能,在后面的章节再介绍Spring的另一个重要功能AOP。