本文共 10943 字,大约阅读时间需要 36 分钟。
Spring的配置方法概览
正如在一文中提到的,Spring容器负责创建应用中的bean,并通过DI维护这些bean之间的协作关系。作为开发人员,你应该负责告诉Spring容器需要创建哪些bean以及如何将各个bean装配到一起。Spring提供三种装配bean的方式:
- 基于XML文件的显式装配
- 基于Java文件的显式装配
- 隐式bean发现机制和自动装配
自动装配bean
Spring通过两个特性实现自动装配:
- Component scanning——Spring自动扫描和创建应用上下文中的beans;
- Autowiring——Spring自动建立bean之间的依赖关系;
这里用一个例子来说明:假设你需要实现一个音响系统,该系统中包含CDPlayer和CompactDisc两个组件,Spring将自动发现这两个bean,并将CompactDisc的引用注入到CDPlayer中。
创建可发现的beans
首先创建CD的概念——CompactDisc接口,如下所示:
package com.spring.sample.soundsystem;public interface CompactDisc { void play();}
CompactDisc接口的作用是将CDPlayer与具体的CD实现解耦合,即面向接口编程。这里还需定义一个具体的CD实现,如下所示:
package com.spring.sample.soundsystem;import org.springframework.stereotype.Component;@Componentpublic class SgtPeppers implements CompactDisc { private String title = "Sgt. Perppers' Lonely Hearts Club Band"; private String artist = "The Beatles"; public void play() { System.out.println("Playing " + title + " by " + artist); }}
这里最重要的是@Component注解,它告诉Spring需要创建SgtPeppers bean。除此之外,还需要启动自动扫描机制,有两种方法:基于XML配置文件;基于Java配置文件,代码如下(二选一):
- 创建soundsystem.xml配置文件
在这个XML配置文件中,使用<context:component-scan>标签启动Component扫描功能,并可设置base-package属性。
- 创建Java配置文件
package com.spring.sample.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration@ComponentScan(basePackages = "com.spring.sample.soundsystem")public class SoundSystemConfig {}
在这个Java配置文件中有两个注解值得注意:@Configuration表示这个.java文件是一个配置文件;@ComponentScan表示开启Component扫描,并且可以设置basePackages属性——Spring将会设置该目录以及子目录下所有被@Component注解修饰的类。
- 自动配置的另一个关键注解是@Autowired,基于之前的两个类和一个Java配置文件,可以写个测试
package com.spring.sample.soundsystem;import com.spring.sample.config.SoundSystemConfig;import org.junit.Assert;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 = SoundSystemConfig.class)public class SoundSystemTest { @Autowired private CompactDisc cd; @Test public void cdShouldNotBeNull() { Assert.assertNotNull(cd); }}
运行测试,测试通过,说明@Autowired注解起作用了:自动将扫描机制创建的CompactDisc类型的bean注入到SoundSystemTest这个bean中。
给被扫描的bean命名
如果你需要给某个类对应的bean一个特别的名字,则可以给@Component注解传入指定的参数,例如:
@Component("lonelyHeartsClub")public class SgtPeppers implements CompactDisc { ...}pc: @Named 也是一样
设置需要扫描的目标basepackage
在之前的例子中,我们通过给@Component注解传入字符串形式的包路径,来设置需要扫描指定目录下的类并为之创建bean。
可以看出,basePackages是复数,意味着你可以设置多个目标目录,例如:
@Configuration@ComponentScan(basePackages = {"com.spring.sample.soundsystem", "com.spring.sample.video"})public class SoundSystemConfig {}
这种字符串形式的表示虽然可以,但是不具备“类型安全”,因此Spring也提供了更加类型安全的机制,即通过类或者接口来设置扫描机制的目标目录,例如:
@Configuration@ComponentScan(basePackageClasses = {CDPlayer.class, DVDPlayer.class})public class SoundSystemConfig {}
通过如上设置,会将CDPlayer和DVDPlayer各自所在的目录作为扫描机制的目标根目录。
PC:可以做一个空标记(marker interface)作为扫描使用--这些类所在的包会作为组件扫描的基础包(以及其子包)自动装配bean
简单得说,自动装配的意思是让Spring从应用上下文中找到对应的bean的引用,并将它们注入到指定的bean。通过@Autowired注解可以完成自动装配。
例如,考虑下面代码中的CDPlayer类,它的构造函数被@Autowired修饰,表明当Spring创建CDPlayer的bean时,会给这个构造函数传入一个CompactDisc的bean对应的引用。
package com.spring.sample.soundsystem;import org.springframework.beans.factory.annotation.Autowired;@Componentpublic class CDPlayer implements MediaPlayer { private CompactDisc cd; @Autowired public CDPlayer(CompactDisc cd) { this.cd = cd; } public void play() { cd.play(); }}
还有别的实现方法,例如将@Autowired注解作用在setCompactDisc()方法上:
@Autowiredpublic void setCd(CompactDisc cd) { this.cd = cd;}
或者是其他名字的方法上,例如:
@Autowiredpublic void insertCD(CompactDisc cd) { this.cd = cd;}
更简单的用法是,可以将@Autowired注解直接作用在成员变量之上,例如:
@Autowiredprivate CompactDisc cd;
只要对应类型的bean有且只有一个,则会自动装配到该属性上。如果没有找到对应的bean,应用会抛出对应的异常,如果想避免抛出这个异常,则需要设置@Autowired(required=false)。不过,在应用程序设计中,应该谨慎设置这个属性,因为这会使得你必须面对NullPointerException的问题。
如果存在多个同一类型的bean,则Spring会抛出异常,表示装配有歧义,解决办法有两个:(1)通过@Qualifier注解指定需要的bean的ID;(2)通过@Resource注解指定注入特定ID的bean;(3)@Primary
验证自动配置
通过下列代码,可以验证:CompactDisc的bean已经注入到CDPlayer的bean中,同时在测试用例中是将CDPlayer的bean注入到当前测试用例。
package com.spring.sample.soundsystem;import com.spring.sample.config.SoundSystemConfig;import org.junit.Test;import org.junit.runner.RunWith;import org.slf4j.Logger;import org.slf4j.LoggerFactory;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 = SoundSystemConfig.class)public class CDPlayerTest { public final Logger log = LoggerFactory.getLogger(CDPlayerTest.class); @Autowired private MediaPlayer player; @Test public void playTest() { player.play(); }}
基于Java配置文件装配bean
Java配置文件不同于其他用于实现业务逻辑的Java代码,因此不能将Java配置文件业务逻辑代码混在一起。一般都会给Java配置文件新建一个单独的package。
创建配置类
实际上在之前的例子中我们已经实践过基于Java的配置文件,看如下代码:
@Configuration@ComponentScan(basePackageClasses = {CDPlayer.class, DVDPlayer.class})public class SoundSystemConfig {}@Configuration注解表示这个类是配置类,之前我们是通过 @ComponentScan注解实现bean的自动扫描和创建,这里我们重点是学习如何显式创建bean,因此首先将
@ComponentScan(basePackageClasses = {CDPlayer.class, DVDPlayer.class})
这行代码去掉。 定义bean
通过@Bean注解创建一个Spring bean,该bean的默认ID和函数的方法名相同,即sgtPeppers。例如:
@Beanpublic CompactDisc sgtPeppers() { return new SgtPeppers();}
同样,可以指定bean的ID,例如:
@Bean(name = "lonelyHeartsClub")public CompactDisc sgtPeppers() { return new SgtPeppers();}
可以利用Java语言的表达能力,实现类似工厂模式的代码如下:
@Beanpublic CompactDisc randomBeatlesCD() { int choice = (int)Math.floor(Math.random() * 4); if (choice == 0) { return new SgtPeppers(); } else if (choice == 1) { return new WhiteAlbum(); } else if (choice == 2) { return new HardDaysNight(); } else if (choice == 3) { return new Revolover(); }}
JavaConfig中的属性注入
最简单的办法是将被引用的bean的生成函数传入到构造函数或者set函数中,例如:
@Beanpublic CDPlayer cdPlayer() { return new CDPlayer(sgtPeppers());}看起来是函数调用,实际上不是:由于sgtPeppers()方法被 @Bean注解修饰,所以Spring会拦截这个函数调用,并返回之前已经创建好的bean——确保该SgtPeppers bean为单例。
假如有下列代码:
@Beanpublic CDPlayer cdPlayer() { return new CDPlayer(sgtPeppers());}@Beanpublic CDPlayer anotherCDPlayer() { return new CDPlayer(sgtPeppers());}
如果把sgtPeppers()方法当作普通Java方法对待,则cdPlayerbean和anotherCDPlayerbean会持有不同的SgtPeppers实例——结合CDPlayer的业务场景看:就相当于将一片CD同时装入两个CD播放机中,显然这不可能。
默认情况下,Spring中所有的bean都是单例模式,因此cdPlayer和anotherCDPlayer这俩bean持有相同的SgtPeppers实例。
当然,还有一种更清楚的写法:
@Beanpublic CDPlayer cdPlayer(CompactDisc compactDisc) { return new CDPlayer(compactDisc);}@Beanpublic CDPlayer anotherCDPlayer() { return new CDPlayer(sgtPeppers());}
这种情况下,cdPlayer和anotherCDPlayer这俩bean持有相同的SgtPeppers实例,该实例的ID为lonelyHeartsClub。这种方法最值得使用,因为它不要求CompactDisc bean在同一个配置文件中定义——只要在应用上下文容器中即可(不管是基于自动扫描发现还是基于XML配置文件定义)。
基于XML配置文件装配bean
这种是Spring中最原始的定义方式,在此不再详述
混合使用多种配置方法
通常,可能在一个Spring项目中同时使用自动配置和显式配置,而且,即使你更喜欢JavaConfig,也有很多场景下更适合使用XML配置。幸运的是,这些配置方法可以混合使用。
首先明确一点:对于自动配置,它从整个容器上下文中查找合适的bean,无论这个bean是来自JavaConfig还是XML配置。
在JavaConfig中解析XML配置
- 通过@Import注解导入其他的JavaConfig,并且支持同时导入多个配置文件;
@Configuration@Import({CDPlayerConfig.class, CDConfig.class})public class SoundSystemConfig {}
- 通过@ImportResource注解导入XML配置文件;
@Configuration@Import(CDPlayerConfig.class)@ImportResource("classpath: cd-config.xml")public class SoundSystemConfig {}
在XML配置文件中应用JavaConfig
- 通过<import>标签引入其他的XML配置文件;
- 通过<bean>标签导入Java配置文件到XML配置文件,例如
通常的做法是:无论使用JavaConfig或者XML装配,都要创建一个root configuration,即模块化配置定义;并且在这个配置文件中开启自动扫描机制:<context:component-scan>
或者@ComponentScan
。
工程所需的包Maven:
4.0.0 com.qiqi.test springmvc4_test 0.0.1-SNAPSHOT spring新版学习 1.7 javax.servlet javax.servlet-api 3.1.0 javax.servlet.jsp jsp-api 2.1 junit junit 4.12 test org.springframework spring-aop 4.2.5.RELEASE org.springframework spring-beans 4.2.5.RELEASE org.springframework spring-context 4.2.5.RELEASE org.springframework spring-test 4.2.5.RELEASE org.springframework spring-core 4.2.5.RELEASE org.springframework spring-expression 4.2.5.RELEASE org.springframework spring-jdbc 4.2.5.RELEASE org.springframework spring-tx 4.2.5.RELEASE org.springframework spring-web 4.2.5.RELEASE org.springframework spring-webmvc 4.2.5.RELEASE org.springframework spring-aspects 4.2.5.RELEASE org.apache.maven.plugins maven-compiler-plugin
转载地址:https://yuki-ho.blog.csdn.net/article/details/53577089 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!