代理顾名思义就是帮你去做某件事,你无需了解办事的过程和方法,动态代理就是可以帮适合任何人去做任何事,动态的去适应,就我们程序设计来说,就是可以代理任何类的任何方法,但不能去影响原来的类。spring的aop都用到了这样的思想。
假设一个情景,你需要在不改变原类的前提下来给这个类的方法前和后加一段逻辑代码,该怎样实现?
首先我们可以想到继承的方法,即继承原类,然后添加代码,但这样不太灵活,而且容易使类变得很多。所以我们采用另一种方法——聚合,即在目标类中new一个原类,再去添加代码。
假设我们定义一个Moveable接口,如下定义:
package
com.lxy.proxy;
public
interface
Moveable {
public
void
move();
public
void
stop();
}
假设我们的原类为Car,实现了我们的Moveable接口,定义如下:
package
com.lxy.proxy;
public
class
Car
implements
Moveable{
@Override
public
void
move() {
System.
out
.println(
"Car is Moving..."
);
}
@Override
public
void
stop() {
System.
out
.println(
"Car is stop..."
);
}
}
下面就是我们定义的CarTimeProxy代理类,可以代理实现了Moveable接口的类,当然Car类也是可以代理的。
package
com.lxy.proxy
;
public
class
CarTimeProxy
implements
Moveable
{
Moveable obj;
public
CarTimeProxy(
Moveable
obj) {
super
();
this
.
obj
= obj;
}
@
Override
public
void
move()
{
System.
out.println("timeProxy start..."); //在方法前加的逻辑代码
obj
.move();
System.
out.println("timeProxy stop"); //在方法后加的逻辑代码
}
@Override
public
void
stop()
{
System.
out
.println(
"timeProxy start..."
);
//在方法前加的逻辑代码
obj
.stop();
System.
out
.println(
"timeProxy stop"
); //在方法后加的逻辑代码
}
}
上面的代码虽然实现了要求但略显笨拙,我们并不需要知道这个代理类的存在,我们不需要关心他是如何实现的。再改改代码,既
然我们不需要关心代理类是如何让实现,那我们新建了一个Proxy类作为代理类,将上面的
CarTimeProxy拷贝为一个字符串,为简单
起见,我们只拷贝了move方法,然后再
将其编译,返回对象。
package
com.lxy.proxy;
import
java.io.File;
import
java.io.FileWriter;
import
java.io.IOException;
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;
public
class
Proxy
{
public
static
Object newProxyInstance()
throws
Exception{
String rt =
"\r\n"
;
String classStr =
"package com.lxy.proxy;"
+ rt +
"public class CarTimeProxy implements Moveable{"
+ rt +
" Moveable obj;"
+ rt +
" public CarTimeProxy(Moveable obj) {"
+ rt +
" super();"
+ rt +
" this.obj = obj;"
+ rt +
" }"
+ rt +
" @Override"
+ rt +
" public void move() {"
+ rt +
" System.out.println(\"timeProxy start...\");"
+ rt +
" obj.move();"
+ rt +
" System.out.println(\"timeProxy stop\");"
+ rt +
" }"
+ rt +
"}"
;
//生成java文件
String fileName =
"G:/MyEclipse10workspace/proxy/CarTimeProxy.java"
;
File f =
new
File(fileName);
FileWriter fw;
try
{
fw =
new
FileWriter(f);
fw.write(classStr);
fw.flush();
fw.close();
}
catch
(IOException e) {
e.printStackTrace();
}
//compile编译
JavaCompiler compiler = ToolProvider.
getSystemJavaCompiler
();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(
null
,
null
,
null
);
Iterable
units = fileMgr.getJavaFileObjects(fileName);
CompilationTask t = compiler.getTask(
null
, fileMgr,
null
,
null
,
null
,
units
);
t.call();
fileMgr.close();
//load into memory and create an instance加载到内存创建对象
URL[] urls =
new
URL[] {
new
URL(
"file:/"
+
"G:/MyEclipse10workspace"
)};
URLClassLoader ul =
new
URLClassLoader(urls);
Class
c = ul.loadClass(
"com.lxy.proxy.CarTimeProxy"
);
System.
out
.println(c);
Constructor
ctr =
c.getConstructor(Moveable.
class
)
;
Moveable m = (Moveable)ctr.newInstance(
new
Car());
return
m;
}
}
测试函数Main:
package com.lxy.proxy;
public class Main {
public static void main(String[] args) throws Exception {
Moveable Car = (Moveable)Proxy.newProxyInstance();
Car.move();
}
}
如上我们算解决了一部分的问题,但还有关键性的问题摆在眼前,任何类和任何方法还有要加在原类方法前后的方法,该怎么实现呢?
针对以上的问题,我们可以将原类类型和前后的方法对象作为参数传给Proxy类,那么我们就需要定义一个方法接口InvocationHandler,代码如下:
package
com.lxy.proxy;
import
java.lang.reflect.Method;
public
interface
InvocationHandler {
public
void
invoke(Object o, Method m);
}
package
com.lxy.proxy;
import
java.io.File;
import
java.io.FileWriter;
import
java.io.IOException;
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;
public
class
Proxy {
public
static
Object newProxyInstance(
Class
intFace, InvocationHandler handler)
throws
Exception{
Method methods[] = intFace.getMethods();
String
rt =
"\r\n"
;
String
mathedClassStr =
""
;
for
(Method me : methods){
mathedClassStr +=
" @Override"
+ rt +
" public void "
+ me.getName() +
"() {"
+ rt +
" try {"
+ rt +
" Method m = "
+ intFace.getName() +
".class.getMethod(\""
+ me.getName() +
"\");"
+ rt +
" obj.invoke(this, m);"
+ rt +
" }catch(Exception e){ "
+ rt +
" e.printStackTrace();"
+ rt +
" }"
+ rt +
" }"
+ rt ;
}
String
classStr =
"package com.lxy.proxy;"
+ rt +
"import java.lang.reflect.Method;"
+ rt +
"public class CarTimeProxy implements "
+ intFace.getName() +
"{"
+ rt +
" com.lxy.proxy.InvocationHandler obj;"
+ rt +
" public CarTimeProxy(InvocationHandler obj) {"
+ rt +
" super();"
+ rt +
" this.obj = obj;"
+ rt +
" }"
+ rt +
mathedClassStr + rt +
"}"
;
//生成java文件
String
fileName =
"G:/MyEclipse10workspace/com/lxy/proxy/CarTimeProxy.java"
;
File f =
new
File(fileName);
FileWriter fw;
try
{
fw =
new
FileWriter(f);
fw.write(classStr);
fw.flush();
fw.close();
}
catch
(IOException e) {
e.printStackTrace();
}
//compile
JavaCompiler compiler = ToolProvider.
getSystemJavaCompiler
();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(
null
,
null
,
null
);
Iterable
units = fileMgr.getJavaFileObjects(fileName);
CompilationTask t = compiler.getTask(
null
, fileMgr,
null
,
null
,
null
,
units
);
t.call();
fileMgr.close();
//load into memory and create an instance
URL[] urls =
new
URL[] {
new
URL(
"file:/"
+
"G:/MyEclipse10workspace/"
)};
URLClassLoader ul =
new
URLClassLoader(urls);
Class
c = ul.loadClass(
"com.lxy.proxy.CarTimeProxy"
);
Constructor ctr = c.getConstructor(InvocationHandler.class);
Object m = ctr.newInstance(handler);
return
m;
}
}
测试类Main
package
com.lxy.proxy;
public
class
Main {
/**
*
@param
args
*
@throws
Exception
*/
public
static
void
main(String[] args)
throws
Exception {
Car c =
new
Car();
InvocationHandler h =
new
TimeHandler(c);
Moveable Car = (Moveable)Proxy.
newProxyInstance
(Moveable.
class
, h);
Car.move();
Car.stop();
}
}
这样我们就模拟实现了动态代理,可以看出来我们模仿了jdk中动态代理的实现,只是简单的实现了下,还有些细节没有实现,比如方法中含有参数该怎么实现。
本文参照了马士兵老师设计模式讲解中的代码和思想。