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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM