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的初始化