SpringBean 配置及作用域

上篇文章介绍的IOC的理念与优点,这一章我们介绍如何将自己开发的Bean装配到Spring IOC容器中。

Spring 配置有3种方式

  • 在XML文件中显示配置
  • 在Java 接口实现配置
  • 隐式Bean 的发现机制和自动装配原则

     


Spring有多种可选方案来配置bean,如何选择呢?

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


装配Bean的优先级及优点?

在现实的工作中,这 3 种方式都会被用到,并且在学习和工作之中常常混合使用,所以这里给出一些关于这 3 种优先级的建议:

隐式Bean的发现机制和自动装配原则

首选自动装配机制,显示配置越少越好。
优点:减少程序开发者的决定权,简单又不失灵活。

Java 的接口和类中实现配置

在没有办法使用自动装配原则情况,我们考虑此类方法。
优点:减少xml配置文件,也更为容易。

xml文件配置

最后选择xml文件配置。
优点:简单易懂

典型场景:
当使用三方类时,并不是我们开发,也无法修改代码,这个时候通过XML方式配置使用。


自动化装配Bean

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

  • 组件扫描(component scaning)   
  • 自动装配(autowing)

组件扫描

Spring 会自动发现应用上下文所创建的Bean。

自动装配(autowing)

Spring 自动满足Bean之间的依赖。
 

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

代码实现

在这个MP3和流式媒体音乐的时代,磁带显得陈旧,随着以物理载体方式越来越少。尽管如此,磁带为我们阐述DI如何运行提供一个很好的样例。如果不将磁带放入磁带播放器中,那么磁带其实没有太大用处,所有可以说磁带依赖于磁带播放器。


组件扫描 

磁带接口 

public interface Player {
	
	void play();
}

磁带播放器接口

@Component
public class PandaPlayer implements Tape {

	private String singer = "周杰伦";
	
	private String songName = "双截棍";
	
	public void play() {
		System.out.println("开始播放:"+singer+"的"+songName);
	}

}

TapeConfig 配置类(默认扫描)

@Configuration
@ComponentScan
public class TapeConfig {

}

 测试类

@RunWith(SpringJUnit4ClassRunner.class)
// 自动装配的配置
@ContextConfiguration(classes = TapeConfig.class)
public class PlayerTest {

    // 1、通过扫描包的方式实现装配bean,使用@Component注解或@Named 注解进行配置
    @Autowired 
    private Tape tape;
    
    @Test 
    public void player(){ 
        assertNotNull(tape); tape.play();
    }
}

测试结果

开始播放:周杰伦的双截棍

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


自动装配

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

使用@Autowired是放到构造方法中,@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上或者普通方法中。 

Player接口播放接口

public interface Player {
	
	void play();
}

SonyPlayer索尼播放器

@Component
public class SonyPlayer implements Player {
	
	private Tape tape;
	
	@Autowired
	public SonyPlayer (Tape tape) {
		this.tape = tape;
	}

	public void play() {
		System.out.println("----start----");
		tape.play();
		System.out.println("----end----");
	}

}

测试结果

----start----
开始播放:周杰伦的双截棍
----end----

通过Java代码装配Bean

尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通,因此需要明确配 置Spring。比如说,你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添 加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了。 

下面的例子是参考《Sring实战第四版》,觉得比较容易理解,大家可以看下:

CD播放类 

public class CDPlayer {

	private CompactDisc cd;

	public void play() {
		cd.play();
	}
    // 省略setget方法
}

播放接口

public interface CompactDisc {

	void play();
}

歌曲

public class HardDaysNight  implements CompactDisc {

	private String title = "Hard. DaysNight's Lonely Hearts Club Band";
	private String artist = "The Hard";

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

	
}

public class Revolver implements CompactDisc {

	private String title = "Revolver. Revolver's Lonely Hearts Club Band";
	private String artist = "The Revolver";

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

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);
	}
}

public class WhiteAlbum  implements CompactDisc {

    private String title = "White. Album's Lonely Hearts Club Band";
    private String artist = "The White";

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

重点在这里Config配置类

/*
* Spring 的组件扫描默认是不启用的,需要显式配置启用组件扫描去寻找被 @Component 注解修饰的组件类,并为其创建 bean 实例。
* */
/*
* 标记类 CDPlayerConfig 是 Spring 的配置类,通过 java 代码定义 Spring 的装配规则。
* 该类应该包含在Spring应用上下文中如何创建bean的细节。
* */
@Configuration
public class CDPlayerConfig {

	
	 /*
	    * 要在 JavaConfig 中声明 bean ,我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加 @Bean 注解。
	    *
	    * @Bean 注解会告诉 Spring 这个方法将会返回一个对象,该对象要注册为 Spring 应用上下文中的 bean 。
	    * 方法体中包含了最终产生 bean 实例的逻辑。
	    *
	    * 默认,bean 的 ID 和方法名是一样的,如下 bean 的 ID 为 sgtPeppers 。
	    * 如果想手动为 bean 指定一个 ID ,可以使用 @Bean 的 name 属性,如:
	    *   @Bean(name="lonelyHeartsClubBand")
	    * */
	    @Bean
	    public CompactDisc sgtPeppers() {
	        return new SgtPeppers();
	    }

	    // 这里是使用 Java 创建 bean,因此我们可以发挥 Java 提供的所有功能,只要最终生成一个 CompactDisc 实例即可。例如:
	    @Bean
	    public CompactDisc randomBeatlesCD() {
	        CompactDisc cd;
	        int choice = (int) Math.floor(Math.random() * 4);
	        switch (choice) {
	            case 1:
	                cd = new SgtPeppers();
	                break;
	            case 2:
	                cd = new WhiteAlbum();
	                break;
	            case 3:
	                cd = new HardDaysNight();
	                break;
	            default:
	                cd = new Revolver();
	                break;
	        }
	        return cd;
	    }

	    /*
	    * Spring 装配方式一:
	    * 在JavaConfig中装配bean的最简单方式就是引用创建bean的方法。
	    * */
	    @Bean
	    public CDPlayer cdPlayer() {
//	        return new CDPlayer(sgtPeppers());
	        return new CDPlayer(new WhiteAlbum());
	    }

	    /*
	    * Spring 装配方式二:
	    * 当 Spring 调用 cdPlayer() 创建 CDPlayer bean 的时候,它会自动装配一个 CompactDisc 到配置方法之中。
	    * 然后,方法体就可以按照合适的方式来使用它。
	    *
	    * 通过这种方式引用其他的 bean 通常是最佳的选择,因为它不会要求将 CompactDisc 声明到同一个配置类之中。
	    * 在这里甚至没有要求 CompactDisc 必须要在 JavaConfig 中声明,
	    * 实际上它可以通过组件扫描功能自动发现或者通过 XML 来进行配置。
	    * 你可以将配置分散到多个配置类、XML 文件以及自动扫描和装配 bean 之中,
	    * 只要功能完整健全即可。不管 CompactDisc 是采用什么方式创建出来的,
	    * Spring 都会将其传入到配置方法中,并用来创建 CDPlayer bean 。
	    *
	    * 当配置类中有多个同类型的 bean 时,此时可以使用 @Qualifier 注解来指定参数注入的是哪一个具体的 bean 。
	    * */
	    @Bean
	    public CDPlayer cdPlayer(@Qualifier("randomBeatlesCD") CompactDisc compactDisc) {
	        return new CDPlayer(compactDisc);
	    }

	    /*
	    * 我们也可以采用属性赋值的方式来注入依赖值,这里所存在的可能性仅仅受到Java语言的限制。
	    * */
//	    @Bean
//	    public CDPlayer cdPlayer(@Qualifier("sgtPeppers") CompactDisc compactDisc) {
//	        CDPlayer cdPlayer = new CDPlayer();
//	        cdPlayer.setCd(compactDisc);
//	        return cdPlayer;
//	    }
}

测试类 

 
@ContextConfiguration(classes = CDPlayerConfig.class)
public class PlayerTest {

     @Autowired
	 private CDPlayer cdPlayer;
	
	 @Test
	 public void cdShouldNotBeNull() {
		assertNotNull(cdPlayer);
		cdPlayer.play();
	 }

}
     

测试结果

Playing Revolver. Revolver's Lonely Hearts Club Band by The Revolver

  每次运行的结果是不同,因为randomBeatlesCD()方法。


XML文件装配Bean

使用xml文件进行配置,配置比较简单易懂。

创建一个Bean

public class ComplexAssembly {

	private Long id;

	private List<String> list;

	private Map<String, String> map;

	private Properties properties;

	private Set<String> set;

	private String[] array;

	// 省略setget方法
}

这个 Bean 没有任何的实际意义,只是为了介绍如何装配这些常用的集合类:

xml文件

<bean id="complexAssembly" class="com.xml.service.ComplexAssembly">
    <!-- 装配Long类型的id -->
    <property name="id" value="1"/>
    
    <!-- 装配List类型的list -->
    <property name="list">
        <list>
            <value>value-list-1</value>
            <value>value-list-2</value>
            <value>value-list-3</value>
        </list>
    </property>
    
    <!-- 装配Map类型的map -->
    <property name="map">
        <map>
            <entry key="key1" value="value-key-1"/>
            <entry key="key2" value="value-key-2"/>
            <entry key="key3" value="value-key-2"/>
        </map>
    </property>
    
    <!-- 装配Properties类型的properties -->
    <property name="properties">
        <props>
            <prop key="prop1">value-prop-1</prop>
            <prop key="prop2">value-prop-2</prop>
            <prop key="prop3">value-prop-3</prop>
        </props>
    </property>
    
    <!-- 装配Set类型的set -->
    <property name="set">
        <set>
            <value>value-set-1</value>
            <value>value-set-2</value>
            <value>value-set-3</value>
        </set>
    </property>
    
    <!-- 装配String[]类型的array -->
    <property name="array">
        <array>
            <value>value-array-1</value>
            <value>value-array-2</value>
            <value>value-array-3</value>
        </array>
    </property>
</bean>

测试方法

     @Test
	 public void getXmlComplexAssembly() {
		 ApplicationContext context = new ClassPathXmlApplicationContext(
	    			new String[]{"applicationContext.xml"}
	    			);
	    	
		 ComplexAssembly complexAssembly = (ComplexAssembly) context.getBean("complexAssembly");
		 System.out.println(complexAssembly.getId());
		 System.out.println(complexAssembly.getMap().get("key1"));
		 
	 }

测试结果

1
value-key-1
  • 总结:
  • List 属性为对应的 <list> 元素进行装配,然后通过多个 <value> 元素设值
  • Map 属性为对应的 <map> 元素进行装配,然后通过多个 <entry> 元素设值,只是 entry 包含一个键值对(key-value)的设置
  • Properties 属性为对应的 <properties> 元素进行装配,通过多个 <property> 元素设值,只是 properties 元素有一个必填属性 key ,然后可以设置值
  • Set 属性为对应的 <set> 元素进行装配,然后通过多个 <value> 元素设值
  • 对于数组而言,可以使用 <array> 设置值,然后通过多个 <value> 元素设值。

命名空间装配

除了上述的配置之外, Spring 还提供了对应的命名空间的定义,只是在使用命名空间的时候要先引入对应的命名空间和 XML 模式(XSD)文件。

  • p-命名空间
  • c-命名空间
  • util-命名空间

p命名空间

使用p命名空间需要添加       xmlns:p="http://www.springframework.org/schema/p

看下使用上面有什么不同吗?

<!-- 使用p标签前 --> 					   
<bean id="complexAssembly" class="com.xml.service.ComplexAssembly">
        <property name="id" value="1"/>
</bean>
<!-- 使用p标签后 -->
<bean name="complexassembly2" class="com.xml.service.ComplexAssembly"  p:id="2" />

c标签

c标签要放到首行

假设有这个类

package pojo;
public class Student { 
   int id; 
   String name;
  public Student(int id, String name) 
  { this.id = id; this.name = name; 
   
  } 
  // setter and getter 
}

 在 c-命名空间和模式声明之后,我们就可以使用它来声明构造器参数了:

<!-- 引入 c-命名空间之前 -->
<bean name="student1" class="pojo.Student">
    <constructor-arg name="id" value="1" />
    <constructor-arg name="name" value="学生1"/>
</bean>

<!-- 引入 c-命名空间之后 -->
<bean name="student2" class="pojo.Student"
      c:id="2" c:name="学生2"/>

util标签

 

<!-- 引入util-命名空间之前 -->
<property name="list">
    <list>
        <ref bean="bean1"/>
        <ref bean="bean2"/>
    </list>
</property>

<!-- 引入util-命名空间之后 -->
<util:list id="list">
    <ref bean="bean1"/>
    <ref bean="bean2"/>
</util:list>

<util:list> 只是 util-命名空间中的多个元素之一,下表提供了 util-命名空间提供的所有元素:

元素描述
<util:constant>引用某个类型的 public static 域,并将其暴露为 bean
<util:list>创建一个 java.util.List 类型的 bean,其中包含值或引用
<util:map>创建一个 java.util.map 类型的 bean,其中包含值或引用
<util:properties>创建一个 java.util.Properties 类型的 bean
<util:property-path>引用一个 bean 的属性(或内嵌属性),并将其暴露为 bean
<util:set>创建一个 java.util.Set 类型的 bean,其中包含值或引用

Spring 作用域

作用域类别描述
singleton(单例)在Spring IoC容器中仅存在一个Bean实例 (默认的scope)
prototype(多例)每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时 ,相当于执行new XxxBean():不会在容器启动时创建对象
request(请求)用于web开发,将Bean放入request范围 ,request.setAttribute("xxx") , 在同一个request 获得同一个Bean
session(会话)用于web开发,将Bean 放入Session范围,在同一个Session 获得同一个Bean
globalSession(全局会话)一般用于 Porlet 应用环境 , 分布式系统存在全局 session 概念(单点登录),如果不是 porlet 环境,globalSession 等同于 Session

 

总结

在Spring中装配bean的三种主要方式:自动化配置、基于Java的显式配置以及基于XML的显式配置。不管你采用什么方 式,这些技术都描述了Spring应用中的组件以及这些组件之间的关系。 

本文中源码:

https://github.com/xiaonongOne/spring-bean

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值