Java 自定义注解与注解解析实例

  在学习 Java 之后会遇到很多的注解,有加载 JavaBean 的注解:@Component,@Service,@Controller;有获取配置文件中数值的注解 @Value;有获取 Http 请求的数据的注解,@RequestBody。通过这些注解,spring 扫描这些组件,提供相关的服务。如何自定义注解,满足自己的特定服务呢?

【转】http://blog.csdn.net/mafan121/article/details/50212137

【转】http://www.jianshu.com/p/7c2948f64b1c;深入 Spring: 自定义注解加载和使用

【转】http://www.jianshu.com/p/9d4bd8955d1a;spring 自定义注解的使用和解析

欢迎到GitHub 上下载代码

  一、了解元注解

  元注解,元:原子,组成其他注解的基础注解。java 提供了 4 种元注解用于注解其他注解。

@Target({ElementType.TYPE})// 用于描述注解的使用范围,超出范围时编译失败。
  取值类型(ElementType):
1.CONSTRUCTOR:用于描述构造器

2.FIELD:用于描述域(成员变量)

3.LOCAL_VARIABLE:用于描述局部变量

4.METHOD:用于描述方法

5.PACKAGE:用于描述包

6.PARAMETER:用于描述参数

7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

@Retention(RetentionPolicy.RUNTIME)// 描述注解的生命周期,即注解的生效范围。
  取值范围(RetentionPolicy):

1.SOURCE:在源文件中生效,仅存在 java 文件中,class 文件将会去除注解。

2.CLASS:在 class 文件中生效,仅保留在 class 文件中,运行时无法获取注解。

3.RUNTIME: 在运行时生效,保留在 class 文件中且运行时可通过反射机制获取。

@Documented // 用于指定 javac 生成 API 时显示该注解信息。
@Inherited // 标明该注解可以由子类继承,及子类可以继承父类的注解。而默认情况下,子类是不继承父类注解的。

 

  二、读取注解

  Java 通过反射机制解析注解,java 在 java.lang.reflect 包下新增了 AnnotatedElement 接口, AnnotatedElement 是所有注解元素的父接口,所有的注解元素都可以通过某个类反射获取 AnnotatedElement 对象,该对象有一下 4 个方法来访问 Annotation 信息。

(1)<T extends Annotation> T getAnnotation(Class<T> annotationClass)
 返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。 

(2)Annotation[] getAnnotations(): 返回该程序元素上存在的所有注解。

(3)boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)

判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.

(4)Annotation[] getDeclaredAnnotations()

返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。<br></span></pre>

 

 三、自定义 JavaBean 注解。

自定义注解为

package mydefineComponent;

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
public @interface MyDefineComponent {

String value() </span><span style="color: rgba(0, 0, 255, 1)">default</span> ""<span style="color: rgba(0, 0, 0, 1)">;

}

 

JAVA 自定义扫描器继承 ClassPathScanningCandidateComponentProvider,ClassPathBeanDefinitionScanner,并在内部添加自定义的 TypeFilter。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package mydefineComponent;
 
import java.util.Set;
 
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
 
 
 
public  final class  Scanner extends ClassPathBeanDefinitionScanner {
 
    public Scanner(BeanDefinitionRegistry registry) {
        super(registry);
        // TODO Auto-generated constructor stub
    }
 
    public void registerDefaultFilters() {
        this.addIncludeFilter(new AnnotationTypeFilter(MyDefineComponent.class));
    }
 
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        for (BeanDefinitionHolder holder : beanDefinitions) {
            GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
            definition.getPropertyValues().add("innerClassName", definition.getBeanClassName());
            definition.setBeanClass(FactoryBeanTest.class);
        }
        return beanDefinitions;
    }
 
    public boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return super.isCandidateComponent(beanDefinition) && beanDefinition.getMetadata()
                .hasAnnotation(MyDefineComponent.class.getName());
    }
 
}

  

 

ApplicationContextAware 是在 org.springframework.context 包下的一个接口,用于获取 spring 的上下文,就是能通过实现这个接口来获取到 spring 的 IOC 容器中的各个 bean。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package mydefineComponent;
 
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
 
 
@Component
public class BeanScannerConfigurer implements BeanFactoryPostProcessor, ApplicationContextAware{
 
    private ApplicationContext applicationContext;
     
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;
         
    }
 
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
            throws BeansException {
        Scanner scanner = new Scanner((BeanDefinitionRegistry) beanFactory);
        scanner.setResourceLoader(this.applicationContext);
        scanner.scan("mydefineComponent");
    }
 
}

 

FactoryBean 是 Spring 中比较重要的一个类。普通的 JavaBean 是直接使用类的实例,但是如果一个 Bean 继承了这个借口,就可以通过 getObject()方法来自定义实例的内容,在 FactoryBeanTest 的 getObject() 就通过代理了原始类的方法,自定义类的方法。
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package mydefineComponent;
 
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cglib.core.SpringNamingPolicy;
import org.springframework.cglib.proxy.Enhancer;
 
public class FactoryBeanTest<T> implements InitializingBean, FactoryBean<T> {
     
    private String innerClassName;
     
    public void setInnerClassName(String innerClassName) {
        this.innerClassName = innerClassName;
    }
     
    @Override
    public void afterPropertiesSet() throws Exception {
        // TODO Auto-generated method stub
         
    }
 
    @Override
    public T getObject() throws Exception {
        Class innerClass = Class.forName(innerClassName);
        if (innerClass.isInterface()) {
            return (T) InterfaceProxy.newInstance(innerClass);
        } else {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(innerClass);
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            enhancer.setCallback(new MethodInterceptorImpl());
            return (T) enhancer.create();
        }
    }
     
 
    @Override
    public Class<?> getObjectType() {
        try {
            return Class.forName(innerClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
 
    @Override
    public boolean isSingleton() {
        // TODO Auto-generated method stub
        return true;
    }
     
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package mydefineComponent;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
public class InterfaceProxy implements InvocationHandler {
     
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("ObjectProxy execute:" + method.getName());
        return method.invoke(proxy, args);
    }
     
    public static <T> T newInstance(Class<T> innerInterface) {
        ClassLoader classLoader = innerInterface.getClassLoader();
        Class[] interfaces = new Class[] { innerInterface };
        InterfaceProxy proxy = new InterfaceProxy();
        return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
    }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package mydefineComponent;
 
import java.lang.reflect.Method;
 
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
 
public class MethodInterceptorImpl implements MethodInterceptor{
 
    @Override
    public Object intercept(Object o, Method method, Object[] objects,
            MethodProxy methodProxy) throws Throwable {
        System.out.println("MethodInterceptorImpl:" + method.getName());
        return methodProxy.invokeSuper(o, objects);
    }
     
     
 
}

  

测试代码:

1
2
3
4
5
6
7
8
9
package mydefineComponent;
 
@MyDefineComponent
public class ScanClass1 {
 
    public void print() {
        System.out.print("scanclass1");
    }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
package mydefineComponent;
 
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class MyDefineComponentTest {
 
    public static void main(String[] args) {
         AnnotationConfigApplicationContext acc = new AnnotationConfigApplicationContext("mydefineComponent");
         ScanClass1 scanClass = acc.getBean(ScanClass1.class);
         scanClass.print();
    }
 
}

 

输出:

1
2
MethodInterceptorImpl:print
scanclass1

 

三、自定义的注解 ,可通过 Spring 快速的获取所有使用该注解的类或方法或属性,以及注解内的值。

自定义一个注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.my.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;
 
@Target({ ElementType.TYPE, ElementType.METHOD }) //可以用在方法或者类上面
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Fooish {
     
    String[] tags() default { "all" };
}

 

自定义注解的解析功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.my.annotation;
 
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
 
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
 
@Component
public class MyFooishHandler implements ApplicationContextAware, InitializingBean{
 
    private ApplicationContext applicationContext;
     
    private List<String> allFooish = new ArrayList<>();
 
    @Override
    public void afterPropertiesSet() throws Exception {
        scanFooishClass();
        scanFooishMethod();
        System.out.println(allFooish);
         
    }
 
    /**
     * 查找 用 Fooish 注解的 方法
     */
    private void scanFooishMethod() throws Exception{
        final Map<String, Object> permissionMap = applicationContext.getBeansWithAnnotation(Fooish.class);
        System.out.println("this is permissionMap" + permissionMap.toString());
        for (final Object permissionObject : permissionMap.values()) {
            final Class<? extends Object> permissionClass = permissionObject.getClass();
            final Fooish annotation = permissionClass.getAnnotation(Fooish.class);
            if(annotation != null) {
                allFooish.addAll(Arrays.asList(annotation.tags()));
            }
        }
 
 
         
    }
 
    private void scanFooishClass() throws Exception{
        final Map<String, Object> controllerMap = applicationContext.getBeansWithAnnotation(Fooish.class);
        for (final Object controllerObject : controllerMap.values()) {
            final Class<? extends Object> controllerClass = controllerObject.getClass();
            for (Method method : controllerClass.getDeclaredMethods()) {
                Fooish fooish = method.getAnnotation(Fooish.class);
              if (fooish != null) {
                    allFooish.addAll(Arrays.asList(fooish.tags()));
                }
            }
        }
 
    }
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
         this.applicationContext = applicationContext;
         
    }
 
}

 

测试:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.my.controller;
 
import java.io.File;
import java.io.IOException;
import java.util.List;
 
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
 
import com.my.annotation.Fooish;
import com.my.model.Student;
import com.my.service.FirstPageService;
import com.my.service.QRCodeUtil;
 
@RestController
@Fooish(tags={"this_is_class"})
public class FirstPageController {
 
    @Value(value = "${erweima.location:D:/Workspaces/MyEclipse 2015/entrance/src/main/resources/erweima.png}")
    private String imgPath;
 
    @Resource
    private FirstPageService firstPageService;
 
    @RequestMapping(value = "/", method = RequestMethod.GET)
    @Fooish(tags={"this_is_method"})
    String home() {
        return firstPageService.getString();
    }
 
     
 
}

  

 【参考博客】

1、http://blog.csdn.net/mafan121/article/details/50212137

2、http://www.jianshu.com/p/7c2948f64b1c;深入 Spring: 自定义注解加载和使用

3、http://www.jianshu.com/p/9d4bd8955d1a;spring 自定义注解的使用和解析