有好的学网站建设的书吗,1206家校互联,初创公司网站设计苏州,潍坊建设网站多少钱目录 一、interface 关键字
二、元注解
三、简单实现
四、使用切面执行自定义注解逻辑
1) 首先将刚才的注解修改成放在方法上的#xff1a;
2) 定义一个切面类#xff1a;
3#xff09;将注解放入到接口方法中测试#xff1a;
五、切点表达式 一、interface 关键字 …目录 一、interface 关键字
二、元注解
三、简单实现
四、使用切面执行自定义注解逻辑
1) 首先将刚才的注解修改成放在方法上的
2) 定义一个切面类
3将注解放入到接口方法中测试
五、切点表达式 一、interface 关键字
我们想定义一个自己的注解 需要使用 interface 关键字来定义。 如定义一个叫 MyAnnotation 的注解 public interface MyAnnotation { } 二、元注解
光加上 interface 关键字 还不够我们还需要了解5大元注解 Retention Target Documented InheritedJDK8 引入 RepeatableJDK8 引入 1) Retention 指定注解的生命周期
Retention(RetentionPolicy.SOURCE) 其中Retention是一个枚举类:
RetentionPolicy.SOURCE : 注解只保留在源文件当Java文件编译成class文件的时候注解被遗弃(.java文件) RetentionPolicy.CLASS :注解被保留到class文件但jvm加载class文件时候被遗弃这是默认的生命周期(.class文件) RetentionPolicy.RUNTIME: 注解不仅被保存到class文件中jvm加载class文件之后仍然存在(内存中的字节码) 2) Target指定注解可以修饰的元素类型
Target(ElementType.Field) ElementType.ANNOTATION_TYPE - 标记的注解可以应用于注解类型。 ElementType.CONSTRUCTOR - 标记的注解可以应用于构造函数。 ElementType.FIELD - 标记的注解可以应用于字段或属性。 ElementType.LOCAL_VARIABLE - 标记的注解可以应用于局部变量。 ElementType.METHOD - 标记的注解可以应用于方法。 ElementType.PACKAGE - 标记的注解可以应用于包声明。 ElementType.PARAMETER - 标记的注解可以应用于方法的参数。 ElementType.TYPE - 标记的注解可以应用于类的任何元素。 3Documented 指定注解会被JavaDoc工具提取成文档。默认情况下JavaDoc是不包括文档的
4Inherited 表示该注解会被子类继承注意仅针对类成员属性、方法并不受此注释的影响。
5Repeatable 表示注解可以重复使用为了解决同一个注解不能重复在同一类/方法/属性上使用的问题。 其中最常用的就是 Retention 跟 Target。
三、简单实现
例如实现一个简单在标记注解的地方打印一句日志。 定义一个 MyAnnotation 注解并且定义一个属性 message 默认值是 ”aaa“。先将该注解加到字段上看能不能获取到。 //注解用于字段上
Target(ElementType.FIELD)
//运行时使用
Retention(RetentionPolicy.RUNTIME)
public interface MyAnnotation {
String message() default aaa;
} 定义一个Student类用于测试 Data
public class Student {
JSONField(ordinal 0)MyAnnotation(message AAAAAAAAA)public String name;MyAnnotation(message AAAAAAAAA)public Integer score;
} 在字段上标注该注解然后编写一个main方法获取该注解的属性 public static void main(String[] args) {Class? studentClass Student.class;//获取直接Field[] fields studentClass.getDeclaredFields();//获取所有的类成员变量字段for (Field field : fields) {String fieldName field.getName(); //获取该类成员变量的名字System.out.println(成员变量名是 fieldName);Annotation[] annotations field.getAnnotations(); //获取该类成员变量上所有声明周期是运行时的注解for (Annotation annotation : annotations) {//annotationsClass? extends Annotation annotationType annotation.annotationType();String annotationName annotationType.getSimpleName();//注解的简短名称System.out.println( 使用的注解是 annotationName);//判断该注解是不是 MyAnnotation 注解是的话打印其 id 和 describe 属性if (annotationType.equals(MyAnnotation.class)) {MyAnnotation myAnnotation field.getAnnotation(MyAnnotation.class);String message myAnnotation.message();System.out.println( MyAnnotation注解中的message是 message);}}System.out.println();}
} 执行后打印的内容 以上就是一个注解的简单实现。
四、使用切面执行自定义注解逻辑
在开发中一般加上注解之后会自动执行一些逻辑大部分实现的原理是使用AOP切面来实现注解的逻辑的。
1) 首先将刚才的注解修改成放在方法上的 //注解用于方法
Target(ElementType.METHOD)
//运行时使用
Retention(RetentionPolicy.RUNTIME)
public interface MyAnnotation {
String message() default aaa;
} 2) 定义一个切面类 Component Aspect Slf4j public class MyAnnotationAspect { /** 这是一个切入点* */
Pointcut(annotation(com.demo.aaa.annotation.MyAnnotation))
public void cutMethod(){}
/*** 切点之前*/
Before(cutMethod())
public void before(JoinPoint joinPoint) throws Throwable {log.info( before );
}/*** 切点之后*/
After(cutMethod())
public void after() throws Throwable {log.info( after );
}/*** 切点返回内容后*/
AfterReturning(cutMethod())
public void afterReturning() throws Throwable {log.info( afterReturning );
}/*** 切点抛出异常后*/
AfterThrowing(cutMethod())
public void afterThrowing() throws Throwable {log.info( afterThrowing );
} Around(cutMethod() annotation(myAnnotation))
public Object around(ProceedingJoinPoint point, MyAnnotation myAnnotation) throws Throwable {log.info( around1 );Object obj point.proceed(point.getArgs());log.info( around2 );return obj;
} } 在使用aop之前需要先引入一个依赖 dependencygroupIdorg.aspectj/groupIdartifactIdaspectjweaver/artifactIdversion1.8.13/version/dependency 简单说一下各个注解代表什么含义
Aspect:作用是把当前类标识为一个切面供容器读取 也就是加上这个注解spring才知道你这是一个切面类用于处理切点的逻辑的。 Pointcut:切入点Pointcut切点表达式非常丰富可以将 方法(method)、类(class)、接口(interface)、包(package) 等作为切入点非常灵活常用的有annotation、within、execution等方式上面的示例使用的是annotation方式意思就是说被Spring扫描到方法上带有annotation中的注解 就会执行切面通知。 Before该注解标注的方法在业务模块代码执行之前执行其不能阻止业务模块的执行除非抛出异常 AfterReturning该注解标注的方法在业务模块代码执行之后执行 AfterThrowing该注解标注的方法在业务模块抛出指定异常后执行 After该注解标注的方法在所有的 Advice 执行完成后执行无论业务模块是否抛出异常类似于 finally 的作用 Around该注解功能最为强大其所标注的方法用于编写包裹业务模块执行的代码通知的第一个参数必须是 ProceedingJoinPoint 类型。在通知体内调用 ProceedingJoinPoint 的 proceed () 方法使得连接点方法执行如果不调用 proceed () 方法连接点方法则不会执行。无论是调用前逻辑还是调用后逻辑都可以在该方法中编写甚至其可以根据一定的条件而阻断业务模块的调用 如果切面中使用了Around 注解如果不调用 ProceedingJoinPoint 的 proceed () 方法的话那么 Before 和 After 直接标注的方法也不会被触发。Around 注解标注的方法在 ProceedingJoinPoint 的 proceed () 方法 前的逻辑是比Before的逻辑还要靠前 在proceed () 方法之后的逻辑比 After 的逻辑还要靠后。 Joint PointJointPoint是程序运行过程中可识别的点这个点可以用来作为AOP切入点。JointPoint对象则包含了和切入相关的很多信息。比如切入点的对象方法属性等。我们可以通过反射的方式获取这些点的状态和信息用于追踪tracing和记录logging应用信息。
3将注解放入到接口方法中测试 GetMapping(/aaa)
MyAnnotation(message 成功拉)
public void test() {System.out.println(执行代码逻辑);
} 调用接口之后打印
上面就是自定义注解最简单的示例。
五、切点表达式
我们定义切点除了使用 Pointcut() 之外我们还有丰富的切点表达式可以定义切点。 1切点表达式简介
2通配符合与逻辑运算符
AspectJ 支持三种通配符
逻辑运算符 切点表达式由切点函数组成切点函数之间还可以进行逻辑运算组成复合切点。 3切点表达式
1.arg() :匹配切入点方法的参数类型匹配的上才是切点。语法args(param-pattern) param-pattern参数类型的全路径。注意要先匹配到某些类不然会报错也就是不能单独用示例
Pointcut(args(java.lang.String)) //这样就是错的不能单独使用要匹配到某些类
Pointcut(within(com.example.demo.service.impl.UserServiceImpl) args(java.lang.String,java.lang.String)) //要像这样使用 within 先匹配到某个具体的类在使用args匹配到某个类型参数的方法 2.args匹配切入点方法上的参数的类上参数的类必须要有指定的注解 语法args(annotation-type) annotation-type注解类型的全路径 注意也不能单独使用必须先指定到类而且匹配参数个数至少有一个且为第一个参数的类含有该注解才能匹配的上 示例
Pointcut(within(com.demo.RedisTest) args(com.demo.aaa.annotation.MyAnnotation)) 3.within:匹配切入点的指定类的任意方法不能匹配接口。 语法within(declaring-type) 参数为全路径的类名可使用通配符表示匹配当前表达式的所有类都将被当前方法环绕 注意 这个是指定到具体的类 示例
//within表达式的粒度为类其参数为全路径的类名可使用通配符表示匹配当前表达式的所有类都将被当前方法环绕。如下是within表达式的语法 Pointcut(within(declaring-type-pattern))
//within表达式只能指定到类级别如下示例表示匹配com.spring.service.BusinessObject中的所有方法 Pointcut(within(com.spring.service.BusinessObject)) //within表达式路径和类名都可以使用通配符进行匹配比如如下表达式将匹配com.spring.service包下的所有类不包括子包中的类 Pointcut(within(com.spring.service.*))
//如下表达式表示匹配com.spring.service包及子包下的所有类 Pointcut(within(com.spring.service..*)) 4.within:表示匹配带有指定注解的类。 语法within(annotation-type) 注解的全类名 注意这个是指定到带有某个注解的类 示例
//如下所示示例表示匹配使用com.spring.annotation.BusinessAspect注解标注的类
within(com.spring.annotation.BusinessAspect) 5.annotation() :匹配带有指定注解的连接点 语法annotation(annotation-type) annotation-type注解类型的全路径 示例
Pointcut(annotation(com.test.annotations.LogAuto)) 6.execution() 用于匹配是连接点的执行方法,Spring 切面粒度最小是达到方法级别而 execution 表达式可以用于明确指定方法返回类型类名方法名和参数名等与方法相关的配置所以是使用最广泛的。 用法
modifiers-pattern方法的可见性修饰符如 publicprotectedprivate ret-type-pattern方法的返回值类型如 intvoid 等 declaring-type-pattern方法所在类的全路径名如 com.spring.Aspect name-pattern方法名如 getOrderDetail() param-pattern方法的参数类型如 java.lang.String throws-pattern方法抛出的异常类型如 java.lang.Exception 示例
modifiers-pattern方法的可见性修饰符如 publicprotectedprivate ret-type-pattern方法的返回值类型如 intvoid 等 declaring-type-pattern方法所在类的全路径名如 com.spring.Aspect name-pattern方法名如 getOrderDetail() param-pattern方法的参数类型如 java.lang.String throws-pattern方法抛出的异常类型如 java.lang.Exception 示例
// 匹配目标类的所有 public 方法第一个 * 代表返回类型第二个 * 代表方法名..代表方法的参数 execution(public * *(..))
// 匹配目标类所有以 User 为后缀的方法。第一个 * 代表返回类型User 代表以 User 为后缀的方法 execution( *User(..))
// 匹配 User 类里的所有方法 execution(* com.test.demo.User.*(..))
// 匹配 User 类及其子类的所有方法 execution(* com.test.demo.User.*(..)) :
// 匹配 com.test 包下的所有类的所有方法 execution(* com.test..(..))
// 匹配 com.test 包下及其子孙包下所有类的所有方法 execution(* com.test...(..)) :
// 匹配 getOrderDetail 方法且第一个参数类型是 Long第二个参数类型是 String execution(* getOrderDetail(Long, String))
六、切面中获取各个参数 示例 Around(value annotation(basisLogAnnotation)) public Object demoAop(ProceedingJoinPoint proceedingJoinPoint, final BasisLogAnnotation basisLogAnnotation) throws Throwable { logger.debug(执行前);Object object proceedingJoinPoint.proceed(); //执行连接点方法object方法返回值logger.debug(执行后); // 类名String className proceedingJoinPoint.getTarget().getClass().getName();//方法名String methodName proceedingJoinPoint.getSignature().getName();//参数我这里是对象具体根据个人的参数类型来强转BasisUser basisUser (BasisUser)proceedingJoinPoint.getArgs()[0];return object;
} 原文链接Java 实现自定义注解_java 自定义注解-CSDN博客