javascript
Spring AOP详细介绍
AOP稱為面向切面編程,在程序開發中主要用來解決一些系統層面上的問題,比如日志,事務,權限等待,Struts2的攔截器設計就是基于AOP的思想,是個比較經典的例子。
一 AOP的基本概念
(1)Aspect(切面):通常是一個類,里面可以定義切入點和通知
(2)JointPoint(連接點):程序執行過程中明確的點,一般是方法的調用
(3)Advice(通知):AOP在特定的切入點上執行的增強處理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入點):就是帶有通知的連接點,在程序中主要體現為書寫切入點表達式
(5)AOP代理:AOP框架創建的對象,代理就是目標對象的加強。Spring中的AOP代理可以使JDK動態代理,也可以是CGLIB代理,前者基于接口,后者基于子類
二 Spring AOP
Spring中的AOP代理還是離不開Spring的IOC容器,代理的生成,管理及其依賴關系都是由IOC容器負責,Spring默認使用JDK動態代理,在需要代理類而不是代理接口的時候,Spring會自動切換為使用CGLIB代理,不過現在的項目都是面向接口編程,所以JDK動態代理相對來說用的還是多一些。
三 基于注解的AOP配置方式
1.啟用@AsjectJ支持
在applicationContext.xml中配置下面一句:
<aop:aspectj-autoproxy />2.通知類型介紹
(1)Before:在目標方法被調用之前做增強處理,@Before只需要指定切入點表達式即可
(2)AfterReturning:在目標方法正常完成后做增強,@AfterReturning除了指定切入點表達式后,還可以指定一個返回值形參名returning,代表目標方法的返回值
(3)AfterThrowing:主要用來處理程序中未處理的異常,@AfterThrowing除了指定切入點表達式后,還可以指定一個throwing的返回值形參名,可以通過該形參名
來訪問目標方法中所拋出的異常對象
(4)After:在目標方法完成之后做增強,無論目標方法時候成功完成。@After可以指定一個切入點表達式
(5)Around:環繞通知,在目標方法完成前后做增強處理,環繞通知是最重要的通知類型,像事務,日志等都是環繞通知,注意編程中核心是一個ProceedingJoinPoint
3.例子:
(1)Operator.java --> 切面類
@Component @Aspect public class Operator {@Pointcut("execution(* com.aijava.springcode.service..*.*(..))")public void pointCut(){}@Before("pointCut()")public void doBefore(JoinPoint joinPoint){System.out.println("AOP Before Advice...");}@After("pointCut()")public void doAfter(JoinPoint joinPoint){System.out.println("AOP After Advice...");}@AfterReturning(pointcut="pointCut()",returning="returnVal")public void afterReturn(JoinPoint joinPoint,Object returnVal){System.out.println("AOP AfterReturning Advice:" + returnVal);}@AfterThrowing(pointcut="pointCut()",throwing="error")public void afterThrowing(JoinPoint joinPoint,Throwable error){System.out.println("AOP AfterThrowing Advice..." + error);System.out.println("AfterThrowing...");}@Around("pointCut()")public void around(ProceedingJoinPoint pjp){System.out.println("AOP Aronud before...");try {pjp.proceed();} catch (Throwable e) {e.printStackTrace();}System.out.println("AOP Aronud after...");}}(2)UserService.java --> 定義一些目標方法
@Service public class UserService {public void add(){System.out.println("UserService add()");}public boolean delete(){System.out.println("UserService delete()");return true;}public void edit(){System.out.println("UserService edit()");int i = 5/0;}}(3).applicationContext.xml
<context:component-scan base-package="com.aijava.springcode"/><aop:aspectj-autoproxy />(4).Test.java
public class Test {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");UserService userService = (UserService) ctx.getBean("userService");userService.add();} }上面是一個比較簡單的測試,基本涵蓋了各種增強定義。注意:做環繞通知的時候,調用ProceedingJoinPoint的proceed()方法才會執行目標方法。
4.通知執行的優先級
進入目標方法時,先織入Around,再織入Before,退出目標方法時,先織入Around,再織入AfterReturning,最后才織入After。
注意:Spring AOP的環繞通知會影響到AfterThrowing通知的運行,不要同時使用!同時使用也沒啥意義。
5.切入點的定義和表達式
切入點表達式的定義算是整個AOP中的核心,有一套自己的規范
Spring AOP支持的切入點指示符:
(1)execution:用來匹配執行方法的連接點
A:@Pointcut("execution(* com.aijava.springcode.service..*.*(..))")
第一個*表示匹配任意的方法返回值,..(兩個點)表示零個或多個,上面的第一個..表示service包及其子包,第二個*表示所有類,第三個*表示所有方法,第二個..表示
方法的任意參數個數
B:@Pointcut("within(com.aijava.springcode.service.*)")
within限定匹配方法的連接點,上面的就是表示匹配service包下的任意連接點
C:@Pointcut("this(com.aijava.springcode.service.UserService)")
this用來限定AOP代理必須是指定類型的實例,如上,指定了一個特定的實例,就是UserService
D:@Pointcut("bean(userService)")
bean也是非常常用的,bean可以指定IOC容器中的bean的名稱
6.基于XML形式的配置方式
開發中如果選用XML配置方式,通常就是POJO+XML來開發AOP,大同小異,無非就是在XML文件中寫切入點表達式和通知類型
例子:
(1)Log.java
public class Log {private Integer id;//操作名稱,方法名private String operName;//操作人private String operator;//操作參數private String operParams;//操作結果 成功/失敗private String operResult;//結果消息private String resultMsg;//操作時間private Date operTime = new Date();setter,getter}(2).Logger.java
/*** 日志記錄器 (AOP日志通知)*/ public class Logger {@Resourceprivate LogService logService;public Object record(ProceedingJoinPoint pjp){Log log = new Log();try {log.setOperator("admin");String mname = pjp.getSignature().getName();log.setOperName(mname);//方法參數,本例中是User userObject[] args = pjp.getArgs();log.setOperParams(Arrays.toString(args));//執行目標方法,返回的是目標方法的返回值,本例中 voidObject obj = pjp.proceed();if(obj != null){log.setResultMsg(obj.toString());}else{log.setResultMsg(null);}log.setOperResult("success");log.setOperTime(new Date());return obj;} catch (Throwable e) {log.setOperResult("failure");log.setResultMsg(e.getMessage());} finally{logService.saveLog(log);}return null;} }(3).applicationContext.xml
<aop:config><aop:aspect id="loggerAspect" ref="logger"><aop:around method="record" pointcut="(execution(* com.aijava.distributed.ssh.service..*.add*(..))or execution(* com.aijava.distributed.ssh.service..*.update*(..))or execution(* com.aijava.distributed.ssh.service..*.delete*(..)))and !bean(logService)"/></aop:aspect> </aop:config>注意切入點表達式,!bean(logService) 做日志通知的時候,不要給日志本身做日志,否則會造成無限循環!
有關更詳細的Spring AOP知識,可以查看Spring官方文檔第9章Aspect Oriented Programming with Spring?
7.JDK動態代理介紹
例子:
(1)UserService.java
public interface UserService {public void add(); }(2)UserServiceImpl.java
public class UserServiceImpl implements UserService{public void add() {System.out.println("User add()...");}}(3)ProxyUtils.java
public class ProxyUtils implements InvocationHandler{private Object target;public ProxyUtils(Object target){this.target = target;}public Object getTarget() {return target;}public void setTarget(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("do sth before...");method.invoke(target, args);System.out.println("do sth after...");return null;}}(4)Test.java
public class Test {public static void main(String[] args) {UserService userService = new UserServiceImpl();ProxyUtils proxyUtils = new ProxyUtils(userService);UserService proxyObject = (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),UserServiceImpl.class.getInterfaces(), proxyUtils);proxyObject.add();} }JDK動態代理核心還是一個InvocationHandler,記住這個就行了。
from:?http://www.cnblogs.com/liuruowang/p/5711563.html
總結
以上是生活随笔為你收集整理的Spring AOP详细介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聊聊clean code
- 下一篇: Spring学习总结——Spring实现