《spring实战》学习笔记:3.高级装配Bean

使用Spring profile

软件开发过程一般涉及“开发 -> 测试 -> 部署上线”多个阶段,每个阶段的环境的配置参数会有不同,如数据源,文件路径等。为避免每次切换环境时都要进行参数配置等繁琐的操作,可以通过spring的profile功能来进行配置参数的切换。

profile可以理解为我们在Spring容器中所定义的Bean的逻辑组名称,只有当这些Profile被激活的时候,才会将Profile中所对应的Bean注册到Spring容器中。举个更具体的例子,我们以前所定义的Bean,当Spring容器一启动的时候,就会一股脑的全部加载这些信息完成对Bean的创建;而使用了Profile之后,它会将Bean的定义进行更细粒度的划分,将这些定义的Bean划分为几个不同的组,当Spring容器加载配置信息的时候,首先查找激活的Profile,然后只会去加载被激活的组中所定义的Bean信息,而不被激活的Profile中所定义的Bean定义信息是不会加载用于创建Bean的。

配置profile bean

现在假设有三个课程course的实现类,分别是数学math、英语English、语文Chinese.分别对应开发、测试、生成阶段使用的对象。

public interface Course {
    void teach();
}

public class Math implements Course {
    @Override
    public void teach() {
        System.out.println("教数学");
    }
}

public class English implements Course {
    @Override
    public void teach() {
        System.out.println("教英语");
    }
}

public class Chinese implements Course {
    @Override
    public void teach() {
        System.out.println("教语文");
    }
}

1.在xml中配置profile

        方式一:不同的profile配置在不同的配置文件中,开发,测试,生产各对应一个配置文件,在<beans>标签中使用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"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd" profile="dev">

    <bean name="math" class="com.demo.spring.Math"></bean>

</beans>

       方式二:都配置在同一个配置文件中,在根<beans>标签中再嵌套一层<beans>

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <beans profile="dev">
        <bean name="math" class="com.demo.spring.Math" ></bean>
    </beans>

   <beans profile="test">
       <bean name="english" class="com.demo.spring.English"></bean>
   </beans>
    
    <beans profile="pro">
        <bean name="chinese" class="com.demo.spring.Chinese"></bean>
    </beans>

</beans>

2.使用注解配置profile

      方式一,创建三个配置类,在类上使用@Profile注解

@Configuration
@Profile("dev")
public class DevConfig {
    @Bean
    public Math getMath(){
        Math math = new Math();
        return math;
    }
}

    方法二,使用同一个配置类,在不同的方法上使用@Profile注解

@Configuration
public class Config2 {
    @Bean
    @Profile("dev")
    public Math getMath(){
        Math math = new Math();
        return math;
    }
    @Bean
    @Profile("test")
    public English getEnglish(){
        English english = new English();
        return english;
    }
    @Bean
    @Profile("pro")
    public Chinese getChinese(){
        Chinese chinese = new Chinese();
        return chinese;
    }
}

当某个bean被定义了但是没有指定profile时,是一定会被spring容器创建的。

激活Profile

Spring依靠spring.profiles.active和spring.profiles.default两个属性来判断哪些profile需要被激活,如果当spring.profiles.active属性被设置时,那么Spring会优先使用该属性对应值来激活Profile。当spring.profiles.active没有被设置时,那么Spring会根据spring.profiles.default属性的对应值来进行Profile进行激活。如果上面的两个属性都没有被设置,那么就不会有任务Profile被激活,只有定义在Profile之外的Bean才会被创建。要设置这两个属性方式有:

  • 作为SpringMVC中的DispatcherServlet的初始化参数
  • 作为Web 应用上下文中的初始化参数
  • 作为JNDI的入口
  • 作为环境变量
  • 作为虚拟机的系统参数
  • 使用@AtivceProfile来进行激活

这样在切换应用环境时,只需要修改spring.profiles.active的值就可以了。

作为Web 应用上下文中的初始化参数和Servlet的配置参考如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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_3_0.xsd">

    <display-name>Archetype Created Web Application</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath*:/applicationContext*.xml
        </param-value>
    </context-param>

    <!-- 在上下文context-param中设置profile.default的默认值 -->
    <context-param>
        <param-name>spring.profiles.default</param-name>
        <param-value>development</param-value>
    </context-param>

    <!-- 在上下文context-param中设置profile.active的默认值 -->
    <!-- 设置active后default失效,web启动时会加载对应的环境信息 -->
    <context-param>
        <param-name>spring.profiles.active</param-name>
        <param-value>development</param-value>
    </context-param>

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 在DispatcherServlet参数中设置profile的默认值,active同理 -->
        <init-param>
            <param-name>spring.profiles.default</param-name>
            <param-value>development</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

测试代码如下:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Config.class)
@ActiveProfiles("dev")
public class TeacherTest {
   @Autowired
   private Course course;
   @Test
   public void test(){
       course.teach();
   }
}

测试结果:

教数学

条件化的bean

使用@Conditional注解,主要作用于@Bean上,判断给定的条件,条件成立时,才会去创建这个Bean.常用使用场景是根据不同的系统创建不同的bean.通过@Target可以知道它可作用于类,方法中。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

如存在一个服务接口

public interface ListService {
	public String showListCmd();

在不同系统下有不同的实现:

public class WindowsListService implements ListService {
 
	@Override
	public String showListCmd() {
		
		return "windows";
	}
 

public class LinuxListService implements ListService {
 
	@Override
	public String showListCmd() {
		
		return "linux";
	}
 
}

我们希望Windows系统中创建WindowsListService服务。则使用@Conditional注解如下;

public class ConditionConfig {	 
	@Bean
	@Conditional(WindowsCondition.class)// 使用@Conditional注解,符合Windows条件就实例化WindowsListService
	public ListService windowsListService() {
		return new WindowsListService();
	}
}

@Conditional注解需要传入一个Conditon接口的实现类。

public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}

Conditon接口只有一个方法matches,只需要根据需要自定义mathces方法即可。了解下这两个参数

ConditionContext是个接口:

public interface ConditionContext {
    BeanDefinitionRegistry getRegistry();
    ConfigurableListableBeanFactory getBeanFactory();
    Environment getEnvironment();
    ResourceLoader getResourceLoader();
    ClassLoader getClassLoader();
}
  • 借助getRegistry()返回的BeanDefinitionRegistry检查bean定义;
  • 借助getBeanFactory()返回的ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性;
  • 借助getEnvironment()返回的Environment检查环境变量是否存在以及它的值是什么;常用
  • 读取并探查getResourceLoader()返回的ResourceLoader所加载的资源;
  • 借助getClassLoader()返回的ClassLoader加载并检查类是否存在。

 AnnotatedTypeMetadata接口:

public interface AnnotatedTypeMetadata {
    boolean isAnnotated(String var1);

    @Nullable
    Map<String, Object> getAnnotationAttributes(String var1);

    @Nullable
    Map<String, Object> getAnnotationAttributes(String var1, boolean var2);

    @Nullable
    MultiValueMap<String, Object> getAllAnnotationAttributes(String var1);

    @Nullable
    MultiValueMap<String, Object> getAllAnnotationAttributes(String var1, boolean var2);
}

AnnotatedTypeMetadata则能够让我们检查带有@Bean注解的方法上还有什么其他的注解

所以要实现一个WindowsConditon来判断环境是不是Windows:


public class WindowsCondition implements Condition {
 
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		return context.getEnvironment().getProperty("os.name").contains("Windows");
	}

现在WindowsListService就只会在Windows环境中创建了。

Spring运行时值注入

1.使用Environment类获取外部值

存在一个Teacher类

public class Teacher {
    private String name;
    private String age;

    public Teacher(String name, String age) {
        this.name = name;
        this.age = age;
    }

    void work(){
        System.out.println(name + ":" +age);
    }

}

存在配置文件config.properties内容如下:

theacher.age = 11
theacher.name = xxxx

如下配置便可获取配置文件中的值,以后只需修改配置文件即可

@Configuration
@PropertySource("classpath:config.properties")
public class DevConfig {
    @Autowired
    Environment env;
    @Bean
    public Teacher getTeacher(){
        String name = env.getProperty("teacher.name");
        String age = env.getProperty("teacher.age");
        return new Teacher(name, age);
    }
}

2.使用属性占位符

Spring一直支持将属性定义到外部的属性的文件中,并使用占位符值将其插入到Spring bean中。在Spring装配中,占位符的形式为使用“${... }”包装的属性名称。

1.使用@Value注解,修改Teacher类如下;


@PropertySource("classpath:config.properties")
public class Teacher {
    @Value("${teacher.name}")
    private String name;
    @Value("${teacher.age}")
    private String age;

    public void work(){
        System.out.println(name+age);
    }
}

引入PropertySourcePlaceholderConfigurer,会解析占位符。

  @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
      return new PropertySourcesPlaceholderConfigurer();
  }

2.在配置文件中引入properties文件:

  <context:property-placeholder location="classpath:config.properties"/>

3.使用SpEL表达式

使用#{...}进行占位。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值