什麼是AOP?web
它利用一種稱爲"橫切"的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其命名爲"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減小系統的重複代碼,下降模塊之間的耦合度,並有利於將來的可操做性和可維護性。若是說面向對象編程(oop)是水平擴展的話,那麼aop則是縱向的擴展,使程序立體化。數據庫
定義一個註解編程
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Loggable { /** 操做類型(INSERT、UPDATE、SELECT、DELETE)*/ public String optType(); /** 描述 */ public String describe(); /** 模塊 */ public String module();
自定義註解app
@Retention:標示註解在何時可見(運行時可見、僅在.class文件及源代碼中可見、僅在源代碼中可見),value可用參數有: RetentionPolicy.RUNTIME 標示該註解能夠再運行時經過反射找到(ORM框架許多註解使用了該參數) RetentionPolicy.CLASS 標示該註解保存在.class文件中,但在運行時不能經過反射找到 RetentionPolicy.SOURSE 標示該註解只在源碼中可見 @Target:標示該註解用於註解什麼元素(類、方法、變量等),value可用參數有: ElementType.PACKAGE 標示該註解用於註解包聲明 ElementType.ANNOTATION_TYPE 標示該註解用於註解其餘註解 ElementType.CONSTRUCTOR 標示該註解用於註解構造函數 ElementType.FIELD 標示該註解用於註解成員變量 ElementType.METHOD 標示該註解用於註解方法 ElementType.TYPE 標示該註解用於註解類,接口,枚舉類型 ElementType.PARAMETER 標示該註解用於註解參數 ElementType.LOCAL_VARIABLE 標示該註解用於註解局部變量
定義一個切面類(根據項目狀況在裏面科將日誌記錄存到數據庫):框架
@Aspect @Component(value = "loggerAspect") public class LoggerAspect { private Logger logger = LoggerFactory.getLogger(LoggerAspect.class); //切入點:* com.wb.wbao.web.*.*(..)路徑下和使用了@Loggable註解的方法 @Pointcut("execution(public * com.wb.wbao.web.*.*(..)) && @annotation(com.wb.wbao.common.annotation.Loggable)") public void log(){ } @AfterReturning(value = "log()", returning = "retVal") public void log(JoinPoint joinPoint, Object retVal) { // 獲取參數 Object[] params = joinPoint.getArgs(); // 獲取方法名 String methodName = joinPoint.getSignature().getName(); Class<?> targetClass = joinPoint.getTarget().getClass(); Method method = null; for (Method mt : targetClass.getMethods()) { if (methodName.equals(mt.getName())) { method = mt; break; } } Loggable loggable = method.getAnnotation(Loggable.class); if(Objects.isNull(loggable)){ return; } logger.info("loggable desc:{}, optType:{}, module:{},params:{}", loggable.describe(), loggable.optType(), loggable.module(), params); //loggable desc:登陸, optType:POST, module:LOGIN,params:[User{loginName='wangbao', password='wangbao'} } @AfterThrowing(value = "log()", throwing = "ex") public void log(JoinPoint joinPoint, Throwable ex) { // 獲取參數 Object[] params = joinPoint.getArgs(); // 獲取方法名 String methodName = joinPoint.getSignature().getName(); Class<?> targetClass = joinPoint.getTarget().getClass(); Method method = null; for (Method mt : targetClass.getMethods()) { if (methodName.equals(mt.getName())) { method = mt; break; } } Loggable loggable = method.getAnnotation(Loggable.class); if(Objects.isNull(loggable)){ return; } logger.info("loggable desc:{}, optType:{}, module:{}, exception:{}, params:{}", loggable.describe(), loggable.optType(), loggable.module(), ex.getMessage(), params); //loggable desc:登陸, optType:POST, module:LOGIN, exception:/ by zero, params:[User{loginName='wangbao', password='wangbao'} } }
實際使用方法:函數
@Loggable(describe = "登陸", optType = "POST", module = "LOGIN") @RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public CommonDTO login(@RequestBody User user) {...}