原文地址:java動(dòng)態(tài)代理
代理
最近在學(xué)習(xí) Spring 框架,AOP涉及到動(dòng)態(tài)代理的知識(shí),故整理一下,了解動(dòng)態(tài)代理之前,我們首先應(yīng)該了解下什么是代理。
代理是基本的設(shè)計(jì)模式之一,它是你為了提供額外的或不同的操作,而插入的用來(lái)代替的"實(shí)際"對(duì)象的
對(duì)象,這些操作通常涉及與"實(shí)際"對(duì)象的通信,因此代理通常充當(dāng)著中間人的角色。
當(dāng)我們希望跟蹤對(duì)方法的調(diào)用,或者希望度量這些調(diào)用的開(kāi)銷時(shí),我們肯定不希望將這些代碼合并到業(yè)務(wù)代碼中,此時(shí)我們就可以使用代理來(lái)實(shí)現(xiàn)。下面我們通過(guò)一個(gè)示例了解下代理
代碼演示1
定義一個(gè)接口
public interface Behavior {
void eat();
void say(String str);
}
接口的實(shí)現(xiàn)類
public class Student implements Behavior {
@Override
public void eat() {
System.out.println("我是學(xué)生,我要吃學(xué)生餐");
}
@Override
public void say(String str) {
System.out.println(str);
}
}
定義一個(gè)代理類
public class SimpleProxy implements Behavior {
private Behavior behavior;
public SimpleProxy(Student behavior) {
this.behavior = behavior;
}
@Override
public void eat() {
System.out.println("---- 方法執(zhí)行前邏輯 ----");
behavior.eat();
System.out.println("---- 方法執(zhí)行后邏輯 ----");
}
@Override
public void say(String str) {
System.out.println("---- 方法執(zhí)行前邏輯 ----");
behavior.say(str);
System.out.println("---- 方法執(zhí)行后邏輯 ----");
}
}
測(cè)試
@Test
public void test(){
SimpleProxy simpleProxy = new SimpleProxy(new Student());
simpleProxy.eat();
System.out.println("\n");
simpleProxy.say("hello,teacher");
}
打印

動(dòng)態(tài)代理
Java的動(dòng)態(tài)代理比代理的思想更進(jìn)一步。因?yàn)樗梢詣?dòng)態(tài)的創(chuàng)建代理并動(dòng)態(tài)的處理對(duì)所代理方法的調(diào)用在動(dòng)
態(tài)代理上所做的所有調(diào)用都會(huì)被重定到單一的調(diào)用處理器上他的工作是揭示調(diào)用的類型并確定相應(yīng)的對(duì)策
實(shí)現(xiàn)Java動(dòng)態(tài)代理的過(guò)程中涉及到一個(gè)接口 InvocationHandler 以及一個(gè)類 Proxy。
接口:InvocationHandler
InvocationHandler is the interface implemented by the invocation handler
of a proxy instance.
譯:
InvocationHandler是代理實(shí)例化對(duì)象的調(diào)用處理程序(invocation handler)實(shí)現(xiàn)的接口。
Each proxy instance has an associated invocation handler.When a method is
invoked on a proxy instance, the method invocation is encoded and dispatched
to the {@code invoke}method of its invocation handler.
譯:
每個(gè)代理實(shí)例化對(duì)象都具有一個(gè)關(guān)聯(lián)的調(diào)用處理程序(invocation handler)。當(dāng)代理實(shí)例化對(duì)象
調(diào)用方法時(shí),將對(duì)方法調(diào)用進(jìn)行編碼并將其指派到它的調(diào)用處理程序的 invoke 方法
InvocationHandler 僅有一個(gè)方法invoke()
Object invoke(Object proxy,Method method,Object[] args)throws Throwable
* @param proxy 調(diào)用方法的代理實(shí)例化對(duì)象
* @param method 指代的是我們所要調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象
* @param args 指代的是調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù)
類:Proxy
Proxy provides static methods for creating dynamic proxy classes and instances,
and it is also the superclass of all dynamic proxy classes created by those
methods.
譯:
Proxy 提供了創(chuàng)建動(dòng)態(tài)代理類和實(shí)例化對(duì)象的方法,同時(shí)也是這些方法創(chuàng)建的所有動(dòng)態(tài)代理類的超類
方法:newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
該方法返回一個(gè)指定接口的動(dòng)態(tài)代理實(shí)例化對(duì)象,該接口可以將方法調(diào)用指派到指定的調(diào)用處理程
序(invocation handler)
參數(shù):
loader - 定義代理類的類加載器
interfaces - 代理類要實(shí)現(xiàn)的接口列表
h - 指派方法調(diào)用的調(diào)用處理程序
代碼演示2
定義一個(gè)接口
public interface Behavior {
void eat();
void say(String str);
}
接口的實(shí)現(xiàn)類
public class Student implements Behavior {
@Override
public void eat() {
System.out.println("我是學(xué)生,我要吃學(xué)生餐");
}
@Override
public void say(String str) {
System.out.println(str);
}
}
創(chuàng)建一個(gè)動(dòng)態(tài)代理處理器
/**
* Created by lizhen on 2018/9/3 下午8:51
* 定義一個(gè)動(dòng)態(tài)代理處理器,該類必須實(shí)現(xiàn) InvocationHandler 接口
*/
public class DynamicProxyHandler implements InvocationHandler {
// 目標(biāo)對(duì)象
private Object target;
public DynamicProxyHandler(Object target){
this.target = target;
}
/**
* @param proxy 調(diào)用方法的代理實(shí)例
* @param method 指代的是我們所要調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象
* @param args 指代的是調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù)
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------- 方法調(diào)用前操作 ------");
System.out.println("proxy:" + proxy.getClass());
System.out.println("Method:" + method);
method.invoke(target, args);// 當(dāng)代理對(duì)象調(diào)用真實(shí)對(duì)象的方法時(shí),其會(huì)自動(dòng)的跳轉(zhuǎn)到代理對(duì)象關(guān)聯(lián)的handler對(duì)象的invoke方法來(lái)進(jìn)行調(diào)用
System.out.println("------- 方法調(diào)用后操作 ------");
return method.invoke(target, args);
}
}
測(cè)試
@Test
public void test() {
// 我們要代理的真實(shí)對(duì)象
Student student = new Student();
// 我們要代理哪個(gè)真實(shí)對(duì)象,就將該對(duì)象傳進(jìn)去,最后是通過(guò)該真實(shí)對(duì)象來(lái)調(diào)用其方法的
InvocationHandler handler = new DynamicProxyHandler(student);
/*
* 實(shí)例化代理對(duì)象
* loader:定義代理類的類加載器,一個(gè)ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來(lái)對(duì)生成的代理對(duì)象進(jìn)行加載
* interfaces: student.getClass().getInterfaces(),我們這里為代理對(duì)象提供的接口是真實(shí)對(duì)象所實(shí)行的接口,表示我要代理的是該真實(shí)對(duì)象,這樣我就能調(diào)用這組接口中的方法了
* h: 一個(gè)InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上
*/
Behavior proxies = (Behavior) Proxy.newProxyInstance(student.getClass().getClassLoader(), student.getClass().getInterfaces(), handler);
System.out.println(proxies.getClass().getName());
proxies.eat();
System.out.println("\n");
System.out.println("=========================== 看 我 華 不 華 麗 ================================");
System.out.println("\n");
proxies.say("hello,teacher");
}
打印

代碼演示3
通過(guò)內(nèi)部類定義具體的InvocationHandler
定義接口、實(shí)現(xiàn)類同上
創(chuàng)建 ProxyFactory
public class ProxyFactory {
// 目標(biāo)對(duì)象
private Object target;
public ProxyFactory(Object target){
this.target = target;
}
// 給目標(biāo)對(duì)象生成代理對(duì)象(內(nèi)部類)
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------- 方法調(diào)用前操作 ------");
System.out.println("proxy:" + proxy.getClass());
if (args != null){
for (Object obj: args) {
System.out.println("args:" + obj);
}
}
System.out.println("Method:" + method);
method.invoke(target, args);// 當(dāng)代理對(duì)象調(diào)用真實(shí)對(duì)象的方法時(shí),其會(huì)自動(dòng)的跳轉(zhuǎn)到代理對(duì)象關(guān)聯(lián)的handler對(duì)象的invoke方法來(lái)進(jìn)行調(diào)用
System.out.println("------- 方法調(diào)用后操作 ------");
return null;
}
}
);
}
}
測(cè)試
@Test
public void test(){
Student student = new Student();
ProxyFactory proxyFactory = new ProxyFactory(student);
Behavior behavior = (Behavior)proxyFactory.getProxyInstance();
System.out.println("\n");
System.out.println(behavior.getClass().getName());
System.out.println("\n");
behavior.eat();
System.out.println("\n");
System.out.println("=========================== 看 我 華 不 華 麗 ================================");
System.out.println("\n");
behavior.say("hello,teacher");
}
打印

Reference
1.java的動(dòng)態(tài)代理機(jī)制詳解 以及文章的評(píng)論使我受益匪淺
3.《Thinking In Java》