Spring注入方式
Spring有以下两种注入方式:
手动注入
在XML中界说Bean时, 就是手动注入,因为步伐员手动给某个属性指定了值
- <bean id="orderService" class="com.fanqiechaodan.service.OrderService"></bean>
- <bean id="userService" class="com.fanqiechaodan.service.UserService" >
- <property name="orderService" ref="orderService"/>
- </bean>
复制代码 上面这种方式底层就是通过 set方法 举行注入,UserService里面必须要有orderService的set方法.没有会报错
- <bean id="orderService" class="com.fanqiechaodan.service.OrderService"></bean>
- <bean id="userService" class="com.fanqiechaodan.service.UserService" >
- <constructor-arg index="0" ref="orderService"/>
- </bean>
复制代码 上面这种底层通过 构造方法 举行注入,UserService里面要有对应的构造方法,没有会报错
以是手动注入的底层也就是分为两种:
主动注入
主动注入分为以下两种:
- XML的autowire主动注入
- @Autowired注解的主动注入
XML的autowire主动注入
在XML中,可以界说一个Bean时去指定这个Bean的主动注入模式:
- byType
- byName
- constructor
- default
- no
- <bean id="orderService" class="com.fanqiechaodan.service.OrderService"></bean>
- <bean id="userService" class="com.fanqiechaodan.service.UserService" autowire="byType"></bean>
复制代码 这么写,表现Spring会主动给UserService中所有属性主动赋值,不必要这个属性上有@Autowired注解,但必要这个属性有对应的set方法
在创建Bean的过程中,在添补属性时,Spring回去解析当前类,把当前类的所有方法都解析出来,Spring会去解析每个方法得到对应的PropertyDescriptor对象,PropertyDescriptor中有几个属性:
- name:这个name并不是方法的名字,而是拿方法名字举行处理惩罚后的
- 如果方法名字以"get"开头,比方"getXXX",name=XXX
- 如果方法名字以"is"开头,比方"isXXX",name=XXX
- 如果方法名字以"set"开头,比方"setXXX",name=XXX
- readMethodRef:表现get方法的Method
- readMethodName:表现get方法的名字
- writeMethodRef:表现set方法的Method
- writeMethodName:表现set方法的名字
- propertyTypeRef: 如果有get方法对应的就是返回值的范例,如果是set方法对应的就是set方法中唯一参数的范例
get方法的界说是:方法参数个数为0,并且方法名字以get开头或者方法名字以is开头并且方法返回值范例为boolean
set方法的界说是:方法参数个数为1,并且方法名字以set开头方法返回范例为void
以是,Spring在通过byName的主动添补属性时流程是:
- 找到所有set方法所对应的XXX部分的名字
- 根据名字去获取Bean
Spring在通过byType的主动添补属性时流程是:
- 获取到set方法中唯一参数的参数范例,并且根据该范例去容器中获取Bean
- 如果找到多个,会报错
如果是constructor,那么就可以不写set方法了,当某个Bean是通过构造方法来注入时,Spring使用构造方法参数信息从Spring容器中去找Bean,找到Bean之后作为参数传给构造方法,从而实例化得到一个Bean对象,并完成属性赋值,其实构造方法注入相称于byType+byName,平凡的byType是根据set方法中的参数范例去找Bean.找到多个会报错,而constructor就是通过构造方法中的参数范例去找Bean,如果找到多个会根据参数名确定.
别的两个:
- no:关闭autowire
- default:默认值,如果在<bean>设置了autowire为default,则会使用<beans>中设置的autowire
@Autowired注解的主动注入
从本质上讲,@Autowired注解提供了与autowire相同的功能,但是拥有 更细粒度的控制 和广泛的实用性
XML中的autowire控制的是整个Bean的所有属性,而@Autowired注解是直接写在某个属性,某个set方法,某个构造方法上的.如果一个类有多个构造方法,XML的autowire=constructor,无法指定用谁人构造方法,用@Autowired注解可以指定构造方法.
同时,使用@Autowired注解,还可以控制,那些属性想被主动注入,那些属性不想,这也是细粒度的控制.
但是@Autowired无法区分byType和byName,@Autowired是先byType,如果找到多个再byName
@Autowired注解可以写在:
- 属性上(属性注入):先根据 属性范例 去找Bean,如果找到多个再根据 属性名 确定一个
- set方法上(set方法注入):先根据方法 参数范例 去找Bean,如果找到多个再根据 参数名 确定一个
- 构造方法上(构造方法注入): 先根据方法 参数范例 去找Bean,如果找到多个再根据 参数名 确定一个
寻找注入点
在创建一个Bean的过程中,Spring会使用org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition找到注入点并缓存,找注入点的流程为:
- 遍历当前类的所有属性字段Field
- 检察字段上是否存在@Autowired,@Value中的任意一个存在,则以为该字段是一个注入点
- 如果字段是static关键字修饰的,则不举行注入
- 获取@Autowired中required的值
- 将字段信息构造成一个 AutowiredFieldElement 对象,作为一个 注入点 对象添加到currElements中
- 遍历当前类所有的方法Method
- 判定当前Method是否是 桥接方法,如果是找到原方法
- 检察方法上是否存在@Autowired,@Value中的任意一个存在,则以为该字段是一个注入点
- 如果方法是static关键字修饰的,则不举行注入
- 获取@Autowired中的required属性的值
- 将方法信息构造成一个 AutowiredMethodElement 对象,作为一个 注入点 对象天机道currElements中
- 遍历完当前类的方法和字段后, 将 遍历父类 的,知道没有父类
- 末了将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对应的注入点集合对象,并缓存
对应的源码实现
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
- @Override
- public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
- InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
- metadata.checkConfigMembers(beanDefinition);
- }
复制代码 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata
- 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;
- }
复制代码 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata
- private InjectionMetadata buildAutowiringMetadata(final 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);
- }
复制代码 static的字段或方法为什么不支持?
再Spring中,依赖注入重要针对对象级别的依赖关系举行管理,即在对象创建时通过构造函数,Setter方法或者字段注入等方式将依赖项注入到对象中.而静态字段属于类级别的属性,通常用于生存类级别的状态或者常量值,而不是对象之间的依赖关系.同样静态方法同样属于类级别的方法,它们可以直接通过类名调用,而不必要创建对象的实例.因此Spring的设计理念重要集中在对象之间的依赖关系管理,而对于静态字段/方法并不是其重要关注点.不支持static的字段/方法举行注入.
注入点举行注入
Spring在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties方法中,遍历所找到的注入点,依次举行注入.
字段注入
- 遍历所有的 AutowiredFieldElement对象
- 查找当前是否存在缓存的 cachedFieldValue,存在使用缓存,不存在解析字段
- 将对应的字段封装成 DependencyDescriptor对象
- 调用beanFactory.resolveDependency方法,传入 DependencyDescriptor对象,举行依赖查找,找到当前字段匹配的Bean对象
- 将 DependencyDescriptor对象 和所找到的 效果对象beanName 封装成一个 ShortcutDependencyDescriptor对象 作为缓存,好比如果当前Bean是原型Bean,那么下次再来创建Bean时,就可以直接拿缓存的效果对象beanName去BeanFactory中去拿bean对象了,不消再次举行查找了
- 使用反射将效果对象赋值给字段
set方法注入
- 遍历所有的 AutowiredMethodElement
- 获取方法参数数组,如果缓存有就从缓存中获取,缓存没有就解析方法参数
- 遍历方法参数,将每个参数封装成 MethodParameter对象
- 将 MethodParameter对象 封装成 DependencyDescriptor对象
- 调用beanFactory.resolveDependency,传入 DependencyDescriptor对象,举行依赖查找,找到当火线法参数所匹配的Bean对象
- 将 DependencyDescriptor对象 和所找到的 效果对象beanName 封装成一个 ShortcutDependencyDescriptor对象 作为缓存,好比如果当前Bean是原型Bean,那么下次再来创建Bean时,就可以直接拿缓存的效果对象beanName去BeanFactory中去拿bean对象了,不消再次举行查找了
- 使用反射将找到的所有用果对象传给当火线法,并执行
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |