Spring管理Bean-IOC-05

3.基于注解配置bean

3.3自动装配

基本说明:

  1. 基于注解配置bean,也可以实现自动装配,使用的注解是:@AutoWired或者@Resource

  2. @AutoWired 的规则说明

    (1)在IOC容器中查找待装配的组件的类型,如果有唯一的bean装配(按类型),则使用该bean装配

    (2)如果待装配的类型对应的bean在IOC容器中有多个,则使用待装配的属性的属性名作为id值进行查找,找到就装配,找不到就抛异常

  3. @Resource 的规则说明

    (1)@Resource 有两个属性比较重要,分别是name和type

    Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType的自动注入策略

    (2)如果@Resource没有指定name或者type,则先使用ByName注入策略,如果匹配不上,再使用byType策略,如果都不成功就会报错

  4. 不管是@AutoWired 还是 @Resource,都保证属性名是规范的写法就可以注入。

  5. 除了有特殊要求,一般推荐使用@Resource

3.3.1应用实例1-@AutoWired

应用实例需求:

  1. 以Action、Service、Dao几个组件来进行演示
  2. 这里演示UserAction和UserService的两级自动组装

UserService:

package com.li.component;

import org.springframework.stereotype.Service;

/**
* @author 李
* @version 1.0
* @Service 标识该类是一个Service类/对象
*/
@Service
public class UserService {
public void hi(){
System.out.println("UserService hi()...");
}
}

UserAction:

package com.li.component;

import org.springframework.stereotype.Controller;

/**
* @author 李
* @version 1.0
* @Controller 标识该类是一个控制器Controller,通常该类是一个Servlet
*/
@Controller
public class UserAction {
private UserService userService; public void sayOk() {
System.out.println("UserAction 的 sayOk()");
userService.hi();
}
}

beans05.xml指定要扫描的包:

<context:component-scan base-package="com.li.component"/>

测试类:

//通过注解来配置Bean
@Test
public void setProByAutowired() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.xml");
UserAction userAction = ioc.getBean("userAction", UserAction.class);
System.out.println("userAction=" + userAction);
userAction.sayOk();
}

如下,当运行到userAction.sayOk();时抛出空指针异常:

这是因为userAction中的属性userService指向null,即没有正确的装配UserService对象。

添加@Autowired注解:

在IOC容器中查找待装配的组件的类型,如果有唯一的bean装配(按照类型组装),则使用该bean装配

@Controller
public class UserAction {
//在IOC容器中查找待装配的组件的类型,如果有唯一的bean装配(按类型),则使用该bean装配
@Autowired
private UserService userService; public void sayOk() {
System.out.println("UserAction 的 sayOk()");
userService.hi();
}
}

运行结果如下,成功调用了userService.hi()方法,说明userService对象已经成功装配:


现在我们在beans05.xml容器中再配置两个UserService对象

spring允许同时使用xml配置文件和注解的方式配置bean

<context:component-scan base-package="com.li.component"/>

<!--配置两个UserService对象-->
<bean class="com.li.component.UserService" id="userService200"/>
<bean class="com.li.component.UserService" id="userService300"/>

此时在ioc容器中就有三个UserService对象:

(1)对于自动扫描进去的UserService对象,它的id为它的类名(首字母小写)。

(2)在xml文件中配置的两个UserService对象,它们的id为上面设置的id(userService200、userService300)

现在我们重新运行测试方法:

//通过注解来配置Bean
@Test
public void setProByAutowired() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.xml");
UserAction userAction = ioc.getBean("userAction", UserAction.class);
System.out.println("userAction=" + userAction);
userAction.sayOk();
}

问题一:运行会不会报错?

问题二:@AutoWired注解的属性进行自动装配的时候,装配的是哪一个UserService对象?

@Autowired
private UserService userService;

答案一:没有报错

答案二: 在@AutoWired注解中,如果待装配的类型对应的bean在IOC容器中有多个,则使用待装配属性的名称作为id值进行查找,找到就装配,找不到就抛异常。因此装配的是ioc容器中id与待装配属性的属性名一致的对象。

3.3.2应用实例2-@Resource

使用UserAction和UserService说明,我们在UserAction类的属性userService400添加@Resource注解,并给@Resource注解添加name属性值=“userService”

package com.li.component;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import javax.annotation.Resource; /**
* @author 李
* @version 1.0
* @Controller 标识该类是一个控制器Controller,通常该类是一个Servlet
*/
@Controller
public class UserAction { @Resource(name = "userService")
private UserService userService400; public void sayOk() {
System.out.println("UserAction 的 sayOk()");
userService400.hi();
}
}

同时,我们在配置文件beans05.xml文件中又配置了两个UserService对象:

<context:component-scan base-package="com.li.component"/>

<!--配置两个UserService对象-->
<bean class="com.li.component.UserService" id="userService200"/>
<bean class="com.li.component.UserService" id="userService300"/>

也就是说,在beans05.xml文件对应的ioc容器中,此时一共有三个UserService对象:

(1)@Resource注解中配置的名为userService的对象

(2)在xml文件中配置的两个UserService对象,它们的id为上面设置的id(userService200、userService300)

那么在UserAction类的userService400属性中,spring给此属性自动装配的是三个中的哪一个对象呢?

答:id名为“userService”的对象。因为Spring将@Resource注解的name属性解析为bean的名字。 至于UserAction类的属性是什么名字无所谓。


Spring将@Resource注解的type属性则解析为bean的类型。

现在我们将UserAction类中的注解改为type=UserService.class,表示按照UserService.class的类型来进行装配

@Resource(type = UserService.class)
private UserService userService400;

这意味着在ioc容器中,只能有一个UserService类型的对象。如果配置了多个,就会报错:


问题:如果我们在@Resource注解的属性下什么都不写,会如何装配对象?

@Resource
private UserService userService400;

分析如下:现在ioc容器中,还是有三个UserService对象:

(1)@Resource注解中配置的对象(当没有指定name时,该对象对应的id就是“userService”)

(2)xml文件中配置的两个UserService对象,它们的id为上面设置的id(userService200、userService300)

如果@Resource没有指定name或者type,则先使用ByName注入策略,如果匹配不上,再使用byType策略,如果都不成功就会报错。

这里首先使用ByName策略,即匹配userService400,匹配不上后又使用byType策略,显然这里有三个对象,不符合类型匹配。也就是说两种策略都匹配失败,因此结果是:运行出错。

3.3.3注意事项和细节

  1. 如待装配的类型对应的bean在IOC容器中有多个,则使用待装配的属性的属性名作为id值再进行查找,找到就装配,找不到就抛异常 [ByName策略]

  2. 可以使用@Autowired和@Qualifier两个注解配合,可以指定装配对象的id值(注意这个id的对象要在ioc容器中存在)

    @Autowired
    @Qualifier(value = "userService200")
    private UserService userService;

3.4泛型依赖注入

基本说明:

  1. 为了更好地管理有继承和相互依赖关系的bean的自动装配,spring还提供基于泛型依赖的注入机制
  2. 泛型依赖注入:子类之间的依赖关系由其父类泛型以及父类之间的依赖关系来确定,父类的泛型必须为同一类型。通俗一点来说:两个子类之间的依赖关系不需要在子类中去声明,而是在父类中进行了声明,而依赖的纽带就是 泛型类型,必须是相同的父类泛型类型才具有依赖关系。
  3. 在继承关系复杂的情况下,泛型依赖注入就会有很大的优越性

应用实例

现有多个类,关系如下:

如上图,如果我们想要在BookService类中使用BookDao属性对象,或者要在PhoneService类中使用PhoneDao对象,传统方法是将 PhoneDao /BookDao自动装配到 BookService/PhoneSerive 中。

但是,当这种继承关系多起来时,即要自动装配的属性多起来时,这种配置方式就显得比较麻烦。因此我们可以使用 spring 提供的泛型依赖注入。

下面模拟图中的类:

Javabean-Phone:

package com.li.depeinjection;

/**
* @author 李
* @version 1.0
*/
public class Phone {
}

Javabean-Book:

package com.li.depeinjection;

/**
* @author 李
* @version 1.0
*/
public class Book {
}

自定义泛型类BaseDao:

package com.li.depeinjection;

/**
* @author 李
* @version 1.0
* 自定义泛型类
*/
public abstract class BaseDao<T> {
public abstract void save();
}

BookDao继承BaseDao,指定泛型Book,并添加@Repository注解

package com.li.depeinjection;

import org.springframework.stereotype.Repository;

/**
* @author 李
* @version 1.0
*/
@Repository
public class BookDao extends BaseDao<Book> {
@Override
public void save() {
System.out.println("BookDao save()");
}
}

PhoneDao同样继承BaseDao,指定泛型Phone,并添加@Repository注解

package com.li.depeinjection;

import org.springframework.stereotype.Repository;

/**
* @author 李
* @version 1.0
*/
@Repository
public class PhoneDao extends BaseDao<Phone>{
@Override
public void save() {
System.out.println("PhoneDao save()");
}
}

自定义泛型类BaseService:

package com.li.depeinjection;

import org.springframework.beans.factory.annotation.Autowired;

/**
* @author 李
* @version 1.0
* 自定义泛型类
*/
public class BaseService<T> {
@Autowired
private BaseDao<T> baseDao; public void save() {
baseDao.save();
}
}

BookService继承BaseService,指定泛型Book,并添加@Service注解

package com.li.depeinjection;

import org.springframework.stereotype.Service;

/**
* @author 李
* @version 1.0
*/
@Service
public class BookService extends BaseService<Book> {
//并没有写属性
}

PhoneService也继承BaseService,指定泛型Phone,并添加注解@Service

package com.li.depeinjection;

import org.springframework.stereotype.Service;

/**
* @author 李
* @version 1.0
*/
@Service
public class PhoneService extends BaseService<Phone>{
//并没有写属性
}

在配置文件beans06.xml中配置要扫描的包:

<context:component-scan base-package="com.li.depeinjection"/>

这意味着在com.li.depeinjection包下,添加了四种注解的类将会被进行扫描,在这里就是PhoneService类、BookService类、PhoneDao类、BookDao类

@Controller /@Service/ @Repository/ @Component

现在我们来进行测试:

//通过泛型依赖来配置Bean
@Test
public void setProByDependencyInjection() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans06.xml");
System.out.println("ok");
}

在语句System.out.println("ok");旁打上断点,点击debug,可以看到在ioc容器中已经有四个对象:

打开ioc-->beanFactory-->singletonObjects-->table属性,展开phoneService对象,可以看到该对象已经自动装载了PhoneDao类型的对象!!

这意味着我们通过泛型依赖注入,可以自动装配需要的对象,不必像之前一样一个个地为属性添加注解。

现在我们获取phoneService对象,调用该对象的save()方法:

//通过泛型依赖来配置Bean
@Test
public void setProByDependencyInjection() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans06.xml");
PhoneService phoneService = ioc.getBean("phoneService", PhoneService.class);
phoneService.save();
System.out.println("ok");
}

因为PhoneService类中没有实现save方法,因此在运行的时候会调用父类BaseService的save()方法,而BaseService的save()方法中又调用了baseDao属性的save()方法.

我们在上图可以知道baseDao的实际对象是PhoneDao类型,根据动态绑定,最终调用了PhoneDao类中的save()方法:

最新文章

  1. C#异常处理的几个原则
  2. go语言的 数组、slice、map使用(转)
  3. Fedora 20 创建桌面快捷方式
  4. ANDROID_MARS学习笔记_S01_007Linear_layout嵌套与layout_weight的设置
  5. JS家的排序算法
  6. 从零搭建LNMP环境
  7. RedHat Linux 5.5安装JDK+Tomcat并部署Java项目
  8. Bootstrap 模态框(也可以说的弹出层)
  9. 通过Git Gui Here上传本地项目到GitHub上
  10. Windows Server 2012 NIC Teaming 网卡绑定介绍及注意事项
  11. Unable to find header files
  12. 浅谈SPI总线
  13. AI tensorflow MNIST
  14. Java中的public、private、protected,函数修饰符
  15. pycharm启动慢 &ndash;xms -xmx相关参数设置
  16. 前端框架VUE----cli脚手架(框架)
  17. &lt;Spark&gt;&lt;Introduction to Spark&gt;
  18. async和await学习笔记
  19. 读书笔记 之java编程思想
  20. 【Learning】辛普森积分

热门文章

  1. AIR32F103(四) 27倍频216MHz,CoreMark跑分测试
  2. 「浙江理工大学ACM入队200题系列」问题 K: 零基础学C/C++84——奇偶ASCII值判断
  3. C#使用正则表达式来验证是否是16进制字符串
  4. RabbitMq消息手动应答、放回队列重新消费、设置队列消息持久化、分发模式
  5. mysql删库报错
  6. eBPF 实践 -- 网络可观测
  7. VS 生成后事件中自动修改文件名插入当前时间
  8. IDEA项目下out与target目录的区别详解
  9. 生成requirements.txt
  10. Java9-17新特性一览,了解少于3个你可能脱节了