欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring in Action 第二章

程序员文章站 2022-03-31 21:06:53
...

Spring in Action 第二章

@(Spring in Action)[spring, bean]


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
 */
@Component
public 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注释掉,那么测试类那边是无法进行驻入的。

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

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

@Configuration
public 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去引不就得了,哈哈。

Spring in Action 第二章

总结

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

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

相关标签: spring