轉載位址:http://blog.csdn.net/goskalrie/article/details/52458773
代理模式 JAVA
代理模式的現執行個體子就是——中介,很貼切,它的定義:給某個對象提供一個代理,并由代理對象控制對象對原對象的引用。
代理模式包含如下角色:
(1)抽象主題角色:是一個接口,該接口是對象和它的代理共用的接口。
(2)真實主題角色:是實作抽象主題接口的類。
(3)代理角色:内部含有對真實對象的引用,進而可以操作真實對象。代理對象提供與真實對象相同的接口,一邊在任何時刻都能代替真實對象。同時,地阿裡對象可以在執行真實對象操作時,附加其他操作,相當于對真實對象進行封裝。
代理模式有幾種:虛拟代理,計數代理,遠端代理,動态代理。主要分為:靜态代理和動态代理。靜态代理比較簡單,是有程式員編寫的代理類,并在程式運作前就編譯好了,而不是由程式動态産生代理類,這就是靜态代理。
實作動态代理的關鍵技術就是反射。
靜态代理的實作
适用場景:管理者在網站上執行操作,在生成操作結果的同時需要記錄記錄檔,這是很常見的,此時不需要講日志語句加到某個操作代碼中,這樣會污染代碼。這種時候就是代理模式派用場的時候了。代理模式可以通過聚合和繼承兩種實作方式(如下代碼所示):
public class StaticProxyDemo {
public static void main(String[] args) {
//測試代碼
Admin admin = new Admin();
Manager m = new AdminProxy(admin);
m.doSomething();
}
}
/*
* 聚合式靜态代理
*/
/*
* 抽象主題接口
*/
interface Manager {
void doSomething();
}
/*
* 真實主題類
*/
class Admin implements Manager {
public void doSomething() {
System.out.println("真實類正在進行操作。。。");
}
}
/*
* 以聚合方式實作的代理類
*/
class AdminProxy implements Manager {
private Admin admin;
public AdminProxy(Admin admin) {
super();
this.admin = admin;
}
public void doSomething( ) {
System.out.println("代理類開始日志操作");
admin.doSomething();
System.out.println("代理類結束日志操作");
}
}
繼承式代理模式
/*
* 繼承式靜态代理
*/
/*
* 代理類
*/
class AdminProxy1 extends Admin {
@Override
public void doSomething() {
System.out.println("代理類開始日志操作");
super.doSomething();
System.out.println("代理類結束日志操作");
}
}
public static void main(String[] args) {
//測試代碼
AdminProxy1 a = new AdminProxy1();
a.doSomething();
}
上面就是靜态代理的兩種寫法。
聚合實作方式中代理類聚合了被代理類,且代理類及被代理類都實作了同一個接口,可實作靈活多變。繼承式的實作方式則不夠靈活。
動态代理——不需要編寫代理類,需要編寫事務處理類
靜态代理模式的特點就是一個主題類與一個代理類————對應。但是也存在這樣的情況,有n個主題類,但是代理類中的“前處理,後處理”
都是一樣的,進調用主題不一樣。也就是說,多個主題類對應一個代理類,共享“前處理,後處理”功能,動态調用所需主題,大大減小了程式規模,這就是動态代理模式的優勢所在,什麼意思呢,就是指設計一個代理類給所有主題類使用。不用靜态代理那樣一類主題類建一個主題代理類,n類主題類建n個主題代理類,這樣代碼的備援度太高了。
下面我們看看jdk動态代理是如何實作的吧:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Random;
public class DynamicProxyDemo {
public static void main(String[] args) throws Exception {
Car car = new Car();
InvocationHandler h = new TimeHandler(car);
Class<?> cls = car.getClass();
/*
* loader 類加載器
* interfaces 實作接口
* h InvocationHandler
*/
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
m.move();
}
}
//抽象主題
interface Moveable {
void move() throws Exception;
}
//真是主題
class Car implements Moveable {
@Override
public void move() throws Exception{
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽車行駛中。。。");
}
}
//事務處理器
class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable{
long startTime = System.currentTimeMillis();
System.out.println("汽車開始行駛...");
method.invoke(target, args);
long stopTime = System.currentTimeMillis();
System.out.println("汽車結束行駛。。。。汽車行駛時間:" + (stopTime - startTime));
return null;
}
}
在測試代碼中,Proxy.newProxyInstance()方法有三個參數:類加載器(要進行代理的類)、被代理類實作的接口、事務處理器。是以先執行個體化Car,執行個體化InvocationHandler的子類TimeHandler,将各參數傳入Proxy的靜态方法newProxyInstance()即可獲得Car的代理類,前面的靜态代理,代理類是我們編寫好的,而動态代理則不需要我們去編寫代理類,是在程式中動态生成的。
總結一下:
jdk動态代理的步驟:
(1)建立一個實作invocationHandler接口的類,他必須實作invoke()方法
(2)建立被代理的類及接口
(3)調用Proxy的靜态方法,建立一個代理類
(4) 通過代理調用方法
你肯定會疑惑,Proxy和InvocationHandler有什麼用,直接看源碼比較好,我建議為了更深層次的了解jdk動态代理,一定要看以下内容,畢竟要做一個合格的java攻城獅,啥都要了解!!!
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
}
/*
* Look up or generate the designated proxy class.檢視或者生成制定的代理類
*/
Class<?> cl = getProxyClass0(loader, interfaces);
/*
* 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())) {//當需要代理的類實作一個非public的接口時,因為這樣的接口需要特殊的權限,是以調用doPrivilege(native修
飾的方法)建立代理執行個體
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);
}
}
可以看到關鍵代碼是Class<?>cl = getProxyClass0(loader,intfs);然後由cl獲得構造函數的。那我們來看看getProxyClass0是什麼,請看下面的源碼:
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
還是沒有看到代理類是怎麼生成的,隻知道代理類是從proxyClassCache中取得的,這個變量是與緩存相關的一個對象,檢視該變量的聲明與初始化:
/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
可以看到 proxyClassCache 是一個用來緩存代理類的變量,大家知道類變量的特點:在一個虛拟機中類隻有一個,一個虛拟機中的類變量也隻有一個,且在此處,在Proxy類被加載的時候就指派了,在指派操作中有ProxyClassFactory()這麼一個構造函數,這個動态代理中的而關鍵:生成代理類的類檔案位元組碼。繼續追代碼,找到代理類的生成之處:
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.根據給定的類加載器和接口數組生成代理類的工廠類
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names所有代理類名稱的字首
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names用于生成唯一代理類名稱的下一個序号
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.驗證類加載器将此接口的名稱解析為實際對象的名稱
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.驗證類對象确實是一個接口
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.確定接口唯一
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in代理類包名
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
* 記錄非公開代理接口的包, 以便将代理類定義在同一個包中。确認所有非公共代理接口都在同一個包中。
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.生成代理類名的序号
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;//射生成全類名
/*
* Generate the specified proxy class.生成代理類位元組碼
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
在ProxyClassFactory中,可以看到産生代理類的具體邏輯,大緻上是,根據傳遞的被代理類及其實作的接口生成代理類的位元組碼加載到緩存中,但是加載到緩存中隻是一個.java檔案也不能用,是以底層還有編譯等操作。到這裡,可以大緻的看清JDK中動态代理的面孔了,實作的步驟為:
1. 建立代理類的源碼;
2. 對源碼進行編譯成位元組碼;
3. 将位元組碼加載到記憶體;
4. 執行個體化代理類對象并傳回給調用者;
底層的代碼我們看不到,但是我們可以檢視其生成的位元組碼:
import java.io.FileOutputStream;
import java.io.IOException;
import sun.misc.ProxyGenerator;
public class GenerateByteCode {
public static void main(String[] args) {
byte[] classFile = ProxyGenerator.generateProxyClass(
"$Proxy1", Car.class.getInterfaces());//生成位元組碼
FileOutputStream out = null;
try {
out = new FileOutputStream(System.getProperty("user.dir")+"\\$Proxy1.class");//會在項目目錄中生成位元組碼檔案“user.dir”不用改
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
這邊 說明一下,spring中也用到了動态代理是cglib動态代理,具體的過程在這邊就不做解釋了,下次在更新
本次小結
在ProxyClassFacto動态代理與靜态代理相比較,最大的好處是接口中聲明的所有方法都被轉移到調用處理器一個集中的方法中處理。在接口方法數量比較多的時候,我
們可以進行靈活處理,而不需要像靜态代理那樣對每一個方法或方法組合進行處理。Proxy 很美很強大,但是僅支援 interface 代理。Java 的單繼承機制注定了這些動态代理
類們無法實作對 class 的動态代理。好在有cglib為Proxy提供了彌補。class與interface的差別本來就模糊,在java8中更是增加了一些新特性,使得interface越來越接近class
,當有一日,java突破了單繼承的限制,動态代理将會更加強大。