(二)《Spring实战》——Spring核心

第二章:装配Bean

在Spring中,对象无需自己查找或创建与其所关联的其他对象。相反,容器负责把需要相互协作的对象引用赋予各个对象。例如,一个订单管理组件需要信用卡认证组件,但它不需要自己创建信用卡认证组件。订单管理组件只需要表明自己两手空空,容器就会主动赋予它一个信用卡认证组件。

创建应用对象之间协作关系的行为通常称为装配(wiring),这也是依赖注入(DI)的本质。

1. Spring配置的可选方案

  Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系。但是,作为开发人员,你需要告诉Spring要创建哪些bean并且如何将其装配在一起。当描述bean如何进行装配时,Spring具有非常大的灵活性,它提供了三种主要的装配机制

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

Spring有多种可选方案来配置bean,这是非常棒的,但有时候你必须要在其中做出选择。

  这方面,并没有唯一的正确答案。你所做出的选择必须要适合你和你的项目。而且,谁说我们只能选择其中的一种方案呢?Spring的配置风格是可以互相搭配的,所以你可以选择使用XML装配一些bean,使用Spring基于Java的配置(JavaConfig)来装配另一些bean,而将剩余的bean让Spring去自动发现。

  即便如此,我的建议是尽可能地使用自动配置的机制。显式配置越少越好。当你必须要显式配置bean的时候(比如,有些源码不是由你来维护的,而当你需要为这些代码配置bean的时候),我推荐使用类型安全并且比XML更加强大的JavaConfig。最后,只有当你想要使用便利的XML命名空间,并且在JavaConfig中没有同样的实现时,才应该使XML。

2. 自动化装配bean

  尽管显式装配技术(借助Java和XML来进行Spring装配)非常有用,但是在便利性方面,最强大的还是Spring的自动化配置。

Spring从两个角度来实现自动化装配:

  • 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
  • 自动装配(autowiring):Spring自动满足bean之间的依赖。

  为了阐述组件扫描和装配,我们需要创建几个bean,它们代表了一个音响系统中的组件。首先,要创建CompactDisc 类,Spring会发现它并将其创建为一个bean。然后,会创建一个CDPlayer 类,让Spring发现它,并将CompactDisc bean注入进来。

2.1 创建可被发现的bean

CompactDisc接口在Java中定义了CD的概念

package soundsystem;

public interface CompactDisc {
  void play();
}

  CompactDisc 的具体内容并不重要,重要的是你将其定义为一个接口。作为接口,它定义了CD播放器对一盘CD所能进行的操作。它将CD播放器的任意实现与CD本身的耦合降低到了最小的程度。

带有@Component注解的CompactDisc实现类SgtPeppers

package soundsystem;
import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc {

  private String title = "Sgt. Pepper‘s Lonely Hearts Club Band";
  private String artist = "The Beatles";

  public void play() {
    System.out.println("Playing " + title + " by " + artist);
  }
}

  和CompactDisc 接口一样,SgtPeppers 的具体内容并不重要。你需要注意的就是SgtPeppers 类上使用了@Component 注解。这个简单的注解表明该类会作为组件类,并告知Spring要为这个类创建bean。没有必要显式配置SgtPeppers bean,因为这个类使用了@Component 注解,所以Spring会为你把事情处理妥当。

  不过,组件扫描默认是不启用的。我们还需要显式配置一下Spring,从而命令它去寻找带有@Component 注解的类,并为其创建bean。

@ComponentScan注解启用了组件扫描

package soundsystem;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class CDPlayerConfig {
}

  如果没有其他配置的话,@ComponentScan 默认会扫描与配置类相同的包。因为CDPlayerConfig 类位于soundsystem 包中,因此Spring将会扫描这个包以及这个包下的所有子包,查找带有@Component 注解的类。这样的话,就能发现CompactDisc ,并且会在Spring中自动为其创建一个bean。

  如果你更倾向于使用XML来启用组件扫描的话,那么可以使用Spring context 命名空间的<context:component-scan> 元素。

通过XML启用组件扫描

<?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:context="http://www.springframework.org/schema/context"
  xmlns:c="http://www.springframework.org/schema/c"
  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
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

  <context:component-scan base-package="soundsystem" />

</beans>

测试组件扫描能够发现CompactDisc

package soundsystem;

import static org.junit.Assert.*;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {

  @Autowired
  private CompactDisc cd;

  @Test
  public void cdShouldNotBeNull() {
    assertNotNull(cd);
  }

}

  CDPlayerTest 使用了Spring的SpringJUnit4ClassRunner ,以便在测试开始的时候自动创建Spring的应用上下文。注解@ContextConfiguration 会告诉它需要在CDPlayerConfig 中加载配置。因为CDPlayerConfig 类中包含了@ComponentScan ,因此最终的应用上下文中应该包含CompactDisc bean。

  测试代码中有一个CompactDisc 类型的属性,并且这个属性带有@Autowired 注解,以便于将CompactDisc bean注入到测试代码之中。

2.2 为组件扫描的bean命名

  Spring应用上下文中所有的bean都会给定一个ID。在前面的例子中,尽管我们没有明确地为SgtPeppers bean设置ID,但Spring会根据类名为其指定一个ID。具体来讲,这个bean所给定的ID为sgtPeppers ,也就是将类名的第一个字母变为小写。

  如果想为这个bean设置不同的ID,你所要做的就是将期望的ID作为值传递给@Component 注解。

  还有一种方式是使用Java依赖注入规范(Java Dependency Injection)中所提供的@Named 注解来为bean设置ID。(不常用)

2.3  设置组件扫描的基础包

  为了指定不同的基础包,你所需要做的就是在@ComponentScan 的value属性中指明包的名称:

  要表明你所设置的是基础包,那么你可以通过basePackages属性进行配置:

  要设置多个基础包的话,只需要将basePackages 属性设置为要扫描包的一个数组即可:

  除了将包设置为简单的String类型(缺点类型不安全【not type-safe】)之外,@ComponentScan 还提供了另外一种方法,那就是将其指定为包中所包含的类或接口:

  可以看到,basePackages 属性被替换成了basePackageClasses 。同时,我们不是再使用String 类型的名称来指定包,为basePackageClasses 属性所设置的数组中包含了类。这些类所在的包将会作为组件扫描的基础包。

2.4 通过为bean添加注解实现自动装配

  简单来说,自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean。为了声明要进行自动装配,我们可以借助Spring的@Autowired 注解。

  如下,构造器上添加了@Autowired 注解,这表明当Spring创建CDPlayer bean的时候,会通过这个构造器来进行实例化并且会传入一个可设置给CompactDisc 类型的bean。

package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CDPlayer implements MediaPlayer {
  private CompactDisc cd;

  @Autowired
  public CDPlayer(CompactDisc cd) {
    this.cd = cd;
  }

  public void play() {
    cd.play();
  }
}

  @Autowired 注解不仅能够用在构造器上,还能用在属性的Setter方法上。

@Autowired
public setCompactDisc(CompactDisc cd) {
    this.cd = cd;
}

  @Autowired 注解可以用在类的任何方法上。

@Autowired
public insertCompactDisc(CompactDisc cd) {
    this.cd = cd;
}

  不管是构造器、Setter方法还是其他的方法,Spring都会尝试满足方法参数上所声明的依赖。假如有且只有一个bean匹配依赖需求的话,那么这个bean将会被装配进来。

  如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常。为了避免异常的出现,你可以将@Autowiredrequired 属性设置为false

@Autowired(required=false)
public CDPlayer(CompactDisc cd) {
    this.cd = cd;
}

  将required 属性设置为false 时,Spring会尝试执行自动装配,但是如果没有匹配的bean的话,Spring将会让这个bean处于未装配的状态。但是,把required 属性设置为false 时,你需要谨慎对待。如果在你的代码中没有进行null检查的话,这个处于未装配状态的属性有可能会出现NullPointerException

  @Autowired 是Spring特有的注解。如果你不愿意在代码中到处使用Spring的特定注解来完成自动装配任务的话,那么你可以考虑将其替换为@Inject

  @Inject 注解来源于Java依赖注入规范,该规范同时还为我们定义了@Named 注解。在自动装配中,Spring同时支持@Inject@Autowired 。尽管@Inject@Autowired 之间有着一些细微的差别,但是在大多数场景下,它们都是可以互相替换的。

2.5 验证自动装配

package soundsystem;

import static org.junit.Assert.*;

import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {

  //测试代码使用StandardOutputStreamLog 基于控制台的输出编写断言
  @Rule
  public final StandardOutputStreamLog log = new StandardOutputStreamLog();

  //将CDPlayer bean注入到测试代码的player 成员变量之中(它是更为通用的MediaPlayer 类型)
  @Autowired
  private MediaPlayer player;

  @Autowired
  private CompactDisc cd;

  @Test
  public void cdShouldNotBeNull() {
    assertNotNull(cd);
  }

  @Test
  public void play() {
    player.play();
    assertEquals(
        "Playing Sgt. Pepper‘s Lonely Hearts Club Band by The Beatles\n",
        log.getLog());
  }

}

3. 通过Java代码装配bean

  在进行显式配置时,JavaConfig是更好的方案,因为它更为强大、类型安全并且对重构友好。因为它就是Java代码,就像应用程序中的其他Java代码一样。

  同时,JavaConfig与其他的Java代码又有所区别,在概念上,它与应用程序中的业务逻辑和领域代码是不同的。尽管它与其他的组件一样都使用相同的语言进行表述,但JavaConfig是配置代码。这意味着它不应该包含任何业务逻辑,JavaConfig也不应该侵入到业务逻辑代码之中。尽管不是必须的,但通常会将JavaConfig放到单独的包中,使它与其他的应用程序逻辑分离开来,这样对于它的意图就不会产生困惑了。

3.1 创建配置类

package soundsystem;import org.springframework.context.annotation.Configuration;

@Configuration
public class CDPlayerConfig {
}

  创建JavaConfig类的关键在于为其添加@Configuration 注解,@Configuration 注解表明这个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。

3.2 声明简单的bean

  要在JavaConfig中声明bean ,我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean 注解。

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

  默认情况下,bean的ID与带有@Bean 注解的方法名是一样的。当然也可通过name属性自定义。

 @Bean(name="lonelyHeartsClubBand")
  public CompactDisc compactDisc() {
    return new SgtPeppers();
  }

3.3 借助JavaConfig实现注入

  可以使用上面的代码,通过调用方法来引用bean,但最常用的方法是下面这种:

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

  在这里,cdPlayer() 方法请求一个CompactDisc 作为参数。当Spring调用cdPlayer() 创建CDPlayer bean的时候,它会自动装配一个CompactDisc 到配置方法之中。然后,方法体就可以按照合适的方式来使用它。借助这种技术,cdPlayer() 方法也能够将CompactDisc 注入到CDPlayer 的构造器中,而且不用明确引用CompactDisc@Bean 方法。

4. 通过xml装配bean

4.1 创建xml配置规范

  创建新的配置规范。在使用JavaConfig的时候,这意味着要创建一个带有@Configuration 注解的类,而在XML配置中,这意味着要创建一个XML文件,并且要以<beans> 元素为根。

<?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:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

</beans>

4.2 声明一个简单的bean

<bean id="compactDisc"  class="soundsystem.SgtPeppers" />

4.3 借助构造器注入初始化bean

构造器注入,有两种基本的配置方案可供选择:

  • <constructor-arg> 元素
  • 使用Spring 3.0所引入的c-命名空间

4.3.1 构造器注入bean引用

<constructor-arg >使用ref 来引入其他bean

    

c-命名空间

       

4.3.2 将字面量注入到构造器中

<constructor-arg>使用value 属性表明给定的值要以字面量的形式注入到构造器之中。

c-命名空间

  ①引用构造器参数的名字

   

  ②通过参数索引装配相同的字面量值

   

4.3.3 装配集合

 

4.4 设置属性

引用bean

<property >通过ref引用bean

 

p-命名空间

 

将字面量注入到属性中

<property> 使用value 属性设置属性值

 

p-命名空间

 

不能使用p-命名空间来装配集合,装配集合可使用<util:list>

 

现在,我们能够像使用其他的bean那样,将磁道列表bean注入到BlankDisc bean的tracks 属性中

 

5. 导入和混合配置

  在自动装配时,它并不在意要装配的bean来自哪里。自动装配的时候会考虑到Spring容器中所有的bean,不管它是在JavaConfig或XML中声明的还是通过组件扫描获取到的。

5.1 在JavaConfig中引用XML配置

  假设BlankDisc 定义在名为cd-config.xml 的文件中,该文件位于根类路径下,那么可以修改SoundSystemConfig ,让它使用@ImportResource 注解,如下所示:

  

  两个bean——配置在JavaConfig中的CDPlayer 以及配置在XML中BlankDisc ——都会被加载到Spring容器之中。因为CDPlayer 中带有@Bean 注解的方法接受一个CompactDisc 作为参数,因此BlankDisc 将会装配进来,此时与它是通过XML配置的没有任何关系。

5.2 在XML配置中引用JavaConfig

  在XML中,我们可以使用<import >元素来拆分XML配置。

  

原文地址:https://www.cnblogs.com/zjfjava/p/8448963.html

时间: 2024-09-30 00:02:59

(二)《Spring实战》——Spring核心的相关文章

【书籍推荐】Spring实战-----Spring入门经典

如果看完了Servlet学习指南后,可以开始考虑使用框架了,这时候是完全没有什么问题的,因为有太多的开发者在帮我们解决问题了,作为一个新手,我们可以先使用别人的轮子,等到使用得很熟练,技术提高了,我们才考虑制造一些小轮子,当然这个过程需要很长的时间,但我相信只要不断的学习,就一定能成功. 当然,个人认为这本书只是入门级,因为文档是更好的资料,但是每个人迈出的第一步都倾向于轻松有趣,另外看英文版当然是更好有更好的提升,但是个人认为既然入门就可以选择看中文版的快速入门,然后再看英文版的文档,或者在下

Redis实战和核心原理详解(5)使用Spring Session和Redis解决分布式Session跨域共享问题

Redis实战和核心原理详解(6)使用Spring Session和Redis解决分布式Session跨域共享问题 前言 对于分布式使用Nginx+Tomcat实现负载均衡,最常用的均衡算法有IP_Hash.轮训.根据权重.随机等.不管对于哪一种负载均衡算法,由于Nginx对不同的请求分发到某一个Tomcat,Tomcat在运行的时候分别是不同的容器里,因此会出现session不同步或者丢失的问题. 实际上实现Session共享的方案很多,其中一种常用的就是使用Tomcat.Jetty等服务器提

《02.Spring Boot连载:Spring Boot实战.Spring Boot核心原理剖析》

在上节中我们通过了一个小的入门案例已经看到了Spring Boot的强大和简单之处.本章将详细介绍Spring Boot的核心注解,基本配置和运行机制.笔者一直认为:精通一个技术一定要深入了解这个技术帮助我们做了哪些动作,深入理解它底层的运行原理,只有达到这个目标才可以熟练使用框架,最终达到融会贯通的目的. 一.启动类与@SpringBootApplication Spring Boot的项目一般都会有注解*Application标注的入口类,入口类中会有一个main方法,main方法是一个标准

(一)《Spring实战》——Spring核心

<Spring实战>(第4版) 第一章:Spring之旅 1. 简化Java开发 为了降低Java开发的复杂性,Spring采取了以下4种关键策略: 基于POJO的轻量级和最小侵入性编程: 通过依赖注入和面向接口实现松耦合: 基于切面和惯例进行声明式编程: 通过切面和模板减少样板式代码. 1.1 激发POJO的潜能 在基于Spring构建的应用中,它的类通常没有任何痕迹表明你使用了Spring.最坏的场景是,一个类或许会使用Spring注解,但它依旧是POJO. Spring赋予POJO魔力的

【Spring实战】--1Spring的核心

最近面试总会涉及Spring的优点,SpringMVC与Struts2的比较,生活慢慢稳定下来,这些面试还是应了那句话“只顾盲目拉车,不会低头看路”,回过头来还是要好好研究一下Spring,如果仅仅是停留在粘贴拷贝像从前一样机械的完成任务,可能再过一段时间我还是不会太明白Spring事务的妙处,以及Spring松耦合为什么就会让代码看起来清爽.以前上学那会也是在半梦半醒中学了一部分Spring,看了开涛的Spring的一些博客.这次潜心研究一下,主要参考资料: 1<Spring实战(第4版)>

Extjs5.0从入门到实战开发信息管理系统(Extjs基础、Extjs5新特性、Spring、Spring mvc、Mybatis)视频教程

Extjs5.0从入门到实战开发信息管理系统(Extjs基础.Extjs5新特性.Spring.Spring mvc.Mybatis)视频教程下载   联系QQ:1026270010 Extjs作为一款优秀的JS前端开发框架以其良好的架构.丰富的UI组件库.完善的文档和社区支持等诸多优点拥有广泛的市场应用空间,开发人员无需过多的关注HTML.CSS甚至各种常用JS算法,只需把精力放在业务逻辑上,利用各种组件的相互组合调用便可轻松而高效的开发出系统的前端页面. Extjs5在之前版本的基础上又推出

[Spring实战系列](16)面向切面编程(AOP)概述

1. 简介 在软件中,有些行为对于大多数应用都是通用的.日志,安全和事务管理的确很重要,但他们是都是应用对象主动参与的行为呢?如果让应用对象只关注自己所针对的业务领域问题,而其他方面的问题由其他应用对象来处理,这样会不会更好? 在软件开发中,分布于应用中多处的功能被称为横切关注点.通常,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往直接嵌入到应用的业务逻辑中).将这些横切关注点与业务逻辑相分离是面向切面编程索要解决的. 上图展示了一个被划分为模块的典型应用.每个模块的核心功能都是为特

《Spring实战(第三版)》 之旅(一)

前言 一个寒假吧SSM学完了,也不能说是学完了,只能说是过了一遍,知道大体的流程,框架的搭建,但具体的实现原理,为什么要这样,什么时候要这样,概念还是很模糊. 反正经过这五大框架的学习,感觉最重要的还是Spring框架,于是乎,开学时期偶得一书<Spring实战>,在网上口碑还是蛮高的,于是继续深入学习(复习)下Spring这个框架. 到目前为止已经读到了第8章 使用Spring Web Flow ,突然想到了写写博客记录下读书的历程,也能说是总结?笔记?反正就是自己在看这部分内容的时候心里在

《Spring实战》读书笔记--SpringMVC处理Multipart数据

<Spring实战>读书笔记--SpringMVC处理Multipart数据 1.Multipart介绍 1.1 Multipart形式的数据 Multipart格式数据会将一个表单拆分为多个部分(part),每个部分对应一个输入域.在一般的表单输入域中,它对应的部分会放置文本型数据,如果是文件上传形式,它对应的部分可以是二进制. 1.2 Multipart/form-data请求方式 Multipart/form-data是建立在HTTP的POST请求方式以上的请求,其一般用于HTTP文件上

Memcached笔记——(二)XMemcached&amp;Spring集成

今天研究Memcached的Java的Client,使用XMemcached 1.3.5,做个简单的测试,并介绍如何与Spring集成. 相关链接: Memcached笔记——(一)安装&常规错误&监控Memcached笔记——(二)XMemcached&Spring集成 Memcached笔记——(三)Memcached使用总结 Memcached笔记——(四)应对高并发攻击  一.Memcached Client简要介绍 Memcached Client目前有3种: Memca