Spring学习之旅(三)--装配Bean
装配 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
像上面的实例中,没有指定扫描包的路径的话,会默认将当前类所在的包当作基础包来扫描(即:只扫描当前包和子包)。
如果需要扫描其他包的话需要使用 basePackages 或 basePackageClasses 属性,两者的区别在于一个接收的参数是字符型而另一个接收的参数是类。并且它们两个都是复数的形式,表示它们可以接收多个值。
@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 来进行自动装配。
@Autowired 是 Spring 特有的注解,如果你不想使用它的话,也可以使用 jsr330规范 的 @Inject 注解,两者的功能在大多数情况下都是一样的。
JavaConfig 显式装配
在使用第三方库的时候就无法使用自动化配置,只能采用显式装配。显式装配有两种方式:
- JavaConfig
- XML
这里我们先讲 JavaConfig。JavaConfig 从语法上和普通的 Java 代码没有区别,但是概念上却有所区别,它不应该包含任何业务逻辑。一般来说都会将这些配置类单独放到一个包下,使其和业务逻辑相分离。
实例
**1.JavaConfig 显式声明 Bean **
我们移除之前的 ApplicationConfig 类上的 @ComponentScan 注解。采用 JavaConfig 的方式申明 Bean。
@Configuration
public class ApplicationConfig {
@Bean
public CompactDisc compactDisc(){
return new CompactDisc();
}
}
2.运行之前的单元测试类,查看控制台输出
开始播放
可以看到结果跟之前的一致。
通过 @Bean 注解创建的 bean 的 Id 默认就是方法名,如果需要命名成不同的也可以使用 @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的更多相关文章
- Spring学习笔记(二)之装配Bean
一,介绍Bean的装配机制 在Spring中,容器负责对象的创建并通过DI来协调对象之间的关系.但是我们要告诉Spring创建哪些Bean并且如何将其装配在一起.,装配wiring就是DI依赖注入的本 ...
- Spring 学习指南 第三章 bean的配置 (未完结)
第三章 bean 的配置 在本章中,我们将介绍以下内容: bean 定义的继承: 如何解决 bean 类的构造函数的参数: 如何配置原始类型 (如 int .float 等) .集合类型(如 ja ...
- Spring学习系列(二) 自动化装配Bean
一.Spring装配-自动化装配 @Component和@ComponentScan 通过spring注解(@Component)来表明该类会作为组件类,并告知Spring要为这类创建bean,不过组 ...
- Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探
由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...
- Spring入门2. IoC中装配Bean
Spring入门2. IoC中装配Bean 20131125 前言: 上一节学习了Spring在JavaProject中的配置,通过配置文件利用BeanFactory和ApplicationConte ...
- Spring学习之旅(十)--MockMvc
在之前的 Spring学习之旅(八)--SpringMVC请求参数 我们是通过在控制台输出来验证参数是否正确,但是这样做实在是太耗时间了,我们今天来学习下 MockMvc,它可以让我们不需要启动项目就 ...
- spring在IoC容器中装配Bean详解
1.Spring配置概述 1.1.概述 Spring容器从xml配置.java注解.spring注解中读取bean配置信息,形成bean定义注册表: 根据bean定义注册表实例化bean: 将bean ...
- (转)java之Spring(IOC)注解装配Bean详解
java之Spring(IOC)注解装配Bean详解 在这里我们要详细说明一下利用Annotation-注解来装配Bean. 因为如果你学会了注解,你就再也不愿意去手动配置xml文件了,下面就看看 ...
- Spring学习之旅(四)--高级装配Bean
条件化 bean 有时候我们要满足某种情况才将bean 初始化放入容器中. 基于环境初始化不同的 bean 1.申明接口并创建两个实现类 public interface Teacher { void ...
随机推荐
- 关于tomcat-startup.bat启动失败或者一闪而过问题解决记录
一.前言 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选.对于一个初学者来说 ...
- 开发者福音!面向Web场景的云开发服务正式开放!
导 语 继支持小程序开发之后,云开发也支持Web使用啦!开发者们可以使用云开发提供的云端能力,直接开发网站应用,如PC端网页.公众号中的网页等.由此开发者可以在网站应用中借助云函数实现业务逻辑,通过与 ...
- git取消链接并删除本地库
有的时候我们需要删除从GitHub上克隆下来的库 从github上clone一个仓库: git clone git@github.com:USERNAME/repo.git 在本地目录下关联远程rep ...
- CDN绕过姿势小结
公司的各业务主站都挂了CDN,总结一波CDN绕过技巧. 什么是CDN CDN的全称是Content Delivery Network,即内容分发网络. 其基本思路是尽可能避开互联网上有可能影响数据传输 ...
- 2019杭电多校第二场hdu6602 Longest Subarray(线段树)
Longest Subarray 题目传送门 解题思路 本题求一个最大的子区间,满足区间内的数字要么出现次数大于等于k次,要么没出现过.给定区间内的数字范围是1~c. 如果r为右边界,对于一种数字x, ...
- 【iOS】Receiver type 'XXX' for instance message is a forward declaration
今天遇到这个错误.刚开始字体太大,没显示全,后来调小字体之后看到了完整提示信息: 之后就忽然想起没引入相关的类,添加 #import "RDVTabBarItem.h" 就行了.
- Docker Toolbox安装
公司最近搭建docker环境,其中会遇到一些问题,在这里记录一下. 先来了解一下docker 一.基本概念 1.Docker中基本概念镜像(Image) 提到镜像,有对操作系统有一定认知的都知道,镜像 ...
- 给最近正在找工作(iOS)的朋友一些建议/经验
众所周知, iOS开发找工作越来越难, 企业要求越来越高,一方面是资本寒冬期+七八月是企业招人淡季, 另外一方面也是iOS市场饱和.最近有出去看新机会, 所以下面记录一下面试XimalayaFM的大概 ...
- Kubernetes容器集群管理环境 - 完整部署(中篇)
接着Kubernetes容器集群管理环境 - 完整部署(上篇)继续往下部署: 八.部署master节点master节点的kube-apiserver.kube-scheduler 和 kube-con ...
- Java动态,安全追踪工具
Java动态,安全追踪工具 在我们日常的开发中,总是难以避免的要解决线上的问题.如果线上的问题我们在本地调试的时候无论调试多少次发现明明本地调用了这个方法呀,怎么线上就是没调呢?还有就是出了问题的时候 ...