java之代理

Java之代理... 1

一.         概念... 1

二.         jdk的靜態代理... 1

三.         jdk動態代理... 4

四.         cglib 動態代理... 7

五. jdk動態和cglib動態代理比較... 9

六. 面向切面編程... 10

1.幾個應用... 13

2.面向切面的概念... 16

3.使用aspectj實現aop編程... 17

七.總結... 25

 

一.概念

代理是什麼呢?舉個例子,一個公司是賣攝像頭的,但公司不直接跟用戶打交道,而是通過代理商跟用戶打交道。如果:公司接口中有一個賣產品的方法,那麼公司需要實現這個方法,而代理商也必須實現這個方法。如果公司賣多少錢,代理商也賣多少錢,那麼代理商就賺不了錢。所以代理商在調用公司的賣方法後,加上自己的利潤然後再把產品賣給客戶。而客戶部直接跟公司打交道,或者客戶根本不知道公司的存在,然而客戶最終卻買到了產品。

專業點說:代理模式是對象的結構型模式,代碼模式給某一個對象提供代理,並由代理對象控制原對象(目標對象,被代理對象)的引用。簡單點說,就是通過一個工廠生成一個類的代理對象,當客戶端使用的時候不直接使用目標對象,而是直接使用代理對象。

 

二.jdk的靜態代理

Jdk的靜態代理要求,目標對象和代理對象都要實現相同的接口。然後提供給客戶端使用。這個代理對客戶端是可見的,其結果圖如下:

 

下面給出一個例子:

首先建立1個接口:UserService.java定義如下方法:

 

[java] view plaincopy
  1. package com.xie.service;  
  2.   
  3. public interface UserService {  
  4.     public void addUser(String userId,String userName);  
  5.     public void delUser(String userId);  
  6.     public void modfiyUser(String userId,String userName);  
  7.     public String findUser(String userId);  
  8. }  

然後實現這個接口的目標對象:UserServiceImpl.java

package com.xie.serviceimpl;

 

import com.xie.service.UserService;

 

public class UserServiceImpl implements UserService {

 

    @Override

    public void addUser(String userId, String userName) {

       System.out.println("UserServiceImpl addUser userId->>"+userId);

    }

 

    @Override

    public void delUser(String userId) {

       System.out.println("UserServiceImpl delUser userId->>"+userId);

    }

 

    @Override

    public void modfiyUser(String userId, String userName) {

       System.out.println("UserServiceImpl modfiyUser userId->>"+userId);

    }

 

    @Override

    public String findUser(String userId) {

       System.out.println("UserServiceImpl findUser userId->>"+userId);

       return "張山";

    }

 

}

爲目標對象創建代理對象:UserServiceImplProxy.java代理對象持有目標對象的引用。

package com.xie.serviceproxy;

 

import com.xie.service.UserService;

 

public class UserServiceImplProxy implements UserService {

   

    private UserService userService;

   

    public UserServiceImplProxy(UserService userService){

       this.userService = userService;

    }

 

    @Override

    public void addUser(String userId, String userName) {

       try {

           System.out.println("開始執行:addUser");

           userService.addUser(userId, userName);

           System.out.println("addUser執行成功。");

       } catch (Exception e) {

           System.out.println("addUser執行失敗。");

       }

    }

 

    @Override

    public void delUser(String userId) {

    }

 

    @Override

    public void modfiyUser(String userId, String userName) {

    }

 

    @Override

    public String findUser(String userId) {

       return null;

    }

 

}

最後調用代理對象完成功能:Client.java

package com.xie.client;

 

import com.xie.service.UserService;

import com.xie.serviceimpl.UserServiceImpl;

import com.xie.serviceproxy.UserServiceImplProxy;

 

public class Client {

         public static void main(String[] args) {

          UserService userService = new UserServiceImplProxy(new UserServiceImpl());

          userService.addUser("001", "centre");

         }

}

一.jdk動態代理

靜態代理要爲每個目標類創建一個代理類,當需要代理的對象太多,那麼代理類也變得很多。同時代理類違背了可重複代理只寫一次的原則。jdk給我們提供了動態代理。其原理圖如下:


Jdk的動態要求目標對象必須實現接口,因爲它創建代理對象的時候是根據接口創建的。如果不實現接口,jdk無法給目標對象創建代理對象。被代理對象可以可以實現多個接口,創建代理時指定創建某個接口的代理對象就可以調用該接口定義的方法了。

首先定義2個接口:Service接口和UserService接口(上面的接口)

Service.java

package com.xie.service;

 

public interface Service {

      public void sayHello(String name);

      public int addOperter(int num,int num2);

}

然後定義實現這2個接口的目標對象:UserServiceImpl.java

package com.xie.serviceimpl;

 

import com.xie.service.Service;

import com.xie.service.UserService;

 

public class UserServiceImpl implements UserService ,Service{

 

         @Override

         public void addUser(String userId, String userName) {

                   System.out.println("UserServiceImpl addUser userId->>"+userId);

         }

 

         @Override

         public void delUser(String userId) {

                   System.out.println("UserServiceImpl delUser userId->>"+userId);

         }

 

         @Override

         public void modfiyUser(String userId, String userName) {

                   System.out.println("UserServiceImpl modfiyUser userId->>"+userId);

         }

 

         @Override

         public String findUser(String userId) {

                   System.out.println("UserServiceImpl findUser userId->>"+userId);

                   return "張山";

         }

 

         @Override

         public void sayHello(String name) {

                   System.out.println("你好:"+name);

         }

 

         @Override

         public int addOperter(int num, int num2) {

                   return num+num2;

         }

}

提供一個生成代理對象的的類:LogHandler.java

package com.xie.serviceproxy;

 

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

 

public class LogHandler implements InvocationHandler {

        

         private Object targertObject;

         public Object newInstance(Object targertObject){

                   this.targertObject = targertObject;

                   Class targertClass = targertObject.getClass();

                   return Proxy.newProxyInstance(targertClass.getClassLoader(), targertClass.getInterfaces(),this);

         }

 

         @Override

         public Object invoke(Object proxy, Method method, Object[] args)

                            throws Throwable {

                   System.out.println("調用方法"+method.getName());

                   Object ret = null;

                   try {

                            ret = method.invoke(targertObject, args);

                            System.out.print("成功調用方法:"+method.getName()+";參數爲:");

                            for (int i = 0; i < args.length; i++) {

                                     System.out.println(args[i]);

                            }

                   } catch (Exception e) {

                            e.printStackTrace();

                            System.out.print("調用方法:"+method.getName()+"失敗;參數爲:");

                            for (int i = 0; i < args.length; i++) {

                                     System.out.print(args[i]);

                            }

                   }

                   return ret;

         }

}

編寫一個客戶端類來使用工廠類生成目標類的代理:Client.java

package com.xie.client;

 

import com.xie.service.Service;

import com.xie.service.UserService;

import com.xie.serviceimpl.UserServiceImpl;

import com.xie.serviceproxy.LogHandler;

 

public class Client {

         public static void main(String[] args) {

                   Service Service = (Service)new LogHandler().newInstance(new UserServiceImpl());

                   UserService userService = (UserService)new LogHandler().newInstance(new UserServiceImpl());

                   userService.addUser("001", "centre");

                   String name = userService.findUser("002");

                   System.out.println(name);

                   int num = Service.addOperter(12, 23);

                   System.out.println(num);

                   Service.sayHello("centre");

         }

}

一.cglib 動態代理

jdk給目標類提供動態要求目標類必須實現接口,當一個目標類不實現接口時,jdk是無法爲其提供動態代理的。cglib 卻能給這樣的類提供動態代理。Spring在給某個類提供動態代理時會自動在jdk動態代理和cglib動態代理中動態的選擇。

使用cglib爲目標類提供動態代理:需要導入cglib.jar和asm.jar

如果出現asm中的類無法找到的異常,在java工程中是真的缺少asm.jar,而在web工程中很可能是asm.jar和spring提供的org.springframework.asm-3.0.4.RELEASE.jar包衝突。

首先編寫一個目標類:UserServiceImpl.java(上面的類),

然後爲其創建一個代理工廠,用於生成目標類的代理對象:CglibProxy.java

 

注意:如果一個類繼承了某個類,在子類中沒有一個方法,用cglib生成該子類的動態代理類中將沒有一個方法。

 

package com.xie.serviceproxy;

 

import java.lang.reflect.Method;

 

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

 

public class CglibProxy implements MethodInterceptor{

 

         @Override

         public Object intercept(Object obj, Method method, Object[] args,

                            MethodProxy proxy) throws Throwable {

        System.out.println("調用的方法是:" + method.getName());

        Object ret = null;

        try {

            ret = proxy.invokeSuper(obj, args);

                            System.out.print("成功調用方法:"+method.getName()+";參數爲:");

                            for (int i = 0; i < args.length; i++) {

                                     System.out.print(args[i]);

                            }

                   } catch (Exception e) {

                            e.printStackTrace();

                            System.out.print("調用方法:"+method.getName()+"失敗;參數爲:");

                            for (int i = 0; i < args.length; i++) {

                                     System.out.print(args[i]);

                            }

                   }

                   return ret;

         }

}

編寫一個客戶端使用代理工廠生成代理對象:CglibClient.java

package com.xie.client;

 

import net.sf.cglib.proxy.Enhancer;

 

import com.xie.serviceimpl.UserServiceImpl;

import com.xie.serviceproxy.CglibProxy;

 

public class CglibClient {

         public static void main(String[] args) {

                   cglibUse1();

         }

         public static void cglibUse1(){

                   Enhancer enhancer = new Enhancer();

 

                   // 設置被代理的類(目標類)

                   enhancer.setSuperclass(UserServiceImpl.class);

                   //使用回調

                   enhancer.setCallback(new CglibProxy());

 

                   // 創造 代理 (動態擴展了UserServiceImpl類)

                   UserServiceImpl my = (UserServiceImpl) enhancer.create();

 

                   //my.addUser("001", "centre");

                   int ret = my.addOperter(15, 22);

                   System.out.println("返回的結果是:"+ret);

         }

}

五. jdk動態和cglib動態代理比較

Jdk動態代理要求被代理的類要實現接口,而cglib不需要,cglib能根據內存中爲其創建子類(代理對象)。那麼它們的效率是怎麼樣的呢?可以做如下測試:

我們用jdk和cglib動態代理分別爲同一個代理創建10000個代理對象。

代碼1:

    public static void testFlexiable(){

       UserService test = new UserServiceImpl();

       long nums = 1000;

       Date start = new Date();

       for (int i = 0; i < nums; i++) {

           UserService userService = (UserService)new LogHandler().newInstance(test);

       }

       Date end = new Date();

       System.out.println("創建"+nums+"個代理代理對象用時:"+(end.getTime()-start.getTime())+"毫秒。");

      

    }

測試結果:

創建1000個代理代理對象用時:32毫秒。

創建1000個代理代理對象用時:31毫秒。

創建1000個代理代理對象用時:31毫秒。

創建1000個代理代理對象用時:31毫秒。

創建10000個代理代理對象用時:94毫秒。

創建10000個代理代理對象用時:78毫秒。

創建10000個代理代理對象用時:78毫秒。

創建10000個代理代理對象用時:78毫秒。

代碼2:

    public static void testFlexiable(){

       Enhancer enhancer = new Enhancer();

 

       // 設置被代理的類(目標類)

       enhancer.setSuperclass(UserServiceImpl.class);

       //使用回調

       enhancer.setCallback(new CglibProxy());

       long nums = 1000;

       Date start = new Date();

       for (int i = 0; i < nums; i++) {

           UserServiceImpl my = (UserServiceImpl) enhancer.create();

       }

       Date end = new Date();

       System.out.println("創建"+nums+"個代理代理對象用時:"+(end.getTime()-start.getTime())+"毫秒。");

    }

測試結果:

創建1000個代理代理對象用時:47毫秒。

創建1000個代理代理對象用時:62毫秒。

創建1000個代理代理對象用時:62毫秒。

創建1000個代理代理對象用時:47毫秒。

創建1000個代理代理對象用時:47毫秒。

創建1000個代理代理對象用時:47毫秒。

創建10000個代理對象會拋異常,cglib運行速度明顯比jdk動態代理慢,由於是通過類創建的子類,比jdk通過接口創建代理更耗內存。因此在s2sh框架中,spring通過爲類提供代理採用jdk比cglib應該要好一些吧。

 

六. 面向切面編程

面向切面編程是繼面向對象後,又一種重要的思維方式。面向對象比較重要的是通過繼承實現代碼重用。而面向切面編程,則注重縱向編程,他能將2個不同的功能分開,實現最大程度的解耦,比如我們現在有業務邏輯層和日誌層,如果不分開,那麼在每個業務邏輯方法中除了要實現業務外還要加上日誌代碼,如果某一天我不需要日誌了,而有很多這樣的類的,很多方法都加上日誌代碼,那改動的工作量是可想而知的。有沒有一種方法讓我們只寫一次日誌代碼,而把他應用在需要寫日誌的方法前面,當不需要的時候直接刪除呢?面向切面編程給我們提供了辦法。

Struts2的攔截器就是採用這種思想編寫的。下面模擬實現一個攔截器,設計圖如下:

 

首先定義一個攔截器接口:Interceptor.java

package com.xie.interceptor;

 

public interface Interceptor {

       public void intercept(ActionInvocation invocation);

}

然後實現攔截器接口,實現3個攔截器:

FirstInterceptor.java,SecondInterceptor.java,ThirdInterceptor.java

package com.xie.interceptor;

 

public class FirstInterceptor implements Interceptor {

 

    @Override

    public void intercept(ActionInvocation invocation) {

        System.out.println(1);

        invocation.invoke();

        System.out.println(-1);

    }

}

 

package com.xie.interceptor;

 

public class SecInterceptor implements Interceptor {

 

    @Override

    public void intercept(ActionInvocation invocation) {

        System.out.println(2);

        invocation.invoke();

        System.out.println(-2);

    }

 

}

 

package com.xie.interceptor;

 

public class ThirInterceptor implements Interceptor{

 

    @Override

    public void intercept(ActionInvocation invocation) {

       System.out.println(3);

       invocation.invoke();

       System.out.println(-3);

    }    

}

接下來定義一個InvokeInterceptory.java

package com.xie.interceptor;

 

import java.util.ArrayList;

import java.util.List;

 

 

public class ActionInvocation {

         List<Interceptor> interceptors=new ArrayList<Interceptor>();

         int index=-1;

         Action action=new Action();

        

         public ActionInvocation(){

                   this.interceptors.add(new FirstInterceptor());

                   this.interceptors.add(new SecInterceptor());

                   this.interceptors.add(new ThirInterceptor());

         }

    public void invoke(){

             if (index+1>=this.interceptors.size()) {

                            action.execute();

                   }else {

                            index++;

                            this.interceptors.get(index).intercept(this);

                   }

    }

}

 

定義一個action:Action.java

package com.xie.interceptor;

 

public class Action {

     public void execute(){

     System.out.println("execute action.");

     }

}

 

最後定義Main類來調用action的execute方法:

package com.xie.interceptor;

 

public class Main {

    public static void main(String[] args) {

       new ActionInvocation().invoke();

    }

}

運行結果如下:

1

2

3

execute action.

-3

-2

-1

 

在javaEE中,像Filter(過濾器),Intercepetor(攔截器),spring的事務管理都採用了面向切面的編程思想。

1.幾個應用

寫一個過濾器:這個過濾器的功能是實現web頁面訪問權限:

package com.ibeifeng.filter;

 

import java.io.IOException;

 

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

 

public class LoginFilter implements Filter {

 

         public void destroy() {

                  

         }

 

         public void doFilter(ServletRequest req, ServletResponse res,

                            FilterChain chain) throws IOException, ServletException {

                   HttpServletRequest request = (HttpServletRequest) req;

                   HttpSession session = request.getSession();

                   String username = (String) session.getAttribute("username");

                   HttpServletResponse response = (HttpServletResponse) res;

                   String uri = request.getRequestURI();

                   //如果用戶請求了index.html,這時就必須做登錄判斷,判斷用戶是否登錄。

                   if("/Pfms/index.html".equals(uri)) {

                            if(username == null || "".equals(username)) {

                                     response.sendRedirect("login.html");

                            } else {

                                     chain.doFilter(request, response);

                            }

                   }else {

                            chain.doFilter(request, response);

                   }

         }

 

         public void init(FilterConfig filterConfig) throws ServletException {

                  

         }

        

}

再寫一個struts2的攔截器:

package interceptor;

 

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.interceptor.*;

import com.opensymphony.xwork2.*;

import java.util.*;

 

public class AuthorizationInterceptor extends AbstractInterceptor

{

         private String ignoreActions;

 

         // ignoreActions屬性的getter方法

         public String getIgnoreActios()

         {

                   return ignoreActions;

         }

 

         // ignoreActions屬性的setter方法

         public void setIgnoreActions(String ignoreActions)

         {

                   this.ignoreActions = ignoreActions;

         }

 

         @Override

         public String intercept(ActionInvocation invocation) throws Exception

         {

                   ActionContext ctx = invocation.getInvocationContext();

                  

                   Map session = ctx.getSession();

                   String user = (String) session.get("username");

        

                   boolean ignore = false;

                   String currentAction = invocation.getProxy().getActionName();

                   String[] actions = ignoreActions.split(",");

 

                   for (String action : actions)

                   {

                            if (currentAction.matches(action.trim()))

                            {

                                     ignore = true;

                                     break;

                            }

                   }

 

                   if (user != null || ignore == true)

                   {

 

                            return invocation.invoke();

                   }

                   else

                   {

                            return Action.LOGIN;

                   }

 

         }

}

 

看看spring是如何配置事物的(xml):

    <!-- 配置事務管理器 -->

    <bean id="txManager"

       class="org.springframework.orm.hibernate3.HibernateTransactionManager">

       <property name="sessionFactory">

           <ref local="sessionFactory" />

       </property>

       <property name="nestedTransactionAllowed" value="true" />

    </bean>

 

    <!-- 配置事務的傳播特性 -->

    <tx:advice id="txAdvice" transaction-manager="txManager">

       <tx:attributes>

           <!-- 在開發的時候可以這樣定義,但部署的時候一定要詳細定義 -->

           <tx:method name="*" propagation="REQUIRED" />

           <tx:method name="get*" read-only="true" />

           <tx:method name="load*" read-only="true" />

           <tx:method name="find*" read-only="true" />

           <tx:method name="query*" read-only="true" />

           <tx:method name="pagedQuery*" read-only="true" />

           <tx:method name="*" />

       </tx:attributes>

    </tx:advice>

 

    <!-- 以AspectJ方式 定義 AOP -->

 

    <aop:config proxy-target-class="true" expose-proxy="true">

       <aop:advisor pointcut="execution(public * com.hikvision.webclient8100.manager.*.*(..))"

           advice-ref="txAdvice" />

    </aop:config>

2.面向切面的概念

1)aspect(切面):實現了cross-cutting功能,是針對切面的模塊。最常見的是logging模塊,這樣,程序按功能被分爲好幾層,如果按傳統的繼承的話,商業模型繼承日誌模塊的話根本沒有什麼意義,而通過創建一個logging切面就可以使用AOP來實現相同的功能了。

將橫切多個業務對象的程序對了出來,模塊化,該模塊可以無侵入式的集成到業務對象中,如:事務,日誌,權限等。

2)jointpoint(連接點):連接點是切面插入應用程序的地方,該點能被方法調用,而且也會被拋出意外。連接點是應用程序提供給切面插入的地方,可以添加新的方法。比如以上我們的切點可以認爲是findInfo(String)方法。

通知執行的時機,如方法調用,拋出異常時。

3)advice(處理邏輯):advice是我們切面功能的實現,它通知程序新的行爲。如在logging裏,logging advice包括logging的實現代碼,比如像寫日誌到一個文件中。advice在jointpoint處插入到應用程序中。以上我們在MyHandler.java中實現了advice的功能 。

Advice(通知):指切面的具體實現,如記錄日誌,驗證權限。通知有各種類型,其中包括“before”,“after”,“around”和“throw”等通知

4)pointcut(切入點):pointcut可以控制你把哪些advice應用於jointpoint上去,通常你使用pointcuts通過正則表達式來把明顯的名字和模式進行匹配應用。決定了那個jointpoint會獲得通知。

切入點:是感興趣的連接點。通知和一個切入點表達式關聯,並在滿足這個切入點上運行,(如:執行某個特定名稱的方法時。)切入點表達式如何和連接點匹配時AOP的核心:spring缺省使用AspectJ切入點語法。

  5)introduction:允許添加新的方法和屬性到類中。

6)target(目標類):是指那些將使用advice的類,一般是指獨立的那些商務模型。比如以上的StudentInfoServiceImpl.

是一個被代理對象,被通知對象,被一個或者多個切面所通知的對象。

7)proxy(代理類):使用了proxy的模式。是指應用了advice的對象,看起來和target對象很相似。

AOP代理的對象,用來實現切面的功能,在spring中,AOP代理可以使用jdk動態代理和cglib動態代理。

8)weaving(插入):是指應用aspects到一個target對象創建proxy對象的過程:complie time,classload time,runtime。把切面連接到其它應用程序類型或者對象上,並創建一個被通知的對象,在運行時完成織入。

3.使用aspectj實現aop編程

使用aspectj編寫系統日誌。

Aspectj是一個使用面向切面,底層採用動態代理的框架。AspectJ是一個面向切面的框架,它擴展了Java語言。AspectJ定義了AOP語法所以它有一個專門的編譯器用來生成遵守Java字節編碼規範的Class文件。spring的AOP實現使用了Aspectj框架。

1.       採用annotation

a)首先開啓aspectj支持,如下:

 

<?xml version="1.0" encoding="UTF-8"?>

 

<beans default-lazy-init="false"

    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"xmlns:tx="http://www.springframework.org/schema/tx"

    xsi:schemaLocation="

           http://www.springframework.org/schema/beans

           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

           http://www.springframework.org/schema/context

           http://www.springframework.org/schema/context/spring-context-3.0.xsd

           http://www.springframework.org/schema/tx

           http://www.springframework.org/schema/tx/spring-tx-3.0.xsd

           http://www.springframework.org/schema/aop

           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 </beans>

 

如果採用dtd:

 

b)定義切面對象

@Aspect

c)申明切入點

任何public方法的執行:

execution(public * *(..))

任何以set開頭的方法:

execution(* set*(..))

任何在接口AccountService的方法:

execution(* com.xyz.service.AccountService.*(..))

任何在service包的方法:

execution(* com.xyz.service.*.*(..))

任何在service包和其子包的方法:

execution(* com.xyz.service..*.*(..))

 

any join point (執行方法必須在Spring Aop方式下) within the service package:

within(com.xyz.service.*)

any join point (執行方法必須在Spring Aop方式下) within the service package or a sub-package:

within(com.xyz.service..*)

any join point (執行方法必須在Spring Aop方式下) where the proxy implements the AccountService interface:

 

this(com.xyz.service.AccountService)

 

any join point (method execution only in Spring AOP) where the target object implements the AccountService interface:

 

target(com.xyz.service.AccountService)

 

'target' is more commonly used in a binding form :- see the following section on advice for how to make the target object available in the advice body.

 

 

any join point (method execution only in Spring AOP) which takes a single parameter, and where the argument passed at runtime is Serializable:

 

args(java.io.Serializable)

'args' is more commonly used in a binding form :- see the following section on advice for how to make the method arguments available in the advice body.

any join point (method execution only in Spring AOP) where the target object has an @Transactional annotation:

 

@target(org.springframework.transaction.annotation.Transactional)

 

'@target' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

 

 

any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional annotation:

 

@within(org.springframework.transaction.annotation.Transactional)

 

'@within' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

 

 

any join point (method execution only in Spring AOP) where the executing method has an @Transactional annotation:

 

@annotation(org.springframework.transaction.annotation.Transactional)

 

'@annotation' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

 

 

any join point (method execution only in Spring AOP) which takes a single parameter, and where the runtime type of the argument passed has the @Classified annotation:

 

@args(com.xyz.security.Classified)

 

'@args' can also be used in a binding form :- see the following section on advice for how to make the annotation object(s) available in the advice body.

 

 

any join point (method execution only in Spring AOP) on a Spring bean named 'tradeService':

 

bean(tradeService)

any join point (method execution only in Spring AOP) on Spring beans having names that match the wildcard expression '*Service':

 

bean(*Service)

d)切入點表達式:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?

name-pattern(param-pattern) throws-pattern?)

--modifiers-pattern 訪問修飾符,public,private等

-- ret-type-pattern 返回類型,void,String等

-- declaring-type-pattern 申明類型

-- name-pattern 方法名稱

-- param-pattern 參數名稱

-- throws-pattern 異常名稱

 

編寫一個接口:ServiceAspectjAnnotation.java

package com.xie.aspectj.test;

 

import java.util.List;

 

import com.hikvision.webclient8100.domain.Depart;

 

public interface ServiceAspectjAnnotation {

    public boolean delete(List<Depart> list);

    public boolean change(List<Depart> list);

    public List<Depart> queryAll();

    public Depart queryByName(String pname);

    public Depart queryBySequenceNo(String sequenceNo);

    public String pageDivide(int index, int pageSize);

    public String findParentOrgsName() throws Exception;

    boolean save(List<Depart> list);

}

實現該接口:ServiceAspectjAnnotationImpl.java

package com.xie.aspectj.test;

 

import java.util.List;

 

import org.springframework.stereotype.Component;

 

import com.hikvision.webclient8100.domain.Depart;

@Component(value="serviceAspectjAnnotationImpl")

public class ServiceAspectjAnnotationImpl //implements ServiceAspectjAnnotation

{

 

 

    public boolean save(List<Depart> list) {

       System.out.println("保存方法執行成功。");

       return false;

    }

 

 

    public boolean delete(List<Depart> list) {

       System.out.println("刪除方法執行成功。");

       return false;

    }

 

 

    public boolean change(List<Depart> list) {

       System.out.println("修改方法執行成功。");

       return false;

    }

 

 

    public List<Depart> queryAll() {

       System.out.println("查詢成功。");

       return null;

    }

 

 

    public Depart queryByName(String pname) {

       System.out.println("通過名字查詢方法執行成功。");

       return null;

    }

 

 

    public Depart queryBySequenceNo(String sequenceNo) {

       System.out.println("通過序列號查詢成功。");

       return null;

    }

 

 

    public String pageDivide(int index, int pageSize) {

       System.out.println("分頁方法成功返回。");

       return null;

    }

 

    public String findParentOrgsName() throws Exception {

       System.out.println("查找上級組織名稱成功。");

           throw new Exception();

 

    }

 

}

建立一個日誌類:LogHandler.java

package com.hikvision.webclient8100.util;

 

import org.apache.log4j.Logger;

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.springframework.stereotype.Component;

 

@Component(value = "logHandler")

@Aspect

// 切面

public class LogHandler {

    private static Logger logger = Logger.getLogger(LogHandler.class);

 

    @Before// 通知

    // * com.hikvision.webclient8100.manager.*.*(..))切點

    ("execution(* com.hikvision.webclient8100.manager.*.*(..))")

    // 連接點,連接點是切面插入應用程序的地方

    public void manager() {// 通知,面向切面加入的業務邏輯

       logger.info("manager層的某個方法開始執行");

    }

 

    @After("execution(* com.xie.aspectj.test.*.*(..))")

    public void dao() {

       logger.info("dao層的某個方法執行完成");

    }

 

    @Around("execution(* com.xie.aspectj.test.ServiceAspectjAnnotationImpl.change(..))")

    public void arund(ProceedingJoinPoint pjp) throws Throwable {

       logger.info("方法開始執行....................");

       pjp.proceed();

       logger.info("方法執行完成。");

    }

 

    @AfterThrowing(pointcut = "execution(* com.xie.aspectj.test.*.*(..))", throwing = "ex")

    public void doRecovery() {

       logger.error("出現異常。");

    }

 

    @AfterReturning("execution(* com.xie.aspectj.test.ServiceAspectjAnnotationImpl.delete(..))")

    public void reOpertion() {

       logger.info("操作完成");

    }

}

編寫測試方法:ServiceAspectjAnnotationImplTest.java

package com.xie.aspectj.test;

 

 

import java.util.ArrayList;

import java.util.List;

 

import org.junit.AfterClass;

import org.junit.BeforeClass;

import org.junit.Test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

import com.hikvision.webclient8100.dao.DepartDao;

import com.hikvision.webclient8100.domain.Depart;

 

public class ServiceAspectjAnnotationImplTest {

        

         private static ClassPathXmlApplicationContext atc = new

ClassPathXmlApplicationContext("applicationContext.xml");

 

         @BeforeClass

         public static void setUpBeforeClass() throws Exception {

         }

 

         @AfterClass

         public static void tearDownAfterClass() throws Exception {

         }

         @Test

         public void testSave(){

                   ServiceAspectjAnnotation serviceAspectjAnnotation = (ServiceAspectjAnnotation) atc.getBean("serviceAspectjAnnotationImpl");

                   System.out.println(serviceAspectjAnnotation.save(new ArrayList<Depart>()));

         }

         @Test

         public void testSave1(){

                   ServiceAspectjAnnotationImpl serviceAspectjAnnotation =

(ServiceAspectjAnnotationImpl) atc.getBean("serviceAspectjAnnotationImpl");

                   System.out.println(serviceAspectjAnnotation.save(new ArrayList<Depart>()));

         }

        

         @Test

         public void testDelete(){

                   ServiceAspectjAnnotationImpl serviceAspectjAnnotation =

(ServiceAspectjAnnotationImpl) atc.getBean("serviceAspectjAnnotationImpl");

                   System.out.println(serviceAspectjAnnotation.delete(new ArrayList<Depart>()));

         }

        

         @Test

         public void testChange(){

                   ServiceAspectjAnnotationImpl serviceAspectjAnnotation =

(ServiceAspectjAnnotationImpl) atc.getBean("serviceAspectjAnnotationImpl");

                   System.out.println(serviceAspectjAnnotation.change(new ArrayList<Depart>()));

         }

        

         @Test

         public void testFind() throws Exception{

                   ServiceAspectjAnnotationImpl serviceAspectjAnnotation =

(ServiceAspectjAnnotationImpl) atc.getBean("serviceAspectjAnnotationImpl");

                   serviceAspectjAnnotation.findParentOrgsName();

         }

}

 

2.使用xml:

    <!-- <bean id="logHandler1" class="com.hikvision.webclient8100.util.LogHandler1"></bean> -->

<!—採用了spring的註解生成bean-->

    <aop:config><!-- spring 的配置 -->

       <aop:aspect ref="logHandler1"><!-- 定義切面 -->

           <aop:pointcut id="manager1"

              expression="execution(* com.hikvision.webclient8100.manager.*.*(..))" /><!-- 定義切點 -->

           <aop:before pointcut-ref="manager1" method="reOpertion" /><!-- 通知執行時機,這是我們要在主業務邏輯出要加入的代碼 -->

       </aop:aspect>

       <aop:aspect ref="logHandler1">

           <aop:pointcut id="test"

              expression="execution(* com.xie.aspectj.test.*.*(..))" />

           <aop:after pointcut-ref="test" method="dao" />

       </aop:aspect>

       <aop:aspect ref="logHandler1">

           <aop:pointcut id="around"

              expression="execution(* com.xie.aspectj.test.ServiceAspectjAnnotationImpl.change(..))" />

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

       </aop:aspect>

       <aop:aspect ref="logHandler1">

           <aop:pointcut id="throw"

              expression="execution(* com.xie.aspectj.test.*.*(..))" />

           <aop:after-throwing pointcut-ref="throw" method="doRecovery"

              throwing="ex" />

       </aop:aspect>

       <aop:aspect ref="logHandler1">

           <aop:pointcut id="afterreturn"

              expression="execution(* com.xie.aspectj.test.ServiceAspectjAnnotationImpl.delete(..))" />

           <aop:after-returning pointcut-ref="afterreturn"

              method="reOpertion" />

       </aop:aspect>

    </aop:config>

 

七.總結

面向切面(方面)編程:實際上就是在不修改原來代碼的前提下爲原來的業務增加新的邏輯代碼。它可以方便的添加和刪除,讓代碼維護起來更加方便。Aspectj是一個AOP框架,底層實現採用動態代理。Spring的AOP採用aspectj,動態代理採用jdk和cglib中切換。

發佈了29 篇原創文章 · 獲贊 16 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章