上篇文章(http://blog.csdn.net/u014527058/article/details/76682378)介绍了一种Spring下的logback多profile配置方案,但是这种方法过于粗暴,由于是直接拉取JVM参数获得当前profile的值,而在激活profile时,JVM参数却只是其中一种方式,假如profile是使用其他方法配置的,这样做就行不通了。擅长刨根问底的我自然是很不甘心,非要想办法找到完美一点的解决方案不可。经过一番大查特查之后,终于找到了解决方案。
首先依赖包自然是少不了,这里就再贴一遍吧。
清单1 build.gradle依赖包配置
//Log
compile 'org.slf4j:slf4j-api:1.7.21'
compile 'org.slf4j:jcl-over-slf4j:1.7.21'
compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
compile group: 'org.logback-extensions', name: 'logback-ext-spring', version: '0.1.4'
现在我们假定有三个环境,分别为dev(开发环境)、test(测试环境)和prod(生产环境),在src/main/resources下存在这三个logback配置文件:
- logback.dev.xml
- logback.test.xml
- logback.prod.xml
配置的思路和上篇差不多,都是先通过Environment获取当前的profile,再通过profile生成对应logback配置文件的路径,然后根据路径手动指定logback所使用的配置文件。不过相比于上篇文章采用获取JVM参数的方式获取profile名称,这次我们采用Spring自带的Environment接口来获取profile。为什么说Environment能够获取Spring所使用的profile呢?我们先来看看Environment的接口定义。
清单2 Environment接口定义
package org.springframework.core.env;
public interface Environment extends PropertyResolver {
String[] getActiveProfiles();
String[] getDefaultProfiles();
boolean acceptsProfiles(String... profiles);
}
此外,Environment是一个由Spring自动生成的bean,可以直接bean中通过@Autowired, @Resource等注解或者以XML的<property>元素进行注入,也可以在Spring 4的Java config类中通过传入参数的方式注入。类似于这样:
清单3 Environment在Bean中自动注入
@Autowired
private Environment environment;
清单4 Environment在Java config中自动注入
@Bean
public ExampleBean getExampleBean(Environment environment) {
//do something
}
不难发现,这种获取profile的方式,比之从JVM参数中获取,确实要优雅得多。
另外一个要说明的是,由于这次是在Spring中获取的profile,比WebApplicationInitializer的加载要晚一些,所以在配置logback文件时,就不能在WebApplicationInitializer里面配了。而是要在Spring里面进行配置。logback-ext-spring包里面有一个LogbackConfigurer类,通过调用里面的initLogging(String location)方法(该方法是static void方法,参数是配置文件路径),手动地在Spring中指定logback所使用的配置文件。这样的话,如何手动指定配置文件路径的问题也解决了。
但是另一个问题又来了,LogbackConfigurer.initLogging(location)这个method该如何触发呢?这就需要我们想办法让Spring在创建bean容器的过程中调用它。好在我们还有MethodInvokingFactoryBean。MethodInvokingFactoryBean,顾名思义就是方法调用FactoryBean,只要在Spring中配置了MethodInvokingFactoryBean,Spring在创建bean容器的时候就可以自动调用这个FactoryBean中指定类的指定方法。具体使用方法示意如下:
清单5 MethodInvokingFactoryBean使用方法示意
@Bean
public MethodInvokingFactoryBean factoryBean() {
MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
factoryBean.setTargetClass(Class<?> targetClass);
factoryBean.setTargetMethod(String targetMethod);
factoryBean.setArguments(setArguments(Object[] arguments));
return factoryBean;
}
profile获取、指定配置文件路径和触发问题都解决了,我们就可以优雅地在Spring中对logback进行多profile配置了。
-------------懒人分界线,不想看原理的童鞋可以直接跳到这里---------------
先上配置:
清单6 Java config配置文件
package org.fhp.logbackdemo.config;
import ch.qos.logback.ext.spring.LogbackConfigurer;
import org.fhp.logbackdemo.util.SpringProfileUtils;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
@Configuration
@ComponentScan("org.fhp.logbackdemo")
public class SpringRootConfig {
@Bean
public MethodInvokingFactoryBean logbackConfigurer(Environment environment) {
String currentProfile = SpringProfileUtils.getProfileFromEnvironment(environment);
MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
factoryBean.setTargetClass(LogbackConfigurer.class);
factoryBean.setTargetMethod("initLogging");
factoryBean.setArguments(new String[]{String.format("classpath:logback.%s.xml", currentProfile)});
return factoryBean;
}
}
其中,SpringProfileUtils的实现如下:
清单7 SpringProfileUtils的实现
package org.fhp.logbackdemo.util;
import org.springframework.core.env.Environment;
public class SpringProfileUtils {
public static String getProfileFromEnvironment(Environment environment) {
String[] profiles = environment.getActiveProfiles();
if(null != profiles && profiles.length != 0) {
return profiles[0];
}
profiles = environment.getDefaultProfiles();
if(null != profiles && profiles.length != 0) {
return profiles[0];
}
throw new IllegalStateException("Must specify a spring profile in the environment!");
}
}