spring中自定义注解(annotation)与AOP中获取注解

一、自定义注解(annotation)

自定义注解的作用:在反射中获取注解,以取得注解修饰的类、方法或属性的相关解释。

package me.lichunlong.spring.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;  //自定义注解相关设置
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented public @interface LogAnnotation {  //自定义注解的属性,default是设置默认值String desc() default "无描述信息";
}

二、自定义注解的使用

package me.lichunlong.spring.service;import me.lichunlong.spring.annotation.LogAnnotation;
import me.lichunlong.spring.jdbc.JdbcUtil;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {//与其它注解一样的使用@LogAnnotation(desc="this is UserService")public void add() {System.out.println("UserService add...");}
}

三、AOP中获取注解

//   环绕通知:类似与动态代理的全过程
//   携带参数ProceedingJoinPoint,且必须有返回值,即目标方法的返回@Around(value = "execution(* me.lichunlong.spring.service.*.*(..)) && @annotation(log)")public Object aroundMethod(ProceedingJoinPoint pjd, LogAnnotation log) {Object result = null;System.out.println(log.desc());try {System.out.println("前置通知");result = pjd.proceed();System.out.println("后置通知");} catch (Throwable e) {System.out.println("异常通知");}System.out.println("返回通知");return result;}

使用aspectj的@Around注解实现用户操作和操作结果日志

自定义注解,将需要记录日志的方法进行标记

/** 常用注解说明:* 1. RetentionPolicy(保留策略)是一个enum类型,有三个值* SOURCE        --  这个Annotation类型的信息只会保留在程序源码里,源码如果经过了编译后,Annotation的数据就会消失,并不会保留在编译好的.class文件里* CLASS         --  这个Annotation类型的信息保留在程序源码中,同时也会保留在编译好的.class文件里面,在执行的时候,并不会把这一些信息加载到虚拟 机(JVM)中去.注意一下,当你没有设定一个Annotation类型的Retention值时,系统默认值是CLASS。* RUNTIME       --  在源码、编译好的.class文件中保留信息,在执行的时候会把这一些信息加载到JVM中去的。** 2.ElementType @Target中的ElementType用来指定Annotation类型可以用在哪些元素上* TYPE(类型)    -- 在Class,Interface,Enum和Annotation类型上* FIELD        -- 属性上* METHOD       -- 方法上* PARAMETER    -- 参数上* CONSTRUCTOR  -- 构造函数上* LOCAL_VARIABLE -- 局部变量* ANNOTATION_TYPE   -- Annotation类型上* PACKAGE           -- 包上** 3.Documented    -- 让这个Annotation类型的信息能够显示在API说明文档上;没有添加的话,使用javadoc生成的API文件找不到这个类型生成的信息*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface TestAnnotation {//操作内容String operation() default "";
}

配置Aspect,创建规则和方法

package com.consumer.interceptor;import com.consumer.annotation.TestAnnotation;
import com.consumer.entity.LogMessage;
import com.consumer.entity.ReturnMessage;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;import org.springframework.stereotype.Component;import java.util.Date;/** 特别注意: Spring的配置文件中添加:** <aop:aspectj-autoproxy />* spring-mvc-dispatcher.xml中天机* <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller-->* <aop:aspectj-autoproxy proxy-target-class="true"/>** <aop:config>节点中proxy-target-class="true"不为true时。* 当登录的时候会报这个异常java.lang.NoSuchMethodException: $Proxy54.login(),*/
@Aspect
@Component
public class LogInterceptor {/*** 环绕通知 用于拦截指定内容,记录用户的操作* pj:ProceedingJoinPoint 是切入点对象* annotation:TestAnnotation 自定义的注解对象* object:Object 方法的第一个参数*/@Around(value = "@annotation(annotation) && args(object,..) ", argNames = "pj,annotation,object")public Object interceptorTest(ProceedingJoinPoint pj,TestAnnotation annotation, Object object) throws Throwable {System.out.println("执行方法 "+pj.getSignature().getName());// 初始化日志数据LogMessage logMessage = new LogMessage();// 获取操作的参数Object[] args = pj.getArgs();if(args.length>=1){// 写入idlogMessage.setManId(args[0].toString());}// 写入操作时间logMessage.setDate(new Date().getTime());// 写入操作名logMessage.setOperation(annotation.operation());// 执行操作,获取操作的返回结果ReturnMessage returnMessage = (ReturnMessage) pj.proceed();// 写入操作结果logMessage.setSuccess(returnMessage.getStatus());// 如果操作结果失败,写入失败原因if(!logMessage.isSuccess()){logMessage.setReason(returnMessage.getMsg());}//输出日志信息System.out.println(logMessage.toString());// 输出结束标识System.out.println("执行结束 "+pj.getSignature().getName());// 返回操作的原本结果return returnMessage;}
}

添加到配置文件xml

<context:annotation-config/>
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.consumer" />

日志封装

操作结果封装

记录controller的日志记录,单纯返回固定的测试数据

 @RequestMapping("aopTest")@ResponseBody@TestAnnotation(operation = "测试AOP日志记录")public ReturnMessage aopTest(@RequestParam(name = "manId")String manId){return new ReturnMessage(false, "草泥马", null);}

使用PostMan测试接口,数据返回无误

查看控制台日志信息,操作名,参数,结果和时间都被记录

import java.util.Arrays;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class AdviceTest {@Around("execution(* com.abc.service.*.many*(..))")public Object process(ProceedingJoinPoint point) throws Throwable {System.out.println("@Around:执行目标方法之前...");//访问目标方法的参数:Object[] args = point.getArgs();if (args != null && args.length > 0 && args[0].getClass() == String.class) {args[0] = "改变后的参数1";}//用改变后的参数执行目标方法Object returnValue = point.proceed(args);System.out.println("@Around:执行目标方法之后...");System.out.println("@Around:被织入的目标对象为:" + point.getTarget());return "原返回值:" + returnValue + ",这是返回结果的后缀";}@Before("execution(* com.abc.service.*.many*(..))")public void permissionCheck(JoinPoint point) {System.out.println("@Before:模拟权限检查...");System.out.println("@Before:目标方法为:" + point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName());System.out.println("@Before:参数为:" + Arrays.toString(point.getArgs()));System.out.println("@Before:被织入的目标对象为:" + point.getTarget());}@AfterReturning(pointcut="execution(* com.abc.service.*.many*(..))", returning="returnValue")public void log(JoinPoint point, Object returnValue) {System.out.println("@AfterReturning:模拟日志记录功能...");System.out.println("@AfterReturning:目标方法为:" + point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName());System.out.println("@AfterReturning:参数为:" + Arrays.toString(point.getArgs()));System.out.println("@AfterReturning:返回值为:" + returnValue);System.out.println("@AfterReturning:被织入的目标对象为:" + point.getTarget());}@After("execution(* com.abc.service.*.many*(..))")public void releaseResource(JoinPoint point) {System.out.println("@After:模拟释放资源...");System.out.println("@After:目标方法为:" + point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName());System.out.println("@After:参数为:" + Arrays.toString(point.getArgs()));System.out.println("@After:被织入的目标对象为:" + point.getTarget());}
}

触发

String result = manager.manyAdvices("aa", "bb");
System.out.println("Test方法中调用切点方法的返回值:" + result);

控制台结果

@Around:执行目标方法之前...
@Before:模拟权限检查...
@Before:目标方法为:com.abc.service.AdviceManager.manyAdvices
@Before:参数为:[改变后的参数1, bb]
@Before:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e
方法:manyAdvices
@Around:执行目标方法之后...
@Around:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e
@After:模拟释放资源...
@After:目标方法为:com.abc.service.AdviceManager.manyAdvices
@After:参数为:[改变后的参数1, bb]
@After:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e
@AfterReturning:模拟日志记录功能...
@AfterReturning:目标方法为:com.abc.service.AdviceManager.manyAdvices
@AfterReturning:参数为:[改变后的参数1, bb]
@AfterReturning:返回值为:原返回值:改变后的参数1 、 bb,这是返回结果的后缀
@AfterReturning:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e
Test方法中调用切点方法的返回值:原返回值:改变后的参数1 、bb,这是返回结果的后缀

spring中自定义注解(annotation)与AOP中获取注解___使用aspectj的@Around注解实现用户操作和操作结果日志相关推荐

  1. 数据库中自定义排序规则,Mysql中自定义字段排序规则,Oracle中自定义字段排序规则,decode函数的用法,field函数的用法

    数据库中自定义排序 场景:有一张banner表,表中有一个status字段,有0, 1, 2三个状态位,我想要 1,0,2的自定义排序(这里是重点),然后再进行之上对sequence字段进行二次排序( ...

  2. 正确实现用spring扫描自定义的annotation

    背景 在使用spring时,有时候有会有一些自定义annotation的需求,比如一些Listener的回调函数. 比如: @Service public class MyService {@MyLi ...

  3. android项目中自定义顶部标题栏,Android项目中自定义顶部标题栏

    Android项目中自定义顶部标题栏 下面给大家详细介绍android中自定义顶部标题栏的思路及实现方式 先来图: 思路及实现步骤 1.定义标题栏布局 2.自定义TitleActivity控制标题栏按 ...

  4. spring mvc 中自定义404页面在IE中无法显示favicon.ico问题的解决方法。

    此处用的是jsp,控制层用的是ModelAndView, 具体解决方法如下: @RequestMapping(value = "notfound", method = Reques ...

  5. Winform中自定义xml配置文件,并配置获取文件路径

    场景 在Winform程序中,需要将一些配置项存到配置文件中,这时就需要自定义xml的配置文件格式.并在一些工具类中去获取配置文件的路径并加载其内容. 关注公众号 霸道的程序猿 获取编程相关电子书.教 ...

  6. 蓝牙广播包中自定义厂家数据的设置及获取

    硬件: Nordic nrf52840 SoC 软件: Nordic nRF5_SDK_15.2.0_9412b96 1. 蓝牙 perihperal 设备广播自定义数据设定 蓝牙广播相关数据结构定义 ...

  7. html中自定义右键菜单功能,HTML中自定义右键菜单功能

    我们使用的应用系统很多都有右键菜单功能.但是在网页上面,点击右键一般显示的却是IE默认的右键菜单,那么我们如何实现自己的右键菜单呢?下面将讲解右键菜单功能的实现原理和实现代码. 实现原理 在HTML语 ...

  8. springboot项目中自定义注解的使用总结、java自定义注解实战(常用注解DEMO)

    初学spring的时候使用注解总觉得使用注解很神奇,加一个注解就能实现想要的功能,很好奇,也想自己根据需要写一些自己实现的自定义注解.问题来了,自定义注解到底是什么?肯定会有人和我一样有这个疑惑,我根 ...

  9. (转)Spring对注解(Annotation)处理源码分析1——扫描和读取Bean定义

    1.从Spring2.0以后的版本中,Spring也引入了基于注解(Annotation)方式的配置,注解(Annotation)是JDK1.5中引入的一个新特性,用于简化Bean的配置,某些场合可以 ...

最新文章

  1. mysql教程多表查询_mysql重点,表查询操作和多表查询
  2. oracle数据库enq: TX - allocate ITL entry性能诊断
  3. 使用python获取路径问题
  4. C++源码的调用图生成
  5. python turtle库输出文字_python turtle库学习笔记
  6. Spring Cloud实战小贴士:随机端口
  7. mybatis中#{}和${}
  8. php调用纯真ip,php调用纯真IP数据库进行IP定位
  9. IO流缓冲流等高级流
  10. 运用帝国CMS建站仿站的简单教程(初学者进)
  11. 海大计算机考研经验,关于考研中国海洋大学的一些小经验
  12. 麻雀要革命 第四章 旋转!追逐游戏的命运齿轮 第一节
  13. 游戏计算机电源,吃鸡一族看过来,游戏PC应配什么电源?
  14. ssh 远程锁住解锁_超级管理员被锁定,如何解锁?
  15. 只是为了好玩:linux之父林纳斯自传.pdf,读后感:《只是为了好玩:Linux之父林纳斯自传》...
  16. 数据结构5.2图的存储与遍历
  17. 技术分享 | 带你探索三维激光雷达“眼中”的世界
  18. 音乐相册android studio,音乐相册
  19. 学java的第九天,面向对象 23.2.15
  20. 题目:一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少

热门文章

  1. ElasticSearch 全文搜索引擎;ES 搜索引擎
  2. Linux - 操作系统
  3. IB学霸分享学习经验(家长如何助孩子一臂之力)
  4. 互联网(internet)、因特网(Internet)、万维网(World Wide Web)三者区别
  5. windows10专业版 hyper-v
  6. 百练1041-反反复复-2016正式C题
  7. Matlab AppDesigner编程教程第1章——面向对象编程
  8. MySQL BETWEEN 语法
  9. Java产品:CRM客户关系管理系统
  10. 用python写情书_《使用Python进行自然语言处理》学习笔记一 | 学步园