Spring常用的三种依赖注入方式

柔情只为你懂 2023-07-01 13:59 44阅读 0赞

资料来源:

1、https://blog.csdn.net/a745233700/article/details/80959716

2、https://blog.csdn.net/u010648555/article/details/76299467

1 问题由来

1、传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象,这种开发方式存在的问题是new出来的类实例不好统一管理,实例化多个对象,容易造成内存空间的浪费。

2、spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。这样可以减低对象之间的耦合性,开发出松耦合、扩展性强的程序。

2 Spring常用的三种依赖注入方式

Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:

(1)基于注解的注入。

(2)构造方法注入。

(3)setter注入,

1、创建一个模拟DAO层类:UserTestDao。用于测试依赖注入。其源码如下:

  1. public class UserTestDao {
  2. public boolean login(){
  3. System.out.println("UserTestDao.login()");
  4. return true;
  5. }
  6. }

2.1 基于注解的注入-自动装配模式

2.1.1 bean标签的autowire属性

在介绍注解注入的方式前,先了解bean的一个属性autowire。

autowire属性有5种模式:

(1)no

(默认)不采用autowire机制.。这种情况,当我们需要使用依赖注入,只能用标签。

(2)byName

通过属性的名称自动装配(注入)。Spring会在容器中查找名称与bean属性名称一致的bean,并自动注入到bean属性中。当然bean的属性需要有setter方法。例如:bean A有个属性master,master的setter方法就是setMaster,A设置了autowire=”byName”,那么Spring就会在容器中查找名为master的bean通过setMaster方法注入到A中。

(3)byType

通过类型自动装配(注入)。Spring会在容器中查找类(Class)与bean属性类一致的bean,并自动注入到bean属性中,如果容器中包含多个这个类型的bean,Spring将抛出异常。如果没有找到这个类型的bean,那么注入动作将不会执行。

(4)constructor

类似于byType,但是是通过构造函数的参数类型来匹配。假设bean A有构造函数A(B b, C c),那么Spring会在容器中查找类型为B和C的bean通过构造函数A(B b, C c)注入到A中。与byType一样,如果存在多个bean类型为B或者C,则会抛出异常。但时与byType不同的是,如果在容器中找不到匹配的类的bean,将抛出异常,因为Spring无法调用构造函数实例化这个bean。

(5)default

采用父级标签(即beans的default-autowire属性)的配置。

2.1.2 使用注解注册bean

有四种注解可以注册bean,每种注解可以任意使用,只是语义上有所差异:

(1)@Component:可以用于注册所有bean

(2)@Repository:主要用于注册dao层的bean

(3)@Controller:主要用于注册控制层的bean

(5)@Service:主要用于注册服务层的bean

这些都是注解在平时的开发过程中出镜率极高,@Component、@Repository、@Service、@Controller实质上属于同一类注解,用法相同,功能相同,区别在于标识组件的类型。@Component可以代替@Repository、@Service、@Controller,因为这三个注解是被@Component标注的。

2.1.3 装配bean时常用的注解

2.1.3.1 @Resource

1、 @Resource是默认以byName的方式去匹配与属性名相同的bean的id。

如果没有找到就会以byType的方式查找,如果byType查找到多个的话,使用@Qualifier注解(spring注解)指定某个具体名称的bean。

2、 @Resource不属于spring的注解,而是Java的注解,它来自于JSR-250位于java.annotation包下,使用该annotation为目标bean指定协作者Bean。

- @Resource注解的定义:

  1. @Target({TYPE, FIELD, METHOD})
  2. @Retention(RUNTIME)
  3. public @interface Resource {
  4. String name() default "";
  5. Class type() default java.lang.Object.class;
  6. ...

使用demo

  1. public class AnotationExp {
  2. @Resource(name = "HappyClient")
  3. private HappyClient happyClient;
  4. @Resource(type = HappyPlayAno.class)
  5. private HappyPlayAno happyPlayAno;
  6. }

2.1.3.2 @Autowired

1、使用@Autowired注解来自动装配指定的bean。

2、在使用@Autowired注解之前需要在Spring配置文件进行配置,。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

- 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;

- 如果查询的结果不止一个,那么@Autowired会根据名称来查找;

- 如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

3、@Autowired属于Spring 的org.springframework.beans.factory.annotation包下,可用于为类的属性、构造器、方法进行注值。

- @Autowired注解的定义:

  1. @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Autowired {
  5. boolean required() default true;
  6. }

使用demo:

  1. @Controller
  2. public class HappyController {
  3. @Autowired //默认依赖的ClubDao 对象(Bean)必须存在
  4. //@Autowired(required = false) 改变默认方式
  5. @Qualifier("goodClubService")
  6. private ClubService clubService;
  7. // Control the people entering the Club
  8. // do something
  9. }

2.1.3.3 @Autowired和@Resource的总结

1、相同点

@Resource的作用相当于@Autowired,均可标注在字段或属性的setter方法上。

2、不同点

(1)提供方

@Autowired是Spring的注解;@Resource是javax.annotation注解,而是来自于JSR-250,J2EE提供,需要JDK1.6及以上。

(2)注入方式

@Autowired只按照Type 注入;@Resource默认按Name自动注入,也提供按照Type 注入;

(3)属性

@Autowired注解可用于为类的属性、构造器、方法进行注值。

默认情况下,其依赖的对象必须存在(bean可用),如果需要改变这种默认方式,可以设置其required属性为false。

还有一个比较重要的点就是,@Autowired注解默认按照类型装配,如果容器中包含多个同一类型的Bean,那么启动容器时会报找不到指定类型bean的异常,解决办法是结合@Qualified注解进行限定,指定注入的bean名称。

@Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。

需要注意的是:

- @Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。

- @Resource注解的使用性更为灵活,可指定名称,也可以指定类型 ;

- @Autowired注解进行装配容易抛出异常,特别是装配的bean类型有多个的时候,而解决的办法是需要在增加@Qualitied进行限定。

2.2 构造方法注入

创建一个模拟Service类:UserTestServiceImpl。源码如下:

  1. public class UserTestServiceImpl {
  2. private UserTestDao userTestDao;
  3. /**
  4. * 如果只有一个有参数的构造方法并且参数类型与注入的bean的类型匹配,那就会注入到该构造方法中。
  5. *
  6. * @param userTestDao
  7. */
  8. public UserTestServiceImpl(UserTestDao userTestDao) {
  9. this.userTestDao = userTestDao;
  10. }
  11. public void login() {
  12. System.out.println("UserTestServiceImpl.login()");
  13. userTestDao.login();
  14. }
  15. }

1、在spring的配置文件中注册UserTestServiceImpl,将UserTestDao 通过constructor-arg标签注入到UserTestServiceImpl的某个有参数的构造方法。spring配置如下:

  1. <!--dao -->
  2. <bean id="userTestDao" class="com.chenlw.java.web.utils.springwiki.UserTestDao">
  3. </bean>
  4. <!-- 注册userService -->
  5. <bean id="userTestServiceImpl" class="com.chenlw.java.web.utils.springwiki.UserTestServiceImpl">
  6. <!-- 将DAO对象注入Service层 -->
  7. <constructor-arg ref="userTestDao"/>
  8. </bean>

2、测试代码:

  1. @Test
  2. public void testSpringInjection() {
  3. // 获取Service实例
  4. UserTestServiceImpl userTestService = (UserTestServiceImpl) applicationContext.getBean("userTestServiceImpl");
  5. // 模拟业务方法
  6. userTestService.login();
  7. }

3、运行结果:

  1. UserTestServiceImpl.login()
  2. UserTestDao.login()

2.3 setter注入

1、修改UserTestServiceImpl类。源码如下:

  1. public class UserTestServiceImpl {
  2. private UserTestDao userTestDao;
  3. public UserTestDao getUserTestDao() {
  4. return userTestDao;
  5. }
  6. /**
  7. * setter注入,该setter方法不可缺少,否则会注入失败。
  8. */
  9. public void setUserTestDao(UserTestDao userTestDao) {
  10. this.userTestDao = userTestDao;
  11. }
  12. public void login() {
  13. System.out.println("UserTestServiceImpl.login()");
  14. userTestDao.login();
  15. }
  16. }

2、spring配置如下:

  1. <!-- 注册dao -->
  2. <bean id="userTestDao" class="com.chenlw.java.web.utils.springwiki.UserTestDao">
  3. </bean>
  4. <!-- 注册userService -->
  5. <bean id="userTestServiceImpl" class="com.chenlw.java.web.utils.springwiki.UserTestServiceImpl">
  6. <!-- 将DAO对象注入Service层 -->
  7. <property name="userTestDao" ref="userTestDao"/>
  8. </bean>

注意:

- spring会将name值的每个单词首字母转换成大写,然后再在前面拼接上”set”构成一个方法名,然后去对应的类中查找该方法,通过反射调用,实现注入。

- name属性值与类中的成员变量名以及set方法的参数名都无关,只与对应的set方法名有关。

3、测试代码:

  1. @Test
  2. public void testSpringInjection() {
  3. // 获取Service实例
  4. UserTestServiceImpl userTestService = (UserTestServiceImpl) applicationContext.getBean("userTestServiceImpl");
  5. // 模拟业务方法
  6. userTestService.login();
  7. }

4、运行结果:

  1. UserTestServiceImpl.login()
  2. UserTestDao.login()

发表评论

表情:
评论列表 (有 0 条评论,44人围观)

还没有评论,来说两句吧...

相关阅读