Spring in Action 第二章

来源:互联网 发布:仓库数据流程图 编辑:程序博客网 时间:2024/05/23 18:58

Spring in Action 第二章

@(Spring in Action)[spring, bean]


  • Spring in Action 第二章
    • Spring装配bean
      • 自动化装配bean
        • 创建可被发现的bean
        • 为组件扫描的bean命名
        • 设置组件扫描的基础包
        • 通过为bean添加注解实现自动装配
      • 通过Java代码装配bean显示装配
        • 声明简单的bean
        • 命名空间
      • 导入和混合配置
        • 在JavaConfig中引用XML配置还有java配置
        • 在XML配置中引用JavaConfig
      • 总结

Spring装配bean

1.在XML中进行显式配置
2.在Java中进行显式配置
3.隐式的bean发现机制和自动装配

显式配置越少越好。当你必须要显式配置bean的时候(比如,有些源码不是由你来维护的,而当你需要为这些代码配置bean的时候),推荐使用类型安全并且比XML更加强大的JavaConfig。下面将先介绍自动化装配。

自动化装配bean

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

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

组件扫描和自动装配组合在一起就能发挥出强大的威力,它们能够将你的显式配置降低到最少。

创建可被发现的bean

创建接口:

package com.springTest;/** * @Author: YLBG-YCY-1325 * @Description: * @Date: 2017/7/21 */public interface CompactDisc {    void play();}

创建带有@Component注解的CompactDisc实现类,这个简单的注解表明该类会作为组件类,并告知Spring要为这个类创建
bean。没有必要显式配置SgtPeppersbean,因为这个类使用了@Component注解,所以Spring会为你把事情处理妥当。

package com.springTest;import org.springframework.stereotype.Component;/** * @Author: YLBG-YCY-1325 * @Description: * @Date: 2017/7/21 */@Componentpublic class SgtPepper implements CompactDisc {    private String title = "this is SgtPepper title";    private String artist = "The beatiles";    @Override    public void play() {        System.out.println("play " + title + ": artist" + artist);    }}

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

<?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:contex="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-3.2.xsd">    <!--自动扫描包-->    <contex:component-scan base-package="com.lemontree.web"/></beans>

创建配置类,@Configuration注解表明这个类是一个配置类

package com.springTest;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;/** * @Author: YLBG-YCY-1325 * @Description: 设置配置类 * @Date: 2017/7/21 */@Configuration //@Configuration注解表明这个类是一个配置类@ComponentScan // 没有包名默认是当前类包public class CDplayerConfig {}

创建测试类, 测试组件扫描能够发现CompactDisc::

package com.springTest;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ActiveProfiles;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/** * @Author: YLBG-YCY-1325 * @Description: * @Date: 2017/7/21 */@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = CDplayerConfig.class)@ActiveProfiles("dev")public class CDplayerTest {    @Autowired    private CompactDisc cd;    @Test    public void testName() throws Exception {        cd.play();    }}

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

为组件扫描的bean命名

两种方式命名bean:
第一种

@Component(名字)

第二种 Java依赖注入规范(Java Dependency Injection)中所提供的@Named注解来为bean设置ID

@Name(名字)

建议用@Component

设置组件扫描的基础包

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

@Configuration// 没有包名默认是当前类包// @ComponentScan("com.lemontree.springTest")// @ComponentScan(basePackages = "com.lemontree.springTest") @ComponentScan(basePackages = {"com.lemontree.springTest,com.lemontree.xxxx"}) public class CDplayerConfig {}

在上面的例子中,所设置的基础包是以String类型表示的。我认为这是可以的,但这种方法是类型不安全(not type-safe)的。如果你重构代码的话,那么所指定的基础包可能就会出现错误了。
除了将包设置为简单的String类型之外,@ComponentScan还提供了另外一种方法,那就是将其指定为包中所包含的类或接口:

/** * @Author: YLBG-YCY-1325 * @Description: * @Date: 2017/7/21 */@Configuration// @ComponentScan(basePackageClasses = CDplayer.class) @ComponentScan(basePackageClasses = {CDplayer.class,CompactDisc.class) public class CDplayerConfig {}

basePackageClasses属性所设置的数组中包含了类,这些类所在的包将会作为组件扫描的基础包。

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

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

// 在CDplaer中驻入CompactDisc @Component("cDplayer")public class CDplayer {    private CompactDisc sgtPepper;    public CDplayer(CompactDisc sgtPepper) {        this.sgtPepper = sgtPepper;    }    @Autowired    public void test(CompactDisc sgtPepper){        System.out.println("test 方法 驻入");        this.sgtPepper = sgtPepper;    }    public CompactDisc getSgtPepper() {        return sgtPepper;    }    public void setSgtPepper(CompactDisc sgtPepper) {        this.sgtPepper = sgtPepper;    }}// 测试例子@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = CDplayerConfig.class)@ActiveProfiles("dev")public class CDplayerTest {    @Autowired    private CompactDisc cd;    @Autowired    private CDplayer cDplayer;    @Test    public void cdshoudNotNull() throws Exception {        cDplayer.getSgtPepper().play();        Assert.notNull(cDplayer);    }}

上面的例子会先输出test 方法 驻入,然后打印playe()方法中的文字。如果有两个@Autowired那么会出现两次驻入

通过Java代码装配bean(显示装配)

你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了

声明简单的bean

注意到下例中将componentScan给去掉了,同时在方法上添加@Bean注解。因为sgtPeppers()方法上添加了@Bean注解,
Spring将会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。
默认情况下,Spring中的bean都是单例的,我们并没有必要为第二个CDPlayer bean创建完全相同的SgtPeppers实例。所以,Spring会拦截对sgtPeppers()的调用并确保返回的是Spring所创建的bean,也就是Spring本身在调用sgtPeppers()时所创建的CompactDiscbean。因此,两个CDPlayer bean会得到相同的SgtPeppers实例。
如果把@Bean注释掉,那么测试类那边是无法进行驻入的。

@Configurationpublic class CDplayerConfig {    @Bean    public CompactDisc sgtPerppers(){        return new SgtPepper();    }    @Bean    public CDplayer cDplayer(){        return new CDplayer(new SgtPepper());    }}

通过这种下面方式引用其他的bean通常是最佳的选择,因为它不会要求将CompactDisc声明到同一个配置类之中。在这里甚至没有要求CompactDisc必须要在JavaConfig中声明,实际上它可以通过组件扫描功能自动发现或者通过XML来进行配置。

@Configurationpublic class CDplayerConfig {    @Bean    public CompactDisc sgtPerppers(){        return new SgtPepper();    }    @Bean    public CDplayer cDplayer(CompactDisc sgtPepper){        return new CDplayer(sgtPepper);    }}

带有@Bean注解的方法可以采用任何必要的Java功能来产生bean实例。构造器和Setter方法只是@Bean方法的两个简单样
例。这里所存在的可能性仅仅受到Java语言的限制。

命名空间

通过命名空间来简易化xml配置:

导入和混合配置

在JavaConfig中引用XML配置(还有java配置)

我们临时假设CDPlayerConfig已经变得有些笨重,我们想要将其进行拆分。

方式一:采用@Import,在一个JavaConfig中导入另一个JavaConfig(类似于xml配置中的 <\import resource=”xxxx”/>)

package com.lemontree.spring;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;/** * Created by lemontree on 2017/8/2. */@Configuration@Import(SpringConfig.class)public class MyConfig {}

方式二:一个更好的方法创建一个高级的JavaConfig,将另外两个导入

package com.lemontree.spring;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;/** * Created by lemontree on 2017/8/2. */@Configuration//@Import(SpringConfig.class)@Import({SpringConfig.class,SpringMVCConfig.class})public class MyConfig {}

方式三:导入Java的配置文件还不够?那就导入xml文件呗

注意:实例中没有列出复数的形式该如何写,点源码看下立马就能知道该如何写了!

package com.lemontree.spring;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;import org.springframework.context.annotation.ImportResource;/** * Created by lemontree on 2017/8/2. */@Configuration@Import(SpringConfig.class)@ImportResource("classpath:spring.xml")//@ImportResource(locations = {"classpath:spring-mvc.xml","classpath:spring-mybatis.xml"})public class MyConfig {}

在XML配置中引用JavaConfig

假设你正在使用Spring基于XML的配置并且你已经意识到XML逐渐变得无法控制,在被无数的尖括号淹没之前,我们决定将XML配置文件进行拆分。

xml中引另外一个xml配置文件很简单,但是如何去引一个java的配置文件那?想必都能猜到,xml文件是吧,不是有<
bean/>这个标签在么,java的配置也是java类啊,直接用bean去引不就得了,哈哈。

这里写图片描述

总结

对于xml和java的配置作者是如何取舍的,看看书上原话:

总结我同时建议尽可能使用自动化配置,以避免显式配置所带来的维护成本。但是,如果你确实需要显式配置Spring的话,应该优先选择基于Java的配置,它比基于XML的配置更加强大、类型安全并且易于重构。

原创粉丝点击