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()); } } |
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" ); } } |
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 自定义注解的使用和解析