通过注解实现AOP日志 简单入门,萌新

AOP日志

1通过注解的方式实现

 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

1.1定义注解

/**
 * 自定义操作日志注解
 * @author wu
 */
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented
public @interface LogAction {
    String operModul() default ""; // 操作模块
    String operType() default "";  // 操作类型
    String operDesc() default "";  // 操作说明
}

1.2创建切面类记录操作日志

/**
 * 切面处理类,操作日志异常日志记录处理
 *
 * @author wu
 * @date 2019/03/21
 */
@Aspect // 表示这是一个切面
@Component
public class OperLogAspect {

    /**
     * 操作版本号
     * <p>
     * 项目启动时从命令行传入,例如:java -jar xxx.war --version=201902
     * </p>
     */
    @Value("${version}")
    private String operVer;

    @Autowired
    private OperationLogService operationLogService;

    @Autowired
    private ExceptionLogService exceptionLogService;

    /**
     * 方法-  通过扫描注解实现记录AOP
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码 此处可以直接写注解的名称
     */
    @Pointcut("@annotation(com.hyd.zcar.cms.common.utils.annotation.LogAction)")
    public void operLogPoinCut() {
    }

    /**
     * 方法二,扫描指定位置
     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作 通过扫描的方式
     */
    @Pointcut("execution(* com.hyd.zcar.cms.controller..*.*(..))") 
    public void operExceptionLogPoinCut() {
    }
    
    @Before(value = "operLogPoinCut()") // 通过注解的方式
    public void saveOperLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println(signature);
        List<Object> list = Arrays.asList(joinPoint.getArgs()); // 获取到的参数
        System.out.println(list);
    }

    // 也可以不写operLogPoinCut 方法,直接这样写也可以
    @Before(value = "@annotation(LogAction)")
    public void saveOperLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println(signature);
        List<Object> list = Arrays.asList(joinPoint.getArgs());
        System.out.println(list);
    }

    ==================几种Aop的方式==============
        
         /**
     * 前置通知
     *
     * @param joinPoint 包含了目标方法的关键信息
     * @Before 注解标识这是一个前置通知,既在目标方法执行之前执行,注解中免去要填入切点
     */
    @Before(value = "@annotation(LogAction)")
    public void before(JoinPoint joinPoint) {

        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "方法开始执行了。。。");

    }


    /**
     * 后置通知
     *
     * @param joinPoint 包含了目标方法的关键信息
     * @After 注解标识这是一个后置通知,既在目标方法执行之后执行,注解中免去要填入切点
     */
    @After(value = "@annotation(LogAction)")
    public void after(JoinPoint joinPoint) {

        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "方法执行结束了。。。");

    }


    /**
     * 返回通知
     *
     * @param joinPoint 包含了目标方法的关键信息
     * @AfterReturning 注解标识这是一个返回通知,既有目标方法有返回值的时候才会触发,
     * 该注解中returning属性标识目标方法返回值的变量名,这个需要和参数一一对应,
     * 注意,目标方法的返回值类型和这里方法的返回值参数可以为object
     */
    @AfterReturning(value = "@annotation(LogAction)", returning = "r")
    public void afterReturning(JoinPoint joinPoint, Object r) {

        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "方法返回" + r);

    }


    /**
     * 异常通知
     *
     * @param joinPoint 包含了目标方法的关键信息
     * @param e         目标方法所抛出的异常,注意,这个参数必须是目标方法所抛出的异常,
     *                  或者所抛出异常的异常父类,只有这样,才会捕获,如果想拦截所有,
     *                  参数类型声明为Exception
     * @AfterThrowing 注解标识这是一个异常通知,改方法会繁华一个异常e,
     */
    @AfterThrowing(value = "@annotation(LogAction)", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Exception e) {

        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
        System.out.println(name + "方法返回" + e);
    }

    /**
     * 环绕通知
     * <p>
     * 环绕通知是集大成这,可以用环绕通知实现上面的四个通知,这个方法的核心有点类似于在这里通过反射这些方法
     *
     * @param pjp 包含了目标方法的关键信息
     * @Around 注解标识这是一个环绕通知
     */
    @Around(value = "@annotation(LogAction)")
    public Object Around(ProceedingJoinPoint pjp) {
        Object proceed = null;
        try {
            System.out.println("环绕通知里面的【前置通知】。。。");
            //这个相当于method.invoke方法,我们可以在这个方法的前后分别添加日志,就相当于前置通知和后置通知
            proceed = pjp.proceed();   //这里相当于执行目标方法 如果不写目标方法就不会执行
			// proceed是目标方法的返回值		
System.out.println("环绕通知里面的【后置通知】...");

        } catch (Throwable throwable) {
            
            System.out.println("这里是执行环绕通知里面的【异常通知】。。。");
			throwable.printStackTrace();
        }
        finally {
                System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
            }

        return proceed;
    }

    
    
    

}

2.通过配置实现更加灵活

1.概述
    问题:
        当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
    分析:
        通过对比动态代理的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用.而我们的没有
    解决:
        Spring框架为我们提供了一个接口,proceedingJoinPoint.该接口有一个方法proceed()此方法就相当于明确调用切入点方法.
        该接口可以作为环绕通知的方法参数,在程序执行时,Spring框架会为我们提供该接口的实现类供我们使用
 
    Spring中的环绕通知:
    它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式
 
2. 使用
    2.1 基于注解:
        @Aspect
        public class AnnotationAudienceAround{
 
            //使用@Pointcut注解声明切入点表达式
            @Pointcut("execution(* com.qin.util.*.*(..))")
            public void pt1(){}
 
            @Around("pt1()")
            public Object aroundPringLog(ProceedingJoinPoint pjp){
            Object rtValue = null;
            try{
                Object[] args = pjp.getArgs();//得到方法执行所需的参数
 
                System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
 
                rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
 
                System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
 
                return rtValue;
            }catch (Throwable t){
                System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
                throw new RuntimeException(t);
            }finally {
                System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
            }
            }
        }
 
    2.2 基于XML配置文件:
        1. 通知类.
        //去掉了所有的注解
        public class XmlAudienceAround{
 
            public Object aroundPringLog(ProceedingJoinPoint pjp){
            Object rtValue = null;
            try{
                Object[] args = pjp.getArgs();//得到方法执行所需的参数
 
                System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
 
                rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
 
                System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
 
                return rtValue;
            }catch (Throwable t){
                System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
                throw new RuntimeException(t);
            }finally {
                System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
            }
            }
        }
 
        2. 在xml文件中配置
            <!--声明bean-->
            <bean name="xmlAudienceAround" class="com.qin.util.XmlAudienceAround"/>
            <!--配置切面及通知-->
            <aop:config>
            <aop:aspect ref="xmlAudienceAround">
                <aop:pointcut id="pt1" expression="execution(* com.qin.util.*.*(..))"/>
                <aop:around method="aroundPringLog" pointcut-ref="pt1"/> 对于切面调用的什么方法
            </aop:aspect>
            </aop:config>
            

      
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值