摘要:前言目前系統(tǒng)剛啟動(dòng),骨架剛剛搭建完成,在項(xiàng)目中,使用了一些切面,做一些業(yè)務(wù)無關(guān)的處理。在現(xiàn)在的項(xiàng)目里面,分別在,調(diào)用,分頁(yè),處理,均使用到了切面。希望本文的閱讀者也不吝將項(xiàng)目中使用的切面分享出來。
前言
目前系統(tǒng)剛啟動(dòng),骨架剛剛搭建完成,在項(xiàng)目中,使用了一些切面,做一些業(yè)務(wù)無關(guān)的處理。在本文中,將各個(gè)切面例舉出來,用以加深自己對(duì)切面的理解。記得在初學(xué)切面的時(shí)候,一般文章介紹切面的時(shí)候,主要是日志,消息收集等,其實(shí)在真實(shí)項(xiàng)目中,遠(yuǎn)不止于此。在現(xiàn)在的項(xiàng)目里面,分別在controller,rpc調(diào)用,分頁(yè),dao處理,均使用到了切面。下面逐個(gè)進(jìn)行說明。希望本文的閱讀者也不吝將項(xiàng)目中使用的切面分享出來。
Controller層 ControllerInterceprot用于統(tǒng)計(jì)每個(gè)后臺(tái)的controller方法的執(zhí)行時(shí)間,并執(zhí)行打印,切面中用到了TheadLocal。
public class ControllerInterceprot implements HandlerInterceptor{ private static final Logger log = LoggerFactory.getLogger(ControllerInterceprot.class); private static final ThreadLocalBizExceptionHandlerstartTimeThreadLocal = 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); } }
用于對(duì)系統(tǒng)中拋出的異常進(jìn)行統(tǒng)一處理,主要是捕獲異常,多語(yǔ)處理
@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); ListRPC層 MasterXingInterceptormessages = 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的接口層,在項(xiàng)目中,所有的RPC方法都規(guī)定在第一個(gè)參數(shù)是ClientInfo對(duì)象,里面封裝了一些用于更新表中的一些公共的字段,比如操作人,語(yǔ)言偏好等。接口會(huì)將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 { // 把線程池清空掉,以免出現(xiàn)線程池緩存導(dǎo)致的坑 ThreadLocalUtil.refreshThreadLocal(); } } } public class ThreadLocalUtil { public static ListDAO層 DaoAspectInterceptor> 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 { // 放在一個(gè)統(tǒng)一的的方法,便于事務(wù)結(jié)束后統(tǒng)一銷毀(線程池緩存的問題) 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; } }
主要作用有兩個(gè):1.獲取threadlocal中的clientInfo,切在創(chuàng)建方法,更新方法前,設(shè)置樂觀鎖,創(chuàng)建人,創(chuàng)建時(shí)間,更新人,更新時(shí)間等這些字段,這也要求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 先從緩存中獲取,若緩存中沒有,則從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)并存放到緩存中 * * @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 先從緩存中獲取,若緩存中沒有,則從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)并存放到緩存中 * * @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 { // 插入記錄的行數(shù) 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; } }分頁(yè) BasPageInterceptor
結(jié)合PageHelper,在切面中處理分頁(yè)相關(guān)的計(jì)算。本類中切的面有點(diǎn)不精確,還是要在方法名上團(tuán)隊(duì)進(jìn)行一些約定。
@Component @Aspect public class BasPageInterceptor { @Before("execution(* com.best.gwms.master.dao..*.*(..))") public void sharePage(JoinPoint jpj){ //如果DAO的方法是以count開頭,則直接跳過,不進(jìn)行分頁(yè)處理 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); } } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/68468.html
摘要:前言和切面一樣,在項(xiàng)目中同樣使用了自定義注解,目前項(xiàng)目中使用的自定義注解主要分為以下一些方面參數(shù)解析,緩存方法聲明,導(dǎo)入功能中的聲明。 前言 和切面一樣,在項(xiàng)目中同樣使用了自定義注解,目前項(xiàng)目中使用的自定義注解主要分為以下一些方面:controller參數(shù)解析,緩存方法聲明,導(dǎo)入功能中的POJO聲明。 @JsonObject 用在controller的方法參數(shù),解析前臺(tái)提交的json參...
摘要:示例代碼如下添加的設(shè)置默認(rèn)的配置對(duì)應(yīng)的是原來的如何使用注解從主庫(kù)到備庫(kù)的切換 摘要: 本篇文章的場(chǎng)景是做調(diào)度中心和監(jiān)控中心時(shí)的需求,后端使用TDDL實(shí)現(xiàn)分表分庫(kù),需求:實(shí)現(xiàn)關(guān)鍵業(yè)務(wù)的查詢監(jiān)控,當(dāng)用Mybatis查詢數(shù)據(jù)時(shí)需要從主庫(kù)切換到備庫(kù)或者直接連到備庫(kù)上查詢,從而減小主庫(kù)的壓力,在本篇文章中主要記錄在Spring Boot中通過自定義注解結(jié)合AOP實(shí)現(xiàn)直接連接備庫(kù)查詢。 一.通過A...
摘要:生產(chǎn)環(huán)境服務(wù)器環(huán)境搭建安裝安裝在系統(tǒng)中通過以下命令輸入查看是否安裝正確,輸出如下創(chuàng)建發(fā)布目錄,并給出相應(yīng)的權(quán)限服務(wù)器和后臺(tái)文件夾上傳前端文件夾改變文件所有權(quán)文件的所有權(quán)變?yōu)榧尤雲(yún)?shù),文件夾以及文件夾里的所有 生產(chǎn)環(huán)境服務(wù)器環(huán)境搭建 安裝jdk 安裝 openjdk-8-jdk 在 ubuntu-16.04 系統(tǒng)中通過以下命令: 1.sudo add-apt-repository pp...
閱讀 3226·2021-10-13 09:40
閱讀 3688·2019-08-30 15:54
閱讀 1309·2019-08-30 13:20
閱讀 2993·2019-08-30 11:26
閱讀 475·2019-08-29 11:33
閱讀 1099·2019-08-26 14:00
閱讀 2357·2019-08-26 13:58
閱讀 3366·2019-08-26 10:39