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()方法:

day07-Spring管理Bean-IOC-05的更多相关文章

  1. (转)编码剖析Spring管理Bean的原理

    http://blog.csdn.net/yerenyuan_pku/article/details/52832434 在Spring的第一个案例中,我们已经知道了怎么将bean交给Spring容器进 ...

  2. 采用Spring管理Bean和依赖注入

    1. 实例化spring容器和从容器获取Bean对象 实例化Spring容器常用的两种方式: 方法一: 在类路径下寻找配置文件来实例化容器 [推荐使用] ApplicationContext ctx ...

  3. Spring管理bean的生命周期

    1: bean的创建:   如果我们默认的scope配置为Singleton的话, bean的创建实在Spring容器创建的时候创建: 如果scope的配置为Prototype的话,bena的创建是在 ...

  4. Spring、编码剖析Spring管理Bean的原理

    引入dom4j jar包 1.新建Person接口和PersonBean public interface PersonIService { public void helloSpring(); } ...

  5. Spring第三弹—–编码剖析Spring管理Bean的原理

    先附一下编写的Spring容器的执行结果: 代码如下: 模拟的Spring容器类:   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...

  6. Spring——管理Bean的生命周期

    我们可以自定义bean的初始化和销毁方法,这里所指的的初始化和bean的构造不同,初始化是在bean构造完成后,对bean内部的属性或一些逻辑进行初始化. 首先要弄清一些概念: 构造(对象创建) 单实 ...

  7. 编码剖析Spring管理bean的原理

    project目录 MyClassPathXMLApplicationContext读取xml,以及实例化bean. 因为是一开始实例化配置文件所有bean,所以需要构造器完成这些工作. packag ...

  8. Spring管理Bean的三种创建方式

    1.使用类构造器实例化  (90%以上都是使用这种) <bean id=“orderService" class="cn.itcast.OrderServiceBean&qu ...

  9. 简单模拟Spring管理Bean对象

    1: 首先我们要利用dom4j进行xml的解析,将所有的bean的配置读取出来. 2:利用java的反射机制进行对象的实例化. 3: 直接获得对象 package cn.Junit.test; imp ...

  10. spring 管理bean

    目录结构: Person.java package com.wss.entity; import com.wss.service.doHomeWork; public class Person { p ...

随机推荐

  1. 动词时态=>4.将来时态和过去将来时态构成详解

    将来时态构成详解 使用助动词will构成的将来时态 一般将来时态 与一般过去时态相反(时间上) 如果说 一般过去,我们将其当做一张照片 从这张照片当中,我们无法得知 动作什么时候开始 什么时候结束 只 ...

  2. 1、在SrpingBoot的环境当中使用JSP及相关功能

    创建webapp目录 由于SpringBoot项目不建议直接访问jsp页面,但是我现在要做的事情需要去访问,那么我就需要在原有的项目基础上为访问jsp页面进行一个调整 首先在项目当中,java和res ...

  3. 使用NEON指令加速RGB888和RGB565的相互转换

    最近在做一个项目需要将RGB888转换为RGB565,用C语言转换的代码很简单,这是从ffmpeg中摘抄的代码 static inline void rgb24to16_c(const uint8_t ...

  4. AR人体姿态识别,实现无边界的人机交互

    近年来,AR不断发展,作为一种增强现实技术,给用户带来了虚拟和现实世界的融合体验.但用户已经不满足于单纯地将某件虚拟物品放在现实场景中来感受AR技术,更想用身体姿势来触发某个指令,达到更具真实感的人机 ...

  5. 京东云开发者|京东云RDS数据迁移常见场景攻略

    云时代已经来临,云上很多场景下都需要数据的迁移.备份和流转,各大云厂商也大都提供了自己的迁移工具.本文主要介绍京东云数据库为解决用户数据迁移的常见场景所提供的解决方案. 场景一:数据迁移上云 数据迁移 ...

  6. pytorch 环境配置

    一.下载Anaconda 二.添加清华镜像 # 添加清华镜像 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anac ...

  7. Vue报错:component has been registered but not used

    原因: ​​eslint​​代码检查到你注册了组件但没有使用,然后就报错了.比如代码: 比如​​Vue​​​中注册了​​File​​组件,而实际上却没有使用到(直接取消注册为好): ... impor ...

  8. JAVA系列之类加载机制详解

    类的加载机制 ? 双亲委派机制 ? 什么是类加载器 ? 自定义类加载器有哪些应用场景 ? 通常,在关于Java的类加载部分会遇到以上疑问,本文将对类加载重要部分做详细介绍,包括重要的基础概念和应用场景 ...

  9. (C++) 初始化列表 std::initializer_list

    构造时直接使用初始化列表 T object { arg1, arg2, ... }; (1) T { arg1, arg2, ... } (2) new T { arg1, arg2, ... } ( ...

  10. mysql数据库报错 sql 1452 Cannot add or update a child row:a foreign key constraint fails

    其实这句话的意思就是你添加一个值是一个外键,但是这个外键不在关联的数据库中的主键中,这样就导致了添加失败了,解决办法就是添加对应关联数据库的主键的值,不过我要提醒一下!(也就是我采的坑!) 一定要看清 ...