spring 高级装配
一、环境与profile
1、java配置profile
public interface DateSourceInterFace {//下面两个实现的接口
public void printInfo();
}
@Component
@Profile("dev")
public class DevDateSour implements DateSourceInterFace{
@Override
public void printInfo() {
System.out.println("this DEV profile.......");
}
}
@Component
@Profile("pro")
public class ProDateSour implements DateSourceInterFace{
@Override
public void printInfo() {
System.out.println("this Pro profile.......");
}
}
@ComponentScan//spring扫描到spring容器中创建bean对象
@Configuration
public class ProfileConfig {
}
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ProfileConfig.class)
@ActiveProfiles("pro")//当前激活pro环境
public class ProfileTest {
@Autowired
private DateSourceInterFace dateSource;
@Test
public void test01(){
dateSource.printInfo();
}
}
结果:
this Pro profile.......
2、xml中配置profile
一个xml对象相当于一个配置类;如下有两种方式一种是分别两个xml表示两个环境,第二种是一个xml里面配置两个并设置profile属性,如下就是第二种演示。
<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">
<!-- 方式一 -->
<!-- profile="dev">-->
<!-- 方式二 -->
<beans profile="dev">
<bean id="dateSour" class="part03.DevDateSour"/>
</beans>
<beans profile="pro">
<bean id="dateSour" class="part03.ProDateSour"/>
</beans>
</beans>
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/profileContext.xml"})
@ActiveProfiles("pro")
public class ProfileXmlTest {
@Autowired
private DateSourceInterFace dateSource;
@Test
public void test01(){
dateSource.printInfo();
}
}
二、激活profile
1、激活方式有如下几种:
1、作为DispatcherServlet的初始化参数
2、作为web应用的上下文参数
3、作为环境变量
4、使用@ActiveProfiles激活
下面是web.xml中配置
<!-- 方式一、为上下文设置默认profile -->
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</context-param>
<!-- 二、为servlet设置激活的profile -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>spring.profiles.active</param-name>
<param-value>pro</param-value>
</init-param>
</servlet>
<!-- 方式三、不是web.xml中,直接运行war包时配置环境参数 -->
-Dspring.profiles.active="pro"
2、读取方式和顺序
①如果同时设置了 spring.profile.active=dev 和 spring.profile.default=pro ,只激活dev配置。
②如果没有配置spring.profile.active,只配置了spring.profile.default,才会读取default配置。
③那些没有定义在profile中的bean,任何时候都创建。
三、条件化bean @Condition注解
1、@Condition注解的作用
@Condition注解的bean,只有满足实现 Condition 接口的 matches 方法才创建,否则忽略。
@Configuration
public class MagicConfig {
@Bean
//MagicExistsCondition类中match方法返回true就穿件如下bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean() {
return new MagicBean();
}
}
public class MagicExistsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
return env.containsProperty("magic");
}
}
2、Condition 接口使用
Condition 接口的matches方法的两个参数
public interface ConditionContext {
//可以检查bean定义
BeanDefinitionRegistry getRegistry();
//检查bean是否存在,检查bean属性
ConfigurableListableBeanFactory getBeanFactory();
//获取环境中存在或者值时多少
Environment getEnvironment();
//获取resourceLoader加载的资源
ResourceLoader getResourceLoader();
//返回累加器,并检查类是否存在
ClassLoader getClassLoader();
}
AnnotatedTypeMetadata 可以检查@bean注解的方法上的其他注解信息。
public interface AnnotatedTypeMetadata {
//
boolean isAnnotated(String annotationName);
Map<String, Object> getAnnotationAttributes(String annotationName);
Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);
}
3、@Profile注解源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();
}
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
//获取@Profile注解的属性value值
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
//从环境中检查是否含有该vlaue值,有则激活该环境,并创建该bean对象
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
}
return true;
}
}
四、运行时值注入
1、属性占位符 ${…}
①传统xml脚本配置
public class Food {
private String eatFood;
public void setEatFood(String eatFood) {
this.eatFood = eatFood;
}
public String getEatFood() {
return eatFood;
}
@Override
public String toString() {
return "Food{" + "eatFood='" + eatFood + '\'' +'}';
}
}
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="food" class="part03.Food">
<!--运行时动态从文件中读取值-->
<property name="eatFood" value="${food.eatFood}"/>
</bean>
<!--读取配置属性文件 或者 创建PropertySourcesPlaceholderConfigurer读取-->
<context:property-placeholder location="classpath:/part03/runTime.properties"/>
</beans>
classpath:/part03/runTime.properties配置属性文件
food.eatFood=午餐必须带饭
测试类:
@RunWith(SpringJUnit4ClassRunner.class)
//读取xml配置类
@ContextConfiguration(locations = {"classpath:/part03/runTimeConfig.xml"})
public class RuntTimeTest {
@Autowired
private Food food;
@Test
public void test01(){
System.out.println(food.toString());
}
}
结果: Food{eatFood=‘午餐必须带饭’}
②java注解实现 @PropertySource
如上相同的properties文件,不使用xml,直接Food类绑定属性
@Component
@PropertySource(value = {"part03/runTime.properties"})
public class Food {
@Value("${food.eatFood}")
private String eatFood;
public String getEatFood() {
return eatFood;
}
public void setEatFood(String eatFood) {
this.eatFood = eatFood;
}
@Override
public String toString() {
return "Food{" + "eatFood='" + eatFood + '\'' + '}';
}
}
③ 或者配合自动装配和组件扫描,配合@Value注解
@ContextConfiguration(locations = {“classpath:/part03/runTimeConfig.xml”})配合xml配置文件,直接可以读取值到容器中,并使用@Value设置进去
@Value 和 @Autowired 相似用法,创建bean时自动注入值
public class Food {
@Value("${food.eatFood}")
private String eatFood;
public String getEatFood() {
return eatFood;
}
public void setEatFood(String eatFood) {
this.eatFood = eatFood;
}
@Override
public String toString() {
return "Food{" + "eatFood='" + eatFood + '\'' + '}';
}
}
part03/runTimeConfig.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="food" class="part03.Food"/>
<!--读取配置属性文件-->
<context:property-placeholder location="classpath:/part03/runTime.properties"/>
</beans>
测试:
@RunWith(SpringJUnit4ClassRunner.class)
//读取xml配置类
@ContextConfiguration(locations = {"classpath:/part03/runTimeConfig.xml"})
public class RuntTimeTest {
@Autowired
private Food food;
@Test
public void test02(){
System.out.println(food.toString());
}
}
2、spring表达式语言 #{…}
①bean对象的id引用,可以获取对应的属性和方法
②表达式中使用类型
③运算符
④正则表达式
public class Eating {
//引用bean
@Value("#{food}")
private Food food;
//引用bean的属性,但是该属性必须要有get方式的方法才行
@Value("#{food.eatFood}")
private String eatFood;
//引用bean的方法,?是food不为空才执行方法
@Value("#{food?.toString()}")
private String eatFoods;
//使用T表示类型,可调用静态方法或常量
@Value("#{T(System).currentTimeMillis()}")
private Long currentTime;
//使用T表示类型,可调用静态方法或常量
@Value("#{T(Math).PI * 2}")
private Long PiValue;
//正则表达式匹配
@Value("#{food.eatFood matches '.*'}")
private boolean relax;
public void myFood(){
System.out.println(food.toString());
System.out.println(eatFood);
System.out.println(eatFoods);
System.out.println(currentTime);
System.out.println(PiValue);
System.out.println(relax);
}
}
结果:
Food{eatFood='午餐必须带饭'}
午餐必须带饭
Food{eatFood='午餐必须带饭'}
1658414010364
6
true
总结
一个xml配置文件相当于一个配置类
java配置类可以和xml配置一起混合使用,互相引用,注入属性。
@PropertySource 注解可以直接使用@Value读取容器中的属性值