Spring生成代理对象的方法有两种: jdkProxy和Cglib,具体使用哪个可以由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认是如果目标类是interface则使用jdk,否则使用cglib。
jdk只能通过指定interface来创建代理对象,如果某一个类没有通过接口定义、即没有implement任何接口,那么此时jdk原生的动态代理就无能为力了,只能通过cglib。
JDK动态代理
使用JDK原生来实现动态代理主要是通过Proxy.newProxyInstance方法:
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
方法说明:Returns an instance of a proxy class for the specified interfacesthat dispatches method invocations to the specified invocationhandler.
返回指定接口的代理类实例,该接口将方法调用分派给指定的invocationhandler。
来看一个例子:
/**
* 使用jdk的动态代理,目的是生成Lancer的代理对象,
* 代理对象中对Lancer的原方法动态添加了方法before和after横切逻辑
* 更多参考 https://www.cnblogs.com/jssj/p/11771408.html
* https://blog.csdn.net/bu2_int/article/details/60150319
* */
Class clazz = Lancer.class;
Car lancerProxy = (Car) Proxy.newProxyInstance(clazz.getClassLoader(),
clazz.getInterfaces(),
new InvocationHandler() {
private Car car = new Lancer();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("run")) {
logger.info("启动发动机、踩油门,before run()横切面");
//logger.info(proxy.getClass().getName());
//method.invoke(proxy, args); //proxy是真实对象的代理对象, 这里不能这么写
method.invoke(this.car, args); //真实对象执行自己的方法
//logger.info(clazz.newInstance().getClass().getName());
logger.info("刹车、熄火,after run()横切面");
}
return null;
}});
lancerProxy.run();
-
InvocationHandler.invoke方法的第一个参数proxy的作用
public Object invoke(Object proxy, Method method, Object[] args)
这是InvocationHandler接口需要开发者实现的方法,method是目标接口的方法,args是方法参数,而proxy则是根据目标接口生成的代理对象,注意不是目标对象。作用是在invoke方法最后返回这个proxy对象、可以实现类似链式调用的效果。再一个可以获取一下代理对象的一些信息,比如proxy.getClass().getName(),通过这个我们知道代理对象是jdk动态生成的一个对象。 -
不用Proxy和InvocationHandler如何实现动态代理?
早在JDK未原生的提供动态代理相关的这些类之前,如何来实现动态代理设计模式呢?我们先回顾一下使用jdk实现动态代理的流程:
给定类,这个类的interface
-> JVM负责根据接口和实现类生成代理类(直接生成字节码、不需要通过java文件编译成Class)
-> 生成代理对象的实例
上述流程的中间那步显然需要jdk原生进行支持,也就是Proxy和InvocationHandler机制。但我们假设jdk没有提供这样的原生支持,而我们也是可以实现动态代理模式的。事实上jdk1.6之前就是如此。
流程如下:
给定类,以及这个类的interface
-> 在类的基础上插入切面代码,生成代理类java文件
-> 编译代理类java源文件生成.class文件
-> loader加载代理类Class
-> 生成代理对象的实例
下面根据上述流程设计,不用Proxy和InvocationHandler实现动态代理
/**
* 用于生成代理对象的工厂类
* */
public class CarProxyFactory<T> {
private Logger logger = LoggerFactory.getLogger(CarProxyFactory.class);
/**
* 根据给定的target对象返回代理对象
* 代理对象在target对象的方法逻辑基础上添加切面逻辑
* */
public T newInstance(T target) {
//return new CarProxy(targetCar); //这是所谓的静态代理
Class clazz = target.getClass();
Class interf = clazz.getInterfaces()[0]; //实现了哪个接口
String javaFileContent = genProxyClassContent(interf);
logger.debug(javaFileContent);
//生成java文件
File file = new File("D:/home/com/wangan/CarProxy$.java");
FileWriter writer = null;
try {
writer = new FileWriter(file);
writer.write(javaFileContent);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(null!=writer)
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//编译Class
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(file);
JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
try {
fileMgr.close();
} catch (IOException e) {
e.printStackTrace();
}
//Loader加载Class
URL[] urls = null;
try {
urls = new URL[]{new URL("file:D:/home/")};
} catch (MalformedURLException e) {
e.printStackTrace();
}
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class proxyClass = null;
try {
proxyClass = urlClassLoader.loadClass("com.wangan.CarProxy$");
} catch (ClassNotFoundException | SecurityException | IllegalArgumentException e) {
e.printStackTrace();
}
//实例化
T instance = null;
Class[] parameterTypes = new Class[] {interf};
Constructor<T> constructor;
try {
constructor = proxyClass.getConstructor(parameterTypes);
instance = constructor.newInstance(target);
} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//返回代理实例
return instance;
}
private String genProxyClassContent(Class interfaceClass) {
String interfaceFullName = interfaceClass.getName();//接口的完整类名
StringBuilder sb = new StringBuilder();
sb.append("package com.wangan;");
sb.append("\r\n");
sb.append("public class CarProxy$ implements "+interfaceFullName+"{");
sb.append("\r\n");
sb.append("private "+interfaceFullName+" target;");
sb.append("\r\n");
sb.append("public CarProxy$("+interfaceFullName+" target) { this.target = target; }");
Method[] methods = interfaceClass.getMethods(); //接口定义的方法
for(int i=0; i<methods.length; i++) {
sb.append("public void "+methods[i].getName()+"() {System.out.println(\"横切面\");");
sb.append("\r\n");
sb.append("target."+methods[i].getName()+"();");
sb.append("\r\n");
sb.append("System.out.println(\"横切面\");}");
}
sb.append("\r\n");
sb.append("}");
return sb.toString();
}
}
测试一下:
Car lancer = new Lancer();
CarProxyFactory<Car> factory = new CarProxyFactory<Car>();
Car proxy = factory.newInstance(lancer);
proxy.run();
运行结果:
横切面
com.wangan.dynamic.proxy.jdk.Lancer [codeline:11] - 蓝瑟跑起来了
横切面
- 这里关于生成的代理java文件所在的路径以及加载代理类的路径再说明一下,因为肥兔子这里被绕晕了1天。我们在D:/home/com/wangan/CarProxy$.java路径下生成了一个java文件,而然后加载class的时候是从file:D:/home/路径开始、加载了com.wangan.CarProxy$这样一个类。相当于所有动态生成的类都放在D:/home/目录下,然后需要再按照包来组织目录,好好体会一下。
- 再一个,通过日志输出各个步骤的执行时间,可以发现,整个过程执行最为耗时的是编译java文件的操作,所以先行判断我们的代理Class是否已存在是个优化点。
- URLClassLoader是AppClassLoader和ExtClassLoader的父类,但上面程序里我们new了一个URLClassLoader实例,指定其parrent加载器为当前线程对应的类加载器,初见时感觉有点“我爹是我儿”般的混乱,我们不要把“类的父子继承关系”和“类加载器之间的父子关系”两种不同的父子关系搞混了。
-
jdk的动态代理与我们实现的动态代理内部思路是否一样
前面我们使用了jdk提供的动态代理框架、即Proxy和InvocationHandler,然后又根据功能需求目标,试着实现了一个简单的动态代理框架。但jdk的动态代理框架内部是如何实现的呢,与我们实现的动态代理框架在大致流程和思路上是否是一样的?
这里可以进一步参考:
动态代理系列文章
当中详细分析了jdk动态代理的源码,包括Proxy源码分析、WeakCache缓存机制、ProxyGenerator生成代理类字节码文件等内容。
Cglib动态代理
Cglib是一款强大的代码生成类库,封装了asm可以动态生成class、在运行期动态扩展Java类与实现Java接口。用Cglib实现动态代理,不要求目标类实现自某一interface。官网介绍:Byte Code Generation Library is high level API to generate and transform JAVA byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.
网友评论