title: J2Cache的SpringBootのStarter tags:
- SpringBoot
- J2Cache
- Starter categories: springboot date: 2017-11-27 23:28:37
背景
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的初始化