上篇文章(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提供了getActiveProfiles()和getDefaultProfiles()分别返回当前的Active Profile和Default Profile。由于Spring允许指定多个profile,所以返回类型为String数组类型。还有一个acceptsProfiles()方法,返回指定的profile是否被激活,多个profile之间为OR的关系。 此外,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配置了。 -------------懒人分界线,不想看原理的童鞋可以直接跳到这里--------------- 好了,思路和原理已经讲清楚了,现在我们假定每次只会同时指定一个profile。如果因为某些场景的确需要指定多个profile,那么在获取profile字符串这一块就需要变通一下,不过通过Environment获取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!"); } }
