目录
自定义注解使用很简单,主要取决于需要处理的业务是否复杂 , 自定义注解的使用场景(日志收集、参数校验、操作记录)
元注解
元注解其主要作用就是负责注解其他注解,为其他注解提供了相关的解释说明。 常常在我们自定义注解的时候使用到
这些元注解提供了一种规范,让开发者更好地定义和使用自己的注解。当你需要创建自定义注解时,通常会使用这些元注解来配置你的注解。
@Target
用于指定自定义注解可以应用的程序元素类型,如类、方法、字段等。
@Target(ElementType.TYPE); //类、接口、注解、枚举
@Target(ElementType.FIELD); //字段、枚举常量
@Target(ElementType.METHOD); //方法
@Target(ElementType.PARAMETER); //形式参数
@Target(ElementType.CONSTRUCTOR); //构造方法
@Target(ElementType.LOCAL_VARIABLE); //局部变量
@Target(ElementType.ANNOTATION_TYPE); //注解
@Target(ElementType.PACKAGE); //包
@Target(ElementType.TYPE_PARAMETER); //类型参数
@Target(ElementType.TYPE_USE); //类型使用
@Retention
表示自定义注解的保留策略,即注解应该保留多长时间,包括 SOURCE、CLASS 和 RUNTIME。
@Retention(RetentionPolicy.SOURCE) // 注解仅在源代码中保留
@Retention(RetentionPolicy.CLASS) // 注解在.class文件中保留,但在运行时不可用
@Retention(RetentionPolicy.RUNTIME) // 注解在.class文件中保留,并在运行时可用
一般我们的自定义注解都是RUNTIME其实
@Documented
表示将自定义注解包含在JavaDoc中。默认情况下,javadoc是不包括注解的,但如果使用了 @Documented
注解,则相关注解类型信息会被包含在生成的文档中.
PS : javadoc是一种从文档注释生成HTML帮助文件的工具
@Repeatable
(java 1.8新增)
用来标注一个注解在同一个地方可重复使用的一个注解,比如说你定义了一个注解,如果你的注解没有标记@Repeatable这个JDK自带的注解,那么你这个注解在引用的地方就只能使用一次。
用的时候还需要一个存储注解,底层会将多个单一的注解存储在那个直接的value数组中 , Spring中的@ComponentScan和@ComponentScans就互相搭配使用了
例如 :
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(value = MyAnnotationList.class)
public @interface MyAnnotation {
String name();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotationList {
// 被 @Repeatable引用的注解中,必须得有被 @Repeatable注释的注解(@MyAnnotation)返回类型的value方法
MyAnnotation[] value();
}
public class MyAnnotationTest {
@MyAnnotation(name = "Test1")
@MyAnnotation(name = "Test2")
private void testMethod() {
}
@MyAnnotationList(value = {@MyAnnotation(name = "Test1"), @MyAnnotation(name = "Test2")})
private void testMethod2() {
}
}
上面这个案例中@MyAnnotationList就是存储注解,内部的value数组用来存储多个单一注解的值
底层其实就是语法糖 , 看起来好像用了多个@MyAnnotation , 但是编译后就是@MyAnnotationList包起来的
@Inherited
表示一个自定义注解被用于一个类时,它将自动被该类的子类继承。
自定义注解案例
需求
对某个类中的某个方法前后进行增强,可能有多个类,每个类中可能有多个方法,但需要做到通过注解可以只选指定类中方法这个粒度
思考
可能有多个类,多个方法,我们怎样才能做到一个类在多个类中 “脱颖而出”,这样我们就清楚要对哪个类增强了,方法也是类似的,想想能不能做两个注解
-
一个定义在类上,如果注解中的值为true,那就说明我这个类中可能有方法要增强了,你帮我看一下,注意,也可能没有要增强的方法
-
一个定义在方法上,如果注解中的值同样为true,那就说明我这个方法需要你增强了
好,我们已经可以通过两个注解来确定到底是哪个类中的哪个方法要增强了
为了应用这两个注解,我们还需要将注解应用在一个Bean上,同样还需要一个对Spring容器中的Bean来检测的这么一个类,这个Bean初始化好了之后我们就看下你这个类上有没有这个指定注解,如果有的话再看下方法上有没有指定注解,有的话就直接增强了,Bean初始化好了之后就干这样事情,想一下,Spring有没有提供这样一个钩子函数呢,有,就是Bean的后置处理器BeanPostProcessor
到了这里其实我们还需要将刚才说的这些Bean给注入到Spring容器中,所以我们需要一个配置类,最后写个测试类就行了,思考完毕,开始写代码就很轻松了!!!
编码
应用在类上的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotationClass {
boolean value() default false;
}
应用在方法上的注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotationMethod {
boolean value() default false;
}
需要被增强的类
@Component
@MyAnnotationClass(value = false)
public class AnnotationApplication {
@MyAnnotationMethod(value = false)
public void study() {
System.out.println("学习Spring。。。");
}
}
Bean的后置处理器实现类
PS : 内部使用了cglib来实现的动态代理
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
/**
* 初始化之后 去做代理增强,但不是所有的Bean都代理增强,我们只代理满足条件的方法
*
* @param bean the new bean instance
* @param beanName the name of the bean
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Object instance = bean;
Class<?> aClass = bean.getClass();
//判断类上是否修饰了注解
if(aClass.isAnnotationPresent(MyAnnotationClass.class)) {
//如果修饰了,再次判断注解上的value是否为true,为true再看下方法有没有修饰朱姐
MyAnnotationClass myAnnotationClass = aClass.getAnnotation(MyAnnotationClass.class);
if (myAnnotationClass.value()) {
//判断方法上是否修饰了注解
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods) {
if(method.isAnnotationPresent(MyAnnotationMethod.class)) {
//如果修饰了看下方法的value是否为true,为true可以增强该方法
MyAnnotationMethod myAnnotationMethod = method.getAnnotation(MyAnnotationMethod.class);
if (myAnnotationMethod.value()) {
//增强方法
instance = getInstance(aClass);
}
}
}
}
}
return instance;
}
private Object getInstance(Class<?> classz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(classz);
enhancer.setCallback(
(MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("代理前。。。");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("代理后。。。");
return invoke;
}
);
return enhancer.create();
}
}
Spring配置类
@Configuration
@ComponentScan("com.changjunkai.springdemo01.anno.custom")
public class AnnotationConfig {
}
测试方法
public class SpringCustomAnnotationTest {
@Test
public void test01() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationConfig.class);
AnnotationApplication bean = context.getBean(AnnotationApplication.class);
bean.study();
}
}
这里大家可以去测试一下,是没有问题的,只有两个注解的值都为true才能增强!!!