如上文:代理模式和装饰者模式中的静态代理实例,它具有如下缺陷:
1:代理类不可重用,具有相同代理逻辑的类会大量产生;
2:被代理方法惟一,如果有多个方法都需要相同逻辑的代理,那么代理类中就有大量的相似的方法存在;
3:代理方法不具有参数;
4:只实现了单接口了代理;
所以它并不具有实战意义上的价值!
那么,这些问题该如何解决呢?动态代理又是一步一步如何演变过来的呢?
下面就让我们来一步一步以实例的方式来探究它的演变的细节。
如果我们能动态产生一个代理类的源文件,编译后加载到内存,那么我们就可以获取到动态的代理对象。
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
public class Proxy {
public static Object newProxyInstance(Object target) throws Exception { // JDK6 Complier API, CGLib, ASM
String rt = " \r\n " ;
String t = " \t " ;
String src = " package proxy; " + rt + t +
" public class TankTimeProxy implements Movable { " + rt + t +
" private Movable obj; " + rt + t +
" public TankTimeProxy(Movable obj) { " + rt + t +
" super(); " + rt + t +
" this.obj = obj; " + rt + t +
" } " + rt + t +
" @Override " + rt + t +
" public void move() { " + rt + t +
" long begintime = System.currentTimeMillis(); " + rt + t +
" System.out.println(\ " Tank is begining to move ! \ " ); " + rt + t +
" obj.move(); " + rt + t +
" long endtime = System.currentTimeMillis(); " + rt + t +
" System.out.println(\ " Tank is stop ! \ " ); " + rt + t +
" System.out.println(\ " move time : \ " +(endtime-begintime)); " + rt + t +
" } " + rt + t +
" } " ;
String fileName = System.getProperty( " user.dir " ) + " /src/proxy/TankTimeProxy.java " ;
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
if (f.exists()){
f.delete();
fw.flush();
f = new File(fileName);
};
fw.write(src);
fw.flush();
fw.close();
// compile
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager( null , null , null );
Iterable units = fileMgr.getJavaFileObjects(fileName);
CompilationTask compilationTask = compiler.getTask( null , fileMgr, null , null , null , units);
compilationTask.call();
fileMgr.close();
// load into memory and create an instance
URL[] urls = new URL[] { new URL( " file:/ " + System.getProperty( " user.dir " ) + " /src/ " )};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass( " proxy.TankTimeProxy " );
Constructor ctr = c.getConstructor(target.getClass().getInterfaces()[ 0 ]);
Object m = ctr.newInstance(target);
return m;
}
}
测试类:
public class Client {
public static void main(String[] args) throws Exception{
Movable tank = (Movable)Proxy.newProxyInstance( new Tank());
tank.move();
}
}
上面的类就实现了生成TankTimeProxy.java文件,编译,加载并被调用的功能。(全部源码见:
代理模式和装饰者模式异同点比较)
那么怎么实现代理任意对象呢?
在产生代理类的时候,只要动态的注入目标对象,就实现了对任意对象的代理。
怎么实现对任意方法的代理呢?通过java反射机制,可以获取一个类的所有方法,即可以获取目标类的所有方法,在组成代理类java源码的时候,循环遍历嵌入处理逻辑就可以任意对多方法的代理了。
示例代码如下:
package
proxy;
import
java.io.File;
import
java.io.FileWriter;
import
java.lang.reflect.Constructor;
import
java.lang.reflect.Method;
import
java.net.URL;
import
java.net.URLClassLoader;
import
javax.tools.JavaCompiler;
import
javax.tools.StandardJavaFileManager;
import
javax.tools.ToolProvider;
import
javax.tools.JavaCompiler.CompilationTask;

public
class
Proxy
{
public
static
Object newProxyInstance(Class intface,Object target)
throws
Exception
{
//
JDK6 Complier API, CGLib, ASM
String rt
=
"
\r\n
"
;
String t
=
"
\t
"
;
String methodStr
=
""
;
Method[] methods
=
intface.getDeclaredMethods();
for
(Method method : methods)
{
methodStr
+=
"
@Override
"
+
rt
+
t
+
"
public void
"
+
method.getName()
+
"
() {
"
+
rt
+
t
+
"
long begintime = System.currentTimeMillis();
"
+
rt
+
t
+
"
System.out.println(\
"
Tank is begining to move
!
\
"
);
"
+
rt
+
t
+
"
obj.
"
+
method.getName()
+
"
();
"
+
rt
+
t
+
"
long endtime = System.currentTimeMillis();
"
+
rt
+
t
+
"
System.out.println(\
"
Tank is stop
!
\
"
);
"
+
rt
+
t
+
"
System.out.println(\
"
move time : \
"
+(endtime-begintime));
"
+
rt
+
t
+
"
}
"
+
rt
+
t;
}
System.out.println(methodStr);
String src
=
"
package proxy;
"
+
rt
+
t
+
"
import
"
+
intface.getName()
+
"
;
"
+
rt
+
t
+
"
public class $Proxy1 implements
"
+
intface.getSimpleName()
+
"
{
"
+
rt
+
t
+
"
private
"
+
intface.getSimpleName()
+
"
obj;
"
+
rt
+
t
+
"
public $Proxy1(
"
+
intface.getSimpleName()
+
"
obj) {
"
+
rt
+
t
+
"
super();
"
+
rt
+
t
+
"
this.obj = obj;
"
+
rt
+
t
+
"
}
"
+
rt
+
t
+
methodStr
+
rt
+
"
}
"
;
String fileName
=
System.getProperty(
"
user.dir
"
)
+
"
/src/proxy/$Proxy1.java
"
;
File f
=
new
File(fileName);
FileWriter fw
=
new
FileWriter(f);
if
(f.exists())
{
f.delete();
fw.flush();
f
=
new
File(fileName);
}
;
fw.write(src);
fw.flush();
fw.close();
//
compile
JavaCompiler compiler
=
ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr
=
compiler.getStandardFileManager(
null
,
null
,
null
);
Iterable units
=
fileMgr.getJavaFileObjects(fileName);
CompilationTask compilationTask
=
compiler.getTask(
null
, fileMgr,
null
,
null
,
null
, units);
compilationTask.call();
fileMgr.close();
//
load into memory and create an instance

URL[] urls
=
new
URL[]
{
new
URL(
"
file:/
"
+
System.getProperty(
"
user.dir
"
)
+
"
/src/
"
)}
;
URLClassLoader ul
=
new
URLClassLoader(urls);
Class c
=
ul.loadClass(
"
proxy.$Proxy1
"
);
Constructor ctr
=
c.getConstructor(intface);
Object m
=
ctr.newInstance(target);
return
m;
}
}
但是新的问题又出现了:
在代理类中,被代理方法前后的处理逻辑已经被“写死了”,很难改变增加的功能,这又该如何处理呢?
我们可以这样考虑,增加一个调用处理器InvocationHandler,把对方法的处理逻辑进行进一步的封闭,并把InvocationHandler分离出来,如果可以的话,就实现了对代理逻辑的可修改性。
那么InvocationHandler里面应该封闭些什么东西呢?

for
(Method method : methods)
{
methodStr
+=
"
@Override
"
+
rt
+
t
+
"
public void
"
+
method.getName()
+
"
() {
"
+
rt
+
t
+
"
long begintime = System.currentTimeMillis();
"
+
rt
+
t
+
"
System.out.println(\
"
Tank is begining to move
!
\
"
);
"
+
rt
+
t
+
"
obj.
"
+
method.getName()
+
"
();
"
+
rt
+
t
+
"
long endtime = System.currentTimeMillis();
"
+
rt
+
t
+
"
System.out.println(\
"
Tank is stop
!
\
"
);
"
+
rt
+
t
+
"
System.out.println(\
"
move time : \
"
+(endtime-begintime));
"
+
rt
+
t
+
"
}
"
+
rt
+
t;
}
从这个片段代理可以看出,我们应该在InvocationHandler中封装obj对象,即被代理类的接口(实现类)。还应该实现:被分离出去的InvocationHandler能被代理类调用,我们应该把InvocationHandler聚合进来。
代码演变示例:
package
proxy;
import
java.lang.reflect.Method;

public
interface
InvocationHandler
{
public
void
invoke(Object proxy, Method m);
}
package
proxy;
import
java.lang.reflect.Method;

public
class
TimeHandler
implements
InvocationHandler
{
private
Object target;

public
TimeHandler(Object target)
{
this
.target
=
target;
}

@Override
public
void
invoke(Object o, Method m)
{
long
start
=
System.currentTimeMillis();
System.out.println(
"
starttime:
"
+
start);
System.out.println(o.getClass().getName());
try
{
m.invoke(target);
}
catch
(Exception e)
{
e.printStackTrace();
}
long
end
=
System.currentTimeMillis();
System.out.println(
"
time:
"
+
(end
-
start));
System.out.println(
"
endtime:
"
+
end);
}

}
package
动态代理原理与实现
本文详细介绍了如何通过Java反射机制和编译API实现动态代理的过程,包括生成代理类、编译、加载及调用等步骤,并探讨了如何解决静态代理的局限性。
1320

被折叠的 条评论
为什么被折叠?



