java自定义注解
java自定义注解
最近在研究自定义注解,在此记录一下学习过程。
元注解
自定义注解中使用了java自带的一些元注解,从网上摘抄过来:
@Retention ,注解保留的位置,通过RetentionPolicy的枚举值来进行选择:
- RetentionPolicy.SOURCE :标记的注释仅保留在源级别中,在class文件中不包含,直接被编译器忽略。
- RetentionPolicy.CLASS :存在于class文件中,不会被编译器忽略,但会被jvm忽略。
- RetentionPolicy.RUNTIME:存在于jvm中,可以在运行时使用。由于本人也是初学者,目前只是用过该枚举值,其他后续再做深入研究。
@Documented ,是否包含在javadoc中,具体使用方法未了解,直接默认加上该元注解。
@Target ,标识注解可用于哪些java元素上,通过ElementType的枚举值来进行选择:
- ElementType.TYPE 可以应用于类的任何元素(包含枚举类,但不包含枚举值)。
- ElementType.FIELD 可以应用于字段或属性(包含枚举值)。
- ElementType.METHOD 可以应用于方法级注释。
- ElementType.PARAMETER 可以应用于方法的参数。
- ElementType.CONSTRUCTOR 可以应用于构造函数。
- ElementType.LOCAL_VARIABLE 可以应用于局部变量。
- ElementType.ANNOTATION_TYPE 可以应用于注释类型,就是可以用在注解上的注解,比如Target中就是使用该枚举值。
- ElementType.PACKAGE 可以应用于包声明。
- ElementType.TYPE_PARAMETER ,TYPE_USE用于修饰类型声明,写个代码来看感觉会清楚点
/*
* 新建一个TYPEPARAMETER自定义注解
*/
@Target(ElementType.TYPE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TYPEPARAMETER {
}
修饰的地方:
public class Test<@TYPEPARAMETER T> {
}
/*
* 新建一个TYPE_USE自定义注解
*/
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TYPE_USE {
}
修饰的地方:
public class Test {
public Map<String, @TYPE_USE Object> test(){
return null;
}
}
@Inherited ,指明该注解类型被自动继承。如果一个annotation注解被@Inherited修饰,那么该注解作用于的类 的子类也会使用该annotation注解(目前未使用,后续再做详细验证)。
实现自定义注解
创建自定义注解时直接在eclipse中new一个Annotation:
创建一个注解命名为:DataSourceA,模拟springboot获取不同的数据源,当然只是为了实现自定义注解的使用,数据源获取代码后续再做详细补充,目前只是打印一段文本。代码如下:
-
创建注解:
注意: 注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。
到此自定义注解其实就已经新建完了,下面看一下怎么使用。在main方法中通过反射获取注解相关信息只能证明注解确实创建成功并已经添加在相应的元素上。怎么才能在运行程序的时候实现注解自定义的功能需要用到spring的aop。 -
RetentionPolicy.RUNTIME:存在于jvm中,可以在运行时使用。由于本人也是初学者,目前只是用过该枚举值,其他后续再做深入研究。
通过Aop实现自定义注解功能
测试demo使用的是springboot的一个简单mvc应用,需要加入aop,bootstarter等依赖,具体不再赘述。
由于上面创建的注解是要模拟数据源的获取,所以新建一个service以及controller,dao层忽略,因为并没有真正去获取数据源。
service:
@Service
public class ServiceTest {
@DataSourceA(value="service自定义数据源")
public void test() {
System.out.println("我是被自定义注解修饰的方法,在此假装使用数据源");
}
}
controller:
@Autowired
public ServiceTest serviceTest;
@PostMapping("/aopTest")
public String aopTest(@RequestBody User user) {
serviceTest.test();
return "success";
}
此处只返回了一个success作为请求成功的标志,注解的实现稍后通过控制台来观察。
- 增加切面类,实现注解的自定义功能,先贴上代码
package com.example.demo.annotionTest.aop;
import java.lang.reflect.Method;
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.stereotype.Component;
import com.example.demo.annotionTest.DataSourceA;
/*
* DataSourceAop切面
*/
@Aspect
@Component
public class DataSourceAop {
/*
* 切点,@annotation标识切入所有被该注解修饰的方法。
* spring aop仅支持对方法的拦截
*/
@Pointcut("@annotation(com.example.demo.annotionTest.DataSourceA)"
+ "&&within(com.example.demo.service..*)")
public void dataSourcePointCut() {
}
@Around(value = "dataSourcePointCut()")
public void Around(ProceedingJoinPoint point) throws Throwable{
/*
* point连接点
* getSignature()返回目标方法的签名
* getMethod()获取方法,签名与方法都是通过反射获取
*/
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
//getAnnotation获取指定类型的注解,若不存在该类型注解,则返回null
DataSourceA annotation = method.getAnnotation(DataSourceA.class);
if (null == annotation) {
System.out.println("没有获取到指定注解");
System.out.println("获取默认数据源");
}else {
System.out.println("自定义DataSource。。。"+annotation.value());
}
point.proceed();
}
}
注解中自定义方法的返回值即在使用注解时输入的值:
定义完切面之后加上Component注解,交给spring容器来管理。
结果验证
启动应用后使用postman发送一个post请求,这里随意定义一个json数据
点击send,然后看控制台的输出:
控制台中我们看到在service方法执行之前,根据我们自定义注解中的值去模拟获取相应的数据源。
到此自定义注解算是最终完成。后续将补齐所有springboot多数据源的配置以及自定义参数校验注解。
不足之处还请批评指正,共同学习进步。