1. 程式人生 > >Spring AOP學習

Spring AOP學習

[] .com -s tca demo1 ng- cep pattern pre

什麽是AOP

AOP 面向切面編程

采取橫向抽取機制,取代了傳統縱向繼承體系重復性代碼(性能監視、事務管理、安全檢查、緩存)

Spring AOP使用純Java實現,不需要專門的編譯過程和類加載器,在運行期通過代理方式向目標類織入增強代碼

AspecJ是一個基於Java語言的AOP框架,Spring2.0開始,Spring AOP引入對Aspect的支持,AspectJ擴展了Java語言,提供了一個專門的編譯器,在編譯時提供橫向代碼的織入

AOP底層原理

就是代理機制:

* 動態代理:(JDK中使用)

* JDK的動態代理,對實現了接口的類生成代理.

Spring的AOP代理

JDK動態代理:對實現了接口的類生成代理。沒有實現接口的類,就無法生成代理對象了。

CGLib代理機制:對類生成代理

結論:Spring框架,如果類實現了接口,就使用JDK的動態代理生成代理對象,如果這個類沒有實現任何接口,使用CGLIB生成代理對象.

AOP的術語

Joinpoint(連接點):所謂連接點是指那些被攔截到的點。在spring中,這些點指的是方法,因為spring只支持方法類型的連接點.

Pointcut(切入點):所謂切入點是指我們要對哪些Joinpoint進行攔截的定義.

Advice(通知/增強):所謂通知是指攔截到Joinpoint之後所要做的事情就是通知.通知分為前置通知,後置通知,異常通知,最終通知,環繞通知

(切面要完成的功能)

Introduction(引介):引介是一種特殊的通知在不修改類代碼的前提下, Introduction可以在運行期為類動態地添加一些屬性方法.

Target(目標對象):代理的目標對象

Weaving(織入):是指把增強應用到目標對象來創建新的代理對象的過程.

spring采用動態代理織入,而AspectJ采用編譯期織入和類裝在期織入

Proxy(代理):一個類被AOP織入增強後,就產生一個結果代理類

Aspect(切面):是切入點和通知(引介)的結合

技術分享圖片

不帶有切點的切面

Spring按照通知Advice在目標方法的連接點位置,通知Advice可分為五類

前置通知:org.springframework.aop.MethodBeforeAdvice,在目標方法執行之前實施增強

後置通知:org.springframework.aop.AfterReturningAdvice,在目標方法執行之後實施增強

環繞通知:org.aopalliance.intercept.MethodInterceptor,在目標方法執行前後實施增強

異常拋出通知:org.springframework.aop.ThrowsAdvice,在方法拋出異常之後實施增強

引介通知:org.springframework.aop.IntroductionInterceptor,在目標類中添加一些新的方法和屬性

Spring中切面的類型:

Advisor:Spring中的傳統切面。

Aspect:都是有一個切點和一個通知的組合

Advisor:多個切點和多個通知的組合

Advisor : 代表一般切面,Advice本身就是一個切面,對目標類所有方法進行攔截(*不帶有切點的切面)

PointcutAdvisor : 代表具有切點的切面,可以指定攔截目標類哪些方法帶有切點的切面,針對某個方法進行攔截

IntroductionAdvisor : 代表引介切面,針對引介通知而使用切面(不要求掌握)

不帶切點的切面實例

導入相應的jar包:Spring開發基礎包、spring-aop-4.3.7.RELEASE.jar、aopalliance-1.0.jar(AOP聯盟包)

編寫一個接口Customer:

package com.js.aopStu;
 
public interface CustomerDao {
    public void add();
    public void delete();
    public void update();
    public void find();
}

編寫實現類CustomerImpl:

package com.js.aopStu;
 
public class CustomerImpl implements CustomerDao {
 
    @Override
    public void add() {    
        System.out.println("添加客戶...");
    }
 
    @Override
    public void delete() {    
        System.out.println("刪除客戶...");
    }
 
    @Override
    public void update() {    
        System.out.println("修改客戶...");
    }
 
    @Override
    public void find() {
        System.out.println("查詢客戶...");
    }
 
}

編寫增強的代碼。新建一個類MyBeforeAdvice,以前置增強為例

package com.js.aopStu;
 
import java.lang.reflect.Method;
 
import org.springframework.aop.MethodBeforeAdvice;
 
/**
 * 前置增強
 * 實現指定接口
 * @author hdb
 *
 */
public class MyBeforeAdvice implements MethodBeforeAdvice{
    /**
     * method:執行的方法
     * args:參數
     * target:目標對象
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("前置增強...");
    }
    
}

配置代理生成代理類,基於ProxyFactoryBean類,底層自動選擇使用JDK的動態代理還是CGLIB的代理。

配置applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

<!-- 定義目標對象 -->
<bean id="customerDao" class="com.js.aopStu.CustomerImpl"></bean>
<!-- 定義增強 -->
<bean id="beforeAdice" class="com.js.aopStu.MyBeforeAdvice"></bean>
 <!-- Spring支持配置來生成代理,基於ProxyFactoryBean類,底層自動選擇使用JDK的動態代理還是CGLIB的代理 -->
 <bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
 <!-- 設置目標對象 -->
     <property name="target" ref="customerDao"></property>
     <!-- 設置實現的接口,value中寫接口的全路徑 -->
     <property name="proxyInterfaces" value="com.js.aopStu.CustomerDao"></property>
     <!-- 配置需要攔截的,一定是value,此處對customerDao中的所有方法攔截 -->
     <property name="interceptorNames" value="beforeAdice"></property>
 </bean>

</beans>

我們需要配置一些屬性,不需要都設置。

lProxyFactoryBean常用可配置屬性 •target : 代理的目標對象 •proxyInterfaces : 代理要實現的接口 •如果多個接口可以使用以下格式賦值

<list>

<value></value>

....

</list>

•proxyTargetClass : 是否對類代理而不是接口,設置為true時,使用CGLib代理 •interceptorNames : 需要織入目標的Advice •singleton : 返回代理是否為單實例,默認為單例 •optimize : 當設置為true時,強制使用CGLib (proxyInterfaces、proxyTargetClass二者互斥,不能同時存在)

編寫測試類:

package com.js.aopStu;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAOPDemo1 {
    
    @Autowired
    @Qualifier("customerDaoProxy")
    private CustomerDao customerDao;
    
    //不使用增強的情況下
    @Test
    public void Demo1(){
        customerDao.add();
        customerDao.delete();
        customerDao.find();
        customerDao.update();
    }
}

帶有切點的切面(常用)

用PointcutAdvisor實現類,它有兩個接口: 1、DefaultPointcutAdvisor:最常用的切面類型,它可以通過任意Pointcut和Advice 組合定義切面 2、RegexpMethodPointcutAdvisor:構造正則表達式切點切面一般使用這種

帶有切點的切面實例

新建一個DAO,創建被代理對象:

package com.js.demo3;
/**
 * 目標對象
 * @author  hdb
 *
 */
public class OrderDao {
    public void add() {    
        System.out.println("添加訂單...");
    }
    public void delete() {    
        System.out.println("刪除訂單...");
    }
    public void update() {    
        System.out.println("修改訂單...");
    }
    public void find() {
        System.out.println("查詢訂單...");
    }
}

編寫增強類,這次使用環繞增強:

package com.js.demo3;
 
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
 
/**
 * 增強的類
 * 使用的是環繞增強
 * @author hbd
 *
 */
public class MyAroundAdvice implements MethodInterceptor{
 
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("環繞前增強===");
        Object object=methodInvocation.proceed();//執行目標對象的方法
        System.out.println("環繞後增強===");
        return object;
    }
    
}

生成代理:通過配置的方式:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

<!-- 帶有切點的切面 -->
<!-- 定義目標對象 -->
<bean id="orderDao1" class="com.js.demo3.OrderDao"></bean>

<!-- 定義增強 -->
<bean id="aroundAdvice" class="com.js.demo3.MyAroundAdvice"></bean>

<!-- 定義切點切面: -->
<bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <!-- 定義表達式,規定哪些方法執行攔截 -->
    <!-- . 任意字符  * 任意個 -->
    <!-- <property name="pattern" value=".*"/> -->
    <!-- <property name="pattern" value="cn\.itcast\.spring3\.demo4\.OrderDao\.add.*"/> -->
    <!-- <property name="pattern" value=".*add.*"></property> -->
    <property name="patterns" value=".*add.*,.*find.*"></property>
    <!-- 應用增強 -->
    <property name="advice" ref="aroundAdvice"/>
</bean>

<!-- 定義生成代理對象 -->
<bean id="orderDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <!-- 配置目標 -->
    <property name="target" ref="orderDao1"></property>
    <!-- 針對類的代理 -->
    <property name="proxyTargetClass" value="true"></property>
    <!-- 在目標上應用增強 -->
    <property name="interceptorNames" value="myPointcutAdvisor"></property>
</bean>

</beans>

編寫測試類:

package com.js.demo3;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAOPDemo1 {
@Autowired @Qualifier(
"orderDaoProxy") private OrderDao orderDao;
@Test
public void demo1(){ orderDao.add(); orderDao.delete(); orderDao.find(); orderDao.update(); } }

Spring AOP學習