[原創]JAVA的動態代理機制及Spring的實現方式


JAVA 代理實現

代理的實現分動態代理和靜態代理,靜態代理的實現是對已經生成了的JAVA類進行封裝。

動態代理則是在運行時生成了相關代理累,在JAVA中生成動態代理一般有兩種方式。

JDK自帶實現方法

JDK實現代理生成,是用類 java.lang.reflect.Proxy, 實現方式如下

EX:

public class JDKProxy {

      public static Object getPoxyObject(final Object c) {

            return Proxy.newProxyInstance(c.getClass().getClassLoader(), c.getClass().getInterfaces(),// JDK實現動態代理,但JDK實現必須需要接口

                        new InvocationHandler() {

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

                                    // TODO Auto-generated method stub

                                    Object reObj = null;

                                    System.out.print("you say: ");

                                    reObj = method.invoke(c, args);

                                    System.out.println(" [" + Calendar.getInstance().get(Calendar.HOUR) + ":"

                                                + Calendar.getInstance().get(Calendar.MINUTE) + " "

                                                + Calendar.getInstance().get(Calendar.SECOND) + "]");

                                    return reObj;

                              }

                        });

      }

}

測試代理類方法

public class TestForPoxy {

      public static void main(String[] args) {

            ServiceTest service = new ServiceTestImpl();

            System.out.println(service.getClass().getSimpleName());

            ServiceTest poxyService = (ServiceTest) JDKProxy.getPoxyObject(service);

            System.out.println(poxyService.getClass().getSuperclass());

            poxyService.saySomething("hello,My QQ code is 107966750.");

            poxyService.saySomething("what 's your name?");

            poxyService.saySomething("only for test,hehe.");

      }

}

1, Proxy實現代理的目標類必須有實現接口

2, 生成出來的代理類為接口實現類,和目標類不能進行轉換,只能轉為接口實現類進行調用

明顯特點:通過此方法生成出來的類名叫做 $Proxy0

用CGLIB包實現

CGLIB是一個開源項目,官方網址是:http://cglib.sourceforge.net/,可以去上面下載最新JAR包,

本項目用的是cglib-3.0.jar

本項目還加入了依賴JAR包asm-4.0.jar,asm-util-4.0.jar

實現方式如下

EX:

public class CGLIBProxy {

      public static Object getPoxyObject(Object c) {

            Enhancer enhancer = new Enhancer();

            enhancer.setSuperclass(c.getClass());

            enhancer.setCallback(new MethodInterceptor() {

                  public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {

                        System.out.print("you say: ");

                        proxy.invokeSuper(arg0, arg2);

                        System.out.println(" [" + Calendar.getInstance().get(Calendar.HOUR) + ":"

                                    + Calendar.getInstance().get(Calendar.MINUTE) + " " + Calendar.getInstance().get(Calendar.SECOND)

                                    + "]");

                        return null;

                  }

            });

            return enhancer.create();

      }

}

測試代理類方法

public class TestForPoxy {

      public static void main(String[] args) {

            ServiceTest service = new ServiceTestImpl();

            System.out.println(service.getClass().getSimpleName());

//          ServiceTest poxyService = (ServiceTest) JDKProxy.getPoxyObject(service);

            ServiceTest poxyService = (ServiceTest) CGLIBProxy.getPoxyObject(service);

            System.out.println(poxyService.getClass().getSuperclass());

            poxyService.saySomething("hello,My QQ code is 107966750.");

            poxyService.saySomething("what 's your name?");

            poxyService.saySomething("only for test,hehe.");

      }

}

 

1, CGLIB實現方式是對代理的目標類進行繼承

2, 生成出了的代理類可以沒方法,生成出來的類可以直接轉換成目標類或目標類實現接口的實現類,因JAVA向上轉換

明顯特點:通過輸出看出,看出生成出的代理類的parent類為代理的目標類

 

Spring  AOP的代理類機制分析

 

在spring中,bean都是由動態代理生成出來的,那么到底是用JDK的Proxy類實現呢,還是用CGLIB方式實現呢。

AOP  Spring需要的依賴JAR包有:

spring-asm-3.2.0.M1.jar

spring-beans-3.2.0.M1.jar

spring-context-3.2.0.M1.jar

spring-core-3.2.0.M1.jar

spring-expression-3.2.0.M1.jar

spring-aop-3.2.0.M1.jar

spring-aspects-3.2.0.M1.jar

commons\commons-logging-1.1.1\commons-logging-1.1.1.jar

aopalliance\aopalliance.jar

lib\aspectjweaver.jar

 

實現AOP

 

先簡單的實現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:aop="http://www.springframework.org/schema/aop"

      xsi:schemaLocation="

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

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

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

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

      <bean id="test" class="org.ben.spring.service.Test" />

      <bean id="aspectBean" class="org.ben.spring.TestAspect" />

      <!-- 對Test類進行AOP攔截 -->

      <aop:config>

            <aop:aspect id="TestAspect" ref="aspectBean">

                  <!--配置切面-->

                  <aop:pointcut id="businessService"

                        expression="execution(* org.ben.spring.service.Test.say(..))" />

                  <aop:before pointcut-ref="businessService" method="doBefore" />

                  <aop:after pointcut-ref="businessService" method="doAfter" />

            </aop:aspect>

      </aop:config>

</beans>

 

 

然后進行運行結果如下,表示AOP攔截成功

AOP測試類

public class TestBeans {

      public static void main(String[] args) {

            ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml");

            Test test=(Test) ctx.getBean("test");

            System.out.println(test.getClass().getSimpleName());

            test.say();

      }

}

輸出:

do something in befor

welcome for test

do something in after

打印代理類的生成方式

 

第一種情況, Test不實現任何接口,代碼如下

public class Test {

      public void say() {

            System.out.println("welcome for test,My QQ is 107966750");

      }

}

 

在TestBeans中加入打印當前對象的名稱

如下:

ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml");

Test test=(Test) ctx.getBean("test");

System.out.println(test.getClass().getSimpleName());

test.say();

輸出:

Test$$EnhancerByCGLIB$$4791b36c

super class is class org.ben.spring.service.Test

do something in befor

welcome for test

do something in after

 

明顯看到用了AOP之后,輸出的是代理類對象Test$$EnhancerByCGLIB$$bb9b6a7c.而且它的父類是我們的代理目標類。說明是有CGLIB生成的

 

 

 

 

 

第二種情況

 

XML的配置不變,改變代理目標類Test的實現方法,如下

public class Test implements TestInter{

      public void say() {

            System.out.println("welcome for test,My QQ is 107966750");

      }

}

和原來不同的是多繼承了一個接口,接口中定義了say()方法

在TestBeans中加入打印當前對象的名稱

如下:

ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml");

TestInter test=(TestInter) ctx.getBean("test");

System.out.println(test.getClass().getSimpleName());

System.out.println("super class is "+test.getClass().getSuperclass());

test.say();

 

輸出:

$Proxy0

super class is class java.lang.reflect.Proxy

do something in befor

welcome for test,My QQ is 107966750

do something in after

結論

Spring AOP中,當攔截對象實現了接口時,生成方式是用JDK的Proxy類。當沒有實現任何接口時用的是GCLIB開源項目生成的攔截類的子類.

 附上本文測試的源碼內容  源碼下載


免責聲明!

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



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