标签:
问题:
要理解动态代理及其优点,我们先从这样一个问题入手,比如现在我们有UserDao这样一个接口,里面有addUser()方法,同时有一个UserDaoImpl类实现了该接口,具体实现了addUser()方法,现在我要实现在该方法前后记录日志的功能,有什么解决办法呢?
public class ExtendsImpl1 extends UserDaoImpl {
@Override
public void addUser() {
//模拟记录日志
System.out.println("开始记录日志......");
super.addUser();
System.out.println("记录日志结束。");
}
}
这样是可以完成功能。但是缺点有三:1.Java只支持单继承,这意味着该类在没法继承别的类,2.比如我现在功能拓展为先记录日志,在处理事务?如果按照继承的思路只能在一个ExtendsImpl2继承ExtendsImpl1在外面在包装上一层事务处理的代码。如果现在我要求先处理事务在记录日志,那我又要在重新写两个类……到这里大家就可以看出弊端,我有N多种功能,每种功能之间有不同的组合,那我们要写的类的数量就很多了,因此该方法也不推荐。
public class LogProxy implements UserDao{
UserDao u ;
public LogProxy(UserDao u) {
super();
this.u = u;
}
@Override
public void addUser() {
//模拟记录日志
System.out.println("开始记录日志......");
u.addUser();
System.out.println("记录日志结束。");
}
}
public class TransacProxy implements UserDao{
UserDao u ;
public TransacProxy(UserDao u) {
super();
this.u = u;
}
@Override
public void addUser() {
//模拟事务
System.out.println("transaction begin......");
u.addUser();
System.out.println("transaction commit.");
}
}
假如现在我要增加事务功能,同时实现和日志功能的组合,只需要创建实现事务和日志的两个类的对象,并互相包装即可,因为这两个类都实现了USerDao接口,并持有一个该接口类型的变量。LogProxy log = new LogProxy(userDao);TransacProxy trac = new TransacProxy(log);就是完成先记录日志,在处理事务。TransacProxy trac = new TransacProxy(userDao);LogProxy log = new LogProxy(trac);完成先处理事务,在记录日志。
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
//先记录日志,在处理事务
//LogProxy log = new LogProxy(userDao);
//TransacProxy trac = new TransacProxy(log);
//完成先处理事务,在记录日志
TransacProxy trac = new TransacProxy(userDao);
LogProxy log = new LogProxy(trac);
log.addUser();
}
多种功能的组合的时候,道理一样。这个时候LogProxy就可以看成是UserDaoImpl的一个代理。继承的思路基本接近动态代理的思路了,只是现在LogProxy只是UserDaoImpl类的addUser方法的日志代理类,我现在想把它写成任意接口任意方法的日志代理类,这时候就要用到动态代理了。
动态代理
自己写了个两个类模拟Java动态代理的实现,即可实现对任意接口的任意方法的AOP,只注重实现思路,并没实现细节,默认被切入点的被代理的方法是无参的(有参数反射依然可以实现,默认无参只是为了说明问题)。比如我现在要在任意接口的所有方法执行时统计方法执行的时间,即在每个方法执行前后获得系统时间取差值即为执行时间。首先做一个接口InvocationHandler,他的实现类TimeHandler中的invoke方法根据传递进来的被代理对象和方法,用反射执行该方法,并在方法执行前后统计时间;
public interface InvocationHandler {
public void invoke(Method m) throws Exception ;
}
public class TimeHandler implements InvocationHandler {
//被代理对象
Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
@Override
public void invoke(Method m) throws Exception {
long start = System.currentTimeMillis();
System.out.println("starttime:" + start);
//接收代理类传递过来的方法
m.invoke(target);
long end = System.currentTimeMillis();
System.out.println("time:" + (end-start));
}
}
第2个类Proxy,是一个动态产生代理的类,在newProxyInstance()方法中,只需要你传递接口的Class和你需要的InvocationHandler实现类,通过拼接字符串,该字符串通过FileWriter写入到一个Java文件中去,该Java类就是我们的时间代理类,该类会调用InvocationHandler对象的invoke(Method method)方法,就会实现每个方法的时间统计功能。我们会将该代理类手动编译,使用classLoader加载,通过反射返回被代理接口类型的对象。
public class Proxy {
public static Object newProxyInstance(Class inter , InvocationHandler handler) throws Exception{
String methodStr="";
String rt = "\r\n";
Method[] methods = inter.getMethods();
for(Method m : methods){
/* methodStr +="@Override"+rt+
"public void "+m.getName()+"() {"+rt+
" long start = System.currentTimeMillis();" + rt +
" System.out.println(\"starttime:\" + start);" + rt +
" t.move();" + rt +
" long end = System.currentTimeMillis();" + rt +
" System.out.println(\"time:\" + (end-start));" + rt +
"}";*/
methodStr +="@Override"+rt+
"public void "+m.getName()+"() {"+rt+
"java.lang.reflect.Method method;"+rt+
"try {"+rt+
"method = "+inter.getName()+".class.getMethod(\""+m.getName()+"\");"+rt+
"h.invoke(this , method);"+rt+
"} catch (Exception e) {"+rt+
"e.printStackTrace();"+rt+
"}"+rt+
"}";
}
String src =
"package com.proxy;" + rt +
"public class $proxy1 implements "+inter.getName()+ "{" + rt +
" public $proxy1(InvocationHandler h) {" + rt +
" super();" + rt +
" this.h = h;" + rt +
" }" + rt +
" InvocationHandler h;" + rt +
methodStr+rt+
"}";
String filePath = System.getProperty("user.dir")+"\\src\\com\\proxy\\$proxy1.java";
File file = new File(filePath);
FileWriter fw = new FileWriter(file);
fw.write(src);
fw.flush();
fw.close();
//compile
JavaCompiler javaCompile = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = javaCompile.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(filePath);
CompilationTask t = javaCompile.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:/"+System.getProperty("user.dir")+"/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.proxy.$proxy1");
//反射
Constructor constructor = c.getConstructor(InvocationHandler.class);
Object object = constructor.newInstance(handler);
return object;
}
}
自动生成的Java代理对象如下:
package com.proxy;
public class $proxy1 implements com.test.UserManager{
public $proxy1(InvocationHandler h) {
super();
this.h = h;
}
InvocationHandler h;
@Override
public void addUsers() {
java.lang.reflect.Method method;
try {
method = com.test.UserManager.class.getMethod("addUsers");
h.invoke(method);
} catch (Exception e) {
e.printStackTrace();
}
}
}
该类就是自动生成的代理类,跟聚合时使用的代理有点像,只是那个被代理类对代理类是透明的,被代理对象在TimeHandler中声明,并通过反射,将被代理对象的方法交给TimeHandler来做。如此,我们便可代理任何借口的任意方法,比如下面我要在实现了Moveable接口的Tank类里面的方法统计时间,只需如下操作:
public class Client {
public static void main(String[] args) throws Exception {
Moveable t = new Tank();
InvocationHandler h = new TimeHandler(t);
Moveable moveable = (Moveable)Proxy.newProxyInstance(Moveable.class, h);
moveable.move();
}
}
Java动态代理技术的强大由此可见一斑!
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/sch20095100/article/details/47985177