歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Spring AOP詳細介紹

Spring AOP詳細介紹

日期:2017/3/1 9:13:45   编辑:Linux編程

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 {
    
    @Resource
    private LogService logService;
    
    public Object record(ProceedingJoinPoint pjp){
        
        Log log = new Log();
        try {
            log.setOperator("admin");
            String mname = pjp.getSignature().getName();
            log.setOperName(mname);
            
            //方法參數,本例中是User user
            Object[] args = pjp.getArgs();
            log.setOperParams(Arrays.toString(args));
            
            //執行目標方法,返回的是目標方法的返回值,本例中 void
            Object 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,記住這個就行了。

Spring AOP自定義注解方式實現日志管理 http://www.linuxidc.com/Linux/2015-11/125019.htm

Spring AOP進行日志記錄 http://www.linuxidc.com/Linux/2015-11/124731.htm

使用Spring AOP進行性能監控 http://www.linuxidc.com/Linux/2012-07/64681.htm

利用Spring AOP 更新Memcached 緩存策略的實現 http://www.linuxidc.com/Linux/2012-03/56503.htm

Spring AOP的兩種代理 http://www.linuxidc.com/Linux/2015-11/125017.htm

Spring AOP的注解實例 http://www.linuxidc.com/Linux/2015-11/125018.htm

Copyright © Linux教程網 All Rights Reserved