注解:
可以看作是對 一個 類/方法 的一個擴展的模版,每個 類/方法 按照注解類中的規則,來
為 類/方法 注解不同的參數,在用到的地方可以得到不同的 類/方法 中注解的各種參數
與值。
注解的原理:
注解本質是一個繼承了Annotation的特殊接口,其具體實現類是Java運行時生成的動
態代理類。而我們通過反射獲取注解時,返回的是Java運行時生成的動態代理對象
$Proxy1。通過代理對象調用自定義注解(接口)的方法,會最終調用
AnnotationInvocationHandler的invoke方法。該方法會從memberValues這個Map中
索引出對應的值。而memberValues的來源是Java常量池
java.lang.annotation提供了四種元注解,專門注解其他的注解(在自定義注解的時候,
需要使用到元注解):
@Documented –注解是否將包含在JavaDoc中
@Retention –什么時候使用該注解
@Target –注解用於什么地方
@Inherited – 是否允許子類繼承該注解
1.)@Retention– 定義該注解的生命周期
● RetentionPolicy.SOURCE : 在編譯階段丟棄。這些注解在編譯結束之后就不再有任
何意義,所以它們不會寫入字節碼。@Override, @SuppressWarnings都屬於這類注
解。
● RetentionPolicy.CLASS : 在類加載的時候丟棄。在字節碼文件的處理中有用。注解
默認使用這種方式
● RetentionPolicy.RUNTIME : 始終不會丟棄,運行期也保留該注解,因此可以使用
反射機制讀取該注解的信息。我們自定義的注解通常使用這種方式。
2.)Target – 表示該注解用於什么地方。默認值為任何元素,表示該注解用於什么地
方。可用的ElementType參數包括
● ElementType.CONSTRUCTOR:用於描述構造器
● ElementType.FIELD:成員變量、對象、屬性(包括enum實例)
● ElementType.LOCAL_VARIABLE:用於描述局部變量
● ElementType.METHOD:用於描述方法
● ElementType.PACKAGE:用於描述包
● ElementType.PARAMETER:用於描述參數● ElementType.TYPE:用於描述類、接口(包括注解類型) 或enum聲明
3.)@Documented–一個簡單的Annotations標記注解,表示是否將注解信息添加在java
文檔中。
4.)@Inherited – 定義該注釋和子類的關系
@Inherited 元注解是一個標記注解,@Inherited闡述了某個被標注的類型是被繼承
的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個
annotation將被用於該class的子類。
自定義注解:
自定義注解類編寫的一些規則:
1. Annotation型定義為@interface, 所有的Annotation會自動繼承
java.lang.Annotation這一接口,並且不能再去繼承別的類或是接口.
2. 參數成員只能用public或默認(default)這兩個訪問權修飾
3. 參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本
數據類型和String、Enum、Class、annotations等數據類型,以及這一些類型的數組.
4. 要獲取類方法和字段的注解信息,必須通過Java的反射技術來獲取 Annotation對象,
因為你除此之外沒有別的獲取注解對象的方法
5. 注解也可以沒有定義成員, 不過這樣注解就沒啥用了
PS:自定義注解需要使用到元注解
aop的講解
(1)AOP是什么?AOP與攔截器的區別?
太抽象的不說,如果你知道Struts2的攔截器,攔截器就是應用的AOP的思想,它用於攔截
Action以進行一些預處理或結果處理。而Spring的AOP是一種更通用的模式,可以攔截
Spring管理的Bean,功能更強大,適用范圍也更廣,它是通過動態代理與反射機制實現的。
( 更 詳 細 的 解 釋 可 參 看 博
客 http://blog.csdn.net/zhangliangzi/article/details/51648032 )
(2)使用AOP需要的一些概念。
1.通知(Advice)
通知定義了在切入點代碼執行時間點附近需要做的工作。
Spring支持五種類型的通知:
Before(前) org.apringframework.aop.MethodBeforeAdvice
after(后)
After-returning(返回后) org.springframework.aop.AfterReturningAdvice
After-throwing(拋出后) org.springframework.aop.ThrowsAdvice
Arround(周圍) org.aopaliance.intercept.MethodInterceptorIntroduction(引入) org.springframework.aop.IntroductionInterceptor
2.連接點(Joinpoint)
程序能夠應用通知的一個“時機”,這些“時機”就是連接點,例如方法調用時、異常
拋出時、方法返回后等等。
3.切入點(Pointcut)
通知定義了切面要發生的“故事”,連接點定義了“故事”發生的時機,那么切入點就
定義了“故事”發生的地點,例如某個類或方法的名稱,Spring中允許我們方便的用正
則表達式來指定。
4.切面(Aspect)
通知、連接點、切入點共同組成了切面:時間、地點和要發生的“故事”。
5.引入(Introduction)
引入允許我們向現有的類添加新的方法和屬性(Spring提供了一個方法注入的功能)。
6.目標(Target)
即被通知的對象,如果沒有AOP,那么通知的邏輯就要寫在目標對象中,有了AOP之后它
可以只關注自己要做的事,解耦合!
7.代理(proxy)
應用通知的對象,詳細內容參見設計模式里面的動態代理模式。
8.織入(Weaving)
把切面應用到目標對象來創建新的代理對象的過程,織入一般發生在如下幾個時機:
(1)編譯時:當一個類文件被編譯時進行織入,這需要特殊的編譯器才可以做的到,例
如AspectJ的織入編譯器;
(2)類加載時:使用特殊的ClassLoader在目標類被加載到程序之前增強類的字節代碼;
(3)運行時:切面在運行的某個時刻被織入,SpringAOP就是以這種方式織入切面的,原
理應該是使用了JDK的動態代理技術。
切點表達式
在使用spring框架配置AOP的時候,不管是通過XML配置文件還是注解的方式都需要定義
pointcut"切入點" 例如定義切入點表達式
expression="execution(* com.fh.service..*.*(..))"
expression="execution(* com.fh.action.login.LoginAction.login(..))"
execution()是最常用的切點函數,其語法如下所示: 整個表達式可以分為五個部分:
1、execution(): 表達式主體。
2、第一個*號:表示返回類型,*號表示所有的類型。
3、包名:表示需要攔截的包名,后面的兩個句點表示當前包和當前包的所有子包,
com.sample.service.impl包、子孫包下所有類的方法。
4、第二個*號:表示類名,*號表示所有的類。5、*(..):最后這個星號表示方法名,*號表示所有的方法,后面括弧里面表示方法的參
數,兩個句點表示任何參數。
三、使用AOP的幾種方式
1.經典的基於代理的AOP
2.@AspectJ注解驅動的切面
3.純POJO切面
如何使用
@AspectJ 進行注解配置
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.8</version>
</dependency>
1.配置
在springMVC中增加配置
頭部文件中加入
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
2.加入配置
<!-- 開啟注解模式 -->
<aop:aspectj-autoproxy/>
<!-- 掃描注解路徑 -->
<context:component-scan base-package="com.fh.aop" />
3.定義自定義注解接口
1 @Target({ ElementType.METHOD, ElementType.TYPE }) 2 3 @Retention(RetentionPolicy.RUNTIME) 4 5 @Documented 6 7 public @interface ILogAcpect { 8 9 String methodInfo() default "";String modelName() default ""; 10 11 }
4.配置注解版的AOP切面類
1 @Aspect 2 3 @Component 4 5 public class LogAspect { 6 7 @Pointcut("execution(* com.fh.controller..*.*(..))") 8 9 private void doMethod() { 10 11 } 12 13 @Before("doMethod()") 14 15 public void beforeAdvice(JoinPoint joinPoint) { 16 17 HttpServletRequest request = ((ServletRequestAttributes) 18 19 RequestContextHolder.getRequestAttributes()) 20 21 .getRequest(); 22 23 // 請求的IP 24 25 String ip = request.getRemoteAddr(); 26 27 System.out.println(ip); 28 29 try { 30 31 // 當前訪問的類路徑 32 33 String targetName = 34 35 joinPoint.getTarget().getClass().getName(); 36 37 // 當前訪問的方法名 38 39 String methodName = joinPoint.getSignature().getName(); 40 41 // 獲取當前類中的公共方法 42 43 Class targetClass = Class.forName(targetName); 44 45 Method[] methods = targetClass.getMethods(); 46 47 String params = ""; 48 49 if (joinPoint.getArgs() != null && 50 51 joinPoint.getArgs().length > 0) { 52 53 params = Arrays.toString(joinPoint.getArgs()); 54 55 } 56 57 Object[] arguments = joinPoint.getArgs();String logName = ""; 58 59 String modelName = ""; 60 61 for (Method method : methods) { 62 63 if (method.getName().equals(methodName)) { 64 65 Class[] clazzs = 66 67 method.getParameterTypes(); 68 69 if (clazzs.length == arguments.length) { 70 71 if 72 73 (method.getAnnotation(ILogAcpect.class) != null) { 74 75 logName = 76 77 method.getAnnotation(ILogAcpect.class).methodInfo(); 78 79 modelName = 80 81 method.getAnnotation(ILogAcpect.class).modelName(); 82 83 } 84 85 break; 86 87 } 88 89 } 90 91 } 92 93 // 通過類路徑獲取所有的方法 94 95 System.out.println("日志輸出的明細為:" + logName); 96 97 System.out.println("日志輸出訪問模塊:" + modelName); 98 99 System.out.println("訪問參數:" + params); 100 101 System.out.println("訪問的類路徑:" + targetName); 102 103 System.out.println("訪問的方法名:" + methodName); 104 105 } catch (ClassNotFoundException e) { 106 107 // TODO Auto-generated catch block 108 109 e.printStackTrace(); 110 111 } 112 113 } 114 115 @After("doMethod()") 116 117 public void afterAdvice() { 118 119 System.out.println("afterAdvice"); 120 121 }@AfterThrowing(pointcut = "doMethod()", throwing = "e") 122 123 public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { 124 125 System.err.println("異常通知:" + e.getMessage()); 126 127 } 128 129 }