背景
J2Cache是OsChina的两级缓存实现框架 但是代码比较老 代码考虑也比较死板 不支持多种配置
对于多profile的实现来说也 只能使用通过Maven的profile实现【不支持动态变更】
public class J2Cache { private final static Logger log = LoggerFactory.getLogger(J2Cache.class); private final static String CONFIG_FILE = "/j2cache.properties"; private final static CacheChannel channel; private final static Properties config; static { try { config = loadConfig(); String cache_broadcast = config.getProperty("cache.broadcast"); if ("redis".equalsIgnoreCase(cache_broadcast)) channel = RedisCacheChannel.getInstance(); else if ("jgroups".equalsIgnoreCase(cache_broadcast)) channel = JGroupsCacheChannel.getInstance(); else throw new CacheException("Cache Channel not defined. name = " + cache_broadcast); } catch (IOException e) { throw new CacheException("Unabled to load j2cache configuration " + CONFIG_FILE, e); } } public static CacheChannel getChannel(){ return channel; } public static Properties getConfig(){ return config; } /** * 加载配置 * @return * @throws IOException */ private static Properties loadConfig() throws IOException { log.info("Load J2Cache Config File : [{}].", CONFIG_FILE); InputStream configStream = J2Cache.class.getClassLoader().getParent().getResourceAsStream(CONFIG_FILE); if(configStream == null) configStream = CacheManager.class.getResourceAsStream(CONFIG_FILE); if(configStream == null) throw new CacheException("Cannot find " + CONFIG_FILE + " !!!"); Properties props = new Properties(); try{ props.load(configStream); }finally{ configStream.close(); } return props; } }
很明显此处代码必须要读取j2cache.properties
那么在不同环境【profile】想要使用不同的配置只有通过maven在编译时修改配置文件了【或者同样的其他手段】
思路
- 为了最小化的修改代价 不建议直接将所有的配置放到SpringBoot的application-${profile}.properties中 因为比如其他Jar中都是依靠J2Cache.getChannel() 来获取对应的cacheChannel
- 那么我们可以修改读取的配置文件即可【换言之我们考虑读取不同的j2cache.properties】
- 保留静态方法 但是静态代码块移除【以免一旦加载到该Class就会直接初始化】
- 在getChannel等方法做检查必须提前调用初始化方法
方案
/** * 缓存入口 * * @author winterlau */ public class J2Cache { private final static Logger log = LoggerFactory.getLogger(J2Cache.class); private final static String CONFIG_FILE = "/j2cache.properties"; private static CacheChannel channel; private static Properties config; private static AtomicBoolean initlized = new AtomicBoolean(false); public static void init() { init(CONFIG_FILE); } public static void init(String filePath) { if (initlized.compareAndSet(false, true)) { try { config = loadConfig(filePath); String cache_broadcast = config.getProperty("cache.broadcast"); if ("redis".equalsIgnoreCase(cache_broadcast)) { channel = RedisCacheChannel.getInstance(); } else if ("jgroups".equalsIgnoreCase(cache_broadcast)) { channel = JGroupsCacheChannel.getInstance(); } else { initlized.set(false); throw new CacheException("Cache Channel not defined. name = " + cache_broadcast); } } catch (IOException e) { initlized.set(false); throw new CacheException("Unabled to load j2cache configuration " + filePath, e); } } else { log.warn("J2cache initlized alerday!"); } } private static void checkInitlized() { if (!initlized.get()) { throw new CacheException("J2cache init not yet! You should call j2Cache.init(String) first!"); } } public static CacheChannel getChannel() { checkInitlized(); return channel; } public static Properties getConfig() { checkInitlized(); return config; } /** * 加载配置 * * @param filePath * @return * @throws IOException */ private static Properties loadConfig(String filePath) throws IOException { if (filePath == null || filePath.trim().length() == 0) filePath = CONFIG_FILE; log.info("Load J2Cache Config File : [{}].", filePath); InputStream configStream = J2Cache.class.getResourceAsStream(filePath); if (configStream == null) configStream = CacheManager.class.getResourceAsStream(filePath); if (configStream == null) throw new CacheException("Cannot find " + filePath + " !!!"); Properties props = new Properties(); try { props.load(configStream); } finally { configStream.close(); } return props; } }
基于目前SpringBoot的使用我们创建对应的Starter
创建配置类
package net.oschina.j2cache.autoconfigure; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "j2cache") public class J2CacheConfig { private String configLocation = "/j2cache.properties"; public String getConfigLocation() { return configLocation; } public void setConfigLocation(String configLocation) { this.configLocation = configLocation; } }
创建初始化调用类
package net.oschina.j2cache.autoconfigure; import net.oschina.j2cache.J2Cache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.PostConstruct; public class J2CacheIniter { private static Logger logger = LoggerFactory.getLogger(J2CacheIniter.class); private final J2CacheConfig j2CacheConfig; public J2CacheIniter(J2CacheConfig j2CacheConfig) { this.j2CacheConfig = j2CacheConfig; } @PostConstruct public void init() { J2Cache.init(j2CacheConfig.getConfigLocation()); } }
根据条件判断加载
package net.oschina.j2cache.autoconfigure; import net.oschina.j2cache.J2Cache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; @ConditionalOnClass(J2Cache.class) @EnableConfigurationProperties({J2CacheConfig.class}) @Configuration public class J2CacheAutoConfigure { private static Logger logger = LoggerFactory.getLogger(J2CacheAutoConfigure.class); private final J2CacheConfig j2CacheConfig; public J2CacheAutoConfigure(J2CacheConfig j2CacheConfig) { this.j2CacheConfig = j2CacheConfig; } @ConditionalOnMissingBean(J2CacheIniter.class) public J2CacheIniter j2CacheIniter() { return new J2CacheIniter(j2CacheConfig); } }
创建META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ net.oschina.j2cache.autoconfigure.J2CacheAutoConfigure
这样就会自动加载对应的
J2CacheAutoConfigure 可以参考SpringBoot之自动配置
使用
系统中我们使用J2Cache作为SpringCache的实现
比如我们可以如此
/* * Copyright (c) 2017. Lorem ipsum dolor sit amet, consectetur adipiscing elit. * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. * Vestibulum commodo. Ut rhoncus gravida arcu. */ package com.f6car.base.config; import net.oschina.j2cache.autoconfigure.J2CacheConfig; import net.oschina.j2cache.autoconfigure.J2CacheIniter; import org.nutz.j2cache.spring.SpringJ2CacheManager; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.CacheManager; import org.springframework.cache.ehcache.EhCacheManagerUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.PropertySource; @Configuration @PropertySource("classpath:j2cache/j2cache-${spring.profiles.active}.properties") public class CacheConfig { @Value("${ehcache.ehcache.name}") private String ehcacheName; @Bean("ehCacheManager") public net.sf.ehcache.CacheManager ehCacheManager() { return EhCacheManagerUtils.buildCacheManager(ehcacheName); } @DependsOn({"ehCacheManager", "j2CacheIniter"}) @Bean public CacheManager springJ2CacheManager() { return new SpringJ2CacheManager(); } @Bean("j2CacheIniter") public J2CacheIniter j2CacheIniter(J2CacheConfig j2cacheConfig) { return new J2CacheIniter(j2cacheConfig); } }
如上我们就可以加载到不同的j2cache/j2cache-${spring.profiles.active}.properties
这样就完成了J2cache的初始化