前言
日常生活中,我們想買(mǎi)房或者租房又沒(méi)有房源的時(shí)候,我們通常會(huì)找中介,由中介負(fù)責(zé)幫我們聯(lián)系房主,我們只需要最后簽合同就行了,而不用去關(guān)心中介怎么聯(lián)系房東的,怎么談價(jià)格的。
什么是代理模式
代理模式(Proxy Pattern): 給某一個(gè)對(duì)象提供一個(gè)代理,并由代理對(duì)象來(lái)控制對(duì)真實(shí)對(duì)象的訪問(wèn)。代理模式是一種結(jié)構(gòu)型設(shè)計(jì)模式。
所謂的代理模式,是指客戶(hù)端不想或者不能直接訪問(wèn)或引用一個(gè)對(duì)象,這時(shí)候可以通過(guò)代理對(duì)象在客戶(hù)端和目標(biāo)對(duì)象之間起到一個(gè)中介作用。
代理(Proxy)模式提供了對(duì)目標(biāo)對(duì)象另外的訪問(wèn)方式,即通過(guò)代理對(duì)象訪問(wèn)目標(biāo)對(duì)象。這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能。
代理模式的三個(gè)角色
Subject(抽象角色):
聲明代理角色和真實(shí)角色的公共接口,也是代理角色代理真實(shí)角色的方法。
RealSubject(真實(shí)角色):
定義代理對(duì)象需要代理的真實(shí)對(duì)象,真正實(shí)現(xiàn)業(yè)務(wù)邏輯的類(lèi);
Proxy(代理角色):
用來(lái)代理和封裝真實(shí)主題,來(lái)控制對(duì)真實(shí)對(duì)象的訪問(wèn),代理對(duì)象持有真實(shí)對(duì)象的應(yīng)用,從而可以隨時(shí)控制客戶(hù)端對(duì)真實(shí)對(duì)象的訪問(wèn)。
代理模式UML圖

靜態(tài)代理
抽象主題角色
package com.java.design.proxy;
/**
* 抽象主題角色:明星
* @author liyongfu
*/
public interface Star {
/**
* 唱歌
*/
void sing();
}
真實(shí)主題角色
package com.java.design.proxy;
/**
* 真實(shí)主題角色:具體的明星
* @author liyongfu
*/
public class RealStar implements Star{
/**
* 唱歌
*/
@Override
public void sing() {
System.out.println("do----真正明星唱歌");
}
}
代理主題角色
package com.java.design.proxy.statics;
/**
*
* 代理主題角色
* 靜態(tài)代理:
* 被代理類(lèi)含有接口和具體實(shí)現(xiàn)類(lèi);
* 代理類(lèi)實(shí)現(xiàn)被代理接口,并含有被代理接口屬性、相應(yīng)屬性構(gòu)造器
* @author liyongfu
*/
public class ProxyStar implements Star{
/**被代理的對(duì)象*/
private Star star;
public ProxyStar(Star star) {
this.star = star;
}
@Override
public void sing() {
doBeforeSing();
star.sing();
doAfterSing();
}
/**
* 明星唱歌前:經(jīng)紀(jì)人賣(mài)票
*/
private void doBeforeSing() {
System.out.println("before---經(jīng)紀(jì)人賣(mài)票");
}
/**
* 明星唱歌后:經(jīng)紀(jì)人收錢(qián)
*/
private void doAfterSing() {
System.out.println("after---經(jīng)紀(jì)人收錢(qián)");
}
public static void main(String[] args){
ProxyStar proxy= new ProxyStar(new RealStar());
proxy.sing();
}
}
控制臺(tái)打印
before---經(jīng)紀(jì)人賣(mài)票
do----真正明星唱歌
after---經(jīng)紀(jì)人收錢(qián)
觀察靜態(tài)代理運(yùn)行的整個(gè)流程,可以看出來(lái):
- 代理對(duì)象和目標(biāo)對(duì)象都實(shí)現(xiàn)了同樣的接口。
- 可以在不修改目標(biāo)對(duì)象的前提下增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能。
- 在明星唱歌之前,真實(shí)主題角色和代理主題角色就被確定下來(lái)了,也就是說(shuō)在編譯時(shí)就已經(jīng)將接口,被代理類(lèi),代理類(lèi)等確定下來(lái)。在程序運(yùn)行之前,代理類(lèi)的.class文件就已經(jīng)生成,是由程序員編寫(xiě)的代理類(lèi),并在程序運(yùn)行前就編譯好的,而不是由程序動(dòng)態(tài)產(chǎn)生代理類(lèi)。
JDK動(dòng)態(tài)代理
在上面的示例中,一個(gè)代理只能代理一種類(lèi)型,而且是在編譯期間就已經(jīng)確定被代理的對(duì)象。而動(dòng)態(tài)代理是在運(yùn)行時(shí),通過(guò)反射機(jī)制實(shí)現(xiàn)動(dòng)態(tài)代理,并且能夠代理各種類(lèi)型的對(duì)象。
由于靜態(tài)代理的局限性,為了解決上面的問(wèn)題,動(dòng)態(tài)代理模式就橫空出世了,我們最常見(jiàn)的就是Spring AOP了,看過(guò)源碼的都知道spring aop有兩種動(dòng)態(tài)代理模式,JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理,那么這兩者之間又有什么區(qū)別呢?
在Java中要想實(shí)現(xiàn)JDK動(dòng)態(tài)代理機(jī)制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 類(lèi)的支持。我們先來(lái)看一下他們的源代碼。
package java.lang.reflect;
/**
* {@code InvocationHandler} is the interface implemented by
* the <i>invocation handler</i> of a proxy instance.
*
* <p>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.
*
* @author Peter Jones
* @see Proxy
* @since 1.3
*/
public interface InvocationHandler {
/**
* Processes a method invocation on a proxy instance and returns
* the result. This method will be invoked on an invocation handler
* when a method is invoked on a proxy instance that it is
* associated with.
*
* @param proxy the proxy instance that the method was invoked on
*
* @param method the {@code Method} instance corresponding to
* the interface method invoked on the proxy instance. The declaring
* class of the {@code Method} object will be the interface that
* the method was declared in, which may be a superinterface of the
* proxy interface that the proxy class inherits the method through.
*
* @param args an array of objects containing the values of the
* arguments passed in the method invocation on the proxy instance,
* or {@code null} if interface method takes no arguments.
* Arguments of primitive types are wrapped in instances of the
* appropriate primitive wrapper class, such as
* {@code java.lang.Integer} or {@code java.lang.Boolean}.
*
* @return the value to return from the method invocation on the
* proxy instance. If the declared return type of the interface
* method is a primitive type, then the value returned by
* this method must be an instance of the corresponding primitive
* wrapper class; otherwise, it must be a type assignable to the
* declared return type. If the value returned by this method is
* {@code null} and the interface method's return type is
* primitive, then a {@code NullPointerException} will be
* thrown by the method invocation on the proxy instance. If the
* value returned by this method is otherwise not compatible with
* the interface method's declared return type as described above,
* a {@code ClassCastException} will be thrown by the method
* invocation on the proxy instance.
*
* @throws Throwable the exception to throw from the method
* invocation on the proxy instance. The exception's type must be
* assignable either to any of the exception types declared in the
* {@code throws} clause of the interface method or to the
* unchecked exception types {@code java.lang.RuntimeException}
* or {@code java.lang.Error}. If a checked exception is
* thrown by this method that is not assignable to any of the
* exception types declared in the {@code throws} clause of
* the interface method, then an
* {@link UndeclaredThrowableException} containing the
* exception that was thrown by this method will be thrown by the
* method invocation on the proxy instance.
*
* @see UndeclaredThrowableException
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
*
* <p>{@code Proxy.newProxyInstance} throws
* {@code IllegalArgumentException} for the same reasons that
* {@code Proxy.getProxyClass} does.
*
* @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
* @throws IllegalArgumentException if any of the restrictions on the
* parameters that may be passed to {@code getProxyClass}
* are violated
* @throws SecurityException if a security manager, <em>s</em>, is present
* and any of the following conditions is met:
* <ul>
* <li> the given {@code loader} is {@code null} and
* the caller's class loader is not {@code null} and the
* invocation of {@link SecurityManager#checkPermission
* s.checkPermission} with
* {@code RuntimePermission("getClassLoader")} permission
* denies access;</li>
* <li> for each proxy interface, {@code intf},
* the caller's class loader is not the same as or an
* ancestor of the class loader for {@code intf} and
* invocation of {@link SecurityManager#checkPackageAccess
* s.checkPackageAccess()} denies access to {@code intf};</li>
* <li> any of the given proxy interfaces is non-public and the
* caller class is not in the same {@linkplain Package runtime package}
* as the non-public interface and the invocation of
* {@link SecurityManager#checkPermission s.checkPermission} with
* {@code ReflectPermission("newProxyInPackage.{package name}")}
* permission denies access.</li>
* </ul>
* @throws NullPointerException if the {@code interfaces} array
* argument or any of its elements are {@code null}, or
* if the invocation handler, {@code h}, is
* {@code null}
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
抽象主題角色(同上,略)
真實(shí)主題角色(同上,略)
代理主題角色
package com.java.design.proxy.dynamic;
import com.java.design.proxy.RealStar;
import com.java.design.proxy.Star;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK動(dòng)態(tài)代理
* @author liyongfu
*/
public class DynamicProxy implements InvocationHandler {
/**被代理的對(duì)象**/
private Star star;
public DynamicProxy(Star star) {
super();
this.star = star;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk動(dòng)態(tài)代理");
doBeforeSing();
method.invoke(star, args);
doAfterSing();
return null;
}
/**
* 明星唱歌前:經(jīng)紀(jì)人賣(mài)票
*/
private void doBeforeSing() {
System.out.println("before---經(jīng)紀(jì)人賣(mài)票");
}
/**
* 明星唱歌后:經(jīng)紀(jì)人收錢(qián)
*/
private void doAfterSing() {
System.out.println("after---經(jīng)紀(jì)人收錢(qián)");
}
public static void main(String[] args) {
Star star = new RealStar();
Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Star.class}, new DynamicProxy(star));
proxy.sing();
}
}
比較: CGLIB創(chuàng)建的動(dòng)態(tài)代理對(duì)象比JDK創(chuàng)建的動(dòng)態(tài)代理對(duì)象的性能更高,但是CGLIB創(chuàng)建代理對(duì)象時(shí)所花費(fèi)的時(shí)間卻比JDK多得多。所以對(duì)于單例的對(duì)象,因?yàn)闊o(wú)需頻繁創(chuàng)建對(duì)象,用CGLIB合適,反之使用JDK方式要更為合適一些。JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理均是實(shí)現(xiàn)Spring AOP的基礎(chǔ)。
源碼實(shí)例
// TODO 待處理
項(xiàng)目實(shí)戰(zhàn)
// TODO 待處理