Spring 高级装配

  • 环境与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;

@Configuration
public 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{

	@Override
	public 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")
@Primary
public 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;

@Component
public class IceCream implements Dessert{

	@Override
	public 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{
	@Override
	public 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)
@Qualifier
public @interface Cold {

}

package test;

import org.springframework.stereotype.Component;

@Component
@Cold
public class IceCream implements Dessert{
	@Override
	public void display() {
	}
}

package test;

import org.springframework.beans.factory.annotation.Autowired;

public class Test {
	
	private Dessert dessert;
	
	@Autowired
	@Cold
	public 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 {
	
	@Autowired
	Environment env;

	@Bean
	public 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属性。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值