Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。
作用在代码的注解是
作用在其他注解的注解(或者说 元注解)是:
从 Java 7 开始,额外添加了 3 个注解:
(01) Annotation 就是个接口。
"每 1 个 Annotation" 都与 "1 个 RetentionPolicy" 关联,并且与 "1~n 个 ElementType" 关联。可以通俗的理解为:每 1 个 Annotation 对象,都会有唯一的 RetentionPolicy 属性;至于 ElementType 属性,则有 1~n 个。
(02) ElementType 是 Enum 枚举类型,它用来指定 Annotation 的类型。
"每 1 个 Annotation" 都与 "1~n 个 ElementType" 关联。当 Annotation 与某个 ElementType 关联时,就意味着:Annotation有了某种用途。例如,若一个 Annotation 对象是 METHOD 类型,则该 Annotation 只能用来修饰方法。
(03) RetentionPolicy 是 Enum 枚举类型,它用来指定 Annotation 的策略。通俗点说,就是不同 RetentionPolicy 类型的 Annotation 的作用域不同。
"每 1 个 Annotation" 都与 "1 个 RetentionPolicy" 关联。
这时,只需要记住"每 1 个 Annotation" 都与 "1 个 RetentionPolicy" 关联,并且与 "1~n 个 ElementType" 关联。学完后面的内容之后,再回头看这些内容,会更容易理解。
理解了上面的 3 个类的作用之后,我们接下来可以讲解 Annotation 实现类的语法定义了。
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
}
说明:
上面的作用是定义一个 Annotation,它的名字是 MyAnnotation1。定义了 MyAnnotation1 之后,我们可以在代码中通过 "@MyAnnotation1" 来使用它。 其它的,@Documented, @Target, @Retention, @interface 都是来修饰 MyAnnotation1 的。下面分别说说它们的含义:
(01) @interface
使用 @interface 定义注解时,意味着它实现了 java.lang.annotation.Annotation 接口,即该注解就是一个Annotation。
定义 Annotation 时,@interface 是必须的。
注意:它和我们通常的 implemented 实现接口的方法不同。Annotation 接口的实现细节都由编译器完成。通过 @interface 定义注解后,该注解不能继承其他的注解或接口。
(02) @Documented
类和方法的 Annotation 在缺省情况下是不出现在 javadoc 中的。如果使用 @Documented 修饰该 Annotation,则表示它可以出现在 javadoc 中。
定义 Annotation 时,@Documented 可有可无;若没有定义,则 Annotation 不会出现在 javadoc 中。
(03) @Target(ElementType.TYPE)
前面我们说过,ElementType 是 Annotation 的类型属性。而 @Target 的作用,就是来指定 Annotation 的类型属性。
@Target(ElementType.TYPE) 的意思就是指定该 Annotation 的类型是 ElementType.TYPE。这就意味着,MyAnnotation1 是来修饰"类、接口(包括注释类型)或枚举声明"的注解。
定义 Annotation 时,@Target 可有可无。若有 @Target,则该 Annotation 只能用于它所指定的地方;若没有 @Target,则该 Annotation 可以用于任何地方。
(04) @Retention(RetentionPolicy.RUNTIME)
前面我们说过,RetentionPolicy 是 Annotation 的策略属性,而 @Retention 的作用,就是指定 Annotation 的策略属性。
@Retention(RetentionPolicy.RUNTIME) 的意思就是指定该 Annotation 的策略是 RetentionPolicy.RUNTIME。这就意味着,编译器会将该 Annotation 信息保留在 .class 文件中,并且能被虚拟机读取。
定义 Annotation 时,@Retention 可有可无。若没有 @Retention,则默认是 RetentionPolicy.CLASS。
通过上面的示例,我们能理解:@interface 用来声明 Annotation,@Documented 用来表示该 Annotation 是否会出现在 javadoc 中, @Target 用来指定 Annotation 的类型,@Retention 用来指定 Annotation 的策略。
理解这一点之后,我们就很容易理解 java 中自带的 Annotation 的实现类,即 Annotation 架构图的右半边。如下图:
java 常用的 Annotation:
@Deprecated -- @Deprecated 所标注内容,不再被建议使用。
@Override -- @Override 只能标注方法,表示该方法覆盖父类中的方法。
@Documented -- @Documented 所标注内容,可以出现在javadoc中。
@Inherited -- @Inherited只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性。
@Retention -- @Retention只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性。
@Target -- @Target只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性。
@SuppressWarnings -- @SuppressWarnings 所标注内容产生的警告,编译器会对这些警告保持静默。
Annotation 是一个辅助类,它在 Junit、Struts、Spring 等工具框架中被广泛使用。
我们在编程中经常会使用到的 Annotation 作用有:
Annotation 具有"让编译器进行编译检查的作用"。
例如,@SuppressWarnings, @Deprecated 和 @Override 都具有编译检查作用。
(02) 若某个方法被 @Override 的标注,则意味着该方法会覆盖父类中的同名方法。如果有方法被 @Override 标示,但父类中却没有"被 @Override 标注"的同名方法,则编译器会报错。示例如下:
在反射的 Class, Method, Field 等函数中,有许多于 Annotation 相关的接口。
这也意味着,我们可以在反射中解析并使用 Annotation。
import java.lang.annotation.Annotation;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Inherited;
import java.lang.reflect.Method;
/**
* Annotation在反射函数中的使用示例
*/
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
String[] value() default "unknown";
}
/**
* Person类。它会使用MyAnnotation注解。
*/
class Person {
/**
* empty()方法同时被 "@Deprecated" 和 "@MyAnnotation(value={"a","b"})"所标注
* (01) @Deprecated,意味着empty()方法,不再被建议使用
* (02) @MyAnnotation, 意味着empty() 方法对应的MyAnnotation的value值是默认值"unknown"
*/
@MyAnnotation
@Deprecated
public void empty(){
System.out.println("\nempty");
}
/**
* sombody() 被 @MyAnnotation(value={"girl","boy"}) 所标注,
* @MyAnnotation(value={"girl","boy"}), 意味着MyAnnotation的value值是{"girl","boy"}
*/
@MyAnnotation(value={"girl","boy"})
public void somebody(String name, int age){
System.out.println("\nsomebody: "+name+", "+age);
}
}
public class AnnotationTest {
public static void main(String[] args) throws Exception {
// 新建Person
Person person = new Person();
// 获取Person的Class实例
Class c = Person.class;
// 获取 somebody() 方法的Method实例
Method mSomebody = c.getMethod("somebody", new Class[]{String.class, int.class});
// 执行该方法
mSomebody.invoke(person, new Object[]{"lily", 18});
iteratorAnnotations(mSomebody);
// 获取 somebody() 方法的Method实例
Method mEmpty = c.getMethod("empty", new Class[]{});
// 执行该方法
mEmpty.invoke(person, new Object[]{});
iteratorAnnotations(mEmpty);
}
public static void iteratorAnnotations(Method method) {
// 判断 somebody() 方法是否包含MyAnnotation注解
if(method.isAnnotationPresent(MyAnnotation.class)){
// 获取该方法的MyAnnotation注解实例
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
// 获取 myAnnotation的值,并打印出来
String[] values = myAnnotation.value();
for (String str:values)
System.out.printf(str+", ");
System.out.println();
}
// 获取方法上的所有注解,并打印出来
Annotation[] annotations = method.getAnnotations();
for(Annotation annotation : annotations){
System.out.println(annotation);
}
}
}
运行结果:
somebody: lily, 18 girl, boy, @com.skywang.annotation.MyAnnotation(value=[girl, boy]) empty unknown, @com.skywang.annotation.MyAnnotation(value=[unknown]) @java.lang.Deprecated()
通过给 Annotation 注解加上 @Documented 标签,能使该 Annotation 标签出现在 javadoc 中。
通过 @Override, @Deprecated 等,我们能很方便的了解程序的大致结构。
另外,我们也可以通过自定义 Annotation 来实现一些功能。
TranscationModel
package com.huangting.annotation.P1;
/**
* enum枚举
*/
public enum TranscationModel {
Read, ReadWrite, Write
}
MyAnnotation1
package com.huangting.annotation.P1;
import java.lang.annotation.*;
/**
* MyAnnotation1注解可以用在类、接口、属性、方法上
* 注解运行期也保留
* 不可继承
*/
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation1 {
String name();
}
MyAnnotation2
package com.huangting.annotation.P1;
import java.lang.annotation.*;
/**
* MyAnnotation2注解可以用在方法上
* 注解运行期也保留
* 不可继承
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation2 {
TranscationModel model() default TranscationModel.ReadWrite;
}
MyAnnotation3
package com.huangting.annotation.P1;
import java.lang.annotation.*;
/**
* MyAnnotation3注解可以用在方法上
* 注解运行期也保留
* 可继承
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAnnotation3 {
TranscationModel[] models() default TranscationModel.ReadWrite;
}
Dome1
package com.huangting.annotation.P1;
/**
* 获取类与方法上的注解值
*/
@MyAnnotation1(name = "abc")
public class Demo1 {
@MyAnnotation1(name = "xyz")
private Integer age;
@MyAnnotation2(model = TranscationModel.Read)
public void list() {
System.out.println("list");
}
@MyAnnotation3(models = {TranscationModel.Read, TranscationModel.Write})
public void edit() {
System.out.println("edit");
}
}
Demo1Test
package com.huangting.annotation.P1;
import org.junit.Test;
public class Demo1Test {
@Test
public void list() throws Exception {
// 获取类上的注解
MyAnnotation1 annotation1 = Demo1.class.getAnnotation(MyAnnotation1.class);
System.out.println(annotation1.name());//abc
// 获取方法上的注解
MyAnnotation2 myAnnotation2 = Demo1.class.getMethod("list").getAnnotation(MyAnnotation2.class);
System.out.println(myAnnotation2.model());//Read
}
@Test
public void edit() throws Exception {
MyAnnotation3 myAnnotation3 = Demo1.class.getMethod("edit").getAnnotation(MyAnnotation3.class);
for (TranscationModel model : myAnnotation3.models()) {
System.out.println(model);//Read,Write
}
}
}
TestAnnotation
package com.huangting.annotation.P2;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//@Retention(RetentionPolicy.SOURCE)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TestAnnotation {
String value() default "默认value值";
String what() default "这里是默认的what属性对应的值";
}
package com.huangting.annotation.P2;
/**
* 获取类属性上的注解属性值
*/
public class Demo2 {
@TestAnnotation(value = "这就是value对应的值_msg1", what = "这就是what对应的值_msg1")
private static String msg1;
@TestAnnotation("这就是value对应的值1")
private static String msg2;
@TestAnnotation(value = "这就是value对应的值2")
private static String msg3;
@TestAnnotation(what = "这就是what对应的值")
private static String msg4;
}
Demo2Test
package com.huangting.annotation.P2;
import org.junit.Test;
public class Demo2Test {
@Test
public void test1() throws Exception {
TestAnnotation msg1 = Demo2.class.getDeclaredField("msg1").getAnnotation(TestAnnotation.class);
System.out.println(msg1.value());
System.out.println(msg1.what());
}
@Test
public void test2() throws Exception{
TestAnnotation msg2 = Demo2.class.getDeclaredField("msg2").getAnnotation(TestAnnotation.class);
System.out.println(msg2.value());
System.out.println(msg2.what());
}
@Test
public void test3() throws Exception{
TestAnnotation msg3 = Demo2.class.getDeclaredField("msg3").getAnnotation(TestAnnotation.class);
System.out.println(msg3.value());
System.out.println(msg3.what());
}
@Test
public void test4() throws Exception{
TestAnnotation msg4 = Demo2.class.getDeclaredField("msg4").getAnnotation(TestAnnotation.class);
System.out.println(msg4.value());
System.out.println(msg4.what());
}
}
IsNotNull
package com.huangting.annotation.P3;
import java.lang.annotation.*;
/**
* 非空注解:使用在方法的参数上,false表示此参数可以为空,true不能为空
*/
@Documented
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsNotNull {
boolean value() default false;
}
Demo3
package com.huangting.annotation.P3;
/**
* 获取参数修饰注解对应的属性值
*/
public class Demo3 {
public void hello1(@IsNotNull(true) String name) {
System.out.println("hello:" + name);
}
public void hello2(@IsNotNull String name) {
System.out.println("hello:" + name);
}
}
Demo3Test
package com.huangting.annotation.P3;
import org.junit.Test;
import java.lang.reflect.Parameter;
public class Demo3Test {
@Test
public void hello1() throws Exception {
Demo3 demo3 = new Demo3();
for (Parameter parameter : demo3.getClass().getMethod("hello1", String.class).getParameters()) {
IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
if(annotation != null){
System.out.println(annotation.value());//true
}
}
}
@Test
public void hello2() throws Exception {
Demo3 demo3 = new Demo3();
for (Parameter parameter : demo3.getClass().getMethod("hello2", String.class).getParameters()) {
IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
if(annotation != null){
System.out.println(annotation.value());//false
}
}
}
}
效果:
MyLog
package com.huangting.annotation.Aop;
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)
public @interface MyLog {
String desc();
}
MyLogAspect
package com.huangting.annotation.Aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyLogAspect {
private static final Logger logger = LoggerFactory.getLogger(MyLogAspect.class);
/**
* 只要用到了com.huangting.annotation.Aop.MyLog这个注解的,就是目标类
*/
@Pointcut("@annotation(com.huangting.annotation.Aop.MyLog)")
private void MyValid() {
}
@Before("MyValid()")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
MyLog myLog = signature.getMethod().getAnnotation(MyLog.class);
System.out.println("[" + signature.getName() + " : start.....]");
System.out.println("【目标对象方法被调用时候产生的日志,记录到日志表中】:"+myLog.desc());
}
}
BaseTestCase
package com.huangting.annotation.Aop;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:applicationContext.xml"})
public class BaseTestCase {
}
LogController
package com.huangting.annotation.Aop;
import org.springframework.stereotype.Component;
@Component
public class LogController {
@MyLog(desc = "这是结合spring aop知识,讲解自定义注解应用的一个案例")
public void testLogAspect(){
System.out.println("墙头马上遥相顾");
}
}
LogControllerTest
package com.huangting.annotation.Aop;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
public class LogControllerTest extends BaseTestCase {
@Autowired
private LogController logController;
@Test
public void testLogAspect(){
logController.testLogAspect();
}
}
JointPoint是程序运行过程中可识别的点,这个点可以用来作为AOP切入点。JointPoint对象则包含了和切入相关的很多信息。比如切入点的对象,方法,属性等。我们可以通过反射的方式获取这些点的状态和信息,用于追踪tracing和记录logging应用信息。
JointPoint使用详解
这里详细介绍JointPoint的方法,这部分很重要是coding核心参考部分。开始之前我们思考一下,我们到底需要获取切入点的那些信息。我的思考如下
切入点的方法名字及其参数
切入点方法标注的注解对象(通过该对象可以获取注解信息)
切入点目标对象(可以通过反射获取对象的类名,属性和方法名)
注:有一点非常重要,Spring的AOP只能支持到方法级别的切入。换句话说,切入点只能是某个方法。
针对以上的需求JDK提供了如下API————————————————
版权声明:本文为CSDN博主「如何在5年薪百万」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/kouryoushine/article/details/105299956
1 获取切入点所在目标对象
Object targetObj =joinPoint.getTarget();
# 可以发挥反射的功能获取关于类的任何信息,例如获取类名如下
String className = joinPoint.getTarget().getClass().getName();
因为一个类有很多方法,为了获取具体切入点所在的方法可以通过如下API
2.获取切入点方法的名字
getSignature());是获取到这样的信息 :修饰符+ 包名+组件名(类名) +方法名
这里我只需要方法名
String methodName = joinPoint.getSignature().getName()
3. 获取方法上的注解
方法1:xxxxxx是注解名字
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
xxxxxx annoObj= method.getAnnotation(xxxxxx.class);
}
return null;
方法2:上面我们已经知道了方法名和类的对象,通过反射可以获取类的内部任何信息。
// 切面所在类
Object target = joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
Method method = null;
for (Method m : target.getClass().getMethods()) {
if (m.getName().equals(methodName)) {
method = m;
// xxxxxx annoObj= method.getAnnotation(xxxxxx.class);同上
break;
}
}
4. 获取方法的参数
这里返回的是切入点方法的参数列表
Object[] args = joinPoint.getArgs();
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiLog
{
/**
* 模块
*/
public String title() default "";
/**
* 日志记录service实现
* @return
*/
public String logService() default "operLogServiceImpl";
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
/**
* 是否追踪用户操作
* @return
*/
public boolean isTrack() default true;
}
切面类
package com.kouryoushine.aop.test;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@Aspect
@Component
public class DemoAspect {
//切入点:aopdemo报下所有对象的save方法
@Pointcut("execution(public * com.kouryoushine.aop.test.*.save*(..))")
public void save(){
}
/**
* 需要在update操作前后分别获取更新前后的值
* @param
* @return
*/
@AfterReturning("save()")
public void afterReturn(JoinPoint joinPoint) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1.获取切入点所在目标对象
Object targetObj =joinPoint.getTarget();
System.out.println(targetObj.getClass().getName());
// 2.获取切入点方法的名字
String methodName = joinPoint.getSignature().getName();
System.out.println("切入方法名字:"+methodName);
// 3. 获取方法上的注解
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
ApiLog apiLog= method.getAnnotation(ApiLog.class);
System.out.println("切入方法注解的title:"+apiLog.title());
}
//4. 获取方法的参数
Object[] args = joinPoint.getArgs();
for(Object o :args){
System.out.println("切入方法的参数:"+o);
}
}
}
服务类
@Service
public class TestServcie {
@ApiLog(title = "注解的标题",isSaveRequestData = false)
public void save(String parm1,int parm2){
System.out.println("执行目标对象的方法"+parm1+parm2);
}
public void update(){
System.out.println("没有注解的方法,不会被拦截");
}
}
测试方法
@Autowired
TestServcie testServcie;
@Test
void test6() throws Exception{
testServcie.save("参数1字符串",33);
}
测试结果
com.kouryoushine.aop.test.TestServcie
切入方法名字:save
切入方法注解的title:注解的标题
切入方法的参数:参数1字符串
切入方法的参数:33
@Cacheable注解作用,将带有该注解方法的返回值存放到redis的的中;
使用方法在方法上使用@Cacheable(键=“测试+#P0 + P1#…”)
表示键值为测试+方法第一个参数+方法第二个参数,值为该方法的返回值。
以下源代码表示获取人员列表,Redis的中存放的关键值为’领袖’+ leaderGroupId + UUID + yearDetailId
@Override
@Cacheable(key="'leader'+#p0+#p1+#p2",value="leader")
public List listLeaders(String leaderGroupId, String uuid, String yearDetailId) {
return sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId);
}
创建的Java的注解@ExtCacheable
package com.huajie.annotation;
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)
public @interface Cacheable {
String key() default "";
String nextKey() default "";
int expireTime() default 1800;//30分钟
}
SpringAop切面CacheableAspect
package com.huajie.aspect;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.huajie.annotation.ExtCacheable;
import com.huajie.utils.RedisUtil;
/**
* redis缓存处理
* 不适用与内部方法调用(this.)或者private
*/
@Component
@Aspect
public class CacheableAspect {
@Autowired
private RedisUtil redisUtil;
@Pointcut("@annotation(com.huajie.annotation.Cacheable)")
public void annotationPointcut() {
}
@Around("annotationPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 获得当前访问的class
Class> className = joinPoint.getTarget().getClass();
// 获得访问的方法名
String methodName = joinPoint.getSignature().getName();
// 得到方法的参数的类型
Class>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
Object[] args = joinPoint.getArgs();
String key = "";
int expireTime = 1800;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
method.setAccessible(true);
// 判断是否存在@ExtCacheable注解
if (method.isAnnotationPresent(ExtCacheable.class)) {
ExtCacheable annotation = method.getAnnotation(ExtCacheable.class);
key = getRedisKey(args,annotation);
expireTime = getExpireTime(annotation);
}
} catch (Exception e) {
throw new RuntimeException("redis缓存注解参数异常", e);
}
// 获取缓存是否存在
boolean hasKey = redisUtil.hasKey(key);
if (hasKey) {
return redisUtil.get(key);
} else {
//执行原方法(java反射执行method获取结果)
Object res = joinPoint.proceed();
//设置缓存
redisUtil.set(key, res);
//设置过期时间
redisUtil.expire(key, expireTime);
return res;
}
}
private int getExpireTime(ExtCacheable annotation) {
return annotation.expireTime();
}
private String getRedisKey(Object[] args,ExtCacheable annotation) {
String primalKey = annotation.key();
//获取#p0...集合
List keyList = getKeyParsList(primalKey);
for (String keyName : keyList) {
int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", ""));
Object parValue = args[keyIndex];
primalKey = primalKey.replace(keyName, String.valueOf(parValue));
}
return primalKey.replace("+","").replace("'","");
}
// 获取key中#p0中的参数名称
private static List getKeyParsList(String key) {
List ListPar = new ArrayList();
if (key.indexOf("#") >= 0) {
int plusIndex = key.substring(key.indexOf("#")).indexOf("+");
int indexNext = 0;
String parName = "";
int indexPre = key.indexOf("#");
if(plusIndex>0){
indexNext = key.indexOf("#") + key.substring(key.indexOf("#")).indexOf("+");
parName = key.substring(indexPre, indexNext);
}else{
parName = key.substring(indexPre);
}
ListPar.add(parName.trim());
key = key.substring(indexNext + 1);
if (key.indexOf("#") >= 0) {
ListPar.addAll(getKeyParsList(key));
}
}
return ListPar;
}
}
参考:
1、深入理解Java注解类型(@Annotation)_zejian_的博客-CSDN博客_java注解
2、java之aop使用及自定义注解 - 沦陷 - 博客园
3、SpringAOP中的JointPoint和ProceedingJoinPoint使用详解(附带详细示例)_如何在5年薪百万的博客-CSDN博客_proceedingjoinpoint有什么用