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

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

日志封装

img

操作结果封装

img

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

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

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

img

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

img

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,这是返回结果的后缀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556434.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php获取40001,php - Discord API错误#40001未经授权 - SO中文参考 - www.soinside.com

我通过OAuth2 URL(https://discordapp.com/api/oauth2/authorize?client_id398437519408103444&permissions59392&scopebot)验证我的机器人我想在我的discord服务器上发送消息到频道码&#xff1a;$curl curl_init();curl_setopt($curl, CURLOPT_URL, https://discor…

python 编译器pyc_有没有办法知道哪个Python版本.pyc文件被编译?

Is there any way to know by which Python version the .pyc file was compiled? 解决方案 You can get the magic number of your Python as follows: $ python -V Python 2.6.2 # python >>> import imp >>> imp.get_magic().encode(hex) d1f20d0a To ge…

php可以支持代码重用技术的命令,Linux下的编程 PHP高级技巧全放送(一)

全球超过300万个互联网网站的管理员都在使用&#xff0c;使得它成为最为普及的端脚本语言之一。其特点是运行速度快、稳定可靠、跨平台&#xff0c;而且是开放源软件。 随你使用的水平不同&#xff0c;PHP可以很简单&#xff0c;也可以很复杂&#xff0c;可以只使用它发送表格元…

python处理word表格格式_python---word表格样式设置

1、word表格样式的设置from docx import * document Document() table document.add_table(3, 3, style"Medium Grid 1 Accent 1") heading_cells table.rows[0].cells heading_cells[0].text 第一列内容 heading_cells[1].text 第二列内容 heading_cells[2].te…

Spring AOP——Spring 中面向切面编程

前面两篇文章记录了 Spring IOC 的相关知识&#xff0c;本文记录 Spring 中的另一特性 AOP 相关知识。 部分参考资料&#xff1a; 《Spring实战&#xff08;第4版&#xff09;》 《轻量级 JavaEE 企业应用实战&#xff08;第四版&#xff09;》 Spring 官方文档 W3CSchool Spri…

python getchar,Linux C编程学习:getchar()和getch()

getchar函数名: getchar功 能: 从stdin流中读字符用 法: int getchar(void);注解&#xff1a;getchar有一个int型的返回值&#xff0c;当程序调用getchar时程序就等着用户按键&#xff0c;用户输入的字符被存放在键盘缓冲区中直到用户按回车为止(回车字符也放在缓冲区中)。当用…

python 折线图中文乱码_彻底解决 Python画图中文乱码问题--Pyplotz组件

1 源起 自从开始学习Python&#xff0c;就非常喜欢用来画图。一直没有需求画要中文显示信息的图&#xff0c;所以没有配置Python中文的环境。由于昨天就需要画几十个形式相同&#xff0c;只是数据不同的图&#xff0c;并且需要显示中文信息。如果用Excel画图会很浪费时间&#…

SpringBoot的AOP是默认开启的,不需要加注解@EnableAspectJAutoProxy____听说SpringAOP 有坑?那就来踩一踩

Aspect Component public class CustomerServiceInterceptor {Before("execution(public * org.example.aop.demo..*.*(..))")public void doBefore() {System.out.println("do some important things before...");} }另外SpringBoot默认是cglib动态代理&a…

php4 apache 配置,[开发环境配置]windows下php4+mysql4+apache2开发环境配置

在网上看了好多php&#xff0b;mysql&#xff0b;apache的开发环境配置文档。但是其中不乏出现了很多的问题导致在配置开发环境的时候出现了很多的问题。这里总结出一份自己配置过程中的文档希望能给大家带来一些帮助。那么废话不多说&#xff0c;我们开始吧。首先我在配置的时…

mysql 开启远程访问_QxOrm 访问 MySQL

在前面的 QxOrm 章节中&#xff0c;我们已经介绍了对本地数据库的操作&#xff0c;现在是时候介绍对远程数据库的访问了&#xff0c;那么就以最常用的 MySQL 为例吧&#xff01;在开始之前&#xff0c;首先要安装 MySQL。如果条件允许&#xff0c;建议将其安装在 Linux 系统上&…

当泛型遇到重载

当泛型遇到了重载&#xff0c;好戏&#xff0c;就发生了。 请看下面代码&#xff1a; 问题&#xff1a;代码能正确编译吗&#xff1f; 这个题目是一个考察泛型的题目。java里面&#xff0c;泛型实际上是“伪泛型”&#xff0c;并不像C#那样是实际上的泛型。 IDE会提示我们下…

oracle 查看时间对应周,oracle数据获取当前自然周,当前周的起始和结束时间

select to_char(sysdate,iw) from dual; --本周是第几个自然周select to_char(sysdate,yyyy) into v_sbzq_nf from dual; -- 当前年份SELECT to_char(TRUNC(TO_DATE(to_char(sysdate,yyyy-MM-dd),yyyy-MM-dd),IW),yyyy-MM-dd) FROM DUAL;--本周的起始时间(本周周一日期)SELECT …

redis查询所有key命令_三歪推荐:Redis常见的面试题

本文公众号来源&#xff1a;科技缪缪作者&#xff1a;科技缪缪本文已收录至我的GitHub说说Redis基本数据类型有哪些吧字符串&#xff1a;redis没有直接使用C语言传统的字符串表示&#xff0c;而是自己实现的叫做简单动态字符串SDS的抽象类型。C语言的字符串不记录自身的长度信息…

springboot系列——redisTemplate和stringRedisTemplate对比、redisTemplate几种序列化方式比较

文章目录一、redisTemplate和stringRedisTemplate对比1、StringRedisTemplate2、RedisTemplate二、redisTemplate序列化方式比较1、性能测试对比2、性能总结3、方案一、考虑效率和可读性&#xff0c;牺牲部分空间4、方案二、空间敏感&#xff0c;忽略可读性和效率影响5、使用示…

oracle rman备份慢,诊断Oracle RMAN备份慢的原因

㈠ 先在系统层面查询CPU、IO使用情况&#xff1a;top、iostat、sar...等㈡ EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT();㈢ rman target / debug trace /tmp/rmandebug.txt log/tmp/rman_log20130727.txt执行备份㈣ EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT();㈤ …

mysql查询默认排序规则_深究 mysql 默认排序, order by 的顺序【收藏】

mysql 语句中如果没有使用 order by 来排序&#xff0c;通常会用 主键正序排列&#xff0c;但是有的时候不是这样&#xff0c;来看一个实例。实例群友问&#xff1a;请教一个问题&#xff0c;mysql 默认排序问题&#xff0c;当sql 语句 的排序没有指定 主键&#xff08;id&…

Spring Boot jackson配置使用详解

Spring Boot系列-json框架jackson配置详解 T1 - 前言 目前Java最常见的3中JSON操作框架分别为Gson、Jackson、FastJson&#xff0c;该篇文章主要讲解jackson在SpringBoot环境中各配置项的具体作用。 T2 - 环境依赖 jackson是spring-boot的web/webflux框架默认依赖的json库&…

oracle字段序列自增长,ORACLE序列(字段自增长)

序列:是oacle提供的用于产生一系列唯一数字的数据库对象。 自动提供唯一的数值 共享对象 主要用于提供主键值 将序列值装入内存可以提高访问效率创建序列&#xff1a;1、 要有创建序列的权限 create sequence 或 create any sequence2、 创建序列的语法CREATE SEQUENCE seque…

频率统计表用c语言_空间矢量脉宽调制建模与仿真(基于C语言的SIMULINK仿真模型 | 基于SVPWM模块的仿真)...

文末有仿真模型下载方式1.1 基于C语言的SIMULINK仿真模型使用C语言在MATLAB/SIMULINK中仿真&#xff0c;需要借助s-function builder模块实现。七段式SVPWM仿真模型如图1-1所示。仿真解算器&#xff08;Solver&#xff09;选择变步长&#xff08;Variable-step&#xff09;、od…

pythonrandom库seed_Python

Python seed() 函数 描述 seed() 方法改变随机数生成器的种子&#xff0c;可以在调用其他随机模块函数之前调用此函数。 语法 以下是 seed() 方法的语法: import random random.seed ( [x] ) 我们调用 random.random() 生成随机数时&#xff0c;每一次生成的数都是随机的。但是…