目录
概述
静态代理
动态代理
基于接口代理(jdk)
原理解析
基于继承代理(cglib)
Java字节码生成开源框架–ASM:
AOP用到了两种动态代理来实现织入功能:
比较:
public interface Subject{
void test();
}
public class RealSubject implements Subject{
@Override
public void tset(){
System.out.printin("target method");
}
}
public class Proxy implements Subject{
private RealSubject realSubject;
public Proxy(RealSubject realSubject){
this.realSubject = realSubject;
}
@Override
public void test(){
System.out.printin("Before");
try{
realSubject.test();
}catch(Exception e){
System.out.println("ex:"+e.getmessage());
throw e;
}finally{
System.out.println("After");
}
}
}
public class Client{
public static void main(String[] args){
//通过接口
Subject subject = new Proxy(new RealSubject());
subject.test();
}
}
静态代理缺点:不灵活,重复代码多
动态代理类
public class JdkProxySubject implements InvocationHandler{
//引入要代理的真实对象
private RealSubject realSubject;
//用构造器注入目标方法,给我们要代理的真实对象赋初值
public JdkProxySubject(RealSubject realSubject){
this.realSubJect=realSubject;
}
//实现接口的方法
@Override
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
System.out.println("before");
Object result = null;
try{
//调用目标方法
//利用反射构造目标对象
// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
result=method.invoke(realSubject,args);
}catch(Exception e){
System.out.println("ex:"+e.getMessage());
throw e;
}finally{
System.out.println("after");
}
return result;
}
}
客户端调用(使用Proxy)
public class Client{
public static void main(String[] args){
//使用Proxy构造对象
//参数
//java泛型需要转换一下
// 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
// * 第一个参数 getClassLoader() ,我们这里使用Client这个类的ClassLoader对象来加载我们
// 的代理对象
// * 第二个参数表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
// * 第三个参数handler,我们这里将这个代理对象关联到了上方的 InvocationHandler这个对象上
Subject subject =
(Subject)java.lang.reflect.Proxy.newProxyInstance(Client.class.getClassLoader(),
new Class[]{Subject.class},new JdkProxySubject(new RralSubject()));
//调用方法
subject.test;
}
}
过程:调用Proxy.newProxyInstance生成代理类的实现类:
ProxyGenerator.generateProxyClass使用生成的代理类的名称,接口,访问标志生成proxyClassFile字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
InvocationHandler接口:
我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?
Proxy类
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException
public class CglibMethodInterceptor implements MethodInterceptor{//主要的方法拦截类,它是Callback接口的子接口,需要用户实现
@Override
public Object intercept(Object obj,Method method,Object
[] args,MethodProxy proxy )throws Throwable{
System.out.println("before cgplib");
Object result = null;
try{
//利用反射创建代理对象
result = proxy.invokeSuper(obj,args);
}catch(Exception e){
System.out.println("ex:"+e.getMessage());
throw e;
}finally{
System.out.println("after cglib");
}
return result;
}
}
public class Client{
public static void main(String[] args){
// 主要的增强类
Enhancer enhancer=new Enhancer;
// 目标类 , 设置父类,被增强的类
enhancer.setSuperclass(RealSubject.class);
// 回调对象
enhancer.setCallback(new CglibMethodInterceptor());
//生成代理类对象,用cglibProxy来增强RealSubject
Subject subject=enhancer.create();
subject.test();
}
}
原理解析
Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:
CGLIB的核心类:
net.sf.cglib.proxy.Enhancer – 主要的增强类
net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。
-net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;
第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。
ASM 是一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
不过ASM在创建class字节码的过程中,操纵的级别是底层JVM的汇编指令级别,这要求ASM使用者要对class组织结构和JVM汇编指令有一定的了解。
相关文档:Java JDK 动态代理(AOP)使用及实现原理分析_衣舞晨风的博客-CSDN博客_动态代理
相关文档:java动态代理_飞!!!!的博客-CSDN博客_java动态代理
相关文档:Java动态代理的实现原理