靜態代理、動態代理和cglib代理

轉:https://www.cnblogs.com/cenyu/p/6289209.htmlhtml

代理(Proxy)是一種設計模式,提供了對目標對象另外的訪問方式;即經過代理對象訪問目標對象.這樣作的好處是:能夠在目標對象實現的基礎上,加強額外的功能操做,即擴展目標對象的功能.
這裏使用到編程中的一個思想:不要隨意去修改別人已經寫好的代碼或者方法,若是需改修改,能夠經過代理的方式來擴展該方法java

代理模式的關鍵點是:代理對象與目標對象.代理對象是對目標對象的擴展,並會調用目標對象編程

一、靜態代理設計模式

靜態代理在使用時,須要定義接口或者父類,被代理對象與代理對象一塊兒實現相同的接口或者是繼承相同父類.框架

代碼示例:ide

接口:IUserDao.java函數

1 /**
2  * 接口
3  */
4 public interface IUserDao {
5 
6     void save();
7 }

目標對象:UserDao.java工具

1 /**
2  * 接口實現
3  * 目標對象
4  */
5 public class UserDao implements IUserDao {
6     public void save() {
7         System.out.println("----已經保存數據!----");
8     }
9 }

代理對象:UserDaoProxy.java性能

 1 /**
 2  * 代理對象,靜態代理
 3  */
 4 public class UserDaoProxy implements IUserDao{
 5     //接收保存目標對象
 6     private IUserDao target;
 7     public UserDaoProxy(IUserDao target){
 8         this.target=target;
 9     }
10 
11     public void save() {
12         System.out.println("開始事務...");
13         target.save();//執行目標對象的方法
14         System.out.println("提交事務...");
15     }
16 }

測試類:App.java測試

 1 /**
 2  * 測試類
 3  */
 4 public class App {
 5     public static void main(String[] args) {
 6         //目標對象
 7         UserDao target = new UserDao();
 8 
 9         //代理對象,把目標對象傳給代理對象,創建代理關係
10         UserDaoProxy proxy = new UserDaoProxy(target);
11 
12         proxy.save();//執行的是代理的方法
13     }
14 }

靜態代理總結:
1.能夠作到在不修改目標對象的功能前提下,對目標功能擴展.
2.缺點:

  • 由於代理對象須要與目標對象實現同樣的接口,因此會有不少代理類,類太多.同時,一旦接口增長方法,目標對象與代理對象都要維護.

如何解決靜態代理中的缺點呢?答案是能夠使用動態代理方式

2.1:動態代理(JDK版)(第一種寫法)

動態代理有如下特色:
1.代理對象,不須要實現接口
2.代理對象的生成,是利用JDK的API,動態的在內存中構建代理對象(須要咱們指定建立代理對象/目標對象實現的接口的類型)
3.動態代理也叫作:JDK代理,接口代理

JDK中生成代理對象的API
代理類所在包:java.lang.reflect.Proxy
JDK實現代理只須要使用newProxyInstance方法,可是該方法須要接收三個參數,完整的寫法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

注意該方法是在Proxy類中是靜態方法,且接收的三個參數依次爲:

ClassLoader loader:指定當前目標對象使用類加載器,獲取加載器的方法是固定的

Class<?>[] interfaces:目標對象實現的接口的類型,使用泛型方式確認類型

InvocationHandler h:事件處理,執行目標對象的方法時,會觸發事件處理器的方法,會把當前執行目標對象的方法做爲參數傳入

代碼示例:
接口類IUserDao.java以及接口實現類,目標對象UserDao是同樣的,沒有作修改.在這個基礎上,增長一個代理工廠類(ProxyFactory.java),

將代理類寫在這個地方,而後在測試類(須要使用到代理的代碼)中先創建目標對象和代理對象的聯繫,而後代用代理對象的中同名方法

代理工廠類:ProxyFactory.java

 1 /**
 2  * 建立動態代理對象
 3  * 動態代理不須要實現接口,可是須要指定接口類型
 4  */
 5 public class ProxyFactory{
 6 
 7     //維護一個目標對象
 8     private Object target;
 9     public ProxyFactory(Object target){
10         this.target=target;
11     }
12 
13    //給目標對象生成代理對象
14     public Object getProxyInstance(){
15         return Proxy.newProxyInstance(
16                 target.getClass().getClassLoader(),
17                 target.getClass().getInterfaces(),
18                 new InvocationHandler() {
19                     @Override
20                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
21                         System.out.println("開始事務2");
22                         //執行目標對象方法
23                         Object returnValue = method.invoke(target, args);
24                         System.out.println("提交事務2");
25                         return returnValue;
26                     }
27                 }
28         );
29     }
30 
31 }

測試類:App.java

 1 /**
 2  * 測試類
 3  */
 4 public class App {
 5     public static void main(String[] args) {
 6         // 目標對象
 7         IUserDao target = new UserDao();
 8         // 【原始的類型 class cn.itcast.b_dynamic.UserDao】
 9         System.out.println(target.getClass());
10 
11         // 給目標對象,建立代理對象
12         IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
13         // class $Proxy0   內存中動態生成的代理對象
14         System.out.println(proxy.getClass());
15 
16         // 執行方法   【代理對象】
17         proxy.save();
18     }
19 }

總結:
代理對象不須要實現接口,可是目標對象必定要實現接口,不然不能用動態代理

2.2:動態代理(JDK版)(第二種寫法)

 1 public class MyProxy {
 2     public interface IHello{
 3         void sayHello();
 4     }
 5     static class Hello implements IHello{
 6         public void sayHello() {
 7             System.out.println("Hello world!!");
 8         }
 9     }
10     //自定義InvocationHandler
11     static  class HWInvocationHandler implements InvocationHandler{
12         //目標對象
13         private Object target;
14         public HWInvocationHandler(Object target){
15             this.target = target;
16         }
17         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
18             System.out.println("------插入前置通知代碼-------------");
19             //執行相應的目標方法
20             Object rs = method.invoke(target,args);
21             System.out.println("------插入後置處理代碼-------------");
22             return rs;
23         }
24     }
25    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
26        //生成$Proxy0的class文件
27        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
28        IHello  ihello = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(),  //加載接口的類加載器
29                new Class[]{IHello.class},      //一組接口
30                new HWInvocationHandler(new Hello())); //自定義的InvocationHandler
31        ihello.sayHello();
32    }
33 }

3.1Cglib代理(第一種寫法)

上面的靜態代理和動態代理模式都是要求目標對象是實現一個接口的目標對象,可是有時候目標對象只是一個單獨的對象,

並無實現任何的接口,這個時候就能夠使用以目標對象子類的方式類實現代理,這種方法就叫作:Cglib代理

Cglib代理,也叫做子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展.

  • JDK的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個接口,若是想代理沒有實現接口的類,就能夠使用Cglib實現.
  • Cglib是一個強大的高性能的代碼生成包,它能夠在運行期擴展java類與實現java接口.它普遍的被許多AOP的框架使用,例如Spring AOP和synaop,爲他們提供方法的interception(攔截)
  • Cglib包的底層是經過使用一個小而塊的字節碼處理框架ASM來轉換字節碼並生成新的類.不鼓勵直接使用ASM,由於它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉.

Cglib子類代理實現方法:
1.須要引入cglib的jar文件,可是Spring的核心包中已經包括了Cglib功能,因此直接引入pring-core-3.2.5.jar便可.
2.引入功能包後,就能夠在內存中動態構建子類
3.代理的類不能爲final,不然報錯
4.目標對象的方法若是爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法.

代碼示例:
目標對象類:UserDao.java

1 /**
2  * 目標對象,沒有實現任何接口
3  */
4 public class UserDao {
5 
6     public void save() {
7         System.out.println("----已經保存數據!----");
8     }
9 }

Cglib代理工廠:ProxyFactory.java

 1 /**
 2  * Cglib子類代理工廠
 3  * 對UserDao在內存中動態構建一個子類對象
 4  */
 5 public class ProxyFactory implements MethodInterceptor{
 6     //維護目標對象
 7     private Object target;
 8 
 9     public ProxyFactory(Object target) {
10         this.target = target;
11     }
12 
13     //給目標對象建立一個代理對象
14     public Object getProxyInstance(){
15         //1.工具類
16         Enhancer en = new Enhancer();
17         //2.設置父類
18         en.setSuperclass(target.getClass());
19         //3.設置回調函數
20         en.setCallback(this);
21         //4.建立子類(代理對象)
22         return en.create();
23 
24     }
25 
26     @Override
27     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
28         System.out.println("開始事務...");
29 
30         //執行目標對象的方法
31         Object returnValue = method.invoke(target, args);
32 
33         System.out.println("提交事務...");
34 
35         return returnValue;
36     }
37 }

測試類:

 1 /**
 2  * 測試類
 3  */
 4 public class App {
 5 
 6     @Test
 7     public void test(){
 8         //目標對象
 9         UserDao target = new UserDao();
10 
11         //代理對象
12         UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
13 
14         //執行代理對象的方法
15         proxy.save();
16     }
17 }

在Spring的AOP編程中:
若是加入容器的目標對象有實現接口,用JDK代理
若是目標對象沒有實現接口,用Cglib代理

3.2Cglib代理(第二種寫法)

被代理類:HelloServiceImpl.java

1 public class HelloServiceImpl {
2     
3     public void sayHello(){
4         System.out.println("this is Hello");
5     }
6 }

實現MethodInterceptor接口生成方法攔截器:HelloMethodInterceptor.java

 1 public class HelloMethodInterceptor implements MethodInterceptor{
 2 
 3     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
 4         System.out.println("Before:" + method.getName());
 5         Object object = methodProxy.invokeSuper(o, objects);
 6         System.out.println("After:" + method.getName());
 7         
 8         return object;
 9     }
10 
11 }

測試類:

1 public static void main(String[] args) {
2 
3     Enhancer enhancer = new Enhancer();
4         enhancer.setSuperclass(HelloServiceImpl.class);
5         enhancer.setCallback(new HelloMethodInterceptor());
6         HelloServiceImpl helloServiceImpl = (HelloServiceImpl)enhancer.create();
7         helloServiceImpl.sayHello();
8 }

 

另:JDK動態代理和cglib代理(JDK動態代理機制實現:InvocationHandler;cglib代理要實現:MethodInterceptor接口)和SpringMVC攔截器(抽象接口HandlerInterceptor)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

指定當前目標對象使用類加載器,獲取加載器的方法是固定的

相關文章
相關標籤/搜索