Java注解作為程序元素(類、成員變量、成員方法等)的一種元數(shù)據(jù)信息,對程序本身的執(zhí)行不會產(chǎn)生影響。通過自定義注解,可以給程序元素添加特殊的聲明。
Spring作為構(gòu)建企業(yè)級應(yīng)用的平臺,提供了豐富的功能。將Java的自定義注解與Spring結(jié)合,在特定場景下實現(xiàn)注解的解析、處理,可以降低應(yīng)用的耦合度,提高程序的可擴(kuò)展性。
2.應(yīng)用場景
下面總結(jié)幾種應(yīng)用場景,僅說明大致思路(ps:并非所有場景都在項目中實踐過)
2.1登陸、權(quán)限攔截
在web項目中,登陸攔截和權(quán)限攔截是一個老生常談的功能。通過自定義登陸注解或權(quán)限注解,在自定義攔截器中解析注解,實現(xiàn)登陸和權(quán)限的攔截功能。
這種使用方式,配置簡單,靈活度高,代碼耦合度低。
2.2定時任務(wù)管理
在系統(tǒng)構(gòu)建過程中,會有各種定時任務(wù)的需求,而定時任務(wù)的集中管理,可以更高效維護(hù)系統(tǒng)的運行。
通過Java注解官方文檔RepeatingAnnotations章節(jié)中的自定義的定時任務(wù)注解,可以實現(xiàn)業(yè)務(wù)方法的定時任務(wù)聲明。結(jié)合Spring的容器后處理器BeanPostProcessor(ps:Spring容器后處理器下篇再說),解析自定義注解。解析后的注解信息再使用QuartzAPI構(gòu)建運行時定時任務(wù),即可完成定時任務(wù)的運行時創(chuàng)建和集中管理。
這種方式能避免定義Quartz定時任務(wù)的配置,提高系統(tǒng)擴(kuò)展性。
2.3多數(shù)據(jù)源路由的數(shù)據(jù)源指定
Spring提供的AbstractRoutingDataSource實現(xiàn)多數(shù)據(jù)源的動態(tài)路由,可應(yīng)用在主從分離的架構(gòu)下。通過對不同的方法指定不同的數(shù)據(jù)源,實現(xiàn)數(shù)據(jù)源的動態(tài)路由(例如:讀方法走從庫數(shù)據(jù)源,寫方法走主庫數(shù)據(jù)源)。而如何標(biāo)識不同的方法對應(yīng)的數(shù)據(jù)源類型,則可使用自定義注解實現(xiàn)。通過解析方法上聲明的自定義注解對應(yīng)的數(shù)據(jù)源類型,實現(xiàn)數(shù)據(jù)源的路由功能。
這種方式避免了對方法的模式匹配解析(例如:select開頭、update開頭等),聲明更加靈活。
自定義注解
先看一個最簡單的例子,在使用SpringWeb應(yīng)用中的過程中,大家免不了會使用@Controller,@Service,@Repository等注解來定義JavaBean。那么怎么自己定義一個注解,Spring可以自動加載呢。所以就有了第一個例子。
1
2
3
4
5
6
7
|
@Target ({ ElementType.TYPE }) @Retention (RetentionPolicy.RUNTIME) @Documented @Component public @interface MyComponent { String value() default "" ; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Configuration public class ComponentAnnotationTest { public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(ComponentAnnotationTest. class ); annotationConfigApplicationContext.refresh(); InjectClass injectClass = annotationConfigApplicationContext.getBean(InjectClass. class ); injectClass.print(); } @MyComponent public static class InjectClass { public void print() { System.out.println( "hello world" ); } } } |
運行這個例子,就會發(fā)現(xiàn),@MyComponent 注解的類,也被Spring加載進(jìn)來了,而且可以當(dāng)成普通的JavaBean正常的使用。查看Spring的源碼會發(fā)現(xiàn),Spring是使用ClassPathScanningCandidateComponentProvider掃描package,這個類有這樣的注釋
1
2
|
A component provider that scans the classpath from a base package . It then applies exclude and include filters to the resulting classes to find candidates. |
這個類的 registerDefaultFilters 方法有這樣幾行代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
protected void registerDefaultFilters() { this .includeFilters.add( new AnnotationTypeFilter(Component. class )); ClassLoader cl = ClassPathScanningCandidateComponentProvider. class .getClassLoader(); try { this .includeFilters.add( new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName( "javax.annotation.ManagedBean" , cl)), false )); logger.debug( "JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning" ); } catch (ClassNotFoundException ex) { // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. } try { this .includeFilters.add( new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName( "javax.inject.Named" , cl)), false )); logger.debug( "JSR-330 'javax.inject.Named' annotation found and supported for component scanning" ); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } } |
這里就會發(fā)現(xiàn)Spring在掃描類信息的使用只會判斷被@Component注解的類,所以任何自定義的注解只要帶上@Component(當(dāng)然還要有String value() default "";的方法,因為Spring的Bean都是有beanName唯一標(biāo)示的),都可以被Spring掃描到,并注入容器內(nèi)。
總結(jié)
以上就是本文關(guān)于淺談自定義注解在Spring中的應(yīng)用的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
原文鏈接:http://blog.csdn.net/liuxigiant/article/details/54296489