资讯专栏INFORMATION COLUMN

基于SpringBoot的后台管理系统(Encache配置、全局异常处理(重点))(四)

Benedict Evans / 372人阅读

摘要:因为抽象类天生就是用来被继承的。由于不支多继承,子类不能够继承多个类,但可以实现多个接口如果基本功能在不断改变,那么就需要使用抽象类。全局异常处理接下来,我们在看看控制统一的异常拦截机制。

3、Spring Boot 缓存配置、全局异常处理 说明

如果您有幸能看到,请认阅读以下内容;

1、本项目临摹自abel533的Guns,他的项目 fork 自 stylefeng 的 Guns!开源的世界真好,可以学到很多知识。

2、版权归原作者所有,自己只是学习使用。跟着大佬的思路,希望自己也能变成大佬。gogogo》。。

3、目前只是一个后台模块,希望自己技能增强到一定时,可以把stylefeng 的 [Guns]融合进来。

4、note里面是自己的学习过程,菜鸟写的,不是大佬写的。内容都是大佬的。

5、如有拼写错误,还请见谅。目前的桌子不适合打字,本文只为自己记录.

目录

1、SpringBoot第一站:分析了启动类。还有各种自动配置的源码点这里

2、SpringBoot第二站:定义了异常、注解、Node节点、Page点这里

3、SpringBoot第三站:SpringBoot数据源配置、Mybatis配置、日志记录点这里

4、SpringBoot第四站:SpringBoot缓存配置、全局异常处理点这里

昨天看了数据源、日志记录纸配置,我们今天再来看看缓存配置。

缓存配置

1、利用Ehcache框架对经常调用的查询进行缓存,从而提高系统性能。还是先看接口定义,需要注意的是get()方法使用了泛型.

/**
 * 通用缓存接口
 */
public interface ICache {

    void put(String cacheName, Object key, Object value);

     T get(String cacheName, Object key);

    @SuppressWarnings("rawtypes")
    List getKeys(String cacheName);

    void remove(String cacheName, Object key);

    void removeAll(String cacheName);

     T get(String cacheName, Object key, ILoader iLoader);

     T get(String cacheName, Object key, Class iLoaderClass);

}
--------------------------------------------------------------------------------
/**
 *  数据重载
 */
public interface ILoader {
    Object load();
}
抽象类

接下来看下基础CacheFactory,注意,这里定义成抽象的。因为抽象类天生就是用来被继承的。

那什么时候使用抽象类和接口呢:

1、如果你拥有一些方法想让他们中的一些默认实现,那么使用抽象类。

2、如果你想实现多重继承,那么你必须使用接口。由于java不支多继承,子类不能够继承多个类,但可以实现多个接口

3、如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口 ,那么就需要改变所有实现了该接口的类。

/**
 * 缓存工厂基类
 */
public abstract class BaseCacheFactory implements ICache {

    @SuppressWarnings("unchecked")
    public  T get(String cacheName, Object key, ILoader iLoader) {..略..}

    @SuppressWarnings("unchecked")
    public  T get(String cacheName, Object key, Class 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()方法,这里使用了双重检查机制,还有延时加载(创建)。有没有想起单例模式啊,直接贴一段代码

关键点是使用了volatilesynchronized保证了可见性和同步性。后者可以用在方法上,代码块上,具体内容看这里吧,不展开了友情提示.

主要作用:延迟初始化降低了初始化类或创建实例的开销,但也增加了访问被延迟初始化的字段的开销。正常初始化要优于延迟加载,

如果确实要对实例字段使用多线程的安全的延迟初始化,使用基于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 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);
    }
}

异常处理看得也差不多了,接下来看什么好呢?持续关注,

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68668.html

相关文章

  • 两年了,我写了这些干货!

    摘要:开公众号差不多两年了,有不少原创教程,当原创越来越多时,大家搜索起来就很不方便,因此做了一个索引帮助大家快速找到需要的文章系列处理登录请求前后端分离一使用完美处理权限问题前后端分离二使用完美处理权限问题前后端分离三中密码加盐与中异常统一处理 开公众号差不多两年了,有不少原创教程,当原创越来越多时,大家搜索起来就很不方便,因此做了一个索引帮助大家快速找到需要的文章! Spring Boo...

    huayeluoliuhen 评论0 收藏0
  • 基于SpringBoot后台管理系统(启动类解析,开源世界真好)(一)

    摘要:目前只是一个后台模块,希望自己技能增强到一定时,可以把的融合进来。目录第一站,分析了启动类。看见没,这个也是配置类,它声明了视图解析器地域解析器以及静态资源的位置,想起来没,就是前置,后置。程序启动类我们点击源码看看。 Guns基于SpringBoot,致力于做更简洁的后台管理系统,完美整合springmvc + shiro + 分页插件PageHelper + 通用Mapper + ...

    SwordFly 评论0 收藏0
  • SpringBoot 实战 (十) | 统一处理异常

    摘要:前言如题,今天介绍是如何统一处理全局异常的。主要是用于异常拦截出获取并将设置到消息类中返回。状态码异常拦截类通过加入来声明该类可拦截请求,同时在方法加入并在该注解中指定要拦截的异常类。测试访问测试正常返回数据结果。 微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。 前言 如题,今天介绍 SpringBoot 是如何统一处理全局异常的。SpringBoot 中...

    arashicage 评论0 收藏0
  • SpringBoot 仿抖音短视频小程序开发(三)

    摘要:仿抖音短视频小程序开发一项目的简介仿抖音短视频小程序开发二项目功能分析与具体实现源代码仿抖音短视频小程序开发全栈式实战项目短视频后台管理系统小程序的后台管理系统涉及的技术栈框架一用户列表的获取与分页前端代码用户列表展示的表格底部 SpringBoot 仿抖音短视频小程序开发(一):项目的简介(https://segmentfault.com/a/11...SpringBoot 仿抖音短...

    songze 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<