AOP-实现日志管理

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
<----------------------------

猜你喜欢

转载自blog.csdn.net/weixin_34032779/article/details/87072937