资讯专栏INFORMATION COLUMN

WMS项目中使用到的切面

Tikitoo / 2925人阅读

摘要:前言目前系统刚启动,骨架刚刚搭建完成,在项目中,使用了一些切面,做一些业务无关的处理。在现在的项目里面,分别在,调用,分页,处理,均使用到了切面。希望本文的阅读者也不吝将项目中使用的切面分享出来。

前言

目前系统刚启动,骨架刚刚搭建完成,在项目中,使用了一些切面,做一些业务无关的处理。在本文中,将各个切面例举出来,用以加深自己对切面的理解。记得在初学切面的时候,一般文章介绍切面的时候,主要是日志,消息收集等,其实在真实项目中,远不止于此。在现在的项目里面,分别在controller,rpc调用,分页,dao处理,均使用到了切面。下面逐个进行说明。希望本文的阅读者也不吝将项目中使用的切面分享出来。

Controller层 ControllerInterceprot

用于统计每个后台的controller方法的执行时间,并执行打印,切面中用到了TheadLocal。

public class ControllerInterceprot implements HandlerInterceptor{
    private static final Logger log = LoggerFactory.getLogger(ControllerInterceprot.class);

    private static final ThreadLocal startTimeThreadLocal = new NamedThreadLocal("ThreadLocal StartTime");

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        startTimeThreadLocal.set(System.currentTimeMillis());
        String methodName = request.getMethod();
        Long userId = SecurityHelper.getCurrentUserId();
        log.info("start: method: " + methodName + "userId: " + userId +  ", starttime: " + System.currentTimeMillis());
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        long excutionTime = System.currentTimeMillis() - startTimeThreadLocal.get();
        String methodName = request.getMethod();
        Long userId = SecurityHelper.getCurrentUserId();
        if (null == ex){
            log.info("finish: method: " + methodName + "userId: " + userId +  ", excutetime: " + excutionTime);
        }else{
            log.info("exception finish: method: " + methodName + "userId: " + userId +  ", excutetime: " + excutionTime);
        }
        startTimeThreadLocal.set(null);
    }
}
BizExceptionHandler

用于对系统中抛出的异常进行统一处理,主要是捕获异常,多语处理

@ControllerAdvice
public class BizExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(BizExceptionHandler.class);
    @Autowired
    private BasDbMessageResource messageSource;

    @ExceptionHandler(value = BizException.class)
    @ResponseBody
    public PackVo execute(BizException e, HttpServletRequest request){
        PackVo packVo = new PackVo();
        packVo.setSuccess(false);
        SessionLocaleResolver sessionLocaleResolver = (SessionLocaleResolver) RequestContextUtils.getLocaleResolver(request);
        List messages = e.getClientMessages();
        for(BizClientMessage message : messages){
            packVo.addMsg(message.getCode(),getLocalMessage(message.getCode(),message.getContent(), sessionLocaleResolver.resolveLocale(request)));
        }
        log.warn("BizException", e);
        return packVo;
    }
    @ExceptionHandler(value = IllegalStateException.class)
    @ResponseBody
    public PackVo handleXingException(IllegalStateException e,HttpServletRequest request){
        PackVo packVo = new PackVo();
        packVo.setSuccess(false);
        SessionLocaleResolver sessionLocaleResolver = (SessionLocaleResolver) RequestContextUtils.getLocaleResolver(request);
        packVo.addMsg(BizExceptionCode.SYS_ERR_XING_SERVICE_FAIL,getLocalMessage(BizExceptionCode.SYS_ERR_XING_SERVICE_FAIL,new String[]{e.getMessage()}, sessionLocaleResolver.resolveLocale(request)));
        return packVo;
    }
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public PackVo handleSysException(Exception e,HttpServletRequest request){
        e.printStackTrace();
        log.error(e.getMessage());
        PackVo packVo = new PackVo();
        packVo.setSuccess(false);
        SessionLocaleResolver sessionLocaleResolver = (SessionLocaleResolver) RequestContextUtils.getLocaleResolver(request);
        packVo.addMsg(BizExceptionCode.SYS_ERR_COMMON,getLocalMessage(BizExceptionCode.SYS_ERR_COMMON,new String[]{e.getMessage()}, sessionLocaleResolver.resolveLocale(request)));
        return packVo;
    }

    public String getLocalMessage(String code,String[] values,Locale locale){
        log.info("parameter:code=["+code+"] values = ["+values+"]locale=["+locale+"]");
        return messageSource.getMessage(code,values,locale);
    }
}
RPC层 MasterXingInterceptor

切在了RPC的接口层,在项目中,所有的RPC方法都规定在第一个参数是ClientInfo对象,里面封装了一些用于更新表中的一些公共的字段,比如操作人,语言偏好等。接口会将ClientInfo放到ThreadLocal中去。

@Component
@Aspect
public class MasterXingInterceptor {
    private static final Log logger = LogFactory.getLog(MasterXingInterceptor.class);

    @Around("execution(* com.best.gwms.master.xing..*.*(..))")
    public Object interceptor(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();
        if (args != null && args.length > 0) {
            Object firstArg = args[0];
            if (firstArg instanceof ClientInfo) {
                ClientInfo clientInfo = (ClientInfo) firstArg;
                // 把client方法theadlocal里
                MasterStatic.setClientInfoTH(clientInfo);
            }
        }

        try {
            Object ret = pjp.proceed();
            return ret;
        } finally {
            // 把线程池清空掉,以免出现线程池缓存导致的坑
            ThreadLocalUtil.refreshThreadLocal();
        }
    }
}
public class ThreadLocalUtil {

    public static List> TL_LIST = new ArrayList>();

    public static void refreshThreadLocal() {
        for (ThreadLocal tl : TL_LIST) {
            tl.set(null);
        }
    }

    public static void addThreadLocal2List(ThreadLocal tl) {
        TL_LIST.add(tl);
    }
}

public class MasterStatic {
    private static ThreadLocal clientInfoHL = new ThreadLocal();

    static {
        // 放在一个统一的的方法,便于事务结束后统一销毁(线程池缓存的问题)
        ThreadLocalUtil.addThreadLocal2List(clientInfoHL);
    }

    public static ClientInfo getClientInfoHL() {
        return clientInfoHL.get();
    }

    public static void setClientInfoTH(ClientInfo clientInfo) {
        clientInfoHL.set(clientInfo);
    }

    public static Long getDomainId() {
        return getClientInfoHL() != null ? getClientInfoHL().getDomainId() : null;
    }

    public static Long getUserId() {
        return getClientInfoHL() != null ? getClientInfoHL().getUserId() : null;
    }
}
DAO层 DaoAspectInterceptor

主要作用有两个:1.获取threadlocal中的clientInfo,切在创建方法,更新方法前,设置乐观锁,创建人,创建时间,更新人,更新时间等这些字段,这也要求PO超类,保存公共字段。 2.缓存处理。在get前,查找缓存,如果不命中,再去获取并更新缓存;在更新后,去更新缓存。

@Aspect
@Component
public class MasterDaoAspectInterceptor {
    private static final Logger log = LoggerFactory.getLogger(MasterDaoAspectInterceptor.class);

    @Autowired
    RedisClient redisClient;

    @Before("execution(* com.best.gwms.common.base.BasDao+.createPo(..)) && (args(po))")
    public void prePersist(AbstractPo po) {
//        po.setCreatorId(0L);
//        po.setDomainId(10000L);
        po.setCreatorId(MasterStatic.getUserId());
        po.setDomainId(MasterStatic.getDomainId());
        po.setLockVersion(0L);
        po.setCreatedTime(new Date());
        po.setUpdatedTime(po.getCreatedTime());
    }

    @Before("execution(* com.best.gwms.common.base.BasDao+.updatePo(..)) && (args(po))")
    // @Before("execution(* com.best.gwms.master.dao..*.updatePo(..)) && (args(po))")
    public void preUpdate(AbstractPo po) {
//        po.setDomainId(10000L);
//        po.setUpdatorId(0L);
        po.setUpdatorId(MasterStatic.getUserId());
        po.setUpdatedTime(new Date());
    }

    /**
     * getpobyid 先从缓存中获取,若缓存中没有,则从数据库中获取数据并存放到缓存中
     *
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.best.gwms.common.base.BasDao+.getPoById(..)) )")
    public Object aroundGetPoById(ProceedingJoinPoint pjp) throws Throwable {

        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Method method = methodSignature.getMethod();

        Class c = methodSignature.getReturnType();
        String simpleName = c.getSimpleName();
        Long id = (Long) pjp.getArgs()[0];

        String key = redisClient.buildKey(simpleName, id);

        if (method.isAnnotationPresent(RedisCache.class)) {

            Object value = redisClient.getObject(key);
            if (value != null) {
                return value;
            }
        }

        Object object = pjp.proceed();

        redisClient.setObject(key, object);

        return object;
    }

    /**
     * getpobyid 先从缓存中获取,若缓存中没有,则从数据库中获取数据并存放到缓存中
     *
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.best.gwms.common.base.BasDao+.updatePo(..)) )")
    public Object aroundUpdatePo(ProceedingJoinPoint pjp) throws Throwable {
        // 先清理缓存
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Method method = methodSignature.getMethod();

        AbstractPo po = (AbstractPo) pjp.getArgs()[0];
        String simpleName = po.getClass().getSimpleName();
        Long id = po.getId();

        String key = redisClient.buildKey(simpleName, id);
        if (method.isAnnotationPresent(RedisCache.class)) {
            redisClient.removeValueByKey(key);
        }

        int count = (int) pjp.proceed();
        if (count <= 0) {
            log.warn("OptiMistic Lock Exception:" + simpleName);
            throw new OptLockException(BizExceptionCode.OPT_LOCK_EXCEPTION);
        }

        return count;
    }

    @Around("execution(* com.best.gwms.common.base.BasDao+.createPo(..)) )")
    public Object aroundCreatePo(ProceedingJoinPoint pjp) throws Throwable {
        // 插入记录的行数
        Long count = (Long) pjp.proceed();

        if (count.intValue() <= 0) {
            MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
            Method method = methodSignature.getMethod();
            throw new DaoException(BizExceptionCode.DAO_EXCEPTION);
        }
        return count;
    }
}
分页 BasPageInterceptor

结合PageHelper,在切面中处理分页相关的计算。本类中切的面有点不精确,还是要在方法名上团队进行一些约定。

@Component
@Aspect
public class BasPageInterceptor {

    @Before("execution(* com.best.gwms.master.dao..*.*(..))")
    public void sharePage(JoinPoint jpj){
        //如果DAO的方法是以count开头,则直接跳过,不进行分页处理
        if(StringUtils.startsWith(jpj.getSignature().getName(),"count")){
            return;
        }
        Object[] args = jpj.getArgs();
        Integer pageNum = null;
        Integer pageSize= null;
        String orderBy=null;
        for (Object arg : args) {
            if (arg instanceof SearchObject) {
                SearchObject so = (SearchObject) arg;
                orderBy = so.getOrderBy();
                pageNum=so.getPageNo();
                pageSize=so.getPageSize();
                break;
            }
        }
        if (pageNum != null && pageSize != null) {
            if (orderBy == null) {
                PageHelper.startPage(pageNum,pageSize);
                return;
            }
            PageHelper.startPage(pageNum,pageSize,orderBy);
        }
    }
}

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

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

相关文章

  • WMS项目使用到的注解

    摘要:前言和切面一样,在项目中同样使用了自定义注解,目前项目中使用的自定义注解主要分为以下一些方面参数解析,缓存方法声明,导入功能中的声明。 前言 和切面一样,在项目中同样使用了自定义注解,目前项目中使用的自定义注解主要分为以下一些方面:controller参数解析,缓存方法声明,导入功能中的POJO声明。 @JsonObject 用在controller的方法参数,解析前台提交的json参...

    zorro 评论0 收藏0
  • Spring Boot自定义注解+AOP实现主备库切换

    摘要:示例代码如下添加的设置默认的配置对应的是原来的如何使用注解从主库到备库的切换 摘要: 本篇文章的场景是做调度中心和监控中心时的需求,后端使用TDDL实现分表分库,需求:实现关键业务的查询监控,当用Mybatis查询数据时需要从主库切换到备库或者直接连到备库上查询,从而减小主库的压力,在本篇文章中主要记录在Spring Boot中通过自定义注解结合AOP实现直接连接备库查询。 一.通过A...

    zhisheng 评论0 收藏0
  • Linux服务器搭建 Java / Postgres / Tomcat / Ngnix 环境 +

    摘要:生产环境服务器环境搭建安装安装在系统中通过以下命令输入查看是否安装正确,输出如下创建发布目录,并给出相应的权限服务器和后台文件夹上传前端文件夹改变文件所有权文件的所有权变为加入参数,文件夹以及文件夹里的所有 生产环境服务器环境搭建 安装jdk 安装 openjdk-8-jdk 在 ubuntu-16.04 系统中通过以下命令: 1.sudo add-apt-repository pp...

    evin2016 评论0 收藏0

发表评论

0条评论

Tikitoo

|高级讲师

TA的文章

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