注解在java中是个很常见很重要的元素, 它的出现简化了我们的业务,由之前的一大段代码逻辑过渡到了一个注解就解决问题,当然注解也只是偏向于解释型的注释而已,需要生效还需要其他功能,最多的也就是反射了。本文将讲述注解如何定义,以及如何用。
说到注解,那就要说说四大元注解啦。
四大元注解
1.@Target
@Target({ElementType.TYPE})
该注解用于注明该注解用在什么地方。几个重要值如下
参数值 | 意义 |
---|---|
ElemenetType.CONSTRUCTOR | 用于构造器上 |
ElemenetType.FIELD | 用于属性上 |
ElemenetType.METHOD | 用于方法上 |
ElemenetType.TYPE | 类,接口(包括注解类型)或enum声明 |
2. @Retention
表示在什么级别保存该注解。有如下值可选
参数值 | 意义 |
---|---|
RetentionPolicy.SOURCE | 用于源码上。编译时将丢弃 |
RetentionPolicy.CLASS | class文件有效。运行时将丢弃 |
RetentionPolicy.RUNTIME | 运行时有效 |
3.@Documented
该注解用于javadoc中为我们自动生成文档
4.@Inherited
该注解表示该允许子类继承父类中的注解
了解了这四个元注解,我们来看怎么用。
1.首先创建一个注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* AnnotationTest class
*
* @author greatwhite
* @date 20200723
*/
@Target({ElementType.TYPE,ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationTest {
String id();
String name() default "测试注解";
String desc();
}
实验中我只用上了@Target、@Retention两个注解,其他两个用得不多。
给了该注解在类上、构造方法上、方法上、属性上的声明,并且是运行时有效,这点很关键,不然后面的反射是获取不到该注解的。
2.将注解用于People类上:
**
* @ClassName People
* @Author greatwhite
* @Date 2020/7/23 0023 13:44
* @Version
**/
@AnnotationTest(id="0",name="类注解",desc="类注解")
public class People {
@AnnotationTest(id = "1",name = "属性注解",desc = "属性注解")
private String name;
@AnnotationTest(id = "2",name = "构造方法注解",desc = "构造方法注解")
public People(String name) {
this.name = name;
}
@AnnotationTest(id = "3",name = "方法注解",desc = "方法注解")
public void test(){
System.out.println("方法注解");
}
}
给People的类上、构造方法上、方法上、属性上都加了该注解。那么注解用上了,怎么拿到呢?
3.获取注解值
我们通过反正拿到注解信息,下面是测试类:
//获取构造方法上的注解
@Test
public void testConstructorAnnotation() throws ClassNotFoundException {
Class<?> aClass = Class.forName("com.wt.annotation.People");
Constructor<?>[] constructors = aClass.getConstructors();
for(Constructor c:constructors){
Annotation[] annotations = c.getAnnotations();
for(Annotation a:annotations){
if(a.annotationType() == AnnotationTest.class){
AnnotationTest test = (AnnotationTest) a;
System.out.println("id:" + test.id() + "|name:" + test.name() + "|desc:" + test.desc());
}
}
}
}
//获取类上注解
@Test
public void testTypeAnnotation() throws ClassNotFoundException {
Class<?> aClass = Class.forName("com.wt.annotation.People");
Annotation[] annotations = aClass.getAnnotations();
for(Annotation a:annotations){
if(a.annotationType() == AnnotationTest.class){
AnnotationTest test = (AnnotationTest) a;
System.out.println("id:" + test.id() + "|name:" + test.name() + "|desc:" + test.desc());
}
}
}
//获取方法上的注解
@Test
public void testMethodAnnotation() throws ClassNotFoundException {
Class<?> aClass = Class.forName("com.wt.annotation.People");
Method[] methods = aClass.getMethods();
for(Method m:methods){
Annotation[] annotations = m.getAnnotations();
for(Annotation a:annotations){
if(a.annotationType() == AnnotationTest.class){
AnnotationTest test = (AnnotationTest) a;
System.out.println("id:" + test.id() + "|name:" + test.name() + "|desc:" + test.desc());
}
}
}
}
//获取属性上的注解
@Test
public void testFieldAnnotation() throws ClassNotFoundException, NoSuchFieldException {
Class<?> aClass = Class.forName("com.wt.annotation.People");
//由于name是私有成员变量,注意使用getDeclaredField方法
Field name = aClass.getDeclaredField("name");
Annotation[] annotations = name.getAnnotations();
for(Annotation a:annotations){
if(a.annotationType() == AnnotationTest.class){
AnnotationTest test = (AnnotationTest) a;
System.out.println("id:" + test.id() + "|name:" + test.name() + "|desc:" + test.desc());
}
}
}
结果:
id:2|name:构造方法注解|desc:构造方法注解
id:0|name:类注解|desc:类注解
id:3|name:方法注解|desc:方法注解
id:1|name:属性注解|desc:属性注解
这样就获取到了注解行的值啦,是不是很方便啦,一般在实际springBoot开发项目中,注解通常与AOP结合使用,通过AOP获取注解的参数值,场景最多的就是日志记录了。
接下来就让我们一起来加入项目实战吧。
以SpringBoot项目为例:
首先定义注解:
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysServiceLog {
String operateContent() default "";
String description() default "";
int operateType() default 7;
}
定义切面:
@Aspect
@Component
public class ServiceAspect {
private static final Logger logger = LoggerFactory.getLogger(ServiceAspect.class);
private final static Log log = LogFactory.getLog(ServiceAspect.class);
// 配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
@Pointcut("@annotation(com.cec.common.annotation.SysServiceLog)")
public void aspect() {
}
/*
* 配置前置通知,使用在方法aspect()上注册的切入点 同时接受JoinPoint切入点对象,可以没有该参数
*/
@Before("aspect()")
public void before(JoinPoint joinPoint) {
//日志记录
}
// 配置后置通知,使用在方法aspect()上注册的切入点
@After("aspect()")
public void after(JoinPoint joinPoint) {
//日志记录
}
// 配置后置返回通知,使用在方法aspect()上注册的切入点
@AfterReturning(pointcut = "aspect()", returning = "object")
public void afterReturn(JoinPoint joinPoint, Object object) {
//日志记录
}
}
然后我们只需要在控制层将注解写在接口上即可。
@RestController
@RequestMapping("testLog")
@SysServiceLog(operateContent = "保存预约规则", operateType = Globals.Log_Type_INSERT)
public String testLog(){
return "日志测试";
}