Spring 高级装配

来源:互联网 发布:软件开发大牛 编辑:程序博客网 时间:2024/05/16 08:23
  • 环境与profile

1.配置profile bean

如下代码所示:

在开发环境中会创建一个javax.sql.DataSource的bean。用EmbeddedDatabaseBuilder会搭建一个嵌入式的HyperSonic数据库。他的模式(schema)定义在schema.sql中,测试数据则是通过test-data.sql加载的。

在生产环境中通过JNDI容器中获取一个DataSource,

在Qa环境中则配置Commons DBCP连接池。

package test;import javax.activation.DataSource;import org.apache.commons.dbcp.BasicDataSource;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;@Configurationpublic class DataSourceConfig {/** * 开发环境 * @return */@Bean(destroyMethod="shutdown")@Profile("dev")public DataSource dataSource() {return (DataSource) new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).addScript("classpath:schema.sql").addScript("classpath:test-data.sql").build();}/** * QA环境 * @return */@Bean@Profile("qa")public DataSource qaDataSource(){BasicDataSource dataSource = new BasicDataSource();dataSource.setUrl("jdbc:h2:tcp://dbserver/~/test");dataSource.setDriverClassName("org.h2.Driver");dataSource.setUsername("sa");dataSource.setPassword("password");dataSource.setInitialSize(20);dataSource.setMaxActive(30);return (DataSource) dataSource;}/** * 生产环境 * @return */@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();}}
注:尽管每个DataSource bean 都背声明在一个profile中,并且只有当规定的profile被激活时,相应的bean才会被创建。而没有用指定profile的bean始终都会被创建,与激活哪个profile没有关系。

  • 在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-4.3.xsd    http://www.springframework.org/schema/jdbc    http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd    http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-4.3.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><!-- QA环境 --><beans profile="qa"><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="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,但是在运行的时候只会创建一个bean,这取决于哪个bean被激活。

2. 激活profile

Spring在确定哪个profile处于激活状态时,需要两个独立的属性:spring.profiles.active和spring.profiles.default。如果设置了spring.profiles.active属性,那么它的值就用来确定哪个profile被激活。否则就查找spring.profile.default的值。如果两个均没有设置,那就没有激活的profile,因此只会创建那些没有定义在profile的bean。

通过以下几种方式设置这两个属性:

作为DispatcherServlet的初始化参数;

作为Web应用的上下文参数;

作为JNDI条目;

作为环境变量;

作为JVM的系统属性;

在继承测试类上,使用@ActiveProfile注解设置

在web.xml文件中设置默认的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/applicationContext.xml</param-value></context-param><!-- 为上下文设置默认的profile --><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><!-- 为Servlet设置默认的profile --><init-param><param-name>spring.profile.default</param-name><param-value>dev</param-value></init-param></servlet><servlet-mapping><servlet-name>appServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping></web-app>
按照这种方式设置的spring.profile.default,所有的开发人员都能从版本控制软件中获得应用程序源码,并使用开发环境设置运行代码,不需要额外的配置。

当系统使用spring.profile.active以后,spring.profile.default将没有任何作用,系统会有限使用spring.profile.active的值。

  • 条件化的bean

假设有一个名为MagicBean的类,我们希望在只有设置了magic环境属性的时候,Spring才会实例化这个类。

当matches()方法的返回值为真的时候则会创建MagicBean,否则不会创建。

package restfun;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Conditional;public class MagicConfig {/** * 条件化的创建bean * @return */@Bean@Conditional(MagicExistsCondition.class)public MagicBean magicBean() {return new MagicBean();}}

MagicExistsCondition需要实现Condition接口并且实现其中的matches()方法。

ConditionContext中的getRegistry()方法返回的BeanDefinitionRegistry检查bean定义。

ConditionContext中的getBEanFactory()方法返回的ConfigurableListBeanFactory检查bean是否存在,甚至探查bean的属性。

ConditionContext中的getEnvironment()方法返回的Environment检查环境变量是否存在以及它的值是什么。

ConditionContext中的getResourceLoader()方法获取返回的ResourceLoader所加载的资源。

ConditionContext中的getClassLoader()方法获取返回的ClassLoader所加载的资源是否存在。


package restfun;import org.springframework.context.annotation.Condition;import org.springframework.context.annotation.ConditionContext;import org.springframework.core.env.Environment;import org.springframework.core.type.AnnotatedTypeMetadata;public class MagicExistsCondition implements Condition{@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {Environment env = context.getEnvironment();//检查magic属性return env.containsProperty("magic");}}

  • 处理自动装配的歧义性

当有多个bean能够匹配结果的话,这种歧义性会阻碍Springz自动装配属性、构造器参数或方法参数。

处理这个方式有两个:标示首选的bean和限定自动装配的bean.

1.标示首选的bean.

通过在@Component和@Primary结合来使用

package soundsystem;import javax.inject.Named;import org.springframework.context.annotation.Primary;import org.springframework.stereotype.Component;@Component("lonelyHearsClub")@Primarypublic class SgtPeppers implements CompactDisc{.....}

<bean id="cdPlayer" class="xmlCongfig.CDPlayer" primary="true" />

2.限定自动装配的bean

(1)@Qualifier注解是使用限定符的主要方式。为@Qualifier注解所设置的参数就是想要注入的bean的ID。

package test;import org.springframework.stereotype.Component;@Componentpublic class IceCream implements Dessert{@Overridepublic void display() {// TODO Auto-generated method stub}}

@Autowired@Qualifier("iceCream")public void setDessert(Dessert dessert) {this.dessert = dessert;}
(2)使用自定义的限定符

为IceCraem类定义其限定符为cold。则在Java配置bean的时候就必须如下配置@Qualifier("cold")。

package test;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.stereotype.Component;@Component@Qualifier("cold")public class IceCream implements Dessert{@Overridepublic void display() {}}

package test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;public class Test {private Dessert dessert;@Autowired@Qualifier("cold")public void setDessert(Dessert dessert) {this.dessert = dessert;}public void fn() {dessert.display();}}
(3)使用自定义的限定符注解

可以自定义一个限定符注解,如定义了一个@Cold的限定符,该限定符必须实现Qualifier注解。然后在IceCream与bean配置中使用该限定符即可


package test;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.beans.factory.annotation.Qualifier;@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic @interface Cold {}

package test;import org.springframework.stereotype.Component;@Component@Coldpublic class IceCream implements Dessert{@Overridepublic void display() {}}

package test;import org.springframework.beans.factory.annotation.Autowired;public class Test {private Dessert dessert;@Autowired@Coldpublic void setDessert(Dessert dessert) {this.dessert = dessert;}public void fn() {dessert.display();}}

  •  bean的作用域

spring中bean的作用域有:

1.单例(Singleton):在整个应用中,只创建bean的一个实例

2.原型(prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例

3.会话(session):在Web应用中,为每个会话请求创建一个bean实例

4.请求(request):在Web应用中,为每个请求创建一个bean实例

package scoped;import org.springframework.beans.factory.config.ConfigurableBeanFactory;import org.springframework.context.annotation.Scope;import org.springframework.context.annotation.ScopedProxyMode;import org.springframework.stereotype.Component;import org.springframework.web.context.WebApplicationContext;@Component/** * 原型模式   @Scope("prototype") 或者   @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)   在xml文件中的配置形式   <bean id="notepad" class="scoped.Notepad" scope="protype" />       会话模式   @Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES)          */@Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES)public class Notepad {}

在XML中声明作用域代理

<bean id="notepad" class="scoped.Notepad" scope="session">    <aop:scoped-proxy proxy-target-class="false"/>    </bean>
<aop:scoped-proxy>是与@Scope注解的proxyMode属性功能相同的SpringXML配置元素。它会告诉Spring为bean创建一个作用域代理。默认情况下,它会使用CGLib创建目标代理。但是将proxy-target-class属性设置为false,进而要求它生成基于接口的代理。

  • 运行时注入

1.注入外部的值

在Spring中,处理外部值得最简单的方式是声明属性源并通过Spring的Environment来检索属性。

package externals;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.PropertySource;import org.springframework.core.env.Environment;@Configuration//声明属性源@PropertySource("classpath:app.properties")public class ExpressiveConfig {@AutowiredEnvironment env;@Beanpublic BlankDisc disc() {return new BlankDisc(env.getProperty("disc.title"),env.getProperty("disc.artist"));}}

2 使用Spring表达式语言进行装配

Spring表达式语言(Spring ExpressionLanguage, SpEL), 它能够以一种强大和简洁的方式将值装配到bean属性和构造器参数中,在这个过程中所使用的表达式会在运行时计算得到的值。“#{.......}”

SpEL的特性:

使用bean的ID来引用bean;

使用方法和访问对象的属性;

对值进行算术、关系和逻辑运算;

正则表达式匹配;

集合操作。


引用bean、属性和方法

#{sgtPeppers.artist}其中sgtPeppers为一个bean的ID,并且使用了该bean的artist属性。


0 0
原创粉丝点击