若是您有幸能看到,请认阅读如下内容;html
一、本项目临摹自abel533的Guns,他的项目 fork 自 stylefeng 的 Guns!开源的世界真好,能够学到不少知识。java
二、版权归原做者全部,本身只是学习使用。跟着大佬的思路,但愿本身也能变成大佬。gogogo》。。git
三、目前只是一个后台模块,但愿本身技能加强到必定时,能够把stylefeng 的 [Guns]融合进来。github
四、note里面是本身的学习过程,菜鸟写的,不是大佬写的。内容都是大佬的。缓存
五、若有拼写错误,还请见谅。目前的桌子不适合打字,本文只为本身记录.安全
问你们一个问题,大家在看本文的时候,以为哪里有须要修改的地方?内容和格式方面,欢迎你们提出来。服务器
昨天看了数据源、日志记录的配置,咱们今天再来看看缓存配置。多线程
该项目所有基于JavaConfig,除enchache.xml
,@EnableCaching注解的意思和cache:annotation-diver的工做方式是相同的。它会都会建立一个切面并触发Spring缓存注解的切点。还有一点须要注意的是EhCacheCacheManager
管理器。还有SimpleCacheManager,ConcurrentMapCacheManager.Spring Data又提供了RedisCacheManager.具体请看《Spring实战》,这本书很是不错,直接再次推荐。app
/** * ehcache配置 */
@Configuration
@EnableCaching
public class EhCacheConfig {
/** * EhCache的配置 */
@Bean
public EhCacheCacheManager cacheManager(CacheManager cacheManager) {
return new EhCacheCacheManager(cacheManager);
}
/** * EhCache的配置 */
@Bean
public EhCacheManagerFactoryBean ehcache() {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
return ehCacheManagerFactoryBean;
}
}
复制代码
一、利用Ehcache框架对常常调用的查询进行缓存,从而提升系统性能。仍是先看接口定义,须要注意的是get()方法使用了泛型.框架
/** * 通用缓存接口 */
public interface ICache {
void put(String cacheName, Object key, Object value);
<T> T get(String cacheName, Object key);
@SuppressWarnings("rawtypes")
List getKeys(String cacheName);
void remove(String cacheName, Object key);
void removeAll(String cacheName);
<T> T get(String cacheName, Object key, ILoader iLoader);
<T> T get(String cacheName, Object key, Class<? extends ILoader> iLoaderClass);
}
--------------------------------------------------------------------------------
/** * 数据重载 */
public interface ILoader {
Object load();
}
复制代码
接下来看下基础CacheFactory,注意,这里定义成抽象的。由于抽象类天生就是用来被继承的。
那何时使用抽象类和接口呢:
/** * 缓存工厂基类 */
public abstract class BaseCacheFactory implements ICache {
@SuppressWarnings("unchecked")
public <T> T get(String cacheName, Object key, ILoader iLoader) {..略..}
@SuppressWarnings("unchecked")
public <T> T get(String cacheName, Object key, Class<? extends ILoader> iLoaderClass) {
Object data = get(cacheName, key);
if (data == null) {
try {
ILoader dataLoader = iLoaderClass.newInstance();
data = dataLoader.load();
put(cacheName, key, data);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return (T) data;
}
}
复制代码
接着在看看具体的EnCacheFactory
,这里你本身也能够定义其余缓存工厂,扩展的时候只要继承BaseCacheFactory
就行。
第一点须要注意的是这里使用了org.slf4j.LoggerFactory
第二点须要注意的是静态getCacheManager()
方法,这里使用了双重检查机制,还有延时加载(建立)。有没有想起单例模式啊,直接贴一段代码
关键点是使用了volatile
和synchronized
保证了可见性和同步性。后者能够用在方法上,代码块上,具体内容看这里吧,不展开了友情提示.
主要做用:延迟初始化下降了初始化类或建立实例的开销,但也增长了访问被延迟初始化的字段的开销。正常初始化要优于延迟加载,
若是确实要对实例字段使用多线程的安全的延迟初始化,使用基于volatile的初始化,若是须要对静态字段使用线程安全的初始化,则使用基于类的初始化方案。
/** * Created by guo on 2018/1/29. */
public class SafeDoubleCheckedLocking {
private volatile static Instacen instance;
public static Instacen getInstance() {
if(instance == null) {
synchronized (SafeDoubleCheckedLocking.class) {
if (instance == null) {
instance = new Instacen();
}
}
}
return instance;
}
}
class Instacen{
}
--------------------------对比-------------------------------------------------
/** * Created by guo on 2018/1/29. * 基于类的初始化解决方案 */
public class InstanceFactory {
private static class InstanceHolder{
public static Instance instance = new Instance();
}
public static Instacen getInstance() {
return InstanceHolder.instance;
}
}
class Instance extends Instacen {
}
复制代码
回到咱们Ehcache缓存工厂吧,重点是CacheManager
.Spring框架底层有许多个Manager。如DataSourceTransactionManager
.还有就是建立CacheManager的create()
方法。人家也使用了双重检查,延迟加载。看见singleton了么。private static volatile CacheManager singleton;
public static CacheManager create() throws CacheException {
if(singleton != null) {
LOG.debug("Attempting to create an existing singleton. Existing singleton returned.");
return singleton;
} else {
Class var0 = CacheManager.class;
synchronized(CacheManager.class) {
if(singleton == null) {
singleton = newInstance();
} else {
LOG.debug("Attempting to create an existing singleton. Existing singleton returned.");
}
return singleton;
}
}
}
复制代码
这里是调用cacheManager.getCache
来获取缓存。你们仍是亲自看看源码把,这里只是本身明白了讨论,记录下。
/** * Ehcache缓存工厂 */
public class EhcacheFactory extends BaseCacheFactory {
private static CacheManager cacheManager;
private static volatile Object locker = new Object();
private static final Logger log = LoggerFactory.getLogger(EhcacheFactory.class);
private static CacheManager getCacheManager() {
if (cacheManager == null) {
synchronized (EhcacheFactory.class) {
if (cacheManager == null) {
cacheManager = CacheManager.create();
}
}
}
return cacheManager;
}
static Cache getOrAddCache(String cacheName) {
CacheManager cacheManager = getCacheManager();
Cache cache = cacheManager.getCache(cacheName);
if (cache == null) {
synchronized(locker) {
cache = cacheManager.getCache(cacheName);
if (cache == null) {
log.warn("没法找到缓存 [" + cacheName + "]的配置, 使用默认配置.");
cacheManager.addCacheIfAbsent(cacheName);
cache = cacheManager.getCache(cacheName);
log.debug("缓存 [" + cacheName + "] 启动.");
}
}
}
return cache;
}
-----------------------省略几个----------------------------------------------
public void put(String cacheName, Object key, Object value) {
getOrAddCache(cacheName).put(new Element(key, value));
}
public <T> T get(String cacheName, Object key) {
Element element = getOrAddCache(cacheName).get(key);
return element != null ? (T)element.getObjectValue() : null;
}
public void remove(String cacheName, Object key) {
getOrAddCache(cacheName).remove(key);
}
}
复制代码
接着咱们来看几个常量的定义及实现
/** * 获取被缓存的对象(用户删除业务) */
String getCacheObject(String para);
-----------------------------------------------------------------------------------
/** * 获取被缓存的对象(用户删除业务) */
@Override
public String getCacheObject(String para) {
return LogObjectHolder.me().get().toString(); //还有一个set()记得吗?
}
复制代码
配置完了你总的使用,看代码。先不关注权限那块。CacheKit
是一个工具类。
/** * 删除角色 */
@RequestMapping(value = "/remove")
@BussinessLog(value = "删除角色", key = "roleId", dict = Dict.DeleteDict)
@Permission(Const.ADMIN_NAME)
@ResponseBody
public Tip remove(@RequestParam Integer roleId) {
if (ToolUtil.isEmpty(roleId)) {
throw new BussinessException(BizExceptionEnum.REQUEST_NULL);
}
//不能删除超级管理员角色
if(roleId.equals(Const.ADMIN_ROLE_ID)){
throw new BussinessException(BizExceptionEnum.CANT_DELETE_ADMIN);
}
//缓存被删除的角色名称
LogObjectHolder.me().set(ConstantFactory.me().getSingleRoleName(roleId));
roleService.delRoleById(roleId);
//删除缓存
CacheKit.removeAll(Cache.CONSTANT);
return SUCCESS_TIP;
}
---------------------------工具类------------------------------------------------
/** * 缓存工具类 */
public class CacheKit {
private static ICache defaultCacheFactory = new EhcacheFactory(); //这里建立Encache工厂。
public static void put(String cacheName, Object key, Object value) {
defaultCacheFactory.put(cacheName, key, value);
}
public static void removeAll(String cacheName) {
defaultCacheFactory.removeAll(cacheName);
}
}
复制代码
到这里缓存部分算是结束了,再次说明,只是本身记录过程,要让我实现,目前不现实,还须要本身请自看看源码,跑一遍。
接下来,咱们在看看控制统一的异常拦截机制。这里用到了切面的思想。第一眼看到的是@ControllerAdvice。这是什么东东,看图说话。GlobalExceptionHandler
所有代码点这里点这里。ResponseStatus状态先不关注。
/** * 全局的的异常拦截器(拦截全部的控制器)(带有@RequestMapping注解的方法上都会拦截) */
@ControllerAdvice
public class GlobalExceptionHandler {
private Logger log = LoggerFactory.getLogger(this.getClass());
/** * 拦截业务异常 * * @author fengshuonan */
@ExceptionHandler(BussinessException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorTip notFount(BussinessException e) {
LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getUser().getId(), e));
getRequest().setAttribute("tip", e.getMessage());
log.error("业务异常:", e);
return new ErrorTip(e.getCode(), e.getMessage());
}
/** * 用户未登陆 */
@ExceptionHandler(AuthenticationException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public String unAuth(AuthenticationException e) {
log.error("用户未登录:", e);
return "/login.html";
}
/** * 拦截未知的运行时异常 */
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorTip notFount(RuntimeException e) {
LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getUser().getId(), e));
getRequest().setAttribute("tip", "服务器未知运行时异常");
log.error("运行时异常:", e);
return new ErrorTip(BizExceptionEnum.SERVER_ERROR);
}
}
复制代码
异常处理看得也差很少了,接下来看什么好呢?持续关注,