美文网首页
AOP-实现日志管理

AOP-实现日志管理

作者: Tiger_Lam | 来源:发表于2019-01-19 21:29 被阅读0次

    AOP

    实现aop的四种方式
    • AspectJ
    • AspectWerkz
    • SpringFramework
    • JBoss

    AOP核心概念

    • 1、横切关注点
      对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
    • 2、切面(aspect)
      类是对物体特征的抽象,切面就是对横切关注点的抽象
    • 3、连接点(joinpoint)
      被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造
    • 4、切入点(pointcut)
      对连接点进行拦截的定义
    • 5、通知(advice)
      所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
    • 6、目标对象
      代理的目标对象
    • 7、织入(weave)
      将切面应用到目标对象并导致代理对象创建的过程
    • 8、引入(introduction)
      在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
    切入点语法
    • 1.@Pointcut("下面的东西")
    任何类的任何返回值的任何方法
    - 1. execution(public * *(..))   
    
    任何类的set开头的方法
    - 2.execution(* set*(..))       
    
    任何返回值的规定类里面的方法
    - 3.execution(* com.example.demo.AccountService.*(..))
    
    任何返回值的,规定包或者规定包子包的任何类任何方法
    - 4.execution(* com.example.demo.service..*.*(..)) 
    
    
    • 2.@Pointcut("@annotation(自定义注解)")

    环绕通知(前置通知+执行方法+后置通知)语法

    • @Around("logPointCut()") //@Pointcut打在哪个方法,就填哪个方法
    • @Around(execution(* com.example.demo.service...(..)))

    注意:执行方法:method(ProceedingJoinPoint joinPoint),要带joinpoint这个参数

    AspectJ

    • AspectJ是目前最完善的AOP语言
    • AspectJ是对Java编程语言的扩展,通过增加了一些新的构造块支持对横切关注点的模块化封装,通过对源代码级别的代码混合实现织入,是一种典型的使用静态织入的AOP实现机制。
    • AspectJ提供了两种横切实现机制
    1. 动态横切(DynamicCrosscutting)
    2. 另一种称为静态横切(StaticCrosscutting)。
    简要api
    Joinpoint
    - java.lang.Object[] getArgs()/*获取连接点方法运行时的入参列表*/
    - Signature getSignature() /*获取连接点的方法签名对象*/
    - java.lang.Object getTarget() //获取连接点所在的目标对象
    - java.lang.Object getThis() //获取代理对象本身
    
    ProceedingJoinPoint

    ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法

    //通过反射执行目标对象的连接点处的方法
    - java.lang.Object proceed() throws Throwable
    //通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。
    - java.lang.Object proceed(java.lang.Object[] args) throws Throwable
    

    示例代码

    使用aop进行日志管理(自定义注解方式)
    自定义注解
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Log{
    
        String value() default ""; //用于描述方法作用
    
        /**
         * 是否忽略返回值,仅方法上有效
         * @return
         */
        boolean ignoreReturn() default false;
    }
    
    切面类
    /**
     * 日志切面
     */
    @Aspect
    //切面优先级
    @Order(100)
    @Component
    public class LogAspect {
        private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
        private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";
        private static final String STRING_START = "\n--------------------------->\n";
        private static final String STRING_END = "\n<----------------------------\n";
    /**
     * 切点
     */
    /**
     * 不使用注解方式打切点
     *  //@Pointcut("execution (* com.example.demo.web..*(..))")")
     *  表示在使用Log注解的地方切入
     */
        @Pointcut("@annotation(Log)")
        public void logPointCut() {
        }
    
        //@Around("logPointCut()")---> 用于controller层
        public Object controllerAround(ProceedingJoinPoint joinPoint) {
            try {
                return printLog(joinPoint);
            } catch (Throwable throwable) {
                log.error(throwable.getMessage(), throwable);
                return true;
            }
        }
        //通知:拦截到连接点之后要执行的代码
            @Around("logPointCut()")
        private Object printLog(ProceedingJoinPoint joinPoint) throws Throwable {
            //获取连接点的方法签名
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            //获取方法签名里的方法:方法签名里有两个方法:getReturnType   getMethod
            Method method = signature.getMethod();
            //获取类
            Class<?> targetClass = method.getDeclaringClass();
            StringBuffer classAndMethod = new StringBuffer();
    
            // 获取目标方法上的Log注解
            Log methodAnnotation = method.getAnnotation(Log.class);
    
            // 判断是否有LOG注解以及是否带有ignore参数
            if (methodAnnotation != null) {
                classAndMethod.append(methodAnnotation.value());
            }
            //拼接目标切入的类名称和方法名称
            String target = targetClass.getName() + "#" + method.getName();
            // 请求参数转JSON,对日期进行格式转换并打印出所有为null的参数
            String params = JSONObject.toJSONStringWithDateFormat(joinPoint.getArgs(), dateFormat, SerializerFeature.WriteMapNullValue);
            //日志打印拼接的调用信息
            log.info(STRING_START + "{} 开始调用--> {} 参数:{}", classAndMethod.toString(), target, params);
    
            long start = System.currentTimeMillis();
            //proceed()通过反射执行目标对象的连接点处的方法;
            Object result = joinPoint.proceed();
            long timeConsuming = System.currentTimeMillis() - start;
            if (methodAnnotation != null && methodAnnotation.ignoreReturn()) {
                log.info("\n{} 调用结束<-- {} 耗时:{}ms" + STRING_END, classAndMethod.toString(), target, timeConsuming);
                return result;
            }
            // 响应参数转JSON,对日期进行格式转换并打印出所有为null的参数
            log.info("\n{} 调用结束<-- {} 返回值:{} 耗时:{}ms" + STRING_END, classAndMethod.toString(), target, JSONObject.toJSONStringWithDateFormat(result, dateFormat, SerializerFeature.WriteMapNullValue), timeConsuming);
          
            return result;
        }
    }
    
    
    实现类
    @Service
    @Slf4j
    public class UserServiceImpl implements UserService{
    
        @Autowired
    private UserMapper usermapper;
        @Override
        @Log
        public User getUser(Integer id) {
            User user=usermapper.getById(id);
            log.info("message: {}",user);
            return usermapper.getById(id);
        }
    }
    
    
    控制台输出
    --------------------------->
     开始调用--> com.example.demo.service.serviceimpl.UserServiceImpl#getUser 参数:[2]
    2019-01-19 21:24:57.027 [http-nio-9091-exec-1] INFO  com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
    2019-01-19 21:24:57.406 [http-nio-9091-exec-1] INFO  com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
    2019-01-19 21:24:57.413 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : ==>  Preparing: SELECT * FROM user u WHERE u.id = ? 
    2019-01-19 21:24:57.436 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : ==> Parameters: 2(Integer)
    2019-01-19 21:24:57.468 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : <==      Total: 1
    2019-01-19 21:24:57.470 [http-nio-9091-exec-1] INFO  c.e.d.s.serviceimpl.UserServiceImpl : message: User(id=2, name=321, birthday=2019-01-17, address=4)
    2019-01-19 21:24:57.471 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : ==>  Preparing: SELECT * FROM user u WHERE u.id = ? 
    2019-01-19 21:24:57.471 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : ==> Parameters: 2(Integer)
    2019-01-19 21:24:57.484 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : <==      Total: 1
    2019-01-19 21:24:57.516 [http-nio-9091-exec-1] INFO  c.example.demo.config.log.LogAspect : 
     调用结束<-- com.example.demo.service.serviceimpl.UserServiceImpl#getUser 返回值:{"address":"4","birthday":"2019-01-17","id":2,"name":"321"} 耗时:478ms
    <----------------------------
    

    相关文章

      网友评论

          本文标题:AOP-实现日志管理

          本文链接:https://www.haomeiwen.com/subject/pjttjqtx.html