在 Spring
中,AutowiredAnnotationBeanPostProcessor
是一个非常重要的后处理器,它可以自动装配标注注解的字段和方法,默认使用 @Autowired
和 @Value
注解,可以支持 JSR-330
的 @Inject
注解。本文通过分析源码介绍它的调用时机和工作原理。
介绍 AutowiredAnnotationBeanPostProcessor
顾名思义,是自动装配注解的 BeanPostProcessor
,但是它处理的不仅仅是 @Autowired
这一个注解。个人认为 Autowired Annotation
的意思更接近“用于标注目标 是被自动装配 的注解 ”。使用“目标”是为了表达注解标注的目标不仅仅限于字段,更是包括构造函数、方法、方法参数以及注解;使用“被自动装配”是为了表达注解描述的是目标的特征或者被处理的结果,体现出被动的语义更准确;使用“注解”是为了表达注解的种类不仅仅限于 @Autowired
,还包括 @Value
和 @Inject
,它们都指示目标需要被自动装配处理。
通过 AutowiredAnnotationBeanPostProcessor
的构造函数可以看到 @Inject
注解的特别之处,为了使用它,需要在 Maven
配置中额外引入 javax.inject
依赖。
1 2 3 4 5 6 7 8 9 10 11 12 public AutowiredAnnotationBeanPostProcessor () { this .autowiredAnnotationTypes.add(Autowired.class); this .autowiredAnnotationTypes.add(Value.class); try { this .autowiredAnnotationTypes.add((Class<? extends Annotation >) ClassUtils.forName("javax.inject.Inject" , AutowiredAnnotationBeanPostProcessor.class.getClassLoader())); logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring" ); } catch (ClassNotFoundException ex) { } }
入口:populateBean
方法 我们在Spring Bean 加载过程 中介绍过为 bean
填充属性值 发生在 populateBean
方法中。我们也将直接从这里开始跟踪代码的处理过程。
个人认为宽松地讲,“填充属性”等于“注入属性”等于“自动装配”,前两者更侧重处理的结果,后者更侧重过程的特征,但请注意在具体的代码上下文中应辨析区别。例如为 bean
填充属性是 Spring
的重要目标之一,基于 Autowired Annotation
进行自动装配某一个后处理器的功能,是 Spring
实现目标的其中一个具体方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 protected void populateBean (String beanName, RootBeanDefinition mbd, BeanWrapper bw) { if (hasInstAwareBpps || needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null ) { return ; } } } } if (needsDepCheck) { checkDependencies(beanName, mbd, filteredPds, pvs); } } }
有时候在 Spring
中看到 BeanPostProcessor
并不能代表将目光转向该接口的方法实现。不同 BeanPostProcessor
的子接口存在不同的调用时机。AutowiredAnnotationBeanPostProcessor
间接实现了 InstantiationAwareBeanPostProcessor
并直接实现了 MergedBeanDefinitionPostProcessor
,这是我们今天要关注的两个重点接口。
AutowiredAnnotationBeanPostProcessor
是什么时候注册的呢?
以 AnnotationConfigApplicationContext
为例,它在构造函数中创建了 AnnotatedBeanDefinitionReader
,AnnotatedBeanDefinitionReader
又在构造函数中注册了基于注解配置的处理器:
1 AnnotationConfigUtils.registerAnnotationConfigProcessors(this .registry);
其中就包括 AutowiredAnnotationBeanPostProcessor
。
后处理 PropertyValues AutowiredAnnotationBeanPostProcessor
实现了 InstantiationAwareBeanPostProcessor
接口,该接口关注 bean
的实例化:
postProcessBeforeInstantiation
(实例化前)
postProcessAfterInstantiation
(实例化后)
postProcessPropertyValues
(实例化后)
postProcessPropertyValues
方法在工厂将给定属性值应用到给定 bean
之前对给定属性值进行后处理。允许检查是否满足所有依赖关系,例如基于 bean
属性 setters
上的 @Required
注解进行检查。还允许替换要应用的属性值,通常是通过基于原始 PropertyValues
创建新的 MutablePropertyValues
实例,并添加或删除特定值。
postProcessPropertyValues
方法做了两件事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override public PropertyValues postProcessPropertyValues ( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException { 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; }
查找自动装配元数据
这部分代码体现了注入(Injection)和自动装配(Autowiring)的等价性。InjectionMetadata
和 AutowiringMetadata
的含义是用于注入(自动装配)的元数据。
InjectionMetadata
是用于管理注入元数据的内部类,不适合直接在应用程序中使用。它和 Class
是一对一的关系,封装了需要被注入 的元素 InjectedElement
。一个 InjectedElement
对应着一个字段(Field
)或一个方法(Method
),分别对应着两个实现类 AutowiredFieldElement
和 AutowiredMethodElement
。这里再次体现了被注入、被自动装配 的语义。
查找自动装配元数据的过程如下:
先从缓存中获取,如果存在且不需要刷新,则直接返回结果
否则构建自动装配元数据并放入缓存
注意:在 postProcessPropertyValues
第一次调用 findAutowiringMetadata
缓存中就已经有结果了。什么时候构建并存入缓存的呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 private InjectionMetadata findAutowiringMetadata (String beanName, Class<?> clazz, PropertyValues pvs) { String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); 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); } try { metadata = buildAutowiringMetadata(clazz); this .injectionMetadataCache.put(cacheKey, metadata); } catch (NoClassDefFoundError err) { throw new IllegalStateException ("Failed to introspect bean class [" + clazz.getName() + "] for autowiring metadata: could not find class that it depends on" , err); } } } } return metadata; } public static boolean needsRefresh (InjectionMetadata metadata, Class<?> clazz) { return (metadata == null || metadata.targetClass != clazz); }
构建自动装配元数据 构建自动装配元数据只需要给定一个 Class
,沿着给定的 Class
的父类向上循环查找直到 Object
类。在每个循环中,先遍历当前类声明的所有属性,找到标注了自动装配注解的属性,为其创建 AutowiredFieldElement
并添加到临时集合,再遍历当前类声明的所有方法,找到标注了自动装配注解的方法,为其创建 AutowiredMethodElement
并添加到临时集合。最后汇总 InjectedElement
封装到 InjectionMetadata
中。
这个处理顺序意味着在注入时方法的优先级高于字段,前者会覆盖后者。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 private InjectionMetadata buildAutowiringMetadata (final Class<?> clazz) { LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList <InjectionMetadata.InjectedElement>(); Class<?> targetClass = clazz; do { final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList <InjectionMetadata.InjectedElement>(); ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils .FieldCallback() { @Override public void doWith (Field field) throws IllegalArgumentException, IllegalAccessException { AnnotationAttributes ann = findAutowiredAnnotation(field); if (ann != null ) { if (Modifier.isStatic(field.getModifiers())) { if (logger.isWarnEnabled()) { logger.warn("Autowired annotation is not supported on static fields: " + field); } return ; } boolean required = determineRequiredStatus(ann); currElements.add(new AutowiredFieldElement (field, required)); } } }); ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils .MethodCallback() { @Override public void doWith (Method method) throws IllegalArgumentException, IllegalAccessException { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return ; } AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { if (logger.isWarnEnabled()) { logger.warn("Autowired annotation is not supported on static methods: " + method); } return ; } if (method.getParameterTypes().length == 0 ) { if (logger.isWarnEnabled()) { logger.warn("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 new InjectionMetadata (clazz, elements); }
注入 对于字段来说,注入意味着将一个解析得到的 value
通过反射设置到字段中;对于方法来说,注入意味着解析得到方法参数的 value
,然后通过反射调用方法。
InjectionMetadata
的 inject
方法比较简单,内部会遍历并调用 InjectedElement
的 inject
方法,AutowiredFieldElement
和 AutowiredMethodElement
各自实现了 inject
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public void inject (Object target, String beanName, PropertyValues pvs) throws Throwable { Collection<InjectedElement> elementsToIterate = (this .checkedElements != null ? this .checkedElements : this .injectedElements); if (!elementsToIterate.isEmpty()) { boolean debug = logger.isDebugEnabled(); for (InjectedElement element : elementsToIterate) { if (debug) { logger.debug("Processing injected element of bean '" + beanName + "': " + element); } element.inject(target, beanName, pvs); } } }
不论是 AutowiredFieldElement
还是 AutowiredMethodElement
,inject
的过程都比较相似:
都使用 DependencyDescriptor
描述即将被注入的特定依赖项,DependencyDescriptor
包装了构造函数参数、方法参数或者字段,允许以统一的方式访问它们的元数据
都会缓存 DependencyDescriptor
都会记录自动装配的 bean
,用于判断是否使用 DependencyDescriptor
的变体 ShortcutDependencyDescriptor
优化缓存
都通过 beanFactory.resolveDependency
解析依赖
字段注入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 private class AutowiredFieldElement extends InjectionMetadata .InjectedElement { private final boolean required; private volatile boolean cached = false ; private volatile Object cachedFieldValue; public AutowiredFieldElement (Field field, boolean required) { super (field, null ); this .required = required; } @Override protected void inject (Object bean, String beanName, PropertyValues pvs) throws Throwable { Field field = (Field) this .member; Object value; if (this .cached) { value = resolvedCachedArgument(beanName, this .cachedFieldValue); } else { DependencyDescriptor desc = new DependencyDescriptor (field, this .required); desc.setContainingClass(bean.getClass()); Set<String> autowiredBeanNames = new LinkedHashSet <String>(1 ); TypeConverter typeConverter = beanFactory.getTypeConverter(); try { value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { throw new UnsatisfiedDependencyException (null , beanName, new InjectionPoint (field), ex); } synchronized (this ) { if (!this .cached) { if (value != null || this .required) { this .cachedFieldValue = desc; registerDependentBeans(beanName, autowiredBeanNames); if (autowiredBeanNames.size() == 1 ) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { this .cachedFieldValue = new ShortcutDependencyDescriptor ( desc, autowiredBeanName, field.getType()); } } } } else { this .cachedFieldValue = null ; } this .cached = true ; } } } if (value != null ) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } } }
方法注入
不理解缓存 DependencyDescriptor
代码上的注释:Shortcut for avoiding synchronization…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 private class AutowiredMethodElement extends InjectionMetadata .InjectedElement { private final boolean required; private volatile boolean cached = false ; private volatile Object[] cachedMethodArguments; public AutowiredMethodElement (Method method, boolean required, PropertyDescriptor pd) { super (method, pd); this .required = required; } @Override protected void inject (Object bean, String beanName, PropertyValues pvs) throws Throwable { if (checkPropertySkipping(pvs)) { return ; } Method method = (Method) this .member; Object[] arguments; if (this .cached) { arguments = resolveCachedArguments(beanName); } else { Class<?>[] paramTypes = method.getParameterTypes(); arguments = new Object [paramTypes.length]; DependencyDescriptor[] descriptors = new DependencyDescriptor [paramTypes.length]; Set<String> autowiredBeans = new LinkedHashSet <String>(paramTypes.length); TypeConverter typeConverter = beanFactory.getTypeConverter(); for (int i = 0 ; i < arguments.length; i++) { MethodParameter methodParam = new MethodParameter (method, i); DependencyDescriptor currDesc = new DependencyDescriptor (methodParam, this .required); currDesc.setContainingClass(bean.getClass()); descriptors[i] = currDesc; try { Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter); if (arg == null && !this .required) { arguments = null ; break ; } arguments[i] = arg; } catch (BeansException ex) { throw new UnsatisfiedDependencyException (null , beanName, new InjectionPoint (methodParam), ex); } } synchronized (this ) { if (!this .cached) { if (arguments != null ) { this .cachedMethodArguments = new Object [paramTypes.length]; for (int i = 0 ; i < arguments.length; i++) { this .cachedMethodArguments[i] = descriptors[i]; } registerDependentBeans(beanName, autowiredBeans); if (autowiredBeans.size() == paramTypes.length) { Iterator<String> it = autowiredBeans.iterator(); for (int i = 0 ; i < paramTypes.length; i++) { String autowiredBeanName = it.next(); if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) { this .cachedMethodArguments[i] = new ShortcutDependencyDescriptor ( descriptors[i], autowiredBeanName, paramTypes[i]); } } } } } else { this .cachedMethodArguments = null ; } this .cached = true ; } } } if (arguments != null ) { try { ReflectionUtils.makeAccessible(method); method.invoke(bean, arguments); } catch (InvocationTargetException ex){ throw ex.getTargetException(); } } } private Object[] resolveCachedArguments(String beanName) { if (this .cachedMethodArguments == null ) { return null ; } Object[] arguments = new Object [this .cachedMethodArguments.length]; for (int i = 0 ; i < arguments.length; i++) { arguments[i] = resolvedCachedArgument(beanName, this .cachedMethodArguments[i]); } return arguments; } }
解析已缓存的方法参数或字段
为什么在这里 beanFactory.resolveDependency
需要的参数和未缓存时不一样啊?虽然内部会通过相同的方式获得 typeConverter
,但是很奇怪啊。
1 2 3 4 5 6 7 8 9 private Object resolvedCachedArgument (String beanName, Object cachedArgument) { if (cachedArgument instanceof DependencyDescriptor) { DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument; return this .beanFactory.resolveDependency(descriptor, beanName, null , null ); } else { return cachedArgument; } }
解析依赖 解析依赖的过程暂不深入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public Object resolveDependency (DependencyDescriptor descriptor, String requestingBeanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); if (javaUtilOptionalClass == descriptor.getDependencyType()) { return new OptionalDependencyFactory ().createOptionalDependency(descriptor, requestingBeanName); } else if (ObjectFactory.class == descriptor.getDependencyType() || ObjectProvider.class == descriptor.getDependencyType()) { return new DependencyObjectProvider (descriptor, requestingBeanName); } else if (javaxInjectProviderClass == descriptor.getDependencyType()) { return new Jsr330ProviderFactory ().createDependencyProvider(descriptor, requestingBeanName); } else { Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); if (result == null ) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 public Object doResolveDependency (DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); try { Object shortcut = descriptor.resolveShortcut(this ); if (shortcut != null ) { return shortcut; } Class<?> type = descriptor.getDependencyType(); Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null ) { if (value instanceof String) { String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null ); value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); return (descriptor.getField() != null ? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); } Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans != null ) { return multipleBeans; } Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null ; } String autowiredBeanName; Object instanceCandidate; if (matchingBeans.size() > 1 ) { autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null ) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { return descriptor.resolveNotUnique(type, matchingBeans); } else { return null ; } } instanceCandidate = matchingBeans.get(autowiredBeanName); } else { Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } if (autowiredBeanNames != null ) { autowiredBeanNames.add(autowiredBeanName); } return (instanceCandidate instanceof Class ? descriptor.resolveCandidate(autowiredBeanName, type, this ) : instanceCandidate); } finally { ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); } }
构建自动装配元数据的时机 你在 Debug
的时候也许会注意到,在第一次进入 postProcessPropertyValues
方法,查找自动装配元数据时,就已经是从缓存中获取的了。那么究竟是什么时候构建自动装配元数据并放入缓存的 呢?这就需要我们目前一直没有讲到的 MergedBeanDefinitionPostProcessor
派上用场了。在 postProcessMergedBeanDefinition
方法中,也调用了 findAutowiringMetadata
方法,这才是真正的第一次查找自动装配元数据。
1 2 3 4 5 6 7 8 public void postProcessMergedBeanDefinition (RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { if (beanType != null ) { InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null ); metadata.checkConfigMembers(beanDefinition); } }
那么 MergedBeanDefinitionPostProcessor
又是什么时候被调用的 呢?在 doCreateBean
方法中,创建实例后,填充属性前。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected Object doCreateBean (final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException (mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed" , ex); } mbd.postProcessed = true ; } } }
检测配置成员
configMember
这个命名不太理解,指的是通过配置实现注入的 Member
(Filed
和 Method
的父类)吗?
检测配置成员,如果不是外部管理的配置成员,则注册为外部管理的配置成员。在合并后的 bean
定义中,externallyManagedConfigMembers
保存了外部管理的配置成员,用于标记一个配置成员是外部管理的。例如当一个字段同时标注了 @Resource
和 @Autowired
注解,当 @Resouce
注解被处理后,该字段已经被标记,当 @Autowired
注解被处理时,就会跳过该字段,避免重复注入造成冲突。
这里的外部管理感觉有点指向依赖注入的控制反转思想。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void checkConfigMembers (RootBeanDefinition beanDefinition) { Set<InjectedElement> checkedElements = new LinkedHashSet <InjectedElement>(this .injectedElements.size()); for (InjectedElement element : this .injectedElements) { Member member = element.getMember(); if (!beanDefinition.isExternallyManagedConfigMember(member)) { beanDefinition.registerExternallyManagedConfigMember(member); checkedElements.add(element); if (logger.isDebugEnabled()) { logger.debug("Registered injected element on class [" + this .targetClass.getName() + "]: " + element); } } } this .checkedElements = checkedElements; }
如果不了解具体的场景,可能会比较难想象这个标记的用处是什么。
尽管 @Autowired
注解配合 @Value
注解可以很灵活,但是应尽量采取清晰明了的配置方式,让注入的结果一眼就能看出来。