什么是AOP?
它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"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();
自定义注解
@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) {...}