代理模式(Proxy)是通過代理對象訪問目標對象刃滓,這樣可以在目標對象基礎(chǔ)上增強額外的功能畜疾,如添加權(quán)限,訪問控制和審計等功能握恳。
Java代理分為靜態(tài)代理和動態(tài)代理和Cglib代理挖帘,下面進行逐個說明完丽。
1.靜態(tài)代理
接口類AdminService.java接口
package com.lance.proxy.demo.service;
public interface AdminService {
void update();
Object find();
}
實現(xiàn)類AdminServiceImpl.java
package com.lance.proxy.demo.service;
public class AdminServiceImpl implements AdminService{
public void update() {
System.out.println("修改管理系統(tǒng)數(shù)據(jù)");
}
public Object find() {
System.out.println("查看管理系統(tǒng)數(shù)據(jù)");
return new Object();
}
}
代理類AdminServiceProxy.java
package com.lance.proxy.demo.service;
public class AdminServiceProxy implements AdminService {
private AdminService adminService;
public AdminServiceProxy(AdminService adminService) {
this.adminService = adminService;
}
public void update() {
System.out.println("判斷用戶是否有權(quán)限進行update操作");
adminService.update();
System.out.println("記錄用戶執(zhí)行update操作的用戶信息、更改內(nèi)容和時間等");
}
public Object find() {
System.out.println("判斷用戶是否有權(quán)限進行find操作");
System.out.println("記錄用戶執(zhí)行find操作的用戶信息拇舀、查看內(nèi)容和時間等");
return adminService.find();
}
}
測試類StaticProxyTest.java
package com.lance.proxy.demo.service;
public class StaticProxyTest {
public static void main(String[] args) {
AdminService adminService = new AdminServiceImpl();
AdminServiceProxy proxy = new AdminServiceProxy(adminService);
proxy.update();
System.out.println("=============================");
proxy.find();
}
}
輸出:
判斷用戶是否有權(quán)限進行update操作
修改管理系統(tǒng)數(shù)據(jù)
記錄用戶執(zhí)行update操作的用戶信息舰涌、更改內(nèi)容和時間等
=============================
判斷用戶是否有權(quán)限進行find操作
記錄用戶執(zhí)行find操作的用戶信息、查看內(nèi)容和時間等
查看管理系統(tǒng)數(shù)據(jù)
總結(jié):
靜態(tài)代理模式在不改變目標對象的前提下你稚,實現(xiàn)了對目標對象的功能擴展瓷耙。
不足:靜態(tài)代理實現(xiàn)了目標對象的所有方法,一旦目標接口增加方法刁赖,代理對象和目標對象都要進行相應(yīng)的修改搁痛,增加維護成本。
2.JDK動態(tài)代理
為解決靜態(tài)代理對象必須實現(xiàn)接口的所有方法的問題宇弛,Java給出了動態(tài)代理鸡典,動態(tài)代理具有如下特點:
1.Proxy對象不需要implements接口;
2.Proxy對象的生成利用JDK的Api枪芒,在JVM內(nèi)存中動態(tài)的構(gòu)建Proxy對象彻况。需要使用java.lang.reflect.Proxy類的
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
*/
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler invocationHandler );
方法谁尸,方法參數(shù)說明:
a.ClassLoader loader:指定當前target對象使用類加載器,獲取加載器的方法是固定的纽甘;
b.Class<?>[] interfaces:target對象實現(xiàn)的接口的類型良蛮,使用泛型方式確認類型
c.InvocationHandler invocationHandler:事件處理,執(zhí)行target對象的方法時,會觸發(fā)事件處理器的方法悍赢,會把當前執(zhí)行target對象的方法作為參數(shù)傳入决瞳。
2.1 實戰(zhàn)代碼:
AdminServiceImpl.java和AdminService.java和原來一樣,這里不再贅述左权。
AdminServiceInvocation.java
package com.lance.proxy.demo.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class AdminServiceInvocation implements InvocationHandler {
private Object target;
public AdminServiceInvocation(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("判斷用戶是否有權(quán)限進行操作");
Object obj = method.invoke(target);
System.out.println("記錄用戶執(zhí)行操作的用戶信息皮胡、更改內(nèi)容和時間等");
return obj;
}
}
AdminServiceDynamicProxy.java
package com.lance.proxy.demo.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class AdminServiceDynamicProxy {
private Object target;
private InvocationHandler invocationHandler;
public AdminServiceDynamicProxy(Object target,InvocationHandler invocationHandler){
this.target = target;
this.invocationHandler = invocationHandler;
}
public Object getPersonProxy() {
Object obj = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), invocationHandler);
return obj;
}
}
DynamicProxyTest.java
package com.lance.proxy.demo.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
// 方法一
System.out.println("============ 方法一 ==============");
AdminService adminService = new AdminServiceImpl();
System.out.println("代理的目標對象:" + adminService.getClass());
AdminServiceInvocation adminServiceInvocation = new AdminServiceInvocation(adminService);
AdminService proxy = (AdminService) new AdminServiceDynamicProxy(adminService, adminServiceInvocation).getPersonProxy();
System.out.println("代理對象:" + proxy.getClass());
Object obj = proxy.find();
System.out.println("find 返回對象:" + obj.getClass());
System.out.println("----------------------------------");
proxy.update();
//方法二
System.out.println("============ 方法二 ==============");
AdminService target = new AdminServiceImpl();
AdminServiceInvocation invocation = new AdminServiceInvocation(adminService);
AdminService proxy2 = (AdminService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), invocation);
Object obj2 = proxy2.find();
System.out.println("find 返回對象:" + obj2.getClass());
System.out.println("----------------------------------");
proxy2.update();
//方法三
System.out.println("============ 方法三 ==============");
final AdminService target3 = new AdminServiceImpl();
AdminService proxy3 = (AdminService) Proxy.newProxyInstance(target3.getClass().getClassLoader(), target3.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("判斷用戶是否有權(quán)限進行操作");
Object obj = method.invoke(target3, args);
System.out.println("記錄用戶執(zhí)行操作的用戶信息、更改內(nèi)容和時間等");
return obj;
}
});
Object obj3 = proxy3.find();
System.out.println("find 返回對象:" + obj3.getClass());
System.out.println("----------------------------------");
proxy3.update();
}
}
輸出結(jié)果:
============ 方法一 ==============
代理的目標對象:class com.lance.proxy.demo.service.AdminServiceImpl
代理對象:class com.sun.proxy.$Proxy0
判斷用戶是否有權(quán)限進行操作
查看管理系統(tǒng)數(shù)據(jù)
記錄用戶執(zhí)行操作的用戶信息赏迟、更改內(nèi)容和時間等
find 返回對象:class java.lang.Object
----------------------------------
判斷用戶是否有權(quán)限進行操作
修改管理系統(tǒng)數(shù)據(jù)
記錄用戶執(zhí)行操作的用戶信息屡贺、更改內(nèi)容和時間等
============ 方法二 ==============
判斷用戶是否有權(quán)限進行操作
查看管理系統(tǒng)數(shù)據(jù)
記錄用戶執(zhí)行操作的用戶信息、更改內(nèi)容和時間等
find 返回對象:class java.lang.Object
----------------------------------
判斷用戶是否有權(quán)限進行操作
修改管理系統(tǒng)數(shù)據(jù)
記錄用戶執(zhí)行操作的用戶信息锌杀、更改內(nèi)容和時間等
============ 方法三 ==============
判斷用戶是否有權(quán)限進行操作
查看管理系統(tǒng)數(shù)據(jù)
記錄用戶執(zhí)行操作的用戶信息甩栈、更改內(nèi)容和時間等
find 返回對象:class java.lang.Object
----------------------------------
判斷用戶是否有權(quán)限進行操作
修改管理系統(tǒng)數(shù)據(jù)
記錄用戶執(zhí)行操作的用戶信息、更改內(nèi)容和時間等
3.Cglib代理
JDK動態(tài)代理要求target對象是一個接口的實現(xiàn)對象抛丽,假如target對象只是一個單獨的對象,并沒有實現(xiàn)任何接口饰豺,這時候就會用到Cglib代理(Code Generation Library)亿鲜,即通過構(gòu)建一個子類對象,從而實現(xiàn)對target對象的代理冤吨,因此目標對象不能是final類(報錯)蒿柳,且目標對象的方法不能是final或static(不執(zhí)行代理功能)。
Cglib依賴的jar包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
3.1實戰(zhàn)
目標對象類AdminCglibService.java
package com.lance.proxy.demo.service;
public class AdminCglibService {
public void update() {
System.out.println("修改管理系統(tǒng)數(shù)據(jù)");
}
public Object find() {
System.out.println("查看管理系統(tǒng)數(shù)據(jù)");
return new Object();
}
}
代理類AdminServiceCglibProxy.java
package com.lance.proxy.demo.service;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class AdminServiceCglibProxy implements MethodInterceptor {
private Object target;
public AdminServiceCglibProxy(Object target) {
this.target = target;
}
//給目標對象創(chuàng)建一個代理對象
public Object getProxyInstance() {
//工具類
Enhancer en = new Enhancer();
//設(shè)置父類
en.setSuperclass(target.getClass());
//設(shè)置回調(diào)函數(shù)
en.setCallback(this);
//創(chuàng)建子類代理對象
return en.create();
}
public Object intercept(Object object, Method method, Object[] arg2, MethodProxy proxy) throws Throwable {
System.out.println("判斷用戶是否有權(quán)限進行操作");
Object obj = method.invoke(target);
System.out.println("記錄用戶執(zhí)行操作的用戶信息漩蟆、更改內(nèi)容和時間等");
return obj;
}
}
Cglib代理測試類CglibProxyTest.java
package com.lance.proxy.demo.service;
public class CglibProxyTest {
public static void main(String[] args) {
AdminCglibService target = new AdminCglibService();
AdminServiceCglibProxy proxyFactory = new AdminServiceCglibProxy(target);
AdminCglibService proxy = (AdminCglibService)proxyFactory.getProxyInstance();
System.out.println("代理對象:" + proxy.getClass());
Object obj = proxy.find();
System.out.println("find 返回對象:" + obj.getClass());
System.out.println("----------------------------------");
proxy.update();
}
}
輸出結(jié)果:
代理對象:class com.lance.proxy.demo.service.AdminCglibService$$EnhancerByCGLIB$$41b156f9
判斷用戶是否有權(quán)限進行操作
查看管理系統(tǒng)數(shù)據(jù)
記錄用戶執(zhí)行操作的用戶信息垒探、更改內(nèi)容和時間等
find 返回對象:class java.lang.Object
----------------------------------
判斷用戶是否有權(quán)限進行操作
修改管理系統(tǒng)數(shù)據(jù)
記錄用戶執(zhí)行操作的用戶信息、更改內(nèi)容和時間等
總結(jié)
理解上述Java代理后怠李,也就明白Spring AOP的代理實現(xiàn)模式圾叼,即加入Spring中的target是接口的實現(xiàn)時,就使用JDK動態(tài)代理捺癞,否是就使用Cglib代理夷蚊。Spring也可以通過<aop:config proxy-target-class="true">強制使用Cglib代理,使用Java字節(jié)碼編輯類庫ASM操作字節(jié)碼來實現(xiàn)髓介,直接以二進制形式動態(tài)地生成 stub 類或其他代理類惕鼓,性能比JDK更強。