Spring装配bean

创建应用之间关联关系的传统做法(通过构造器或者查找)通常会导致结构复杂的代码 , 这些代码很难被复用 , 也很难进行单元测试 , 耦合度高……
在spring中 , 对象无需自己查找或创建与其关联的对象 , 相反 , 容器负责把需要相互协作的对象引用赋予给各个对象 ,例如,一个订单管理组件需要信用卡认证组件,但它不需要自己创建信用卡认证组件。订单管理组件只需要表明自己两手空空,容器就会主动赋予它一个信用卡认证组件。

创建应用对象之间的协作关系的行为通常被称为 装配(wiring) , 这也是依赖注入DI的本质 , di是 spring 最基本要素 , 开发基于spring的应用随时都在使用

spring装备bean有多种方式 , Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系 , 但是作为开发人员 , 我们需要告诉spring要创建那些 bean 并且如何将其装配在一起 , spring具有非常大的灵活性 , 它提供了主要三种主要的装配机制 :
在xml中进行显示配置
在java中进行显示配置
隐式的bean发现机制和自动装配

Spring 的配置风格是可以互相搭配的,所以你可以选择使用 XML 装配一些 bean ,使用 Spring 基于 Java 的配置( JavaConfig )来装配另一些 bean ,而将剩余的bean 让 Spring 去自动发现。
建议是尽可能地使用自动配置的机制。显式配置越少越好。当你必须要显式配置 bean 的时候(比如,有些源码不是由你来维护的,而当你需要为这些代码配置 bean 的时候),我推荐使用类型安全并且比 XML 更加强大的 JavaConfig 。最后,只有当你想要使用便利的 XML命名空间,并且在 JavaConfig 中没有同样的实现时,才应该使用 XML 。

自动化装配bean

在便利性方面,最强大的还是 Spring 的自动化配置
Spring 从两个角度来实现自动化装配:
- 组件扫描( component scanning ): Spring 会自动发现应用上下文中所创建的 bean 。
- 自动装配( autowiring ): Spring 自动满足 bean 之间的依赖。

在类上使用 @Component注解 这个简单的注解表明了该类会作为组件类 , 并告知spring为这个类创建bean , 没有必要显示的配置这个类 , 因为这个类使用了 @Component 注解 , 所以spring会把事情处理妥当 .
不过 , 默认组件扫描是不开启的 , 所以还需要显示的配置一下spring , 从而命令他去寻找带有@Component注解的类 , 并为其创建bean .
@ComponentScan 这个注解能够在spring中启动注解扫描 , 如果没有其他配置的话 , @ComponentScan会默认扫描与配置类相同的包 , spring会扫描这个包以及这个包下所有子包 , 查找带有@Component注解的类 ,只添加一行 @ComponentScan 注解就能自动创建无数个 bean ,这种权衡还是很划算的。
xml配置扫描 可以使用 Spring context 命名空间的 context:component-scan> 元素

* @ComponentScan 和 @Component可以做什么 *
为组件扫描的bean命名
Spring应用上下文中所有的bean都会给定一个id , 尽管没有明确的设置ID , 但是spring会根据类名指定一个ID , 默认是类名首字母变小写 .
如果想为这个bean设置不同的id , 就要将期望的id作为值传给@Component注解 , 比如 :
@Component(“aaa”) : 那么id就是aaa

还有另外一种为bean命名的方式 , 这种方式不用@Component注解 , 而是使用Java依赖注入规范中所提供的@Named注解:
@Named(“aaa”)

Spring 支持将 @Named 作为 @Component 注解的替代方案。两者之间有一些细微的差异,但是在大多数场景中,它们是可以互相替换的。

如果没有为@ComponentScan设置任何属性 , 这意味着 , 按照默认规则 , 它会以配置类所在的包为基础包来扫描组件 , 但是,如果想扫描多个基础包 , 就需要在@ComponentScan的value属性中指明包的名称:
@ComponentScan(“aaa”)
如果想更加清晰的表明所设置的包是基础包 , 可以通过basePackages属性进行配置:
@ComponentScan(basePackages=”aaa”)
basePackages这个属性可以设置多个基础包 , 如下 :
@ComponentScan(basePackages={“aaa”,”bbb”)
上面这种基础包设置是以String类型表示的 , 这种方法是不安全的 , 如果重构代码 , 那么指定的基础包可能就会出现错误 .
所以除了将包设置成简单的String类型之外 , @ComponentScan还提供了一种方法 , 那就是将其指定为包中所包含的接口或者类 , 如下 :
@ComponentScan(basePackageClasses={CDplay.class,DVDplayer.class})
basePackages属性替换成basePackageClasses , 里面的属性所设置的数组包含了类 , 这些类所在的包将会作为组件扫描的基础包 .
可以在包中创建一个用来扫描的空标记接口 , 通过标记接口的方式 , 依然能够保持对重构友好的接口引用 , 但是可以避免任何实际的应用程序代码 .

通过为bean添加注解实现自动装配
自动装配就是让 Spring 自动满足依赖的一种方法 , 在满足依赖的过程中 , 会在 Spring 的上下文中寻找某个bean需求的其他bean , 为了声明要进行自动装配 , 可以借助 spring 的@Autuwired注解

@Autuwired注解
这个注解不仅可以作用在构造器上 , 也可以作用在属性的Setter方法上
spring在初始化bean后 , 他会尽可能的去满足bean的依赖 , @Autuwired注解可以使用在任何方法上 ,
不管是构造器/方法 , spring都会尝试满足方法参数上声明的依赖 , 假如只有一个bean满足依赖需求 , 那么这个bean就会被装配进来 ,
假如有多个bean满足依赖需求 , Spring会抛出一个异常 , 表明没有明确指定选择哪个bean进行自动装配
假如没有bean满足依赖需求 , Spring会抛出一个异常 , 为了避免这个异常 , 可以设置 @Autuwired(required=false) , 这样设置的时候 , Spring还是会尝试执行自动装配 , 但是如果没有bean满足依赖需求的时候 , Spring会让这个bean处于未装配状态 , 但是可能会出现NopointerException , 所以要谨慎对待 ..

@Autuwired 是Spring特有的注解 , 可以使用@Inject替换 , 没有什么太大区别 , @Inject来源java依赖注入规范, 还有@Named也是

通过Java代码装配bean
应用场景 : 比如想把第三方库中的组件装配到应用中 , 这种情况 , 没办法在它的类和方法上添加@Component和@Autuwired注解 , 因此就不能使用自动化装配的方案了 …

两种可选方案 : java和xml

进行显示配置的时候 , JavaConfig是更好的方案 , 它更强大 , 类型安全,而且对重构友好 , 因为他是java代码 , 就像程序里面其他的java代码一样…

JavaConfig和Java的区别 :
在概念上 , 它有应用逻辑和领域代码是不同的 , 尽管都使用相同的语言 , 但JavaConfig是配置代码 , 这意味着他不包含任何业务逻辑 , 也不应该侵入到任何业务代码中去 , 尽管不是必须的 , 但通常会将JavaConfig放到单独的配置包去

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

声明bean
要在JavaConfig中声明bean , 需要编写一个方法 , 这个方法会创建所需类型的实例 , 然后给这个方法添加 @bean注解 , 例如:
@bean
public CompactDisc sgtPepper(){
return new sgtPepper();
}
默认情况下 , 这个bean的id为sgtPepper , 如果想重命名 , 可以 :
@bean(name=”aaaa”)
public CompactDisc sgtPepper(){
return new SgtPepper();
}

借助 JavaConfig 实现注入
在 JavaConfig 中装配 bean 的最简单方式就是引用创建 bean 的方法。例如,下面就是一种声明 CDPlayer 的可行方案:
@bean
public CDplayer cdplayer(){
return new CDpayer(new sgtPepper());
}
cdPlayer() 方法像 sgtPeppers() 方法一样,同样使用了 @Bean 注解,这表明这个方法会创建一个 bean 实例并将其注册到 Spring 应用上下
文中。所创建的 bean ID 为 cdPlayer ,与方法的名字相同。
cdPlayer() 的方法体与 sgtPeppers() 稍微有些区别。在这里并没有使用默认的构造器构建实例,而是调用了需要传入 CompactDisc 对
象的构造器来创建 CDPlayer 实例。
看起来, CompactDisc 是通过调用 sgtPeppers() 得到的,但情况并非完全如此。因为 sgtPeppers() 方法上添加了 @Bean 注解, Spring
将会拦截所有对它的调用,并确保直接返回该方法所创建的 bean ,而不是每次都对其进行实际的调用。
比如说,假设你引入了一个其他的 CDPlayerbean ,它和之前的那个 bean 完全一样
@bean
public CDplayer cdplayer(){
return new CDplayer(sgtPepper());
}
@bean
public CDplayer anotherCDplayer(){
return new CDplayer(sgtPepper());
}
默认情况下, Spring 中的 bean 都是单例
的,我们并没有必要为第二个 CDPlayer bean 创建完全相同的 SgtPeppers 实例。所以, Spring 会拦截对 sgtPeppers() 的调用并确保返回
的是 Spring 所创建的 bean ,也就是 Spring 本身在调用 sgtPeppers() 时所创建的 CompactDiscbean 。因此,两个 CDPlayer bean 会得到相
同的 SgtPeppers 实例。

@bean
public CDplayer cdplayer(CompactDisc compactDisc){
return new CDplayer(compactDisc);
}
在这里, cdPlayer() 方法请求一个 CompactDisc 作为参数。当 Spring 调用 cdPlayer() 创建 CDPlayerbean 的时候,它会自动装配一
个 CompactDisc 到配置方法之中。然后,方法体就可以按照合适的方式来使用它。借助这种技术, cdPlayer() 方法也能够
将 CompactDisc 注入到 CDPlayer 的构造器中,而且不用明确引用 CompactDisc 的 @Bean 方法
通过这种方式引用其他的 bean 通常是最佳的选择,因为它不会要求将 CompactDisc 声明到同一个配置类之中。在这里甚至没有要
求 CompactDisc 必须要在 JavaConfig 中声明,实际上它可以通过组件扫描功能自动发现或者通过 XML 来进行配置。你可以将配置分散到多个
配置类、 XML 文件以及自动扫描和装配 bean 之中,只要功能完整健全即可。不管 CompactDisc 是采用什么方式创建出来的, Spring 都会将其传入到配置方法中,并用来创建 CDPlayer bean 。
带有 @Bean 注解的方法可以采用任何必要的 Java 功能来产生 bean 实例。构造器和 Setter 方法只是 @Bean 方法的两个简单样例。
这里所存在的可能性仅仅受到 Java 语言的限制。

通过XML装配bean

明天再看
6.27

Spring出现的时候 , xml是描述配置的主要方式 , 在Spring的名义下 , 我们创建了无数xml代码 , 在一定程度上 , Spring成为了xml配置的同义词 .
尽管 Spring 长期以来确实与 XML 有着关联,但现在需要明确的是, XML 不再是配置 Spring 的唯一可选方案。 Spring 现在有了强大的自动化配置和基于 Java 的配置, XML 不应该再是你的第一选择了。
不过,鉴于已经存在那么多基于 XML 的 Spring 配置,所以理解如何在 Spring 中使用 XML 还是很重要的.
在完成新的 Spring 工作时,希望你会使用自动化配置和 JavaConfig 。

创建xml配置规范
在使用 XML 为 Spring 装配 bean 之前,你需要创建一个新的配置规范。在使用 JavaConfig 的时候,这意味着要创建一个带有@Configuration 注解的类,而在 XML 配置中,这意味着要创建一个 XML 文件,并且要以 元素为根。
最为简单的 Spring 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”
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-4.0.xsd“>
/bean> —-前面少些了尖括号

这个基本的 XML 配置已经比同等功能的 JavaConfig 类复杂得多了。作为起步,在 JavaConfig 中所需要的只是 @Configuration ,但在使用 XML 时,需要在配置文件的顶部声明多个 XML 模式( XSD )文件,这些文件定义了配置 Spring 的 XML 元素

借助 Spring Tool Suite 创建 XML 配置文件创建和管理 Spring XML 配置文件的一种简便方式是使用 Spring Tool Suite ( https://spring.io/tools/sts )。在 Spring Tool Suite 的菜单中,选择 File>New>Spring Bean Configuration File ,能够创建 Spring
XML 配置文件,并且可以选择可用的配置命名空间。

用来装配 bean 的最基本的 XML 元素包含在 spring-beans 模式之中,在上面这个 XML 文件中,它被定义为根命名空间。 beans是该模式中
的一个元素,它是所有 Spring 配置文件的根元素。

借助构造器注入初始化bean
在 Spring XML 配置中,只有一种声明 bean 的方式:使用 bean 元素并指定 class 属性。 Spring 会从这里获取必要的信息来创建 bean 。

在 XML 中声明 DI 时,会有多种可选的配置方案和风格。具体到构造器注入,有两种基本的配置方案可供选择:

  1. constructor-arg元素
  2. c-命名空间

构造器注入bean引用

<bean id="cdPlayer" class="soundystem.CDplayer">
  <constructor ref="compactDisc"/>
</bean>  

作为替代的方案,你也可以使用 Spring 的 c- 命名空间。 c- 命名空间是在 Spring 3.0 中引入的,它是在 XML 中更为简洁地描述构造器参数的方式。
要使用它的话,必须要在 XML 的顶部声明其模式

<bean id = "aaa" class ="bbb"
c:cd-ref="compactDisc"/>

这里,使用了 c- 命名空间来声明构造器参数
c命名空间的组成
c:cd-ref = “compactDisc”
c : c-命名空间前缀
cd : 构造器参数名
-ref : 注入bean的引用
“compactDisc” : 要注入bean的id

c-命名空间直接引用了构造器参数的名称 , 引用参数的名称看起来有些怪异,
因为这需要在编译代码的时候,将调试标志( debug symbol )保存在类代码中。如果你优化构建过程,将调试标志移除掉,那么这种方式可能就无法正常执行了。
替代的方案是我们使用参数在整个参数列表中的位置信息:

<bean id="aaa" class=bbb c:_0-ref="compactDisc">

将参数的名称替换成了 “0” ,也就是参数的索引。因为在 XML 中不允许数字作为属性的第一个字符,因此必须要添加一个下画线作为前缀。

还有另外一个方案 —— 根本不用去标示参数:

<bean id="aaa" class=bbb c:_ref="compactDisc">

将字面量注入到构造器中

我们所做的依赖注入(DI)通常值得都是类型的装配
—-也就是将对象的引用装配到依赖他们的其他对象中

而有时候 , 我们需要做的只是将一个字面量值用做配置对象 :

public class BlankDisc implements CompactDisc{
   private String title;
   private String artist;

   public BlankDisc(String title , String artist){
      this.title = title ;
      this.artist = artist ; 
  }

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

使用 <constructor-arg> 元素进行构造器参数的注入。但是这一次我们没有使用 “ref” 属性来引用其他的 bean ,而是使用了 value 属性,通过该属性表明给定的值要以字面量的形式注入到构造器之中

<bean id="compactDisc" class="sound.BlankDisc>
   <constructor-arg value="字面值"/>
   <constructor-arg value="字面值"/>
   </bean>

如果使用 c-命名空间 , 则是:

<bean id="compactDisc"
 class="sound.BlankDisc
 c:_title="zimianzhi"
 c:_artist="zimianzhi"
/>  

可以看到,装配字面量与装配引用的区别在于属性名中去掉了 “-ref” 后缀
装配 bean 引用和字面量值方面, <constructor-arg> 和 c- 命名空间的功能是相同的。但是有一种情况是 <constructor-arg> 能够实
现, c- 命名空间却无法做到的。接下来,让我们看一下如何将集合装配到构造器参数中。

装配集合

public class BlankDisc implements CompactDisc {

    private String title;
    private String artist;
    private List<String> list;

    public BlankDisc(String title, String artist, List<String> list) {
        this.title = title;
        this.artist = artist;
        this.list = list;
    }
}

最简单的办法是将列表设置为 null 。因为它是一个构造器参数,所以必须要声明它,不过你可以采用如下的方式传递 null 给它

<bean id="aaa" class="bbb">
<constructor-arg value="第一个参数值">
<constructor-arg value="第二个参数值">
<constructor-arg ><null/></constructor>
</bean>

<null/> 元素所做的事情与你的期望是一样的:将 null 传递给构造器。这并不是解决问题的好办法,但在注入期它能正常执行。当调
用 play() 方法时,你会遇到 NullPointerException 异常,因此这并不是理想的方案

更好的解决方法是提供一个列表

<bean id="aaa" class="bbb">
<constructor-arg value="第一个参数值">
<constructor-arg value="第二个参数值">
<constructor-arg >
<list>
<value>集合里的值一</value>
<value>集合里的值二</value>
<value>集合里的值三</value>
</list>
</constructor>
</bean>

也可以使用ref来代替value实现对其他bean的引用

<bean id="aaa" class="bbb">
<constructor-arg value="第一个参数值">
<constructor-arg value="第二个参数值">
<constructor-arg >
<list>
<ref bean="bean1"/>
<ref bean="bean2"/>
<ref bean="bean3"/>
</list>
</constructor>
</bean>

也可以使用Set元素代替list , 但是跟集合一样 , Set有去重和无序..

设置属性

<bean id="aaa" class="bbb">
<property name="ccc" ref="其他bean的id">
</bean>

Spring 为 <constructor-arg> 元素提供了 c- 命名空间作为替代方案,与之类似, Spring 提供了更加简洁的 p- 命名空间,作
<property> 元素的替代方案。为了启用 p- 命名空间,必须要在 XML 文件中与其他的命名空间一起对其进行声明:

<bean id="aaa" class="bbb" p:compactDisc-ref="compactDisc">

p- 命名空间中属性所遵循的命名约定与 c- 命名空间中的属性类似。

属性的名称以 “-ref” 结尾,这会提示Spring 要进行装配的是引用,而不是字面量。

将字面量注入到属性中
把ref换成value

与 c- 命名空间一样,装配 bean 引用与装配字面量的唯一区别在于是否带有 “-ref” 后缀。如果没有 “-ref” 后缀的话,所装配的就是字面量。
但需要注意的是,我们不能使用 p- 命名空间来装配集合,没有便利的方式使用 p- 命名空间来指定一个值(或 bean 引用)的列表。但是,我们可
以使用 Spring util- 命名空间中的一些功能来简化 BlankDiscbean 。

JavaConfig和XML的混合使用

有时间再看

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值