Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。

Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。

内置的注解

Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

作用在代码的注解是

  • @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。

作用在其他注解的注解(或者说 元注解)是:

  • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
  • @Documented - 标记这些注解是否包含在用户文档中。
  • @Target - 标记这个注解应该是哪种 Java 成员。
  • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

从 Java 7 开始,额外添加了 3 个注解:

  • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
  • @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

(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" 关联。

  • a) 若 Annotation 的类型为 SOURCE,则意味着:Annotation 仅存在于编译器处理期间,编译器处理完之后,该 Annotation 就没用了。 例如," @Override" 标志就是一个 Annotation。当它修饰一个方法的时候,就意味着该方法覆盖父类的方法;并且在编译期间会进行语法检查!编译器处理完后,"@Override" 就没有任何作用了。
  • b) 若 Annotation 的类型为 CLASS,则意味着:编译器将 Annotation 存储于类对应的 .class 文件中,它是 Annotation 的默认行为。
  • c) 若 Annotation 的类型为 RUNTIME,则意味着:编译器将 Annotation 存储于 class 文件中,并且可由JVM读入。

这时,只需要记住"每 1 个 Annotation" 都与 "1 个 RetentionPolicy" 关联,并且与 "1~n 个 ElementType" 关联。学完后面的内容之后,再回头看这些内容,会更容易理解。


java 自带的 Annotation

理解了上面的 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。

2)java自带的Annotation

通过上面的示例,我们能理解:@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 的作用

Annotation 是一个辅助类,它在 Junit、Struts、Spring 等工具框架中被广泛使用。

我们在编程中经常会使用到的 Annotation 作用有:

1)编译检查

Annotation 具有"让编译器进行编译检查的作用"。

例如,@SuppressWarnings, @Deprecated 和 @Override 都具有编译检查作用。

(02) 若某个方法被 @Override 的标注,则意味着该方法会覆盖父类中的同名方法。如果有方法被 @Override 标示,但父类中却没有"被 @Override 标注"的同名方法,则编译器会报错。示例如下:

在反射中使用 Annotation

在反射的 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@Deprecatedpublic 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 {// 新建PersonPerson person = new Person();// 获取Person的Class实例Class<Person> 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 生成帮助文档

通过给 Annotation 注解加上 @Documented 标签,能使该 Annotation 标签出现在 javadoc 中。

4能够帮忙查看查看代码

通过 @Override, @Deprecated 等,我们能很方便的了解程序的大致结构。

另外,我们也可以通过自定义 Annotation 来实现一些功能。

java之aop使用及自定义注解

案例一(获取类与方法上的注解值):

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 {@Testpublic 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}@Testpublic 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 {@Testpublic void test1() throws Exception {TestAnnotation msg1 = Demo2.class.getDeclaredField("msg1").getAnnotation(TestAnnotation.class);System.out.println(msg1.value());System.out.println(msg1.what());}@Testpublic void test2() throws Exception{TestAnnotation msg2 = Demo2.class.getDeclaredField("msg2").getAnnotation(TestAnnotation.class);System.out.println(msg2.value());System.out.println(msg2.what());}@Testpublic void test3() throws Exception{TestAnnotation msg3 = Demo2.class.getDeclaredField("msg3").getAnnotation(TestAnnotation.class);System.out.println(msg3.value());System.out.println(msg3.what());}@Testpublic 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 {@Testpublic 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}}}@Testpublic 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}}}
}

效果:

Aop自定义注解的应用

  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 {@Autowiredprivate LogController logController;@Testpublic void testLogAspect(){logController.testLogAspect();}
}

Joint Point

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("没有注解的方法,不会被拦截");}
}

测试方法

  @AutowiredTestServcie testServcie;@Testvoid  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<Leader> 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 {  @Autowiredprivate RedisUtil redisUtil;@Pointcut("@annotation(com.huajie.annotation.Cacheable)")public void annotationPointcut() {}@Around("annotationPointcut()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {// 获得当前访问的classClass<?> 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<String> 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<String> getKeyParsList(String key) {List<String> ListPar = new ArrayList<String>();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有什么用

【Java注解系列】内置注解与AOP实现自定义注解相关推荐

  1. JDK注解(内置和自定义)

    JDK注解(内置和自定义) 1.内置 @Override:可以确保重写的方法,的确存在与父类.接口中,可以有效避免单词拼错的情况 @Deprecated:给用于提示,该方法由于安全,性能问题等,已经不 ...

  2. 如何使用 AOP 和自定义注解?

    作者 | 一个程序员的成长 责编 | 胡巍巍 记得今年年初刚开始面试的时候,被问的最多的就是你知道Spring的两大核心嘛?那你说说什么是AOP,什么是IOC?我相信你可能也被问了很多次了. 到底是什 ...

  3. 玩转Java注解:元注解、内置注解、自定义注解的原理和实现

    点击关注公众号,实用技术文章及时了解 来源:www.jianshu.com/p/ddd0b880641a 前言 Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种 ...

  4. 注解和反射详细笔记。自定义注解,元注解,内置注解。反射机制,Java Reflection,Java内存分析,反射操作注解,java.lang.reflect.Method,Class

    文章目录 注解 什么是注解 内置注解 元注解 自定义注解 反射机制 静态语言 vs 静态语言 Java Reflection 反射相关的主要API Class类 Java内存分析 创建运行时类的对象 ...

  5. java path 注解_Java内置系统注解和元注解

    第一节:注解(Annotation)的作用 Annotation(注解)是JDK5.0及以后版本引入的.它的作用是修饰程序元素.什么是程序元素呢?例如:包.类.构造方法.方法.成员变量等. 注解,就是 ...

  6. spring中aop拦截自定义注解不生效

    前言 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/bin392328206/six-finger 种一棵树最好的时间是十年前,其次是现在 我知道很多人不玩qq ...

  7. 自定义注解实现权限校验含义_厉害了!老大利用AOP实现自定义注解,半小时完成我三天工作量...

    前面我们已经介绍了AOP适用的一些业务场景以及简单的代码实现,当时我们的切点是用execution表达式来配置的,这种方式有一些局限性在里面: 灵活性不高,一个表达式只能切到某种同类型的方法 个性化不 ...

  8. item系列内置方法重构类

    item系列 和对象使用[]访问值有联系 obj = {'k':'v'} print(obj) # 字典的对象 print(obj['k']) 在有些内置的模块中,有一些特殊的方法,要求对象必须实现_ ...

  9. Septentrio:mosaic系列内置全功能Ntrip

    mosaic的系列产品内置了全功能Ntrip(Full-feature Ntrip),包括Ntrip Server.Ntrip Client以及Ntrip Caster.在这期,将围绕以下三个话题展开 ...

最新文章

  1. 数位dp 3943 二分法
  2. 【Groovy】Groovy 运算符重载 ( 运算符重载 | 运算符重载对应方法 )
  3. 图像sobel梯度详细计算过程_OpenCV-Python 图像梯度 | 十八
  4. Docker 1.10版本发布
  5. SQL Server 装载 msxmlsql.dll 失败的解决办法
  6. 如何分析EFCore引发的内存泄漏
  7. 发布一款给图片批量加水印的程序PicNet V1.0
  8. 苹果首席设计官将离职;华为将从世界范围招揽天才少年;新版 Edge 更新 | 极客头条...
  9. 卫星导航开源代码汇总
  10. BT和eMule协议的比较和分析
  11. CodeWarrior IDE烧写介绍
  12. 1143 Lowest Common Ancestor (30分) 附测试点分析
  13. 爬虫-域名抢注(whois.ai)
  14. 怎么下载老版本android,剪映旧版下载
  15. MySQL之——函数
  16. 用遗传算法GA改进CloudSim自带的资源调度策略
  17. 问题解决 Cannot calibrate from current position
  18. 广度优先搜索(Breadth First Search)
  19. [渝粤教育] 武汉理工大学 流体力学与流体机械 参考 资料
  20. 智能驾驶 车牌检测和识别(五)《C++实现车牌检测和识别(可实时车牌识别)》

热门文章

  1. 《软件工程概论》第二章核心内容
  2. oracle 根据spid查sql,探讨:Oracle数据库查看一个进程是如何执行相关的实际SQL语句...
  3. 增量式pid调节方式有何优点_增量式PID的“假抗饱和”性
  4. tplink控制上网设备_上网总有人抢网速如何解决 上网卡慢解决方法【详解】
  5. 如何保存文件为c语言格式,急求如何将下列C语言程序数据存储到文件中?
  6. java判断44数组是否是4阶幻方_2015蓝桥杯决赛Java A组 第二题--四阶幻方
  7. mysql的内存数据库,MySQL内存数据库的新选择-MariaDB4J
  8. nvidia-smi:Failed to initialize NVML: Driver/library version mismatch
  9. 【机器学习算法专题(蓄力计划)】十一、特征数据预处理
  10. 工程制图 (零件图)