您的位置:首页 > 编程语言 > Java开发

Spring 4 学习笔记6:依赖注入(配置)

2016-09-28 07:04 567 查看
《Spring in Action》4th Edition 学习笔记

Spring 注入 bean 可以有如下三种方式:

自动注入

显式注入(java 配置)

显式注入(xml 配置)

自动注入

自动注入主要使用如下的技术:

Component scanning:Spring 自动发现需要在应用上下文中生成的 bean

Autowiring:自动注入 bean 依赖

配置可被发现的 beans

使用
@Component
注解配置可被发现的 Bean,默认名称为类名(首字母小写)

package soundsystem;
import org.springframework.stereotype.Component;

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

}


配置 Bean 的名称:

@Component("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc {
...
}


定义 Spring 注入配置类

使用
@Configuration
注解标明这是 spring 的配置类。使用
@ComponentScan
注解开启 spring component scanning。

package soundsystem;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class CDPlayerConfig {
}


@Component
注解默认会扫描配置类所在的包,也可以通过
basePackages
属性配置需要扫描的包。

@Configuration
@ComponentScan("soundsystem")//扫描 soundsystem 包
public class CDPlayerConfig {}

// 或者使用 basePackages 属性
@Configuration
@ComponentScan(basePackages="soundsystem")
public class CDPlayerConfig {}

// 配置多个包
@Configuration
@ComponentScan(basePackages={"soundsystem", "video"})
public class CDPlayerConfig {}

// 使用 class 或 interface 指定需要扫描的包
@Configuration
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
public class CDPlayerConfig {}


basePackages
:使用字符串配置,不那么类型安全

basePackageClasses
:使用 java 配置,类型安全(对重构友好)

Note:当使用
basePackageClasses
的时候,可以在需要扫描的包中定义一个空的标签接口(empty marker interface),这样可实现与业务代码的分离。

自动注入依赖

使用
@Autowired
注解自动注入依赖。无论你将
@Autowired
注解放在
构造函数
,
setter方法
还是
任意方法
上,Spring 都会尝试去自动注入定义在方法参数中的依赖。当且仅有一个匹配的 Bean 的时候,Spring 就会自动注入。当匹配多个 Bean 时,Spring 会抛出异常。你也可以通过设置
@Autowired
注解的
required
属性设置这个依赖是否为必须。

package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;

// 构造函数注入
@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}

public void play() {
cd.play();
}

}

// setter方法注入
@Autowired
public void setCompactDisc(CompactDisc cd) {
this.cd = cd;
}

//  任意方法注入
@Autowired
public void insertDisc(CompactDisc cd) {
this.cd = cd;
}

// 设置依赖可为null
// 当spring在容器中不能找到对应的依赖时,不会抛出异常
@Autowired(required=false)
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}


显式注入(Java 配置)

Java 配置 VS XML 配置

Java:更强大、类型安全、重构友好

XML:更灵活

Note:使用 Java 配置时,应该将 JavaConfig 和业务代码分开,最好使用不同的包管理。因为 JavaConfig 毕竟是配置文件,所以不应该和业务逻辑代码混一块。

@Bean
注解声明 Bean

使用
@Bean
注解生成对象的方法,Spring 就会注册这个方法返回的对象到 Spring 应用上下文中。

默认注册的 Bean 的名称为方法名,也可以通过
@Bean
注解的
name
属性定义 Bean 名称。

package soundsystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CDPlayerConfig {

@Bean
public CompactDisc compactDisc() {
return new SgtPeppers();
}

}


// 指定注册 bean 的名称
@Bean(name="lonelyHeartsClubBand")
public CompactDisc compactDisc() {
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 {
return new Revolver();
}
}


如何在 JavaConfig 中声明依赖于其他 Bean 的 Bean?只需要在声明方法的参数中指定需要的依赖类型,Spring 会自动帮你注入。(无论该方法依赖的 Bean 是在 JavaConfig 中声明的,还是 Xml 配置中声明的)

// 构造器注入
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}

// setter方法注入
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
CDPlayer cdPlayer = new CDPlayer(compactDisc);
cdPlayer.setCompactDisc(compactDisc);
return cdPlayer;
}


显式声明(XML 配置)

XML 作为 Spring 以前的配置方法被大量的使用,所以有必要学习如何通过 XML 配置 Spring。但是,当开始一个新的项目时,推荐使用 Java 配置。

创建 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context">

<!-- configuration details go here -->

</beans>


Note:可使用 Spring Tool Suite 自动生成 XML 配置文件

声明一个简单的 Bean

<bean id="compactDisc" class="soundsystem.SgtPeppers" />


<bean>
元素和 JavaConfig 中的
@Bean
注解含义相同。
id
指定了 bean 的名称,
class
指定了 bean 的类型。当 Spring 遇到
<bean>
元素时,它会调用
SgtPepers
的默认构造函数来创建 bean。

正因为
class
属性使用字面量,所以不能进行编译时检查,不过可以使用 IDE 来进行类型的检查。

很多时候,声明一个 bean,还需要初始化很多属性,那么如何使用 Spring 注入这些属性呢?

构造器注入

<constructor-arg>
元素

c-namespace

属性注入

<property>
元素

p-namespace

其中
c-namespace
p-namespace
是 Spring 3.0 引入的新的配置方法,更简洁。但是相比于
<constructor-arg>
<property>
元素,少了集合注入的功能。

使用构造器注入初始化 bean

在 Xml 中配置构造器依赖注入有两种方式:

<constructor-arg>
元素:结构复杂

Spring 3.0 中引入的 c-namespace:简洁,不能注入集合

Bean 引用的构造器注入

使用
<constructor-arg>


<bean id=" class="soundsystem.CDPlayer">
<constructor-arg ref="compactDisc" />
</bean>


使用 c-namespace。首先声明 c-namespace 的 xml schema,然后注入:

<?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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 
<bean id="compactDisc" class="soundsystem.SgtPeppers" />

<bean id="cdPlayer" class="soundsystem.CDPlayer"
c:cd-ref="compactDisc" />

</beans>




也可以使用参数列表的方式注入:

<!-- 注入第一个参数 -->
<bean id="cdPlayer" class="soundsystem.CDPlayer"
c:_0-ref="compactDisc" />

<!-- 如果只有一个参数,可省略参数index -->
<bean id="cdPlayer" class="soundsystem.CDPlayer"
c:_-ref="compactDisc" />


其中
_0
表示第 0 个参数,如果有多个参数依次为
_1
,
_2
。如果,仅有一个构造器参数,那么可以省略参数索引,直接使用
_
就行了。
-ref
后缀表示这是一个引用,而不仅仅是个字面量。

构造器注入字面量

有时候我们不需要引用其他的 bean,只需要自己指定字面量参数,构造器的字面量注入如下:

<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
<constructor-arg value="The Beatles" />
</bean>

<!-- c-namespace -->
<bean id="compactDisc" class="soundsystem.BlankDisc"
c:_title="Sgt. Pepper's Lonely Hearts Club Band"
c:_artist="The Beatles" />

<!-- 使用参数索引 -->
<bean id="compactDisc" class="soundsystem.BlankDisc"
c:_0="Sgt. Pepper's Lonely Hearts Club Band"
c:_1="The Beatles" />

<!-- 只有一个参数,可忽略索引 -->
<bean id="compactDisc" class="soundsystem.BlankDisc"
c:_="Sgt. Pepper's Lonely Hearts Club Band" />


构造器集合注入

因为 c-namespace 不支持集合的注入,所以要注入集合只能使用
<constructor-arg>
元素:

<!-- 注入null -->
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
<constructor-arg value="The Beatles" />
<constructor-arg><null/></constructor-arg>
</bean>

<!-- 集合注入字面量 -->
<bean id="compactDisc" class="soundsystem.collections.BlankDisc">
<constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
<constructor-arg value="The Beatles" />
<constructor-arg>
<list>
<value>Sgt. Pepper's Lonely Hearts Club Band</value>
<value>With a Little Help from My Friends</value>
<value>Lucy in the Sky with Diamonds</value>
</list>
</constructor-arg>
</bean>


如果需要注入的集合中的元素为对象,那么可以使用
<ref>
元素:

// 需注入的构造器
public Discography(String artist, List<CompactDisc> cds) { ... }


<bean id="beatlesDiscography" class="soundsystem.Discography">
<constructor-arg value="The Beatles" />
<constructor-arg>
<list>
<ref bean="sgtPeppers" />
<ref bean="whiteAlbum" />
<ref bean="hardDaysNight" />
<ref bean="revolver" />
</list>
</constructor-arg>
</bean>


属性注入初始化 bean

构造器注入 VS 属性注入:当属性为必须时使用构造器注入,属性非必填时使用构造器注入。

属性注入引用对象

属性注入使用
<property>
元素:

<bean id="cdPlayer" class="soundsystem.CDPlayer">
<property name="compactDisc" ref="compactDisc" />
</bean>


使用 p-namespace 注入:

<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 
<bean id="cdPlayer" class="soundsystem.properties.CDPlayer"
p:compactDisc-ref="compactDisc" />

</beans>


属性注入字面量

<bean id="compactDisc"
class="soundsystem.properties.BlankDisc">
<property name="title" value="Sgt. Pepper's Lonely Hearts Club Band" />
<property name="artist" value="The Beatles" />
<property name="tracks">
<list>
<value>Sgt. Pepper's Lonely Hearts Club Band</value>
<value>With a Little Help from My Friends</value>
<value>Lucy in the Sky with Diamonds</value>
</list>
</property>
</bean>


<bean id="compactDisc"
class="soundsystem.properties.BlankDisc"
p:title="Sgt. Pepper's Lonely Hearts Club Band"
p:artist="The Beatles">
<property name="tracks">
<list>
<value>Sgt. Pepper's Lonely Hearts Club Band</value>
<value>With a Little Help from My Friends</value>
<value>Lucy in the Sky with Diamonds</value>
</list>
</property>
</bean>


使用
util-namespace
简化注入集合,
util-namespace
包含如下工具:

元素描述
<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
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> 
<bean id="compactDisc"
class="soundsystem.properties.BlankDisc"
p:title="Sgt. Pepper's Lonely Hearts Club Band"
p:artist="The Beatles"
p:tracks-ref="trackList" />

<util:list id="trackList">
<value>Sgt. Pepper's Lonely Hearts Club Band</value>
<value>With a Little Help from My Friends</value>
<value>Lucy in the Sky with Diamonds</value>
</util:list>

</beans>


Autowiring 中包冲突解决

Autowiring 能够很方便的为我们自动注入需要的依赖,但是如果需要注入的接口不止一个实现类,那 Spring 如何帮我们定位我们需要的依赖呢?

假设你需要注入一个
Dessert
接口的实现类:

@Autowired
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}


现在有如下三个实现类:

@Component
public class Cake implements Dessert { ... }

@Component
public class Cookies implements Dessert { ... }

@Component
public class IceCream implements Dessert { ... }


因为三个实现类都继承自
Dessert
接口,所以自动注入时 Spring 就不能确定注入哪一个,它就会抛出
NoUniqueBeanDefinitionException
异常。

那么有哪些方法能够帮助 Spring 消除这种不明确的情况呢?

设置一个 primary bean

Qualifying autowired beans

设置 primary bean

当有多个选择时,我们可以通过设置一个 Primary Bean 来告诉 Spring 使用这个 Bean。可以有如下三种方式指定 Primary bean:

// 自动注入时声明一个primary bean
@Component
@Primary
public class IceCream implements Dessert { ... }

// 在JavaConfig中显式注入时
@Bean
@Primary
public Dessert iceCream() {
return new IceCream();
}

// 或者在XML中显式注入时指定primary bean
<bean id="iceCream" class="com.desserteater.IceCream" primary="true" />


现在,如果 Spring 碰到有多个选择的时候,它会选择哪个被设置为 Primary Bean 的 Bean 来注入。但是,如果你需要根据不同情况使用(注入)不同的实现类呢?比如,在这个类中你可能需要注入
IceCream
,而在另一种情况下你需要注入
Cake
呢。这种情况下就需要为这些类添加某种限定符

Qualifying autowired beans

默认情况下每个未显式声明限定符的 bean,都有一个默认的限定符——bean ID。所以,我们在注入时可以使用这个 bean ID 限定符来限定注入的 bean。

在使用
@Autowired
@Inject
注解时,
@Qualifier
注解是使用限定符的主要方式:

@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}


在本例中,Spring 注入时就会去寻找限定符为字符串 iceCream 的 bean,而默认情况下,每个 bean 的 ID 为首字母小写的类名,而 bean ID 又是默认的限定符,所以
IceCream
类就被选中,被注入了。

但是,使用 bean ID 作为限定符有一个缺点:和实现类高度耦合。如果你把类
IceCream
重构为
Gelato
,那么你需要修改注入时
@Qualifier
注解的值,以便使用重构后的类。所以,有什么办法能够做到松耦合呢?答案就是创建自定义限定符

自定义限定符

除了使用默认的 bean ID 限定符,在声明 bean 的时候还可以创建自定义的限定符:

@Component
@Qualifier("cold")
public class IceCream implements Dessert { ... }


注入时也和使用 bean ID 时一样,使用
@Qualifier
注解来指定使用的限定符:

@Autowired
@Qualifier("cold")
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}


这样,你就可以在重构
IceCream
类的同时,不用修改
setDessert
方法,保证了松耦合。

定义自定义限定符注解

如果,一个限定符还是无法满足你的要求,你就需要自己定义限定符的注解,因为 java 不允许一个类使用多个相同的注解。

定义自定义限定符注解(使用
@Qualifier
注解该注解就行):

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold { }

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Creamy { }


声明 bean 时:

@Component
@Cold
@Creamy
public class IceCream implements Dessert { ... }

@Component
@Cold
@Fruity
public class Popsicle implements Dessert { ... }


注入 bean 时:

@Autowired
@Cold
@Creamy
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}


配置的导入和混合

在一个典型的 Spring 应用中,你可能需要同时用到 JavaConfig 和 XML 配置,那么我们如何用两种不同的配置方式来配置一个容器呢?这就需要用到 Spring 配置的导入功能了。

因为 Spring 有 Java 和 XML 两种配置方式,所以导入也就有如下四种:

JavaConfig 导入 JavaConfig

JavaConfig 导入 XML 配置

XML 配置导入 XML 配置

XML 配置导入 JavaConfig

NOTE:如果你的 Spring 应用真的存在多个配置文件,建议创建一个高层级的配置文件。这个文件中不配置任务的 Bean,只是用来导入各个不同配置文件的
root configuration
。这样,显得配置就很清晰,可以一下定位应用的入口。而且,建议在
root configuration
中来开启 component scanning
<context:component-scan>
或则
@ComponentScan
)功能。

JavaConfig 导入 JavaConfig

使用
@Import
注解导入:

package soundsystem;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(CDPlayerConfig.class)
public class SoundSystemConfig {
}


或者在高层级配置文件中导入所有的 JavaConfig:

package soundsystem;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({CDPlayerConfig.class, CDConfig.class})
public class SoundSystemConfig {
}


JavaConfig 导入 XML 配置

使用
@ImportResource
注解在 JavaConfig 中导入 XML 配置:

package soundsystem;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;

@Configuration
@Import(CDPlayerConfig.class)
@ImportResource("classpath:cd-config.xml")
public class SoundSystemConfig {
}


XML 配置导入 XML 配置

使用
<import>
元素导入 XML 配置:

<import resource="cd-config.xml" />


XML 配置导入 JavaConfig

要在 XML 中导入 Java,只需要在 XML 配置中将 JavaConfig 声明为一个 Bean 就行了:

<?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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 
<!-- 导入JavaConfig -->
<bean class="soundsystem.CDConfig" />

<bean id="cdPlayer"
class="soundsystem.CDPlayer"
c:cd-ref="compactDisc" />

</beans>


JavaConfig 使用 properties 配置文件

为了不把数据库配置等信息硬编码到代码中,很多时候我们都倾向于使用
properties
文件。那么,如何使用这些文件呢,参考文章:

Spring @PropertySource example

PropertySource API

环境和配置

每个应用几乎都会遇到从一个环境迁移到另一个环境的问题,大多数时候我们需要为不同的环境设置不同的值,比如数据库配置、加密算法以及和其他系统的集成。

比如,我们需要为开发生产环境配置不同的数据库源

// 开发环境
@Bean(destroyMethod = "shutdown")
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}

// 生产环境
@Bean
public DataSource jndiDataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}


以上,为不同的环境声明了不同的 bean,这些 bean 都是
DataSource
实例。那么,如何实现不同的环境使用不同的数据库配置呢?

一种方案:将这些 bean 分别配置在不同的配置 class (或 XML 文件)中,然后在构建时确定包含哪个(可能使用 Maven profiles),这种方式的弊端在于每次修改配置都需要重新构建。

另一种方案:使用 Spring profile bean。

配置 Profile beans

Spring profile bean 是在运行时确定使用哪种配置,而且是通过根据不同的环境创建不同的 bean 的方式来实现的。这样的话一个相同构建单元(比如 WAR 包)就能应付所有的环境,不需要针对不同的环境去进行不同的构建。

在 Spring 3.1 中,Spring 引入了 bean profiles。你需要将不同环境使用的配置分别声明在不同的配置文件中,然后告诉 Spring 这个配置是哪个环境下使用的,那么 Spring 就会在不同的环境下做出正确的选择。

JavaConfig

在 JavaConfig 中,使用
@Profile
注解标明这个 bean 属于哪个 profile:

package com.myapp;
import javax.activation.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;

// 开发环境
@Configuration
@Profile("dev")
public class DevelopmentProfileConfig {

@Bean(destroyMethod="shutdown")
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
}


package com.myapp;
import javax.activation.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jndi.JndiObjectFactoryBean;

// 生产环境
@Configuration
@Profile("prod")
public class ProductionProfileConfig {

@Bean
public DataSource dataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}
}


这样,Spring 就会根据激活的 profile选择不同的配置。在 Spring 3.1 中,你只能在 class level 使用
@Profile
注解。从 Spring 3.2 开始,你可以在 method level 使用
@Profile
注解了,和
@Bean
注解一起。这样,就能将配置都写在一个 JavaConfig 文件中了。

package com.myapp;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jndi.JndiObjectFactoryBean;

@Configuration
public class DataSourceConfig {

@Bean(destroyMethod = "shutdown")
@Profile("dev")
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}

@Bean
@Profile("prod")
public DataSource jndiDataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}

}


XML 配置 profile

<?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:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation=" http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<beans profile="dev">
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="classpath:schema.sql" />
<jdbc:script location="classpath:test-data.sql" />
</jdbc:embedded-database>
</beans>

<beans profile="prod">
<jee:jndi-lookup id="dataSource"
lazy-init="true"
jndi-name="jdbc/myDatabase"
resource-ref="true"
proxy-interface="javax.sql.DataSource" />
</beans>
</beans>


激活 profiles

Spring 使用两个属性来确定激活的 profile:
spring.profiles.active
spring.profiles.default
。Spring 首先使用
spring.profiles.active
设置的值,如果
spring.profiles.active
没有设值,那么 Spring 就使用
spring.profiles.default
的值。如果
spring.profiles.active
spring.profiles.default
都没有设值,那么没有任何 profile 被激活。

有如下方式可以设置这两个属性:

DispatcherServlet
的初始化参数

Web 应用的 context 参数

JNDI entries

环境变量

JVM 系统变量

在集成测试类中使用
@ActiveProfiles
注解

那么,
spring.profiles.active
spring.profiles.default
两个属性该如何选择呢?一种方式是在
DispatcherServlet
的初始化参数和 web 应用的 context 参数(用于
ContextLoaderListener
)中设置
spring.profiles.default
为开发环境,当切换到其他环境时再通过设置环境变量等其他方式设置
spring.profiles.active
来激活不同的 profile。

在 web.xml 中设置
spring.profiles.default


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>

<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</context-param>

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>

<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>


在测试中使用 profile

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={PersistenceTestConfig.class})
@ActiveProfiles("dev")
public class PersistenceTest {
...
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring