Spring-依赖注入

news/2024/11/29 11:03:51/

Spring中到底有几种依赖注入的方式?

  • 手动注入

    a)set方式注入

    <bean name="userService" class="com.gax.service.UserService"><property name="orderService" ref="orderService"/>
    </bean>

    b)构造方法注入

    <bean name="userService" class="com.gax.service.UserService"><constructor‐arg index="0" ref="orderService"/>
    </bean>
  • 自动注入

    a)XML的autowire自动注入

    b) @Autowired注解的自动注入

属性填充部分源码:

/*** Populate the bean instance in the given BeanWrapper with the property values* from the bean definition.* @param beanName the name of the bean* @param mbd the bean definition for the bean* @param bw the BeanWrapper with bean instance*/
@SuppressWarnings("deprecation")  // for postProcessPropertyValues
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {if (bw == null) {if (mbd.hasPropertyValues()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");}else {// Skip property population phase for null instance.return;}}// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the// state of the bean before properties are set. This can be used, for example,// to support styles of field injection.// 实例化之后,属性设置之前if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return;}}}PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);int resolvedAutowireMode = mbd.getResolvedAutowireMode();if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {// MutablePropertyValues是PropertyValues具体的实现类MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// Add property values based on autowire by name if applicable.if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}// Add property values based on autowire by type if applicable.if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;}boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);PropertyDescriptor[] filteredPds = null;if (hasInstAwareBpps) {if (pvs == null) {pvs = mbd.getPropertyValues();}for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {// 这里会调用AutowiredAnnotationBeanPostProcessor的postProcessProperties()方法,会直接给对象中的属性赋值// AutowiredAnnotationBeanPostProcessor内部并不会处理pvs,直接返回了PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}}pvs = pvsToUse;}}if (needsDepCheck) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}checkDependencies(beanName, mbd, filteredPds, pvs);}// 如果当前Bean中的BeanDefinition中设置了PropertyValues,那么最终将是PropertyValues中的值,覆盖@Autowiredif (pvs != null) {applyPropertyValues(beanName, mbd, bw, pvs);}
}/*** Fill in any missing property values with references to* other beans in this factory if autowire is set to "byName".* @param beanName the name of the bean we're wiring up.* Useful for debugging messages; not used functionally.* @param mbd bean definition to update through autowiring* @param bw the BeanWrapper from which we can obtain information about the bean* @param pvs the PropertyValues to register wired objects with*/
protected void autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {// 当前Bean中能进行自动注入的属性名String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);// 遍历每个属性名,并去获取Bean对象,并设置到pvs中for (String propertyName : propertyNames) {if (containsBean(propertyName)) {Object bean = getBean(propertyName);pvs.add(propertyName, bean);// 记录一下propertyName对应的Bean被beanName给依赖了registerDependentBean(propertyName, beanName);if (logger.isTraceEnabled()) {logger.trace("Added autowiring by name from bean name '" + beanName +"' via property '" + propertyName + "' to bean named '" + propertyName + "'");}}else {if (logger.isTraceEnabled()) {logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +"' by name: no matching bean found");}}}
}/*** Abstract method defining "autowire by type" (bean properties by type) behavior.* <p>This is like PicoContainer default, in which there must be exactly one bean* of the property type in the bean factory. This makes bean factories simple to* configure for small namespaces, but doesn't work as well as standard Spring* behavior for bigger applications.* @param beanName the name of the bean to autowire by type* @param mbd the merged bean definition to update through autowiring* @param bw the BeanWrapper from which we can obtain information about the bean* @param pvs the PropertyValues to register wired objects with*/
protected void autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {TypeConverter converter = getCustomTypeConverter();if (converter == null) {converter = bw;}Set<String> autowiredBeanNames = new LinkedHashSet<>(4);// 当前Bean中能进行自动注入的属性名String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);for (String propertyName : propertyNames) {try {PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);// Don't try autowiring by type for type Object: never makes sense,// even if it technically is a unsatisfied, non-simple property.if (Object.class != pd.getPropertyType()) {MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);// Do not allow eager init for type matching in case of a prioritized post-processor.// eager表示立即初始化,表示在根据类型查找Bean时,允不允许进行Bean的创建,如果当前bean实现了PriorityOrdered,那么则不允许// 为什么不允许,因为我自己是PriorityOrdered,是优先级最高的,不能有比我创建得更早的boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);// 根据类型找到的结果Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);if (autowiredArgument != null) {pvs.add(propertyName, autowiredArgument);}for (String autowiredBeanName : autowiredBeanNames) {registerDependentBean(autowiredBeanName, beanName);if (logger.isTraceEnabled()) {logger.trace("Autowiring by type from bean name '" + beanName + "' via property '" +propertyName + "' to bean named '" + autowiredBeanName + "'");}}autowiredBeanNames.clear();}}catch (BeansException ex) {throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);}}
}/*** Return an array of non-simple bean properties that are unsatisfied.* These are probably unsatisfied references to other beans in the* factory. Does not include simple properties like primitives or Strings.* @param mbd the merged bean definition the bean was created with* @param bw the BeanWrapper the bean was created with* @return an array of bean property names* @see org.springframework.beans.BeanUtils#isSimpleProperty*/
protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {Set<String> result = new TreeSet<>();PropertyValues pvs = mbd.getPropertyValues();PropertyDescriptor[] pds = bw.getPropertyDescriptors();// 什么样的属性能进行自动注入?// 1.该属性有对应的set方法// 2.没有在ignoredDependencyTypes中// 3.如果该属性对应的set方法是实现的某个接口中所定义的,那么接口没有在ignoredDependencyInterfaces中// 4.属性类型不是简单类型,比如int、Integer、int[]for (PropertyDescriptor pd : pds) {if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&!BeanUtils.isSimpleProperty(pd.getPropertyType())) {result.add(pd.getName());}}return StringUtils.toStringArray(result);
}

XML的autowire自动注入

<bean id="userService" class="com.gax.service.UserService" autowire="byType"/>
// Bean的自动注入模式包括:byType、byName、constructor、default、no

注意:上面这种写法Spring会自动给所有属性赋值,不需要在属性上添加@Autowired注解,但是需要属性对应的set方法

get方法的定义: 方法参数个数为0个,并且(方法名字以"get"开头或者方法名字以"is"开头并且方法的返回类型为boolean)

set方法的定义:方法参数个数为1个,并且(方法名字以"set"开头并且方法返回类型为void)

在创建Bean的过程-填充属性时,Spring会去解析当前类,把当前类的所有方法都解析出来,解析每个方法得到对应的PropertyDescriptor对象(属性描述器)

public class PropertyDescriptor extends FeatureDescriptor 
{// get方法对应返回值类型,set方法对应唯一参数类型private Reference<? extends Class<?>> propertyTypeRef;// get方法的Method对象的引用private final MethodRef readMethodRef = new MethodRef();// set方法的Method对象的引用private final MethodRef writeMethodRef = new MethodRef();private Reference<? extends Class<?>> propertyEditorClassRef;private boolean bound;private boolean constrained;// The base name of the method name which will be prefixed with the// read and write method. If name == "foo" then the baseName is "Foo"private String baseName;// set方法的名字private String writeMethodName;// get方法的名字private String readMethodName;... ...
}

Spring通过byName自动填充属性流程:

1、找到所有set方法所对应的XXX部分的名字

(public void setXXX(XXX xxx){ ... ...})

2、根据XXX部分的名字去获取bean

Spring通过byType自动填充属性流程:

1、获取到set方法中的唯一参数的类型,并且根据该类型去容器中获取bean

(public void setXXX(XXX xxx){ ... ...})

2、如果找到多个会报错

Spring通过constructor自动填充属性流程:因为推断构造方法,所以只考虑一个有参构造方法的情况

1、利用构造方法的参数信息从Spring容器中去找bean

2、找到bean之后作为参数传给构造方法,从而实例化得到一个bean对象,并完成属性赋值

(属性赋值的代码得程序员来写,this.XXX = XXX)

构造方法注入相当于byType+byName,普通的byType是根据set方法中的参数类型去找bean,找到多个会报错,而constructor就是通过构造方法中的参数类型去找bean,如果找到多个会根据参数名确定。

no,表示关闭autowire

default,表示默认值<bean> 标签可以定义在<beans>里面,此时设置<bean>的autowire属性为default,就会去使用<beans>标签中设置的注入方式,作为默认

为什么平时都是用的@Autowired注解?而没有用XML自动注入方式呢?

官方描述:

Essentially, the @Autowired annotation provides the same capabilities as described in Autowiring Collaborators but with more fine‐grained control and wider applicability

翻译:从本质上讲,@Autowired注解提供了与autowire相同的功能,但是拥有更细粒度的控制和更广泛的适用性。

XML中的autowire控制的是整个bean的所有属性,而@Autowired注解是直接写在某个属性、某个set方法、某个构造方法上的。

@Autowired注解的自动注入

@Autowired注解,是byType和byName的结合

@Autowired注解可以写在:

1、属性上:先根据属性类型去找Bean,如果找到多个再根据属性名确定一个

2、构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个

3、set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个

底层用到:属性注入、set方法注入、构造方法注入


http://www.ppmy.cn/news/1200285.html

相关文章

R语言_RColorBrewer包--全平台可用

R语言_RColorBrewer包–全平台可用

【RabbitMQ】RabbitMQ 集群的搭建 —— 基于 Docker 搭建 RabbitMQ 的普通集群,镜像集群以及仲裁队列

文章目录 一、集群分类1.1 普通模式1.2 镜像模式1.3 仲裁队列 二、普通集群2.1 目标集群2.2 获取 Erlang Cookie2.3 集群配置2.4 启动集群2.5 测试集群 三、镜像模式3.1 镜像模式的特征3.2 镜像模式的配置3.2.1 exactly 模式3.2.2 all 模式3.2.3 nodes 模式 3.3 测试镜像模式 四…

CDN加速:国内外价格与企业云服务最佳搭配方案

随着互联网的快速发展&#xff0c;CDN&#xff08;内容分发网络&#xff09;已经成为了企业提供高质量、高速度内容传递的不可或缺的工具。CDN通过将内容分发到离用户更近的服务器上&#xff0c;提高了网站性能&#xff0c;减少了加载时间&#xff0c;改善了用户体验。在本文中…

SMART PLC开放式以太网通信(UDP通信)

西门子S7-200 SMART PLC不仅支持开放式以太网通信,还支持MODBU-RTU,以及ModbusTcp通信,详细内容请参考下面文章: MODBUS-RTU主站通信 【精选】PLC MODBUS通信优化、提高通信效率避免权限冲突(程序+算法描述)-CSDN博客文章浏览阅读2.5k次,点赞5次,收藏10次。MODBUS通讯…

windows下使用FCL(Flexible-collision-library)

windows下使用FCL&#xff08;The Flexible-collision-library&#xff09; FCL做为一款开源的碰撞检测库&#xff0c;支持多种基础的几何体&#xff0c;及支持C和python&#xff0c;在windows和linux平台均可以使用。是一款计算高效的碰撞检测工具。在机械臂规划控制框架movei…

Ceph文件存储

1、存储基础 //单机存储设备 ●DAS&#xff08;直接附加存储&#xff0c;是直接接到计算机的主板总线上去的存储&#xff09; IDE、SATA、SCSI、SAS、USB 接口的磁盘 所谓接口就是一种存储设备驱动下的磁盘设备&#xff0c;提供块级别的存储 ●NAS&#xff08;网络附加存储&…

2024济南高企认定从何认定?

企业申请认定时须注册成立一年以上&#xff1b; 注意事项&#xff1a; “须注册成立一年以上”是指企业注册成立365个日历天数以上。 相关证明材料&#xff1a; 证明事项告知承诺书或营业执照。 02企业通过自主研发、受让、受赠、并购等方式&#xff0c;获得对其主要产品&…

一键同步chromedriver版本

ChromeDriver是一个控制Chrome浏览器的驱动程序&#xff0c;它和Selenium一起被广泛用于Web自动化测试。然而&#xff0c;随着Chrome版本的升级&#xff0c;我们需要不断更新ChromeDriver以保持其与Chrome的兼容性。这个过程既费时又繁琐&#xff0c;而且对于非技术人员来说可能…