zoukankan      html  css  js  c++  java
  • 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()
    
        返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。

     

     三、自定义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() default "";
    }

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

    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。
    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()就通过代理了原始类的方法,自定义类的方法。
     
    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;
    	}
    	
    }
    

      

    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);
    	}
    }
    

      

    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);
    	}
    	
    	
    
    }
    

      

    测试代码:

    package mydefineComponent;
    
    @MyDefineComponent
    public class ScanClass1 {
    
    	public void print() {
    		System.out.print("scanclass1");
    	}
    }
    

      

    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();
    	}
    
    }
    

    输出:

    MethodInterceptorImpl:print
    scanclass1
    

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

    自定义一个注解:

    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" };
    }
    

    自定义注解的解析功能

    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;
    		
    	}
    
    }
    

    测试:

     

    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自定义注解的使用和解析

  • 相关阅读:
    修改 MyEclipse 中的 jsp 和 servlet 模板
    javaWeb 数据库连接池连接数据库
    发现一个类的方法不够用时,可以使用的3种方法可以增强
    使用 greenDao 框架 操作数据库
    Android之使用Volley框架在ListView中加载大量图片
    js日期选择控件
    mysql 乱码问题
    java 使用反射技术解耦
    javaWeb 使用jsp开发 html过滤标签
    javaWeb 使用jsp开发 foreach 标签
  • 原文地址:https://www.cnblogs.com/boywwj/p/7714404.html
Copyright © 2011-2022 走看看