装配 Bean 的方式

  • 在 XML 中进行显式配置
  • 在 Java 中进行显式配置
  • 隐式的 Bean 发现机制和自动装配

Spring 提供了以上三种方式进行 Bean 的配置,可以根据自己的需求选择一种或者混合使用。但是我的个人建议还是尽可能的使用自动配置机制,毕竟显式的配置越少越方便。但如果必须要显示的配置 bean 的时候,推荐使用比 XML 类型安全更好的 JavaConfig 方式。

自动化装配 Bean

Spring 通过如下两个方式来实现自动化装配:

  • 组件扫描(component scanning):Spring 会自动发现应用上下文中所创建的 bean

  • 自动化装配(autowiring): Spring 自动满足 bean 之间的依赖。

实例

1.添加 maven 依赖

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.20.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

2.创建一个 bean

@Component
public class CompactDisc { public void play(){
System.out.println("开始播放");
}
public void pause(){
System.out.println("暂停播放");
} public void stop(){
System.out.println("停止播放");
}
}

@Component 注解表示申明这个类为组件类。


3.创建一个配置类并开启组件扫描

@Configuration
@ComponentScan
public class ApplicationConfig { }

@Configuration 表示这是一个配置类

@ComponentScan 表示开启组件扫描


4.创建单元测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={ApplicationConfig.class})
public class CompactDiscTest { @Autowired
private CompactDisc compactDisc;
@Test
public void test(){
compactDisc.play();
} }

运行,查看控制台输出:

开始播放

bean 命名

Spring 中如果没有明确给 bean 指定一个 ID 的话,Spring 会将类名首字母小写作为它的默认 ID。 如果要指定 ID 的话,可以采用如下形式的注解:

@Component("指定ID名")

@ComponentScan

像上面的实例中,没有指定扫描包的路径的话,会默认将当前类所在的包当作基础包来扫描(即:只扫描当前包和子包)。

如果需要扫描其他包的话需要使用 basePackagesbasePackageClasses 属性,两者的区别在于一个接收的参数是字符型而另一个接收的参数是类。并且它们两个都是复数的形式,表示它们可以接收多个值。

@ComponentScan(basePackages = {"com.marklogzhu.bean","com.marklogzhu.service"})
@ComponentScan(basePackageClasses = {CompactDisc.class,UserService.class})

basePackageClasses 相比于 basePackages 属性更安全,不会因为重构包名导致路径错误,basePackageClasses 申明类所在的包就是作为扫描的基础包。

注:可以在这些包里新建一个与功能无关的空接口来作为 basePackageClasses 的值,避免因为功能重构导致 类/接口 被移除。


自动装配

在实例中我们通过 @Autowired 注解 实现了 bean 的自动装配。除了属性之外还可以在方法上也增加 @Autowired 注解 实现 bean 的注入。

如果没有匹配到 bean 的话,Spring 将会抛出一个 UnsatisfiedDependencyException 异常。为了避免此异常的出现,可以将 **@Autowired ** 的 required 属性设置为 false。当 Spring 匹配不到 bean 的时候会将这个 bean 设置为未装配状态。 要注意的是如果你的代码没有对这个 bean 进行 null 检查的话,就会抛出 NullPointerException 异常,所以请谨慎使用这个属性。

如果匹配到多个 bean 的话,Spring 也会抛出一个 NoUniqueBeanDefinitionException 异常,表示没有明确指明使用哪个 bean 来进行自动装配。

@AutowiredSpring 特有的注解,如果你不想使用它的话,也可以使用 jsr330规范@Inject 注解,两者的功能在大多数情况下都是一样的。


JavaConfig 显式装配

在使用第三方库的时候就无法使用自动化配置,只能采用显式装配。显式装配有两种方式:

  • JavaConfig
  • XML

这里我们先讲 JavaConfigJavaConfig 从语法上和普通的 Java 代码没有区别,但是概念上却有所区别,它不应该包含任何业务逻辑。一般来说都会将这些配置类单独放到一个包下,使其和业务逻辑相分离。


实例

**1.JavaConfig 显式声明 Bean **

我们移除之前的 ApplicationConfig 类上的 @ComponentScan 注解。采用 JavaConfig 的方式申明 Bean

@Configuration
public class ApplicationConfig { @Bean
public CompactDisc compactDisc(){
return new CompactDisc();
} }

2.运行之前的单元测试类,查看控制台输出

开始播放

可以看到结果跟之前的一致。


通过 @Bean 注解创建的 beanId 默认就是方法名,如果需要命名成不同的也可以使用 @Bean 注解name 属性。

如果一个 bean 的创建需要另一个 bean 的话,可以采用如下的方式声明:

@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
return new CDPlayer(compactDisc);
}

XML 显式装配

Spring 刚刚出现的时候,XML 是描述配置的主要方式,但是现在已经有了自动化配置JavaConfig 显式配置XML 的使用应该只是用于维护老项目而不是使用到新的项目中去。

实例

** 1.创建 xml 文件 等同于 @Configuration 注解**

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>

**2.申明 bean 等同于 @Bean 注解 **

<bean id="compactDisc" class="com.marklogzhu.bean.CompactDisc"/>

可以没有设置 Id 的话,默认名就会是 com.marklogzhu.bean.CompactDisc#0 。其中 #0 是一个计数的形式,用于和其他 bean 区分。

3.新建单元测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class XmlTest { @Autowired
private CompactDisc compactDisc; @Test
public void test() {
compactDisc.play();
} }

运行查看控制台输出:

开始播放

有这么一个班级类,它里面有班级名称、一个老师、一群学生和课程列表,我们来看看怎么通过 **xml ** 形式注入:

public class Class {

    private String name;

    private Teacher teacher;

    private List<Student> students;

    private List<String> courses;

    public Class(){

    }

    public Class(String name) {
this.name = name;
} public Class(String name, Teacher teacher) {
this(name);
this.teacher = teacher;
} public Class(String name, Teacher teacher, List<String> courses) {
this(name,teacher);
this.courses = courses;
} public Class(String name,Teacher teacher, List<String> courses, List<Student> students) {
this(name,teacher,courses);
this.students = students;
} public String getName() {
return name;
} public Teacher getTeacher() {
return teacher;
} public List<Student> getStudents() {
return students;
} public List<String> getCourses() {
return courses;
}
public void setName(String name) {
this.name = name;
} public void setTeacher(Teacher teacher) {
this.teacher = teacher;
} public void setStudents(List<Student> students) {
this.students = students;
} public void setCourses(List<String> courses) {
this.courses = courses;
}
}

public class Teacher { public String startWorking() {
return "老师教学";
}
} public class Student { public String startWorking() {
return "学生学习";
}
}

构造器注入--字符串

<bean id="aClass" class="com.marklogzhu.bean.xml.Class">
<constructor-arg name="location" value="B栋二楼"/>
</bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class XmlTest { @Autowired
private Class javaClass; @Test
public void test_01() {
Assert.assertEquals(javaClass.getName(),"Java学习");
} }

注:constructor-arg 不显式声明 name 属性那么将会按先后顺序赋值。

构造器注入--对象

<bean id="teacher" class="com.marklogzhu.bean.xml.Teacher"/>

<bean id="aClass" class="com.marklogzhu.bean.xml.Class">
<constructor-arg name="name" value="Java学习"/>
<constructor-arg name="teacher" ref="teacher"/>
</bean>
@Test
public void test_02() {
Assert.assertEquals(javaClass.getName(),"Java学习");
Assert.assertEquals(javaClass.getTeacher().startWorking(),"老师教学");
}

构造器注入--字符串 List

<bean id="aClass" class="com.marklogzhu.bean.xml.Class">
<constructor-arg name="name" value="Java学习"/>
<constructor-arg name="teacher" ref="teacher"/>
<constructor-arg name="courses">
<list>
<value>JavaSe</value>
<value>Sql</value>
<value>JS</value>
</list>
</constructor-arg>
</bean>
@Test
public void test_03() {
Assert.assertEquals(javaClass.getName(),"Java学习");
Assert.assertEquals(javaClass.getTeacher().startWorking(),"老师教学");
Assert.assertEquals(javaClass.getCourses().size(),3);
}

构造器注入--对象 List

<bean id="aClass" class="com.marklogzhu.bean.xml.Class">
<constructor-arg name="name" value="Java学习"/>
<constructor-arg name="teacher" ref="teacher"/>
<constructor-arg name="courses">
<list>
<value>JavaSe</value>
<value>Sql</value>
<value>JS</value>
</list>
</constructor-arg>
<constructor-arg name="students">
<list>
<ref bean="student"/>
<ref bean="student"/>
</list>
</constructor-arg>
</bean>
@Test
public void test_04() {
Assert.assertEquals(javaClass.getName(),"Java学习");
Assert.assertEquals(javaClass.getTeacher().startWorking(),"老师教学");
Assert.assertEquals(javaClass.getCourses().size(),3);
Assert.assertEquals(javaClass.getStudents().size(),2);
}

属性注入

<bean id="aClass" class="com.marklogzhu.bean.xml.Class">
<property name="name" value="Java学习"/>
<property name="teacher" ref="teacher"/>
<property name="courses">
<list>
<value>JavaSe</value>
<value>Sql</value>
<value>JS</value>
</list>
</property>
<property name="students">
<list>
<ref bean="student"/>
<ref bean="student"/>
</list>
</property>
</bean>
@Test
public void test_05() {
Assert.assertEquals(javaClass.getName(),"Java学习");
Assert.assertEquals(javaClass.getTeacher().startWorking(),"老师教学");
Assert.assertEquals(javaClass.getCourses().size(),3);
Assert.assertEquals(javaClass.getStudents().size(),2);
}

可以看到我们把之前的 constructor-arg 替换为 property 了,但是单元测试还是可以通过,说明属性注入成功了。

注:属性注入的前提是类中有 setXX 方法存在。

JavaConfig 显式装配 和 XML 配置混合使用

在 JavaConfig 显式装配中引用 XML 配置

@ImportResource("classpath:applicationContext.xml")

在 XML 配置中引用 JavaConfig 显式装配

  <bean id="compactDisc" class="com.marklogzhu.bean.CompactDisc"/>

Spring学习之旅(三)--装配Bean的更多相关文章

  1. Spring学习笔记(二)之装配Bean

    一,介绍Bean的装配机制 在Spring中,容器负责对象的创建并通过DI来协调对象之间的关系.但是我们要告诉Spring创建哪些Bean并且如何将其装配在一起.,装配wiring就是DI依赖注入的本 ...

  2. Spring 学习指南 第三章 bean的配置 (未完结)

    第三章 bean 的配置 ​ 在本章中,我们将介绍以下内容: bean 定义的继承: 如何解决 bean 类的构造函数的参数: 如何配置原始类型 (如 int .float 等) .集合类型(如 ja ...

  3. Spring学习系列(二) 自动化装配Bean

    一.Spring装配-自动化装配 @Component和@ComponentScan 通过spring注解(@Component)来表明该类会作为组件类,并告知Spring要为这类创建bean,不过组 ...

  4. Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探

    由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...

  5. Spring入门2. IoC中装配Bean

    Spring入门2. IoC中装配Bean 20131125 前言: 上一节学习了Spring在JavaProject中的配置,通过配置文件利用BeanFactory和ApplicationConte ...

  6. Spring学习之旅(十)--MockMvc

    在之前的 Spring学习之旅(八)--SpringMVC请求参数 我们是通过在控制台输出来验证参数是否正确,但是这样做实在是太耗时间了,我们今天来学习下 MockMvc,它可以让我们不需要启动项目就 ...

  7. spring在IoC容器中装配Bean详解

    1.Spring配置概述 1.1.概述 Spring容器从xml配置.java注解.spring注解中读取bean配置信息,形成bean定义注册表: 根据bean定义注册表实例化bean: 将bean ...

  8. (转)java之Spring(IOC)注解装配Bean详解

    java之Spring(IOC)注解装配Bean详解   在这里我们要详细说明一下利用Annotation-注解来装配Bean. 因为如果你学会了注解,你就再也不愿意去手动配置xml文件了,下面就看看 ...

  9. Spring学习之旅(四)--高级装配Bean

    条件化 bean 有时候我们要满足某种情况才将bean 初始化放入容器中. 基于环境初始化不同的 bean 1.申明接口并创建两个实现类 public interface Teacher { void ...

随机推荐

  1. c语言进阶15-数据结构总结

    数据结构结论 1.阿基米德说过:“给我一个支点,我就能翘起地球”. 数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成.记为:Data_Structure=(D, ...

  2. 【带着canvas去流浪(13)】用Three.js制作简易的MARVEL片头动画(下)

    目录 一. 模型的制作 1.1 生成字体模型 1.2 多表面贴图 二. 镜头及动画 三. 大作业总结 示例代码托管在:http://www.github.com/dashnowords/blogs 博 ...

  3. 小白学python-day01-电脑结构知识

    作为一个”0“基础的经济学学士来说,专业分类选择了经统,多多少少和计算机有点关系,从今天开始学习python. 但行努力,莫问前程. day01学习电脑结构等知识. 因为这些知识是 有规则的,客观的文 ...

  4. 从“n!末尾有多少个0”谈起

    在学习循环控制结构的时候,我们经常会看到这样一道例题或习题.问n!末尾有多少个0?POJ 1401就是这样的一道题. [例1]Factorial (POJ 1401). Description The ...

  5. 注解与AOP切面编程实现redis缓存与数据库查询的解耦

    一般缓存与数据库的配合使用是这样的. 1.查询缓存中是否有数据. 2.缓存中无数据,查询数据库. 3.把数据库数据插入到缓存中. 其实我们发现 1,3 都是固定的套路,只有2 是真正的业务代码.我们可 ...

  6. React之动画实现

    React之动画实现 一,介绍与需求 1.1,介绍 1,Ant Motion Ant Motion能够快速在 React 框架中使用动画.在 React 框架下,只需要一段简单的代码就可以实现动画效果 ...

  7. AWS Aurora数据库 Multi-Master 小测

    AWS Aurora Mysql终于推出了Multi-Master,直面硬刚Oracle RAC.在多一份数据库产品选择的小兴奋之余,我们也看看新推出的Multi-Master的特点(包括优缺点). ...

  8. 为什么unsigned (-1)表示无符号整数的最大值

    整数在计算机中的表示 在计算机中,整数采用补码表示.当前主流编译器中整型在内存中占用四个字节,共32位. 机器数:一个数在计算机中的二进制表示形式, 叫做这个数的机器数.机器数是带符号的,在计算机用一 ...

  9. 关于 java中的换行符

    java中实现换行有以下3种方法: 1.使用java中的转义符"\r\n": String str="aaa"; str+="\r\n"; ...

  10. VS调试时修改代码

    最近碰到一个问题,就是vs在调试模式下无法修改代码之后再继续,这种严重影响工作效率的问题怎么能忍,所以决心把这个坑填满.网上搜了大堆有头无尾有尾无头的答案,我一个一个试了几乎都没啥用.最后通过不断的测 ...