一、代理設計模式

代理設計模式是Java常用的設計模式之一。

特點:

01.委託類和代理類有共同的介面或者父類;

02.代理類負責為委託類處理訊息,並將訊息轉發給委託類;

03.委託類和代理類物件通常存在關聯關係,一個代理類物件與一個委託類物件關聯;

04.代理類本身不是真正的實現者,而是通過呼叫委託類方法來實現代理功能;

二、靜態代理與動態代理

按照代理類建立的時機,代理類分為兩種:

01.靜態代理:由我們程式猿或者特定的工具自動生成了原始碼,在程式執行之前,class檔案已經存在了;例如在serviceImpl.java中呼叫dao.xx(),真正的實現者是dao,service就可以理解為一個代理類;

02.動態代理:在程式執行期間,通過反射創建出來的代理類;

三、jdk動態代理

顧名思義,這種方式是由jdk為我們提供的。下面通過一個例子來演示。

01.建立一個Person介面

public interface Person {
void eat();
void sleep();
}

02.建立ZhangSan.java實現Person介面

public class ZhangSan implements Person {

    public void eat() {
System.out.println("吃飯...");
} public void sleep() {
System.out.println("睡覺...");
} }

03.建立代理類ZSProxy.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class ZSProxy implements InvocationHandler { //被代理類物件引用
private ZhangSan zhangSan; public ZSProxy(ZhangSan zhangSan) {
super();
this.zhangSan = zhangSan;
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
//zhangSan.eat();
Object result = method.invoke(zhangSan, args);//可以獲取目標方法的返回值
after();
return null;
} private void before() {
System.out.println("前置...");
}
private void after() {
System.out.println("後置...");
}
}

jdk動態代理中必須瞭解一個類和一個介面:Proxy類和InvocationHandler介面。

001.上述中的代理類實現了 InvocationHandler介面,此介面只有一個invoke方法

public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;

proxy:代理類物件

method:被代理的方法

args:被代理方法的引數列表

002.Proxy類

public class Proxy implements java.io.Serializable {
... public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException ...
}

loader:類載入器

interfaces:代理類實現的所有介面

h:InvocationHandler介面的一個例項

public class Test {

    public static void main(String[] args) throws Throwable {
System.out.println("----------------------JDK動態代理----------------------------");
//獲取代理類物件
Person proxyInstance = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),
new Class[] {Person.class}, new ZSProxy(new ZhangSan()));
proxyInstance.eat();
}

通過Proxy的newProxyInstance方法創建出代理物件,再有代理物件執行方法。

程式執行結果:

雖然效果實現了,但我們並不能從程式碼看到哪裡呼叫的invoke方法??那麼底層到底是怎麼執行的呢??

首先要了解一個類 ==》$Proxy0.java

001. $Proxy0 是記憶體中的代理類,在$Proxy0中會持有一個InvocationHandler介面的例項類的引用,所以此Test類先是呼叫了記憶體中的$Proxy0.eat();

002.執行$Proxy0類中的invoke

我們debug執行觀察:

記憶體中代理類的特徵:

01.它是對目標類的代理,那麼這個記憶體中中的代理類的型別必須和被代理類(目標類)的型別一致。

程式碼中 (Person)Proxy.newProxyInstance.. 進行了向下轉型操作。

02.記憶體中的代理類必須持有InvocationHandler介面的實現類引用。

整個過程中$Proxy0我們是看不到的!那麼有沒有辦法讓它原形畢露呢?

/**
* 使用IO的方式將記憶體中代理類寫入到檔案中,然後反編譯出來進行觀察
*/
private static void createProxyClassFile() {
byte[] data = ProxyGenerator.generateProxyClass("$Proxy0.class", new Class[] {Person.class}); try {
FileOutputStream out = new FileOutputStream("$Proxy0.class");
out.write(data);
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

對$Proxy0.java反編譯後

public final class class extends Proxy
implements Person
{ private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m0; public class(InvocationHandler invocationhandler) //通過構造將handler傳入
{
super(invocationhandler);
} public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} public final void eat()
{
try
{
super.h.invoke(this, m4, null);// h:就是上文說的 此類中必須存在InvocationHandler介面實現類的引用,也是這裡真正呼叫了invoke方法
return;
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} public final void sleep()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m4 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("eat", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("sleep", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
catch (NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch (ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}

那麼就真相大白了。

********************************************************************************************************************************************

以上就是jdk為我們提供的動態代理。我們也可以模仿它的實現原理,自定義我們的動態代理。

01.建立Person介面

package cn.yzx.myProxy;

public interface Person {
void eat() throws Throwable;
void sleep() throws Throwable;
}

02.建立ZhangSan實現類

package cn.yzx.myProxy;

public class ZhangSan implements Person {

    public void eat() {
System.out.println("吃飯...");
} public void sleep() {
System.out.println("睡覺...");
}
}

03.ZSProxy代理類

package cn.yzx.myProxy;
import java.lang.reflect.Method; public class ZSProxy implements MyInvocationHandler {
//目標物件
private Person person; public ZSProxy(Person person) {
this.person = person;
} @Override
public Object invoke(Object proxy, Method method, Object args) {
before();
try {
person.eat();
} catch (Throwable e) {
e.printStackTrace();
}
after();
return null;
} private void before() {
System.out.println("前置...");
}
private void after() {
System.out.println("後置...");
} }

04.自定義動態代理Proxy類

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider; /**
* 自定義的動態代理Proxy類
* @author Xuas
*/
public class MyProxy {
static String rt = "\r\n";
/**
* 自定義建立記憶體中代理例項的方法
* @param loader
* @param interfaces
* @param handler
* @return
*/
public static Object newProxyInstance(ClassLoader loader, Class interfaces, MyInvocationHandler handler) { try {
Method[] methods = interfaces.getMethods();
//01.使用拼湊字串的方式將記憶體中的代理類拼出來
String proxyClass = "package cn.yzx.myProxy;" + rt + "import java.lang.reflect.Method;" + rt
+ "public class $Proxy0 implements " + interfaces.getName() + "{" + rt + "MyInvocationHandler h;" + rt
+ "public $Proxy0(MyInvocationHandler h) {" + rt + "this.h = h;" + rt + "}"
+ getMethodString(methods, interfaces) + rt + "}";
//02.使用IO將拼湊的代理類寫入到檔案中
String filePathName = "F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy/$Proxy0.java";
File file = new File(filePathName);
FileWriter fw = new FileWriter(file);
fw.write(proxyClass);
fw.flush();
fw.close(); //03.編譯上面生成的java檔案
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null,
null, null);//檔案管理器
Iterable javaFileObjects = fileManager.getJavaFileObjects(filePathName);//獲取java檔案
CompilationTask task = compiler.getTask(null, fileManager, null, null, null, javaFileObjects);//獲取編譯任務
task.call();//編譯
fileManager.close();//關閉檔案管理器
/**
* 此時被編譯後的class檔案還在硬碟之中!它應該存在於JVM記憶體中!
* 所以要把硬碟中class檔案載入到JVM記憶體中去!
*/
//04.把硬碟中的class檔案載入到記憶體中去,要使用ClassLoader
MyClassLoader loader1 = new MyClassLoader("F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy"); //proxy0Clazz:就是記憶體中代理類的class物件
Class<?> proxy0Clazz = loader1.findClass("$Proxy0");//返回被代理類的class物件
/**
* public $Proxy0(MyInvocationHandler h) {..}
* 要通過構造器的引數將handler物件的引用傳進來
*/
Constructor<?> constructor = proxy0Clazz.getConstructor(MyInvocationHandler.class);
Object instance = constructor.newInstance(handler);//返回記憶體中代理類例項
return instance;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
} private static String getMethodString(Method[] methods, Class interfaces) { String proxyMe = ""; for (Method method : methods) {
proxyMe += "public void " + method.getName() + "() throws Throwable {" + rt + "Method md = "
+ interfaces.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + rt
+ "this.h.invoke(this,md,null);" + rt + "}" + rt;
} return proxyMe;
}
}

05.自定義InvocationHandler介面

import java.lang.reflect.Method;

/**
* 自定義InvocationHandler介面
* @author Xuas
*
*/
public interface MyInvocationHandler {
public Object invoke(Object proxy, Method method, Object args);
}

06.自定義的類載入器

package cn.yzx.myProxy;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; public class MyClassLoader extends ClassLoader { private File dir; public MyClassLoader(String path) {//要載入的檔案路徑
dir = new File(path);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException { if(null != dir) {
File clazzFile = new File(dir, name + ".class");
if(clazzFile.exists()) {
try {
FileInputStream input = new FileInputStream(clazzFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while((len = input.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
return defineClass("cn.yzx.myProxy."+name,
baos.toByteArray(),
0,
baos.size());//最終把輸出流輸出到JVM記憶體中
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return super.findClass(name);
} }

07.記憶體中自定義生成的 $Proxy0.java

package cn.yzx.myProxy;
import java.lang.reflect.Method;
public class $Proxy0 implements cn.yzx.myProxy.Person{
MyInvocationHandler h;
public $Proxy0(MyInvocationHandler h) {
this.h = h;
}
public void sleep() throws Throwable {
Method md = cn.yzx.myProxy.Person.class.getMethod("sleep",new Class[]{});
this.h.invoke(this,md,null);
}
public void eat() throws Throwable {
Method md = cn.yzx.myProxy.Person.class.getMethod("eat",new Class[]{});
this.h.invoke(this,md,null);
} }

08.測試類

public class Test {

    public static void main(String[] args) throws Throwable {
System.out.println("----------------------自定義動態代理----------------------------");
cn.yzx.myProxy.Person person2 = (cn.yzx.myProxy.Person)MyProxy.newProxyInstance(cn.yzx.myProxy.Person.class.getClassLoader(),
cn.yzx.myProxy.Person.class,
new cn.yzx.myProxy.ZSProxy(new cn.yzx.myProxy.ZhangSan()));
person2.eat();
}
}

程式執行結果:

總結:

通過觀察Proxy中的newProxyInstance方法的引數可知,jdk動態代理只支援委託類和代理類實現共同的介面的方式。

出處:https://www.cnblogs.com/9513-/p/8432276.html