第3章 高级装配
来源:互联网 发布:php 字符串转换成html 编辑:程序博客网 时间:2024/05/19 17:22
高级装配:
- Spring profile
- 条件化的bean声明
- 自动装配与歧义性
- bean的作用域
- Spring表达式语言
3.1 环境与profile
@bean(destroyMethod="shutdown")
public DataSource dataSource()
{
return new EmbeddedDatabaseBuilder()
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
这会创建一个类型为javax.sql.DataSource的bean,这个bean是 如何创建出来的才是最有意思的。使用EmbeddedDatabaseBuilder会搭建一个嵌入式的Hypersonic数 据库,它的模式(schema)定义在schema.sql中,测试数据则是通过 test-data.sql加载的。
schema.sql: create table Things (id identity,name varchar(100));
test-data.sql: insert into Things (name) values ('A')
当你在开发环境中运行集成测试或者启动应用进行手动测试的时候, 这个DataSource是很有用的。每次启动它的时候,都能让数据库处 于一个给定的状态。
尽管EmbeddedDatabaseBuilder创建的DataSource非常适于开 发环境,但是对于生产环境来说,这会是一个糟糕的选择。在生产环境的配置中,你可能会希望使用JNDI从容器中获取一个DataSource。
@Bean
public DataSource dataSource()
{
JudiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) judiObjectFactoryBean.getObject();
}
通过JNDI获取DataSource能够让容器决定该如何创建这 个DataSource,甚至包括切换为容器管理的连接池。即便如此, JNDI管理的DataSource更加适合于生产环境,对于简单的集成和开发测试环境来说,这会带来不必要的复杂性。
在QA环境中,你可以选择完全不同的DataSource配置,可 以配置为Commons DBCP连接池。
@Bean(destoryMethod="close")
public DataSource dataSource()
{
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl("jdbc:h2:tcp://dbserver/~/test");
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUsername("sa");
dataSource.setInitialSize(20);
dataSource.setMaxActive(30);
return dataSource;
}
的三个版本的dataSource()方法互不相同。虽然 它们都会生成一个类型为javax.sql.DataSource的bean,但它们 的相似点也仅限于此了。每个方法都使用了完全不同的策略来生成DataSource bean。
这里的讨论并不是如何配置DataSource(我们将会 在第10章更详细地讨论这个话题)。
在不同的环境 中某个bean会有所不同。我们必须要有一种方法来配置DataSource,使其在每种环境下都会选择最为合适的配置。
一种方式就是在单独的配置类(或XML文件)中配置每个bean, 然后在构建阶段(可能会使用Maven的profiles)确定要将哪一个配置 编译到可部署的应用中。这种方式的问题在于要为每种环境重新构建应用。当从开发阶段迁移到QA阶段时,重新构建也许算不上什么大 问题。但是,从QA阶段迁移到生产阶段时,重新构建可能会引入bug 并且会在QA团队的成员中带来不安的情绪。
Spring所提供的解决方案并不需要重新构建。
3.1.1 配置profile bean
Spring为环境相关的bean所提供的解决方案其实与构建时的方案没有 太大的差别。当然,在这个过程中需要根据环境决定该创建哪个bean 和不创建哪个bean。不过Spring并不是在构建的时候做出这样的决策,而是等到运行时再来确定。这样的结果就是同一个部署单元(可 能会是WAR文件)能够适用于所有的环境,没有必要进行重新构建。
在3.1版本中,Spring引入了bean profile的功能。要使用profile,你首先要将所有不同的bean定义整理到一个或多个profile之中,在将应用部署到每个环境时,要确保对应的profile处于激活(active)的状态。
在Java配置中,可以使用@Profile注解指定某个bean属于哪一个 profile。例如,在配置类中,嵌入式数据库的DataSource可能会配 置成如下所示:
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")
.builid();
}
}
@Profile注解应用在了类级别上。它会告诉 Spring这个配置类中的bean只有在dev profile激活时才会创建。如果 dev profile没有激活的话,那么带有@Bean注解的方法都会被忽略掉。
同时,你可能还需要有一个适用于生产环境的配置,如下所示:
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();
}
}
你也可以在方法级别上使用@Profile注解, 与@Bean注解一同使用。这样的话,就能将这两个bean的声明放到同 一个配置类之中,如下所示:
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.EmbeddedDatabaseType;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jndi.JndiObjectFactoryBean;
@Configuration
public class DataSourceConfig
{
@(destoryMethod="shutdown")
@Profile("dev")
public DataSource embeddedDataSource()
{
return new EmbeddedDatabaseSource()
.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.setResource(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}
}
在XML中配置profile
我们也可以通过<beans>元素的profile属性,在XML中配置 profile 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:jdbc="http://www.springframework.org/schema/jdbc";
xsi:schemaLocation="
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframawork.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd";
profile="dev">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:schema.sql" />
<jdbc:script location="classpath:test-data.sql" />
</jdbc:embedded-database>
</beans>
与之类似,我们也可以将profile设置为prod,创建适用于生产环境的从JNDI获取的DataSource bean。同样,可以创建基于连接池定义的DataSource bean,将其放在另外一个XML文件中,并标注为qaprofile。所有的配置文件都会放到部署单元之中(如WAR文件),但是只有profile属性与当前激活profile相匹配的配置文件才会被用到。
还可以在根<beans>元素中嵌套定义<beans>元素,而不是为每个环境都创建一个
profile XML文件。这能够将所有的profile bean定义 放到同一个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";
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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">;
<beans profile="dev">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:schema.sql" />
<jdbc:script location="classpath:test-data.sql" />
</jdbc:embedded-database>
</beans>
<beans profile="qa">
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-methd="close"
p:url="jdbc:h2:tcp://dbserver/~/test"
p:driverClassName="org.h2.Driver"
p:username="sa"
p:password="password"
p:initialSize="20"
p:maxActive="30" />
</beans>
<beans profile="prod">
<jee:jndi-lookup id="dataSource"
jndi-name="jdbc/myDatabase"
resource-ref="true"
proxy-interface="javax.sql.DataSource" />
</beans>
</beans>
这里有三个bean,类型都 是javax.sql.DataSource,并且ID都是dataSource。但是在运行时,只会创建一个bean,这取决于处于激活状态的是哪个profile。
3.1.2 激活profile
Spring在确定哪个profile处于激活状态时,需要依赖两个独立的属性:spring.profiles.active和spring.profiles.default。如果设置了 spring.profiles.active属性的话,那么它的值就会用来确定哪个profile是激活的。但如果没有设置spring.profiles.active属性的话,那Spring将会查找spring.profiles.default的值。 如果spring.profiles.active和spring.profiles.default 均没有设置的话,那就没有激活的profile,因此只会创建那些没有定义在profile中的bean。
有多种方式来设置这两个属性:
- 作为DispatcherServlet的初始化参数;
- 作为Web应用的上下文参数;
- 作为JNDI条目;
- 作为环境变量;
- 作为JVM的系统属性;
- 在集成测试类上,使用@ActiveProfiles注解设置;
在Web应用中,设置spring.profiles.default的web.xml文件会如下所示:
为上下文设置默认的profile,为Servlet设置默认的profile
<?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-name>dev</param-name>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listerner>
<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>
<url-pattern>/<url-pattern>
</servlet-mapping>
</web-app>
按照这种方式设置spring.profiles.default,所有的开发人员都能从版本控制软件中获得应用程序源码,并使用开发环境的设置 (如嵌入式数据库)运行代码,而不需要任何额外的配置。
当应用程序部署到QA、生产或其他环境之中时,负责部署的人根据情况使用系统属性、环境变量或JNDI设置spring.profiles.active即可
你可以同时激活多个profile,这可以通过列出多个profile名称,并以逗号分隔来实现。
使用profile进行测试
当运行集成测试时,通常会希望采用与生产环境(或者是生产环境的部分子集)相同的配置进行测试。但是,如果配置中的bean定义在了 profile中,那么在运行测试时,我们就需要有一种方式来启用合适的 profile。
Spring提供了@ActiveProfiles注解,我们可以使用它来指定运行测试时要激活哪个profile。在集成测试时,通常想要激活的是开发环境的profile。例如,下面的测试类片段展现了使 用@ActiveProfiles激活dev profile:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={persistenceTestConfig.class})
@ActiveProfiles("dev")
public class PersistenceTest
{
...
}
阅读全文
0 0
- 第3章 高级装配
- 第3章 装配Bean---高级装配--笔记3
- 第3章 装配Bean---高级装配--笔记1
- 第3章 装配Bean---高级装配--笔记2
- 第三章 高级装配
- spring(3)高级装配
- 第2章 装配Bean---java显示装配---笔记3
- 高级装配
- 第2章 装配Bean
- 第三章 高级Bean的装配
- spring 第三章 高级装配 小结
- 【Spring In Action】Section 3 高级装配
- 第2章 装配Bean---xml显示装配---笔记4
- 第 3 章 高级主题
- Spring Bean 高级装配
- SpringInAction4th--高级装配
- 【Spring 核心】高级装配
- Spring 高级装配
- POI设置样式之类
- SpringCloud零基础上手(二)——SpringBoot多模块maven项目
- Oracle sys和system用户、sysdba 和sysoper系统权限、sysdba和dba角色的区别
- FPGA基础知识篇----深入学习串口模块
- LeetCode--Recover Binary Search Tree
- 第3章 高级装配
- java 输出字节流
- ES6你必须掌握的新特性
- 第12周项目2 Kruskal算法的验证
- 2017新疆农业机械展览会(新疆农机展)会刊(参展商名录)
- 制作HTML5游戏1
- jquery的显示隐藏
- 如何查看linux系统安装了哪些服务?
- 机器学习-数学期望