Spring实战-读书笔记(章节三)-高级装配

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fly_zxy/article/details/78534728

Spring profile

Spring profile是在Spring3.1引入的功能。它的作用是根据环境创建不同的Bean,是条件化创建Bean的一种方式。我们的项目可能在开发环境、测试环境和生产环境上部署,三种环境需要使用不同的DataSource,假设对于三种环境的不同DataSource有三个Bean,我们需要根据环境来切换到合适的Bean。这样一个部署文件(如war文件),修个一个配置值在运行时完成对Bean的选择。Spring profile类似于MyBatis中根据不同的数据库厂商使用不同的SQL语句访问数据库功能。其实仔细一想以前我们都是通过标示加载不同的类,每个程序员都是自己的实现,现在Spring帮你做了,你只需要学会怎么用即可,越来越Spring了。
注意:如果bean没有没有profile的配置范围内,此bean总是会被Spring识别。

在javaConfig以及自动装配方式上使用profile功能

涉及注解

@Profile:标记在类(通常和@Configuratin、@Conponent一同使用)或方法(通常同@Bean一同使用)上表示创建的Bean属于那个环境,@Profile("dev")其中dev表示环境名。
@ActiveProfiles:标记在类上表示激活那个环境,@ActiveProfiles("dev")其中dev表示环境名,通常和@ContextConfiguration一起使用,用于测试环境。

事例代码

package com.zhangxy.chapter_3.profiles.main;


import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
public class DataSourceConfig {

	@Bean(name="dataSource")
	@Profile("dev")//指定Bean所属的环境
	public DataSource getDataSource() {
		System.out.println("dev dataSource");
		return null;
	}
	
	@Bean(name="dataSource")
	@Profile("pro")
	public DataSource getJNDISource() {
		System.out.println("pro dataSource");
		return null;
	}
        //没有使用@Pprofile指定属于是那个环境,无论激活那种环境此Bean会一直被创建
	@Bean(name="c3p0DataSource")
	public DataSource getC3P0DataSource() {
		System.out.println("C3P0DataSource");
		return null;
	}
}
~~~测试类~~~
package com.zhangxy.chapter_3.profiles.test;

import javax.sql.DataSource;

import org.junit.Test;
import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.zhangxy.chapter_3.profiles.main.DataSourceConfig;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=DataSourceConfig.class)
@ActiveProfiles("pro")//激活那个环境下的Bean
public class DataSourceTest {
	public final StandardOutputStreamLog log = new StandardOutputStreamLog();

	@Autowired
	private DataSource dataSource;
	@Autowired
	private DataSource c3p0DataSource;
	
	@Test
	public void test() {
/*		输出结果:
		C3P0DataSource
		pro dataSource*/
	}
}

在XML配置上使用profile功能

在XML使用profile功能配置多个<beans>元素,通过profile属性指定环境名,<beans>元素下的所有bean都会归属这个环境。详见截图:

激活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的系统属性;

条件化的Bean

条件化的Bean和profile是很类似的,是在Spring4.0引入的功能。我们可能会根据某个Bean是否存在来决定是否创建另一个Bean,根据配置文件的值来决定是否创建一个Bean。还是拿不同部署环境使用不同DataSource的应用场景,看一下条件化的Bean如何实现。

涉及注解:

@Conditional:同@Bean或@Component一同使用,根据条件结果(true or fasle)决定是否加载Bean,参数是一个实现了Condition接口类的Class类型。

事例代码

事例代码实现了:方法上标记有@UseDataSource注解就会创建此Bean,否则不创建Bean
package com.zhangxy.chapter_3.conditional.main;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface UseDataSource {

}
package com.zhangxy.chapter_3.conditional.main;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class UserDataSourceCondition implements Condition {

	@Override
	public boolean matches(ConditionContext arg0, AnnotatedTypeMetadata arg1) {
		return arg1.isAnnotated(UseDataSource.class.getName());
		//return true;
	}

}
package com.zhangxy.chapter_3.conditional.main;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface UseDataSource {

}
~~~测试类~~~
package com.zhangxy.chapter_3.conditional.test;

import static org.junit.Assert.assertEquals;

import javax.sql.DataSource;

import org.junit.Test;
import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
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;

import com.zhangxy.chapter_3.conditional.main.DataSourceConfig;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=DataSourceConfig.class)
public class DataSourceTest {
	public final StandardOutputStreamLog log = new StandardOutputStreamLog();

	@Autowired
	private DataSource dataSource;
	@Autowired
	private DataSource c3p0DataSource;
	
	@Test
	public void test() {
/*		输出结果:
		C3P0DataSource
		dev dataSource*/
	}
}

处理自动装配的歧义性

在使用自动装配时如果依赖的类型找到了多个实现Bean,那Spring将会无法做出选择,进而抛出异常。我们可以通过两个注解(@Primary、@qualifier)来解决这种歧义性。

@Primary注解

@Primary注解
标记在使用了@Component注解的类上或使用了@Bean注解的方法上,表示此Bean为首先Bean,当Spring找到多个实现Bean时,会将标记有@Primary注解的Bean注入到依赖类型。注意:如果有多个Bean标记了@Primary注解相当于没有首选Bean。这个注解在一个特定情况下还是很有用的,比如说:我们注入的类型是AClass,一开始我们只有一个AClassImpl_1实现,我们使用自动装配,为AClass类型注入AClassImpl_1 Bean,随着然后得扩展我们有增加了AClassImpl_2和AClassImpl_3,这样就有了多个实现Bean,我们在之前的代码上注入AClassImpl_1就需要显示的指定(要打开每个类去改了),这个时候我们可以把AClassImpl_1 Bean 标记成@Primary。
通过XML配置也可以指定一个bean为首选bean,例如:
<bean primary="true"/>

@qualifier注解

标记在使用了@Component注解的类上或使用了@Bean注解的方法上,表示为bean指定一个限定符。
标记在使用@AutoWired的字段、方法或构造函数上,表示注入bean的限定符。
注意(很重要):Spring会将bean id作为Bean的默认限定符。

什么是限定符?

我么可以把限定符理解成给Bean打上的标签或Bean的某些特点,我们用一个字符来表示这些标签或特点。也可以把限定符理解成Bean的名称(类似于bean元素的name属性)。通过这些标签或特点我们可以确定唯一一个Bean。就像一个人有叫做马云,大众叫他阿里CEO,他儿子叫他爸爸,他妈叫他小马,但是无论他有多少个别名和称号,他的身份证ID是唯一的(bean id),如果有人找马云,可能会这么说:我找阿里CEO马云(这句化符合了两个标签或特点)、我找马云他是我爸爸、当然在计算机中你也可以根据他的身份证ID去搜索马云的个人信息。

事例代码

package com.zhangxy.chapter_3.qualifier.main;

public interface Dessert {
	public String getName();
}
package com.zhangxy.chapter_3.qualifier.main;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component("id-cake")
@Qualifier("q-cake")
public class Cake implements Dessert {

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return "Cake";
	}

}
package com.zhangxy.chapter_3.qualifier.main;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component("id-cookie")
@Qualifier("q-cookie")
public class Cookie implements Dessert {

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return "Cookie";
	}

}
package com.zhangxy.chapter_3.qualifier.main;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component("id-iceCream")
@Qualifier("q-iceCream")
public class IceCream implements Dessert {

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return "IceCream";
	}

}
package com.zhangxy.chapter_3.qualifier.main;

public class Popsicle implements Dessert {

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return "Popsicle";
	}
}
<?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="id-popsicle" name="q-Popsicle-1 q-Popsicle-2" class="com.zhangxy.chapter_3.qualifier.main.Popsicle"></bean>
    
</beans>
package com.zhangxy.chapter_3.qualifier.main;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.core.env.Environment;

@Configuration
@ComponentScan(/*basePackages= {"com.zhangxy.chapter_3.qualifier"}*/)
@ImportResource("\\com\\zhangxy\\chapter_3\\qualifier\\main\\config.xml")
public class SystemConfig {

}
~~~测试类~~~
package com.zhangxy.chapter_3.qualifier.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.zhangxy.chapter_3.qualifier.main.Dessert;
import com.zhangxy.chapter_3.qualifier.main.SystemConfig;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=SystemConfig.class)
public class QualifierTest {
	
	@Autowired
	//@Qualifier("id-cake")//根据bean id限定符注入
	//@Qualifier("q-iceCream")//根据自定义限定符注入
	@Qualifier("q-Popsicle-1")//根据XML中bean的name属性注入
	private Dessert dessert;
	Environment env;

	@Test
	public void test() {
		System.out.println("dessert->"+dessert.getName());
	}
	
}

bean的作用域

Spring定义了多种作用域,可以基于这些作用域创建bean,包括:
单例(Singleton):在整个应用中,只创建bean的一个实例。
原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
会话(Session):在Web应用中,为每个会话创建一个bean实例。
请求(Rquest):在Web应用中,为每个请求创建一个bean实例。

运行时值注入

我们有时会需要从property文件中读取值注入到Bean中,我看看Spring是如何实现此功能的。

使用javaConfig配置实现

@PropertySource:标记在使用@Configuration的方法上,表示导入的properties文件。
一下事例都需要用到的properties文件。属性值是中文,为不出现乱码使用Unicode对中文进行编码。
disc.title=\u4e00\u4e2a\u4eba\u7684\u884c\u674e
disc.artist=\u6234\u4f69\u59ae
disc.track=MP4,MP3

事例代码

package com.zhangxy.chapter_3.into_Property_bean.javaConfig.main;

public interface CompactDisc {
  void play();
}
package com.zhangxy.chapter_3.into_Property_bean.javaConfig.main;

import java.util.List;

public class BlankDisc implements CompactDisc {

  private String title;
  private String artist;
  private List<String> tracks;

  public BlankDisc(String title, String artist, List<String> tracks) {
    this.title = title;
    this.artist = artist;
    this.tracks = tracks;
  }

  public void play() {
    System.out.println("Playing " + title + " by " + artist);
    for (String track : tracks) {
      System.out.println("-Track: " + track);
    }
  }

}
package com.zhangxy.chapter_3.into_Property_bean.javaConfig.main;

import java.util.Arrays;

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(value="classpath:com\\zhangxy\\chapter_3\\into_Property_bean\\config.properties")
public class SystemConfig {

	@Autowired
	Environment env;

	@Bean
	public CompactDisc blankDisc() {
		String[] arrays = env.getProperty("disc.track").split(",");
		return new BlankDisc(env.getProperty("disc.title"), env.getProperty("disc.artist"), Arrays.asList(arrays));
	}
}
~~~测试类~~~
package com.zhangxy.chapter_3.into_Property_bean.javaConfig.test;

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;

import com.zhangxy.chapter_3.into_Property_bean.javaConfig.main.CompactDisc;
import com.zhangxy.chapter_3.into_Property_bean.javaConfig.main.SystemConfig;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SystemConfig.class)
public class JavaConfigIntoTest {

	@Autowired
	private CompactDisc compactDisc;
	
	@Test
	public void test() {
		compactDisc.play();
	}
}

使用XML配置实现

使用bean标签创建 org.springframework.beans.factory.config.PropertyPlaceholderConfigurer 类型bean,传入properties文件即可。

事例代码

package com.zhangxy.chapter_3.into_Property_bean.xmlConfig.main;

public interface CompactDisc {
  void play();
}
package com.zhangxy.chapter_3.into_Property_bean.xmlConfig.main;

import java.util.Arrays;
import java.util.List;

public class BlankDisc implements CompactDisc {

  private String title;
  private String artist;
  private List<String> tracks;

  public BlankDisc(String title, String artist, String tracks) {
    this.title = title;
    this.artist = artist;
    this.tracks = Arrays.asList(tracks.split(","));
  }

  public void play() {
    System.out.println("Playing " + title + " by " + artist);
    for (String track : tracks) {
      System.out.println("-Track: " + track);
    }
  }

}
<?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="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>com\zhangxy\chapter_3\into_Property_bean\config.properties</value>
			</list>
		</property>
	</bean>
	
	<bean id="blankDisc"
		class="com.zhangxy.chapter_3.into_Property_bean.xmlConfig.main.BlankDisc">
		<constructor-arg value="${disc.title}" />
		<constructor-arg value="${disc.artist}"></constructor-arg>
		<constructor-arg value="${disc.track}"></constructor-arg>
	</bean>

</beans>
~~~测试类~~
package com.zhangxy.chapter_3.into_Property_bean.xmlConfig.test;

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;

import com.zhangxy.chapter_3.into_Property_bean.xmlConfig.main.CompactDisc;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:com\\zhangxy\\chapter_3\\into_Property_bean\\xmlConfig\\main\\blankDisc-config.xml")
public class XmlConfigIntoTest {

	@Autowired
	private CompactDisc compactDisc;
	
	@Test
	public void test() {
		compactDisc.play();
	}
}

自动装配实现

@Value:需要注入属性值,用${}表达式的形式获取值,例如:@Valuer("jdbc.username")

事例代码

package com.zhangxy.chapter_3.into_Property_bean.autowired.main;

public interface CompactDisc {
  void play();
}
package com.zhangxy.chapter_3.into_Property_bean.autowired.main;

import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class BlankDisc implements CompactDisc {

	@Value("${disc.title}")
  private String title;
	@Value("${disc.artist}")
  private String artist;
	@Value("${disc.track}")
  private String trackStr;
	
 private List<String> tracks;
	
  public BlankDisc() {

  }
  
/*  public BlankDisc(@Value("${disc.title}")String title, @Value("${disc.artist}")String artist, @Value("${disc.track}")String trackStr) {
    this.title = title;
    this.artist = artist;
    this.trackStr = trackStr;
  }*/

  public void play() {
	  this.tracks = Arrays.asList(this.trackStr.split(","));
    System.out.println("Playing " + title + " by " + artist);
    for (String track : tracks) {
      System.out.println("-Track: " + track);
    }
  }

}
package com.zhangxy.chapter_3.into_Property_bean.autowired.main;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;

@Configuration
@ComponentScan
@PropertySource(value="classpath:com\\zhangxy\\chapter_3\\into_Property_bean\\config.properties",name="UTF-8")
public class SystemConfig {
	//###此处必须创建 placeholderConfigurer bean,否则${}表达式无法解析,当作字符串"${}"注入
	//###或在XML配置文件中添加 <context:property-placeholder/> 配置
	@Bean
	public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
		return new PropertySourcesPlaceholderConfigurer();
	}
}
~~~测试类~~~
package com.zhangxy.chapter_3.into_Property_bean.autowired.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.PropertySource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.zhangxy.chapter_3.into_Property_bean.autowired.main.CompactDisc;
import com.zhangxy.chapter_3.into_Property_bean.autowired.main.SystemConfig;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SystemConfig.class)
public class AutoWiredTest {
	
	@Autowired
	private CompactDisc compactDisc;
	
	@Test
	public void test1() {
		compactDisc.play();
	}
}








猜你喜欢

转载自blog.csdn.net/fly_zxy/article/details/78534728