转自:https://blog.csdn.net/leave417/article/details/79167936
SpringAOP中可以用那些方式实现,区别?
区别:
cglib和JDK代理 都是动态构建字节码工具的形式实现,会构造一个全新的类;其中JDK动态代理只能针对实现了接口的类生成代理。而cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。
如果目标对象没有实现接口,则默认会采用CGLIB代理;
如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入)。
Aspectj是Java支持的切面编程,原理:在编译期间,将字节码编译到代理对象中.
CGLIB动态生成class,
Aspectj静态的,在程序编译期间,将代理字节加入到目标对象中.(修改代理目标类的字节,织入代理的字节码);性能比CGLIB和JDK代理 好一些.instrumentation修改目标类的字节码,在类装载时,进行动态拦截去修改.
a. 无论哪种方式实现动态代理,本质其实都是对字节码的修改,区别是从哪里进行切入修改字节码.
1. cglib–IOC beanFactory获取bean时,动态构建字节码,生成这个类
2. JDK代理– 获取或装载bean时,对字节码进行动态构建,装载,实例化
3. Aspectj– 代码编译时,进行织入修改字节码
4. instrumentation– 在类装载时.
总结:
1.动态代理可以由cglib/java Proxy/Aspectj/instrumentation等多种形式实现;
2.动态代理的本质是对class字节码进行动态构建或者修改;
a.修改的工具有ASM(= =比较难用,还是需要知道JVM指令)/javavssist(已经进行封装)
3.多种实现方式的区别在于对字节码切入方式不同.可选方式:
a.java代理/Cglib是基于动态构建接口实现类字节
b.AspectJ是借助eclipse工具在编译时织入代理字节
c.instrumentation是基于javaagent在类装载时,修改class织入代理字节
d.使用自定义classloader在装载时,织入代理字节
关键在于invocationHandler接口,由该接口将代理对象,目标对象和代理逻辑连接在一起.下图是执行过程.
java proxy构建新的代理对象的过程:
1.proxy基于代理接口获取其代理class
a.从缓存接口中获取,若没有则继续下一步 (有缓存说明下一次再去取的时候不会重新生成目标对象)
b.使用proxyGenerator构建代理class字节
c.调用本地方法装载class字节至当前classLoader (所以才需要传我们的加载器给他,是因为他要用这个加载器来加载)
d.使用class.forName()返回新的class对象(使用反射生成代理对象 )
2.调用代理对象class.newInstance()
转自:https://www.cnblogs.com/gonjan-blog/p/6685611.html
JDK动态代理
public interface Person {
void giveMoney();
}
其它类我不写了,一样的
创建StuInvocationHandler类,实现InvocationHandler接口,这个类中持有一个被代理对象的实例target。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。
再在invoke方法中执行被代理对象target的相应方法
下面是调用代理的动态构建字节码的工具来构建一个Class,并保存为class文件,然后反编译看他生成了一个怎么样的class
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces());
String path = "G:/javacode/javase/Test/bin/proxy/StuProxy.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("代理类class文件写入成功");
} catch (Exception e) {
System.out.println("写文件错误");
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;
//这里是实现了我们传进去的接口
public final class $Proxy0 extends Proxy implements Person
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
*注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
*为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
*被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
*
*super(paramInvocationHandler),是调用父类Proxy的构造方法。
*父类持有:protected InvocationHandler h;
*Proxy构造方法:
* protected Proxy(InvocationHandler h) {
* Objects.requireNonNull(h);
* this.h = h;
* }
*
*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
//这个静态块本来是在最后的,我把它拿到前面来,方便描述
static
{
try
{
//看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
//这里就用反射获取到了我们要代理的那个方法的对象
m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
/**
*
*这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
*this.h.invoke(this, m3, null);这里简单,明了。
*来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
*再联系到InvacationHandler中的invoke方法。嗯,就是这样。
*/
public final void giveMoney()
throws
{
try
{
//这里的this.h是上面构造方法中传给父类的InvocationHandler,所以这里invoke看到的3个参数就是我们平时看到的invoke(Object object, Method method, Object[] args)
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。
}
jdk为我们的生成了一个叫$Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件时放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的代理实例。通过对这个生成的代理类源码的查看,我们很容易能看出,动态代理实现的具体过程。
我们可以对InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。
代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的invoke方法,从而达到代理执行被代理对象的方法。也就是说,动态代理通过中介类实现了具体的代理功能。
生成的代理类:$Proxy0 extends Proxy implements Person,我们看到代理类继承了Proxy类,所以也就决定了java动态代理只能对接口进行代理,Java的继承机制注定了这些动态代理类们无法实现对class的动态代理。
上面的动态代理的例子,其实就是AOP的一个简单实现了,在目标对象的方法执行之前和执行之后进行了处理,对方法耗时统计。Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个东西的。