詳解Java靜態署理的完成機制。本站提示廣大學習愛好者:(詳解Java靜態署理的完成機制)文章只能為提供參考,不一定能成為您想要的結果。以下是詳解Java靜態署理的完成機制正文
1、概述
署理是一種設計形式,其目標是為其他對象供給一個署理以掌握對某個對象的拜訪,署理類擔任為拜托類預處置新聞,過濾新聞並轉發新聞和停止新聞被拜托類履行後的後續處置。為了堅持行動的分歧性,署理類和拜托類平日會完成雷同的接口。
依照署理的創立時代,署理類可分為兩種:
靜態署理:由法式員創立署理類或特定對象主動生成源代碼再對其編譯,也就是說在法式運轉前署理類的.class文件就曾經存在。
靜態署理:在法式運轉時應用反射機制靜態創立生成。
上面在將靜態署理的完成機制之前先簡略引見一下靜態署理。
2、靜態署理
下面說過,署理類和拜托類普通都要完成雷同的接口,上面先界說這個接口:
public interface Service
{
public void add();
}
拜托類作為接口的一種完成,界說以下:
public class ServiceImpl implements Service
{
public void add()
{
System.out.println("添加用戶!");
}
}
假設我們要對拜托類加一些日記的操作,署理類可做以下界說:
public class ServiceProxy implements Service
{
private Service service;
public ServiceProxy(Service service)
{
super();
this.service = service;
}
public void add()
{
System.out.println("辦事開端");
service.add();
System.out.println("辦事停止");
}
}
編寫測試類:
public class TestMain
{
public static void main(String[] args)
{
Service serviceImpl=new ServiceImpl();
Service proxy=new ServiceProxy(serviceImpl);
proxy.add();
}
}
運轉測試法式,成果以下圖:
從下面的代碼可以看到,靜態署理類只能為特定的接口辦事,假如要辦事多類型的對象,就要為每種對象停止署理。我們就會想能否可以經由過程一個署理類完玉成部的署理功效,因而引入的靜態署理的概念。
3、靜態署理
Java的靜態署理重要觸及兩個類,Proxy和InvocationHandler。
Proxy:供給了一組靜態辦法來為一組接口靜態地生成署理類及其對象。
// 辦法 1: 該辦法用於獲得指定署理對象所聯系關系的挪用處置器 static InvocationHandler getInvocationHandler(Object proxy) // 辦法 2:該辦法用於獲得聯系關系於指定類裝載器和一組接口的靜態署理類的類對象 static Class getProxyClass(ClassLoader loader, Class[] interfaces) // 辦法 3:該辦法用於斷定指定類對象能否是一個靜態署理類 static boolean isProxyClass(Class cl) // 辦法 4:該辦法用於為指定類裝載器、一組接口及挪用處置器生成靜態署理類實例 static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h)
InvocationHandler:它是挪用處置器接口,自界說了一個invok辦法,用於集中處置在靜態署理類對象上的辦法挪用,平日在該辦法中完成對拜托類的署理拜訪
// 該辦法擔任集中處置靜態署理類上的一切辦法挪用。第一個參數既是署理類實例,第二個參數是被挪用的辦法對象 // 第三個辦法是挪用參數。挪用處置器依據這三個參數停止預處置或分配到拜托類實例上發射履行 Object invoke(Object proxy, Method method, Object[] args)
完成Java的靜態署理,詳細有以下四個步調:
1、經由過程完成InvocationHandler接口創立本身的挪用處置器
2、經由過程為Proxy類指定ClassLoader對象和一組interface來創立靜態署理類
3、經由過程反射機制取得靜態署理類的結構函數,其獨一參數類型是挪用處置器類接口類型
4、經由過程結構函數創立靜態署理類實例,結構時挪用處置器對象作為參數被傳入
上面依據上述的四個步調來完成本身的靜態署理的示例:
接口和接口的完成類(即拜托類)跟下面靜態署理的代碼一樣,這裡我們來完成InvocationHandler接口創立本身的挪用處置器
public class ServiceHandle implements InvocationHandler
{
private Object s;
public ServiceHandle(Object s)
{
this.s = s;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
System.out.println("辦事開端");
//invoke表現對帶有指定參數的指定對象挪用由此 Method 對象表現的底層辦法
Object result=method.invoke(s, args);
System.out.println("辦事停止");
return result;
}
}
編寫測試類:
public class TestMain
{
public static void main(String[] args)
{
Service service=new ServiceImpl();
InvocationHandler handler=new ServiceHandle(service);
Service s=(Service) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), handler);
s.add();
}
}
運轉測試法式,成果同靜態署理。我們可以看到上述代碼並沒有我們之前說的步調2和3,這是由於Prox的靜態辦法newProxyInstance曾經為我們封裝了這兩個步調。詳細的外部完成以下:
// 經由過程 Proxy 為包含 Interface 接口在內的一組接口靜態創立署理類的類對象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
// 經由過程反射從生成的類對象取得結構函數對象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
// 經由過程結構函數對象創立靜態署理類實例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
newProxyInstance函數的外部完成為:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
{
//檢討h不為空,不然拋異常
Objects.requireNonNull(h);
//取得與制訂類裝載器和一組接口相干的署理類類型對象
final Class<?>[] intfs = interfaces.clone();
//檢討接口類對象能否對類裝載器可見而且與類裝載器所能辨認的接口類對象是完整雷同的
final SecurityManager sm = System.getSecurityManager();
if (sm != null)
{
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
//取得與制訂類裝載器和一組接口相干的署理類類型對象
Class<?> cl = getProxyClass0(loader, intfs);
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);
}
}
4、模仿完成Proxy類
依據下面的道理引見,我們可以本身模仿完成Proxy類:
public class Proxy
{
public static Object newProxyInstance(Class inface,InvocationHandle h) throws Exception
{
String rt="\r\n";
String methodStr="";
Method[] methods=inface.getMethods();
for(Method m:methods)
{
methodStr+="@Override"+rt+
"public void "+m.getName()+"()"+rt+"{" + rt +
" try {"+rt+
" Method md="+inface.getName()+".class.getMethod(\""+m.getName()+"\");"+rt+
"h.invoke(this,md);"+rt+
" } catch(Exception e){e.printStackTrace();}"+rt+
"}";
}
String src="package test;"+rt+
"import java.lang.reflect.Method;"+rt+
"public class ServiceImpl2 implements "+inface.getName()+ rt+
"{"+rt+
"public ServiceImpl2(InvocationHandle h)"+rt+
"{"+rt+
"this.h = h;"+rt+
"}"+rt+
" test.InvocationHandle h;"+rt+
methodStr+
"}";
String fileName="d:/src/test/ServiceImpl2.java";
//compile
compile(src, fileName);
//load into memory and create instance
Object m = loadMemory(h);
return m;
}
private static void compile(String src, String fileName) throws IOException
{
File f=new File(fileName);
FileWriter fileWriter=new FileWriter(f);
fileWriter.write(src);
fileWriter.flush();
fileWriter.close();
//獲得此平台供給的Java編譯器
JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
//獲得一個尺度文件治理器完成的新實例
StandardJavaFileManager fileManager=compiler.getStandardFileManager(null,null, null);
//獲得表現給定文件的文件對象
Iterable units=fileManager.getJavaFileObjects(fileName);
//應用給定組件和參數創立編譯義務的 future
CompilationTask t=compiler.getTask(null, fileManager, null, null, null, units);
//履行此編譯義務
t.call();
fileManager.close();
}
private static Object loadMemory(InvocationHandle h)
throws MalformedURLException, ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException
{
URL[] urls=new URL[] {new URL("file:/"+"d:/src/")};
//從途徑d:/src/加載類和資本
URLClassLoader ul=new URLClassLoader(urls);
Class c=ul.loadClass("test.ServiceImpl2");
//前往Class對象所表現的類的指定公共結構辦法。
Constructor ctr=c.getConstructor(InvocationHandle.class);
//應用此 Constructor對象ctr表現的結構辦法來創立該結構辦法的聲明類的新實例,並用指定的初始化參數初始化該實例
Object m = ctr.newInstance(h);
return m;
}
}
5、總結
1、所謂的靜態署理就是如許一種class,它是在運轉時生成的class,在生成它時你必需供給一組interface給它,然後改class就傳播鼓吹它完成了這些interface,然則其實它不會替你作本質性的任務,而是依據你在生成實例時供給的參數handler(即InvocationHandler接口的完成類),由這個Handler來接收現實的任務。
2、Proxy的設計使得它只能支撐interface的署理,Java的繼續機制注定了靜態署理類沒法完成對class的靜態署理,由於多繼續在Java中實質上就行欠亨。
以上就是本文的全體內容,願望對年夜家的進修有所贊助。