Spring框架中注解非常多,开发中有时需要自定义注解,该怎么做呢?
学习Spring自带的注解
package org.springframework.stereotype;
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.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
String value() default "";
}
这是@Controller的注解,这个注解里包含了4个其他的注解,我大致介绍一下(其实源码里面说的很清楚):
1.@Target
用来表示注解作用范围,超过这个作用范围,编译的时候就会报错。
java.lang.annotation.ElementType
Target通过ElementType来指定注解可使用范围的枚举集合,枚举集合如下
package java.lang.annotation;
/**
* A program element type. The constants of this enumerated type
* provide a simple classification of the declared elements in a
* Java program.
*
* <p>These constants are used with the {@link Target} meta-annotation type
* to specify where it is legal to use an annotation type.
*
* @author Joshua Bloch
* @since 1.5
*/
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE
}
ElementType的用法
取值 | 注解使用范围 |
---|---|
TYPE | 可用于类或者接口上 |
FIELD | 可用于域上 |
METHOD | 可用于方法上 |
PARAMETER | 可用于参数上 |
CONSTRUCTOR | 可用于构造方法上 |
LOCAL_VARIABLE | 可用于局部变量上 |
ANNOTATION_TYPE | 可用于注解类型上(被interface修饰的类型) |
PACKAGE | 用于记录java文件的package信息 |
2.@Retention
定义注解的生命周期,可以在枚举的源码中看到,分为以下三种:
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
作用范围如下:
取值 | 作用范围 |
---|---|
SOURCE | 在源文件中有效(即源文件保留) |
CLASS | 在class文件中有效(即class保留) |
RUNTIMR | 在运行时有效(即运行时保留) |
3.@Documented
定义注解会被javadoc或者其他类似工具文档化
4.@Component
Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
自定义注解
package springboot_demol1.springboot_demol1.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;
/**
* @author
* @date 2019/12/5 15:40
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyFirstAnnotation {
String value() default "";
}
定义切面类
创建完自定义注解后,该如何让注解起作用呢。这里以打印控制台字符串为例,当用自定义注解来修饰方法时,我们期望在方法执行的前后输出字符串,那么我们必须采用AOP(面向切面编程)的思想,理所当然地,我们需要定义切面类:
package springboot_demol1.springboot_demol1.annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @author
* @date 2019/12/5 15:43
*/
@Aspect
@Component
public class MyFirstAspect {
@Before("@annotation(MyFirstAnnotation)")
public void beforePointcut(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
MyFirstAnnotation annotation = method.getAnnotation(MyFirstAnnotation.class);
String value = annotation.value();
System.out.println("准备"+value);
}
@After("@annotation(MyFirstAnnotation)")
public void afterPointcut(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
MyFirstAnnotation annotation = method.getAnnotation(MyFirstAnnotation.class);
String value = annotation.value();
System.out.println(value+"结束");
}
}
使用自定义注解
@MyFirstAnnotation("给老库打电话") //自定义的注解
@RequestMapping(value = "/say")
public void sayHello() {
System.out.println("聊天中");
}
控制台输出
准备给老库打电话
聊天中
给老库打电话结束
使用环绕增强通知(Around)
修改 MyFirstAspect.java
代码
package springboot_demol1.springboot_demol1.annotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/**
* @author
* @date 2019/12/5 15:43
*/
@Aspect
@Component
public class MyFirstAspect {
@Around("@annotation(MyFirstAnnotation)")
public void beforePointcut(ProceedingJoinPoint point, MyFirstAnnotation MyFirstAnnotation) throws Throwable{
System.out.println("ANNOTATION welcome");
System.out.println("ANNOTATION 调用方法:"+ MyFirstAnnotation.value());
System.out.println("ANNOTATION 调用类:" + point.getSignature().getDeclaringTypeName());
System.out.println("ANNOTATION 调用类名" + point.getSignature().getDeclaringType().getSimpleName());
point.proceed(); //调用目标方法
System.out.println("ANNOTATION login success");
}
}
控制台输出:
ANNOTATION welcome
ANNOTATION 调用方法:给老库打电话
ANNOTATION 调用类:springboot_demol1.springboot_demol1.controller.EmpController
ANNOTATION 调用类名EmpController
聊天中
ANNOTATION login success
本篇将不定时更新 Spring 相关知识,一起查漏补缺学个痛快!欢迎点赞留香丨留言鼓励丨指出不足!