Spring依赖注入详解
Posted Hide on jdk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring依赖注入详解相关的知识,希望对你有一定的参考价值。
1.set注入
启动容器后看看到底能不能拿到teacherService的值。可以看到拿到了值。我们具体来分析怎么注入的
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
发现pvs里面有一个我们自己set的值
直接进行属性赋值。
org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveValueIfNecessary
org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReference
直接从容器中获取,然后进行属性赋值
真正进行属性赋值:
org.springframework.beans.AbstractPropertyAccessor#setPropertyValues(org.springframework.beans.PropertyValues, boolean, boolean)
问题1:如果set方法乱写可以注入吗?
不行,会报错,我们看看具体在哪里报错
org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue(org.springframework.beans.AbstractNestablePropertyAccessor.PropertyTokenHolder, org.springframework.beans.PropertyValue)
这里会报错
会将set后面的方法去掉set,然后首字母小写放入缓存中,来获取对应的注入值,如果找不到直接报错。
如果是这样会报错吗?不会,底层拿到set方法然后进行反射,故set方法和注入的属性一直就不会报错。
如下就不会报错:
Spring 依赖注入(DI)详解 [Spring][依赖注入的 6 种实现方式][setter注入][构造器注入][注解注入][自动装配注入][静态工厂注入][实例工厂注入]
您的“关注”和“点赞”,是信任,是认可,是支持,是动力......
如意见相佐,可留言。
本人必将竭尽全力试图做到准确和全面,终其一生进行修改补充更新。
1 依赖注入概述
依赖注入,英文叫做 Dependency Injection
,简称 DI
。
DI 和 IoC (《Spring IoC 容器详解》)含义相同,它们是从两个角度描述的同一个概念、做同一件事情。
当某个 Java 实例需要另一个 Java 实例时,使用 Spring 之前都是由调用者创建(使用 new 关键字获得被调用者实例)被调用者的实例,而使用 Spring 框架后,被调用者的实例不再由调用者创建,而是由 Spring IoC 容器创建,这称为控制反转,也即 IoC。
Spring IoC 容器在创建被调用者的实例时,会自动将调用者需要的对象实例注入给调用者,这样,调用者通过 Spring IoC 容器获得被调用者实例,这称为依赖注入,也即 DI。
Bean 的依赖注入方式,或叫 Bean 的装配方式,有多种形式,比如接下来要介绍的基于 XML 的依赖注入(有两种实现方式:Setter Injection 设置注入和 Constructor Injection 构造器注入)、基于 Annotation 的依赖注入、基于自动装配、基于静态工厂的方式的依赖注入和基于实例工厂的方式的依赖注入等。
2 依赖注入的多种方式(Bean 的装配方式)
2.1 基于 XML 的依赖注入
2.1.1 Setter Injection(设置方法注入)
基于设置方法注入,也可以叫做setter
方法注入,这是最简单的注入方式。
指 IoC 容器使用 setter 方法注入被依赖的实例。通过调用无参构造器或无参 static 工厂方法实例化 bean 后,调用该 bean 的 setter 方法,即可实现基于 setter 的 DI。
案例实操,走你!
目的:在UserService
接口的实现类UserServiceImpl
中用setter
方法初始化UserDao
的对象。
具体步骤如下所示,
第一步:在com.manongajie.service
包下创建UserService
接口。如下图所示:
第二步:在com.manongajie.serviceimpl
包下创建接口UserService
的实现类UserServiceImpl
。在类中声明userDao
变量,但是没有初始化它,这里就必须要 setter
方法(是 IoC 容器的注入入口。相当于 IoC 容器调用 setter 方法初始化 userDao 变量)了,如下图所示:
第三步:在 applicationContext.xml
配置文件中添加配置信息。<property>
标签中的 name
属性值为setXxx()
方法的参数(也叫依赖项);ref
属性值为 Bean 的name
或id
值。添加内容,如下图所示:
第四步:编写测试类并使用JUnit 测试运行。在com.manongajie.test
包下的SpringTest
类中创建test2()
方法。如下图所示:
从上图中可以看出,使用 Spring IoC 容器获取 userService
的实例后,调用了该实例的 addUser()
方法,在该方法中又调用了 UserDao
接口的实现类 UserDaoImpl
中的 save()
方法(userDao 对象就是 IoC 容器自动调用 setter 方法注入的。本例中 setter 方法为 setUserDao() 方法)。
2.1.2 Constructor Injection(构造器注入)
指 IoC 容器使用构造方法注入被依赖的实例。基于构造器的 DI 通过调用带参数的构造方法(本文本小节以带参数的构造器进行介绍)实现,每个参数代表一个依赖,Spring 容器会根据 bean 中指定的构造方法参数来决定调用哪个构造函数。
案例实操,走你!
目的:在UserDaoImpl
类中创建成员变量userService
,使用UserDaoImpl
类的构造器注入userService
实例,也就是说 spring 容器在创建 UserDaoImpl
类的实例时,也要将 userService
实例作为构造器的参数值传到UserDaoImpl
类中(配置文件中操作,使用<constructor-arg>
标签,ref
属性表示 Bean 的 id)。
具体步骤如下所示,
第一步:直接在之前的项目中操作,在UserDaoImpl
类中创建一个成员变量userService
,构造器UserService(UserService userService)
,如下图所示:
第二步:配置配置文件,如下图所示:
第三步:编写测试类和JUnit 测试运行。如下图所示:
从上图中可以看出,通过 Spring 容器获取UserDao
的实例后,调用了该实例的save()
方法,在该方法中又调用了UserService
接口的实现类UserServiceImpl
中的addUser()
方法(userService 对象就是 spring 容器通过构造器注入的。)
2.2 基于 Annotation(注解)的依赖注入
2.2.1 基于 Annotation(注解)的依赖注入概述
如果应用中 Bean
的数量较多,基于 XML 配置文件实现 Bean 的装配会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。
基于 Annotation(注解)的依赖注入就可以解决这个问题。
2.2.2 常用注解概述
-
@Component
可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。 -
@Repository
用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 -
@Service
通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 -
@Controller
通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 -
@Autowired(在本文 《2.3 基于自动装配的依赖注入》 小节详细介绍)
用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。 -
@Resource
其作用与 Autowired 一样。其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。@Resource 中有两个重要属性:name 和 type。
Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。
如果指定 name 属性,则按实例名称进行装配;
如果指定 type 属性,则按 Bean 类型进行装配。
如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出NoSuchBeanDefinitionException
异常。 -
@Qualifier
与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。
2.3.3 案例实操,走起
第一步:导入关键 JAR 包。然后创建 DAO 层接口,在com.manongajie.dao
包下创建PersonDao
接口,并添加add()
方法。如下图所示:
第二步:创建 DAO 层接口的实现类。在com.manongajie.daoimpl
包下创建PersonDao
接口的实现类PersonDaoImpl
,并添加 DAO 层 add()
方法。对类使用@Repository
注解将 PersonDaoImpl
类标识为 Spring 中的 Bean,其写法相当于配置文件中 <bean id="personDao" class="com.manongajie.daoimpl.PersonDaoImpl"/>
的书写。如下图所示:
第三步:创建 Service 层接口。图下图所示:
第四步:创建 Service 层接口的实现类。如下图所示:
第五步:创建 Action 控制层。如下图所示:
第六步:创建 Spring 配置文件。需要添加更多的约束文件,如何把约束文件交给 Eclipse 管理,请参见博文《开启 Spring 之旅:第一个 Spring 程序 !》。
第七步:创建测试类和 JUnit 运行测试。如下图所示:
从上图中可以看出,DAO 层、Service 层和 Action 层的 add()
方法都成功输出了结果。
So,使用 Annotation 装配 Bean 的方式已经成功实现了。
2.3 基于自动装配的依赖注入
本文篇幅太长了,另写一文。
请参见博文《Spring 基于自动装配的依赖注入详解》。
2.4 静态工厂注入
需要提供一个静态工厂方法 createBean()
,创建 Bean 的实例(是指createBean()
方法返回哪个类的实例,就创建哪个类的实例)。
需要在配置文件中,使用 <bean>
标签的 factory-method
属性,用于告诉 Spring 容器调用工厂类中的 createBean()
方法获取 Bean 的实例。
案例演示,如下所示:
第一步:创建实体类 Person
。在com.manongajie.static_factory
包下创建。
第二步:创建静态工厂类MyBeanFactory
,并在类中创建createBean()
静态方法,用于创建 Bean 的实例。
第三步:创建配置文件。
第四步:创建测试类和JUnit 测试运行。
从以上运行结果可以看出,使用静态工厂的方式也成功对 Bean 进行了实例化。
2.5 实例工厂注入
使用这种方式,工厂类不再使用静态方法创建 Bean 的实例,而是直接在成员方法中创建 Bean 的实例。
也就是说需要一个成员方法createBean()
来创建 Bean 的实例。
在配置文件中,需要实例化的 Bean 也不是通过 class 属性直接指向其实例化的类,而是通过 factory-bean
属性配置一个实例工厂,然后使用 factory-method
属性确定使用工厂中的哪个方法。
案例演示,如下所示:
第一步:创建实体类 Person
,并在类中添加say()
方法。
第二步:创建实例工厂类,并添加成员方法createBean()
。
第三步:创建配置文件。
第四步:创建测试类和JUnit 测试运行。
从以上运行结果可以看出,使用实例工厂的方式也是可以对 Bean 进行实例化的哦。。
【文章其他地址】
微信公众号:码农阿杰
CSDN 博客
【参考资料】
spring
Artifact Repository Browser
Apache Commons
以上是关于Spring依赖注入详解的主要内容,如果未能解决你的问题,请参考以下文章
Java开发Spring之IOC详解第一篇(xml开发常用APIben标签DI依赖注入)
Spring -- Spring配置文件详解(Bean实例化的三种方式IoC(控制反转) 与 DI(依赖注入)依赖注入详解)