細說java動態代理和cglib的動態代理
提到代理,想必大家對設計模式中的靜態代理和動態代理都比較熟悉,小編之前在部落格中對動態和靜態代理進行了對比,這篇博文就只探討java動態代理和cglib動態代理之間的區別;
♚ 靜態代理的溫習
在我們使用靜態代理的時候,每一個代理類只能為一個介面提供服務,這這樣一來在程式開發中會產生過多的代理,而且所有的代理操作除了呼叫的方法不一樣之外,其他的操作都是相同的,這樣就會造成過多的重複程式碼;
為了解決上述問題,我們使用一個代理類來完成所有的代理功能,而這就需要引入動態代理;
♚ 動態代理
與靜態代理相對的是動態代理,動態代理類的位元組碼在程式執行時由java反射機制動態生成,無需程式設計師手工編寫它的原始碼。動態代理不僅簡化了程式設計工作,而且提高了軟體系統的可擴充套件性,因為java反射機制可以生成任意型別的動態代理類。
下面我們以一個示例來說明動態代理:該示例中以一個checkSecurity()的例子來演示動態代理:
UserManager.java:
<span style="font-size:18px;">package com.ysc.spring; /** * 要實現的介面 * @author root * */ public interface UserManager { public void addUser(String username,String password); public void delUser(int userId); public String findUserById(int userId); public void modifyUser(int userId,String username,String passwordString); }</span>
UserManagerImpl.java
<span style="font-size:18px;">package com.ysc.spring; public class UserManagerImpl implements UserManager { @Override public void addUser(String username, String password) { //checkSecurity(); System.out.println("-------------userManager.add()---------"); } @Override public void delUser(int userId) { //checkSecurity(); System.out.println("-------------userManager.delUser()---------"); } @Override public String findUserById(int userId) { //checkSecurity(); System.out.println("-------------userManager.findUserById()---------"); return "張三"; } @Override public void modifyUser(int userId, String username, String passwordString) { //checkSecurity(); System.out.println("-------------userManager.modifyUser()---------"); } }</span>
由於檢查安全性的方法是可以獨立於當前介面中其他業務方法的,所以我們可以把這個獨立的服務拿出來,放到一個代理類中,使用動態代理把檢查安全性的方法動態的加入到每一個方法中,而這些代理的工作需要由一個單獨的代理類來完成:
SecurityHandler.java:
<span style="font-size:18px;">package com.ysc.spring; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class SecurityHandler implements InvocationHandler { private Object targetObject; public Object createProxyInstanceObject(Object targetobObject){ this.targetObject = targetobObject; return Proxy.newProxyInstance(targetobObject.getClass().getClassLoader(), targetobObject.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { checkSecurity(); //呼叫目標方法 Object ret = method.invoke(targetObject, args); return ret; } private void checkSecurity(){ System.out.println("-----------checkSecurity----------"); } }</span>
這樣,我們就可以直接在客戶端進行呼叫執行了:
Client.java:
<span style="font-size:18px;">package com.ysc.spring; /** * * 動態代理可以將系統中一些獨立的服務(這些獨立的服務具有橫切性) * 而將這些獨立的服務拿出來,使用動態代理就可以在執行時將服務自動加入到裡頭; * @author root * */ public class Client { /** * @param args */ public static void main(String[] args) { SecurityHandler handler = new SecurityHandler(); UserManager userManager = (UserManager)handler.createProxyInstanceObject(new UserManagerImpl()); userManager.addUser("zhangsan", "123"); } }</span>
通過上面的例子可以看出,通過動態代理我們實現了動態的將檢查安全性切入到每一個方法中,但是動態代理依靠介面實現,那麼這樣的話就會導致一些沒有介面的類,就不能通過jdk實現java的動態代理,而這個時候我們就需要轉向cglib的動態代理:
♚ cglib動態代理
cglib是針對類來實現代理的,它的原理是對指定的目標類生成一個類,並覆蓋其中方法實現增強,看上述例子的cglib動態代理的實現:
與動態代理不同,由於是針對類來實現,所以上述UserManagerImpl類不能直接在java程式碼中實現UserManager介面,所以我們需要一個配置檔案來配置,且完成對哪些方法新增檢查安全性的方法,ApplicationContext.xml 如下:
動態代理中例子保持Client.java , Security.java , UserManager.java不變,只對UserManagerImpl.java 做出如下的修改:<span style="font-size:18px;"><?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:mvc="http://www.springframework.org/schema/mvc" 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.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "> <!-- 表示使用cglib代理 --> <aop:aspectj-autoproxy proxy-target-class="true"/> <bean id="userManager" class="com.ysc.spring.UserManagerImpl"></bean> <bean id="securityHandler" class="com.ysc.spring.SecurityHandler"></bean> <!-- 檢查安全性的方法要附加在哪些方法上的配置 --> <aop:config> <aop:aspect id="securityAspect" ref="securityHandler"> <aop:pointcut expression="execution(* add*(..))" id="addAddMethod"/> <aop:before method="checkSecurity" pointcut-ref="addAddMethod"/> </aop:aspect> </aop:config> </beans></span>
<span style="font-size:18px;">package com.ysc.spring; public class UserManagerImpl { //implements UserManager { public void addUser(String username, String password) { //checkSecurity(); System.out.println("-------------userManager.add()---------"); } public void delUser(int userId) { //checkSecurity(); System.out.println("-------------userManager.delUser()---------"); } public String findUserById(int userId) { //checkSecurity(); System.out.println("-------------userManager.findUserById()---------"); return "張三"; } public void modifyUser(int userId, String username, String passwordString) { //checkSecurity(); System.out.println("-------------userManager.modifyUser()---------"); } }</span>
✎ 總結
spring 預設使用的jdk的動態代理;
1.如果目標物件實現了介面,在預設情況下采用jdk的動態代理實現aop
2.如果目標物件實現了介面,也可以強制使用cglib生成代理實現aop
3.如果目標物件沒有實現介面,那麼必須引入cglib,spring會在jdk的動態代理和cglib代理之間切換
如何使用cglib強制生成代理;
* 加入cglib類庫,cglib/*.jar
* 加入如下配置,表示強制使用cglib代理
<aop:aspectj-autoproxy proxy-target-class="true"/>
jdk動態代理和cglib代理的區別:
* jdk動態代理對實現了介面的類進行代理
* cglib代理可以對類代理,主要對指定的類生成一個子類,因為是繼承,所以我們的目標最好不要使用使用final宣告;
到這裡,jdk動態代理和cglib的動態代理之間的對比就已經結束了,希望能幫大家更深刻的理解靜態代理、jdk動態代理和cglib動態代理。如果有更好的理解和建議,請留言哦!!