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