spring(三):springAOP的各種織入方式一覽


前言:

上一篇簡單使用反射和jdk的動態代理模擬了AOP工作原理。在這里將講闡述AOP如何把代碼織入到目標對象的方法上。


一:這里介紹一下AOP一些名詞概念

(1)Aspect(切面):通常是一個類,里面可以定義切入點和通知。

(2)JointPoint(連接點):程序執行過程中明確的點,一般是方法的調用。這就是目標對象的方法。

(3)Advice(通知):AOP在特定的切入點上執行的增強處理,有before,after,afterReturning,
    afterThrowing,around。這些就是需要織入到連接點中的代碼。

(4)Pointcut(切入點):就是帶有通知的連接點,在程序中主要體現為書寫切入點表達式

(5)AOP代理:AOP框架創建的對象,代理就是目標對象的加強。Spring中的AOP代理可以使JDK動態代理,
    也可以是CGLIB代理,前者基於接口,后者基於子類

(6)advisor:增強器,用來篩選類中的哪些方法是我們的連接點(哪些方法需要被攔截).

  (7)wave:織入,把切面/切面類和目標類的動態接入。           

                         

 

二:Advice(通知)

AOP的通知有before前置通知。after后置通知,無論是否成功返回都會執行。afterReturning后置通知,只有成功返回后才會執行。

afterThrowing拋出異常通知,顧名思義,只有拋出異常的時候才會執行。around環繞通知,簡單來說就是集合了before和after的

功能。

這里基於xml文件實現把切面類和目標類動態接入。

(1)pojo類:Account.java

package com.cnblogs.aop.pojo; /** * 簡單模擬銀行賬戶 * */
public class Account { // 卡號
    private Long id; // 姓名 
    private String name; //余額
    private double balance; public Account() { super(); // TODO Auto-generated constructor stub
 } public Account(Long id, String name, double balance) { super(); this.id = id; this.name = name; this.balance = balance; } // get和set方法 }

 

(2)目標類:這里使用的是JDK的接口代理模式,所以需要准備一個接口,和接口的實現類,即目標類。

package com.cnblogs.aop.service; public interface BankService { /** * 存錢 * @param money */
    public Boolean save(double money) throws Exception; /** * 取錢 * @param money * @return
     */
    public boolean withdraw(double money) throws Exception; }

 

package com.cnblogs.aop.service.impl; import com.cnblogs.aop.pojo.Account; import com.cnblogs.aop.service.BankService; public class BankServiceImpl implements BankService { private Account account; public Account getAccount() { return account; } public void setAccount(Account account) { this.account = account; } @Override public Boolean save(double money) throws Exception { System.out.println("存錢: " + money); account.setBalance(account.getBalance() + money); return true; } @Override public boolean withdraw(double money) throws Exception{ System.out.println("取錢: " + money); if(account.getBalance() < money) { throw new Exception("余額不足"); } account.setBalance(account.getBalance() - money); return true; } }

 

(3):目標類准備好以后,就要准備切面類了。這個切面類直接集合了所有的通知類型。

package com.cnblogs.aop.aspect; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.Date; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.AfterReturningAdvice; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.aop.ThrowsAdvice; /** * 切面類 * 實現了前置通知 MethodBeforeAdvice * 成功返回才執行的后置通知 * 環繞通知 * 拋出異常才會執行的通知 * * */
public class MyLogger implements MethodBeforeAdvice,AfterReturningAdvice, MethodInterceptor,ThrowsAdvice{ /** * 前置通知 * @param method 連接點 * @param args 連接點參數 * @param target 目標對象 */ @Override public void before(Method mehtod, Object[] args, Object target) throws Throwable { // 添加日志功能
        System.out.println("前置通知: " + new SimpleDateFormat().format(new Date())); } /** * 后置通知會接收到連接點的返回值,連接點返回值必須為引用類型,否則 * 報錯:org.springframework.aop.AopInvocationException: * Null return value from advice does not match primitive return type for * @param returnValue 連接點的返回值【如果有的話】 * @param method 連接點 * @param args 連接點方法參數 * @param target 目標對象【委托對象】 */ @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { // 添加日志功能
        System.out.println("成功返回才執行的后置通知: " + new SimpleDateFormat().format(new Date())); } /** * 環繞通知 * 在調用連接點前為前置通知 * 在調用連接點后為后置通知 */ @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("環繞通知之前置: " + 
                    new SimpleDateFormat().format(new Date())); // 調用連接點
 invocation.proceed(); System.out.println("環繞通知之后置: " + 
                new SimpleDateFormat().format(new Date())); return null; } /** * 拋出異常才會執行的通知比較特殊,它的接口類體為空, * 只起一個標記作用。 * 不過會默認調用afterThrowing方法作為通知。 * 該方法重載,有4個參數的和1個參數的 * 當兩個重載方法都存在時優先調用4個參數的 */
    public void afterThrowing(Method method, Object[] args, Object target,Exception e) { System.out.println("拋出異常才會執行的通知(4參): " + e.toString()); } public void afterThrowing(Exception e) { System.out.println("拋出異常才會執行的通知(1參): " + e.toString()); } }

 

(4):織入

advice.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    
    <!-- 銀行賬號 -->
    <bean name="account" class="com.cnblogs.aop.pojo.Account">
        <property name="id" value="6217" />
        <property name="name" value="jack" />
        <property name="balance" value="1000.00" />
    </bean>
    
    <!-- 目標對象 -->
    <bean name="service" class="com.cnblogs.aop.service.impl.BankServiceImpl">
        <property name="account" ref="account" />
    </bean>
    
    <!-- 切面對象 -->
    <bean name="log" class="com.cnblogs.aop.aspect.MyLogger">
    </bean>
    
    <!-- 生成代理對象 這里使用的是spring的一個代理對象工廠類產生的 -->
    <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 注入目標對象 -->
        <property name="target" ref="service"></property>

        <!-- 注入目標對象所實現的接口 可以有多個接口,基於JDK的接口代理 -->
        <property name="proxyInterfaces">
            <list>
                <value>com.cnblogs.aop.service.BankService</value>
            </list>
        </property>

        <!-- 注入advice -->
        <property name="interceptorNames">
            <list>
                <value>log</value>
            </list>
        </property>

</bean>
</beans>

 

(5):測試  

package com.cnblogs.aop.jtest;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.cnblogs.aop.service.BankService;

@SuppressWarnings("resource")
public class AOPTest {
    
    @Test
    public void advice(){
        try {
            // 獲取springIOC容器
            String path = "com/cnblogs/aop/aspect/advice.xml";
            
            ApplicationContext container = new ClassPathXmlApplicationContext(path);
            
            // 從容器中獲取代理對象
            BankService proxy = (BankService) container.getBean("proxy");
            // 執行目標對象中的方法
//            proxy.save(1000.0);
            
            // 測試通知會對那些方法起作用
//            proxy.getClass(); // 無
//            proxy.toString(); // 有
            
            // 測試拋出異常通知
//            proxy.withdraw(5000.0);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

1.環繞通知的前置優先前置通知輸出,后置通知優先環繞通知的后置輸出。

環繞通知之前置: 19-5-28 下午7:29
前置通知: 19-5-28 下午7:29
存錢: 1000.0
成功返回才執行的后置通知: 19-5-28 下午7:29
環繞通知之后置: 19-5-28 下午7:29

2.通知對那些方法起作用?

  單獨調用getClass()方法時沒有通知輸出,toString()方法通知有輸出。去java.lang.Objcet查找兩個方法,

public final Class<?> getClass(),public String toString().簡單對比發現只要是非final類型修飾的,通過代理對象調用

目標對象方法時都會把通知織入。

有時候我們希望目標對象的某些方法被織入,其它方法不想被織入,當時又不想用final修飾那些不想被織入的方法該

怎么辦?增強器可以起到這個效果,篩選需要的方法織入。

 

三:Advisor增強器

增強器可以篩選目標類中那些方法被織入。

 advisor.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    
    <!-- 銀行賬號 -->
    <bean name="account" class="com.cnblogs.aop.pojo.Account">
        <property name="id" value="6217" />
        <property name="name" value="jack" />
        <property name="balance" value="1000.00" />
    </bean>
    
    <!-- 目標對象 -->
    <bean name="service" class="com.cnblogs.aop.service.impl.BankServiceImpl">
        <property name="account" ref="account" />
    </bean>
    
    <!-- 切面對象 -->
    <bean name="log" class="com.cnblogs.aop.aspect.MyLogger">
    </bean>
    
    <!-- 配置增強器的bean對象 -->
    <bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 注入advice -->
        <property name="advice" ref="advice"/>
        <!-- 注入需要被攔截的目標對象中的方法(連接點) -->
        <property name="patterns">
            <list>
                <value>.*save</value>
                <value>.*withdraw</value>
            </list>
        </property>
    </bean>
    
    <!-- 生成代理對象 這里使用的是spring的一個代理對象工廠類產生的 -->
    <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 注入目標對象 -->
        <property name="target" ref="service"></property>

        <!-- 注入目標對象所實現的接口 可以有多個接口,基於JDK的接口代理 -->
        <property name="proxyInterfaces">
            <list>
                <value>com.cnblogs.aop.service.BankService</value>
            </list>
        </property>

        <!-- 注入advice -->
        <property name="interceptorNames">
            <list>
                <value>advisor</value>
            </list>
        </property>
  </bean>
</beans>

補充:與advice.xml文件對比,發現只是多了一個org.springframework.aop.support.RegexpMethodPointcutAdvisor類的bean對象,該類就是增強器,

里面需要注入切面類的bean對象,以及指定那些方法被織入。

其次在生成代理對象的bean標簽中,注入advice是注入增強器。

 

測試:

@Test public void advisor(){ try { // 獲取springIOC容器
            String path = "com/cnblogs/aop/advisor/advisor.xml"; ApplicationContext container = new ClassPathXmlApplicationContext(path); // 從容器中獲取代理對象
            BankService proxy = (BankService) container.getBean("proxy"); // proxy.toString(); // proxy.save(1000);
 proxy.getClass(); } catch (Exception e) { e.printStackTrace(); } }

 

 四:AutoProxy,自動代理

 如果目標類不止一個,為多個目標類生成代理對象需要配置多個

<bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">...... </bean>,

自動代理可以用很少的xml配置給目標對象生成代理對象。自動代理用到增強器的功能。

autoProxy.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    
    <!-- 銀行賬號 -->
    <bean name="account" class="com.cnblogs.aop.pojo.Account">
        <property name="id" value="6217" />
        <property name="name" value="jack" />
        <property name="balance" value="1000.00" />
    </bean>
    
    <!-- 目標對象 -->
    <bean name="service" class="com.cnblogs.aop.service.impl.BankServiceImpl">
        <property name="account" ref="account" />
    </bean>
    
    <!-- 切面對象 -->
    <bean name="log" class="com.cnblogs.aop.aspect.MyLogger">
    </bean>
    
    <!-- 配置增強器的bean對象 -->
    <bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 注入advice -->
        <property name="advice" ref="log"/>
        <!-- 注入需要被攔截的目標對象中的方法(連接點) -->
        <property name="patterns">
            <list>
                <value>.*save</value>
                <value>.*withdraw</value>
            </list>
        </property>
    </bean>
    
    <!-- 配置自動代理對象 -->
    <bean name="proxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
    </bean>
</beans>

 

1.不能通過proxy拿代理對象,通過目標對象的名字拿代理對象。

2.當前xml文件中一定要有一個增強器advisor,配置自動代理對象不需要注入任何東西。

3.不管目標對象是否實現了一個或多接口,自動代理的方式都能夠為它產生代理對象(CGLib的方式)。

4.如果目標對象有增強器中篩選的方法,將來調用方法的時候會動態織入。

 

測試:

@Test public void autoProxy(){ try { // 獲取springIOC容器
            String path = "com/cnblogs/aop/autoProxy/autoProxy.xml"; ApplicationContext container = new ClassPathXmlApplicationContext(path); BankService target = (BankService) container.getBean("service"); target.save(1000.0); } catch (Exception e) { e.printStackTrace(); } }

 結果:

環繞通知之前置: 19-5-29 下午4:22
前置通知: 19-5-29 下午4:22
存錢: 1000.0
成功返回才執行的后置通知: 19-5-29 下午4:22
環繞通知之后置: 19-5-29 下午4:22

 

2.AutoProxyByName 通過名字進行自動代理

雖然自動代理可以很方便的給xml文件中的目標對象設置對應的代理對象,但是並不是xml文件中的所有對象都是我們的目標對象,

我們更想希望可以進一步篩選出某幾個對象為我們的目標對象。

autoProxyByName.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    
    <!-- 銀行賬號 -->
    <bean name="account" class="com.cnblogs.aop.pojo.Account">
        <property name="id" value="6217" />
        <property name="name" value="jack" />
        <property name="balance" value="1000.00" />
    </bean>
    
    <!-- 目標對象 -->
    <bean name="service" class="com.cnblogs.aop.service.impl.BankServiceImpl">
        <property name="account" ref="account" />
    </bean>
    
    <!-- 切面對象 -->
    <bean name="log" class="com.cnblogs.aop.aspect.MyLogger">
    </bean>
    
    <!-- 配置增強器的bean對象 -->
    <bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 注入advice -->
        <property name="advice" ref="log"/>
        <!-- 注入需要被攔截的目標對象中的方法(連接點) -->
        <property name="patterns">
            <list>
                <value>.*save</value>
                <value>.*withdraw</value>
            </list>
        </property>
    </bean>
    
    <!-- 配置代理對象 -->
    <!-- 這里使用自動代理的方式 autoproxybyname -->
    <bean name="proxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!-- 注入需要被代理的對象名字 -->
           <property name="beanNames">
               <list>
                <!-- value中填寫bean標簽的id或name屬性值 -->
                   <value>service</value>
               </list>
           </property>
           <!-- 注入advice或者advisor -->
           <property name="interceptorNames">
               <list>
                   <value>advisor</value>
               </list>
           </property>
     </bean>
</beans> 

使用byName自動代理的時候需要注意的方面:

 

             1.當前的配置里面"有沒有"advisor的配置"都沒關系"

 

             2.不管目標對象是否實現了一個或多接口,自動代理的方式都能夠為它產生代理對象.

 

             3.從spring容器中拿代理對象的時候,需要通過目標對象的名字來拿。

 

五:使用<aop>標簽完成織入功能

 execution(modifiers-pattern ret-type-pattern declaring-type-pattern name-pattern(param-pattern)   throws-pattern)

除了返回類型模式(ret-type-pattern),名字模式(name-pattern),參數模式(param-pattern)是必須的,

其它模式都是可選的

ret-type-pattern: ' * '代表匹配任意返回類型,java.lang.String匹配String返回類型。

name-pattern: 名字模式匹配的是方法名。 你可以使用 * 通配符作為所有或者部分命名模式。

param-pattern:參數匹配, ' () '匹配無參,' (*) '匹配一個參數,‘(..)’匹配任意參數(0或多個),

' (*,String) ' 匹配了一個接受兩個參數的方法,第一個可以是任意類型,第二個必須是String類型。

下面給出一些常見切入點表達式的例子。 

    1)任意包下的任意類中的公共方法的執行: 

                  execution(public * *(..))

    2)任何一個以“set”開始的方法的執行: 

                  execution(* set*(..))

    3)BankService接口的任意方法的執行:   

                  execution(* com.cnblogs.service.BankService.*(..))

    4)定義在service包里的任意方法的執行: 

                  execution(* com.cnblogs.service.*.*(..))

    5)定義在service包或者子包里的任意方法的執行: 

                  execution(* com.cnblogs.service..*.*(..))

 

1.第一種方式使用<aop>標簽

<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd ">
    
    <!-- 銀行賬號 -->
    <bean name="account" class="com.cnblogs.aop.pojo.Account">
        <property name="id" value="6217" />
        <property name="name" value="jack" />
        <property name="balance" value="1000.00" />
    </bean>
    
    <!-- 目標對象 -->
    <bean name="service" class="com.cnblogs.aop.service.impl.BankServiceImpl">
        <property name="account" ref="account" />
    </bean>
    
    <!-- 切面對象 -->
    <bean name="log" class="com.cnblogs.aop.aspect.MyLogger">
    </bean>
    
    <!-- 配置aop的代理 也啟用了自動代理的功能-->
    <aop:config>
        <!-- 定義一個切入點 -->
        <aop:pointcut expression="execution(public * com.cnblogs.aop.service.BankService.save(*))" id="save"/>
        <aop:pointcut expression="execution(public * com.cnblogs.aop.service.BankService.withdraw(*))" id="withdraw"/>

        <!-- 定義哪一個advice在哪一個切入點上面起作用 -->
        <aop:advisor advice-ref="log" pointcut-ref="save"  />
        <aop:advisor advice-ref="log" pointcut-ref="withdraw"  />
    </aop:config>

</beans>

 

注意:

                  1.從spring容器中拿代理對象的時候也是要用目標對象的名字來拿。

                   2.沒有實現任何接口的目標對象也能產生代理對象。

 

測試:

@Test public void aop1(){ try { // 獲取springIOC容器
            String path = "com/cnblogs/aop/aopConfig/config1.xml"; ApplicationContext container = new ClassPathXmlApplicationContext(path); BankService target = (BankService) container.getBean("service"); target.save(1000.0); target.withdraw(20000.0); } catch (Exception e) { e.printStackTrace(); } }

 

環繞通知之前置: 19-5-29 下午7:06
前置通知: 19-5-29 下午7:06
存錢: 1000.0
成功返回才執行的后置通知: 19-5-29 下午7:06
環繞通知之后置: 19-5-29 下午7:06
環繞通知之前置: 19-5-29 下午7:06
前置通知: 19-5-29 下午7:06
取錢: 20000.0
拋出異常才會執行的通知(1參): java.lang.Exception: 余額不足

 

2.第二種方式使用aop

在一個切面類中定個多個方法,根據xml文件的配置每個方法都可以織入到切入點的不同位置,並且advice是在aop的標簽中進行配置,

不需要再寫對應的advice類了。

切面類:

package com.cnblogs.aop.aopConfig; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; /** * 切面類,這里包含了多個切面方法,可以把每個切面方法織入到不同切入點的不同位置 * */
public class XmlHandler { public void beforeTest(JoinPoint p){ System.out.println(p.getSignature().getName()+" before..."); } public void afterTest(JoinPoint p){ System.out.println(p.getSignature().getName()+" after..."); } public void afterReturningTest(JoinPoint p){ System.out.println(p.getSignature().getName()+" afterReturning"); } //在和aroundAdvice結合的時候,這個方法一定要加上這個ProceedingJoinPoint類型的參數
    public Object aroundTest(ProceedingJoinPoint pjp)throws Throwable{ //JoinPoint對象不能調用連接點所表示的方法 //ProceedingJoinPoint能調用連接點所表示的方法 pjp.proceed()
        System.out.println(pjp.getSignature().getName()+" is start.."); //調用目標對象中的方法
        Object obj = pjp.proceed(); System.out.println(pjp.getSignature().getName()+" is end.."); return obj; } public void throwingTest(JoinPoint p,Exception ex){ System.out.println(p.getSignature().getName()+" is throwing..."+ex.getMessage()); } }

 

 

<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd ">
    
    <bean name="account" class="com.cnblogs.aop.pojo.Account">
        <property name="id" value="6217" />
        <property name="name" value="jack" />
        <property name="balance" value="1000.00" />
    </bean>
    
    <!-- 目標對象 -->
    <bean name="service" class="com.cnblogs.aop.service.impl.BankServiceImpl">
        <property name="account" ref="account" />
    </bean>
    
    <!-- 切面對象 -->
    <bean name="log" class="com.cnblogs.aop.aopConfig.XmlHandler">
    </bean>
    
    <!-- 配置aop的代理 -->
    <aop:config>
        <!-- 定義切入點名為myPointCut -->
        <aop:pointcut expression="execution(public * com.cnblogs.aop.service.*.*(..))" id="myPointCut"/>
        
        <!-- 定義切面類 以及需要使用的advice -->
        <aop:aspect id="aspect" ref="log">
            <!-- before表示會把切面類log中的beforeTest方法織入到名字 叫myPointCut的切入點前面 -->
            <aop:before method="beforeTest" pointcut-ref="myPointCut"/>

            <!-- after表示不管方法是否正常結束都會起作用 -->
            <aop:after method="afterTest" pointcut-ref="myPointCut"/>

            <!-- after-returning表示方法正常結束才會起作用(拋異常時候不起作用) -->
            <aop:after-returning method="afterReturningTest" pointcut-ref="myPointCut"/>

            <aop:around method="aroundTest" pointcut-ref="myPointCut"/>

            <!-- throwing="ex"表示throwingTest方法中接收異常對象的名字一定要是ex -->
            <aop:after-throwing method="throwingTest" pointcut-ref="myPointCut" throwing="ex"/>

        </aop:aspect>
    </aop:config>
</beans>

 

@Test public void aop2(){ try { // 獲取springIOC容器
            String path = "com/cnblogs/aop/aopConfig/config2.xml"; ApplicationContext container = new ClassPathXmlApplicationContext(path); BankService target = (BankService) container.getBean("service"); target.save(1000.0); // target.withdraw(20000.0);
 } catch (Exception e) { e.printStackTrace(); } }

save before...
save is start..
存錢: 1000.0
save is end..
save afterReturning
save after...

 

 六:使用注解配置AOP

 1.定義切面類:

package com.cnblogs.aop.annotation; import java.text.SimpleDateFormat; import java.util.Date; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Component @Aspect public class AnnotationHandler { /** * 在一個方法上面加上注解來定義切入點 * 這個切入點的名字就是這個方法的名字 * 這個方法本身不需要有什么作用 * 這個方法的意義就是:給這個 @Pointcut注解一個可以書寫的地方 * 因為注解只能寫在方法、屬性、類的上面,並且方法名作為切入點的名字 */ @Pointcut("execution(public * com.cnblogs.aop.service.impl.*.*(..))") public void myPoint(){} /** * 前置通知 * 如果需要用到Joinpoint,可以在方法參數加上 * @param j */ @Before("myPoint()") public void beforeTest(){ System.out.println("前置通知: " + new SimpleDateFormat().format(new Date())); } /** * 最終通知 */ @After("myPoint()") public void afterTest(){ System.out.println("最終通知: " + new SimpleDateFormat().format(new Date())); } /** * 環繞通知 * @throws Throwable */ @Around("myPoint()") public void aroundTest(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("環繞通知之前置: " + 
                new SimpleDateFormat().format(new Date())); //調用連接點的方法去執行
        Object obj = pjp.proceed(); System.out.println("環繞通知之后置: " + 
                new SimpleDateFormat().format(new Date())); } /** * 成功返回才會執行的后置通知 */ @AfterReturning("myPoint()") public void afterReturningTest(){ System.out.println("成功返回才執行的后置通知: " + new SimpleDateFormat().format(new Date())); } /** * 拋出異常通知 */ @AfterThrowing(value="myPoint()",throwing="ex") public void throwTest(Exception ex){ System.out.println("拋出異常才會執行的通知(1參): " + ex.toString()); } }

 

 

2.在BankServiceImpl上加 @Service("service") 注解,在account成員變量加 @Autowired注解。

在Account上加  @Component @Scope("prototype")。

 

3.在xml文件中加上

   <aop:aspectj-autoproxy/>
    <context:component-scan base-package="com.cnblogs.aop.pojo"/>
    <context:component-scan base-package="com.cnblogs.aop.service.*"/>
    <context:component-scan base-package="com.cnblogs.aop.annotation"/>

 

     <context:component-scan base-package=" " />

表示SpringIOC容器會掃描此包下加了@Component,@Service注解的類,並放入IOC容器。在上上篇隨便springIOC的三種注入方式

有介紹。

 

4.最后一步就是測試了

@Test public void annotation(){ try { // 獲取springIOC容器
            String path = "com/cnblogs/aop/annotation/annotation.xml"; ApplicationContext container = new ClassPathXmlApplicationContext(path); BankService service = (BankService) container.getBean("service"); // service.getAccount().setBalance(10000.0);
 service.save(3000.0); } catch (Exception e) { e.printStackTrace(); } }

 

環繞通知之前置: 19-5-29 下午8:41
前置通知: 19-5-29 下午8:41
存錢: 3000.0
環繞通知之后置: 19-5-29 下午8:41
最終通知: 19-5-29 下午8:41
成功返回才執行的后置通知: 19-5-29 下午8:41

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM