专业的编程技术博客社区

网站首页 > 博客文章 正文

java实现动态代理的两种方式-jdk 和 cglib

baijin 2024-09-04 02:03:57 博客文章 9 ℃ 0 评论
  • 为什么要用代理
  • java中实现动态代理的两种方式
  • JDK动态代理编码示例
  • Cglib动态代理编码示例
  • Cglib底层实现原理

为什么要用代理

代理的作用: 增强原有功能,职责分明:让擅长的人做对应的事情


现实场景: 某个人工作厉害薪资高,但是却没有时间处对象,这时候会考虑找媒婆或者中介所,为自己介绍相亲对象 (通过媒婆代理,让自己实现了找对象的需要)


编码场景: 当现有的类的功能满足不了新需求,但又不能改动以前的代码,这时候就可以考虑使用代理,通过代理类,扩展原有类的功能


阅读 spring AOP,mybatis Mapper 相关源码的时候涉及动态代理

java中实现动态代理的两种方式

  • jdk动态代理: JDK 动态代理是基于拦截器和反射实现的,内部采用asm字节码技术动态生成真实对象对应接口的实现类

1 目标对象(被代理的对象)必须要实现业务接口

2 代理对象(代理类):必须实现 InvocationHandler 接口

3 使用 Proxy.newProxyInstance() 创建代理对象

  • cglib动态代理: 对于没有实现接口的类,可以通过cglib实现动态代理

1 目标对象(被代理的对象) 只要不是被 final 修饰即可,不要求实现接口

2 代理对象(代理类):必须实现 MethodInterceptor 接口

3 使用 Enhancer.create()创建代理对象

方式

jdk

cglib

底层实现

生成业务接口实现类

生成目标类的子类

目标对象

必须实现业务接口

不能用final 修饰

代理对象

必须实现InvocationHandler 接口,执行的是代invoke()方法

必须实现MethodInterceptor接口

代理对象

创建方式

Proxy.newProxyInstance()

Enhancer.create()

JDK动态代理编码示例

  • 代理模式涉及对象: 目标对象(真实用户),代理(中介)对象,代理创建类Proxy
//1 相亲业务接口
public interface FindLoveService {
    /**
     * 相亲
     * @param target 相亲对象
     */
    boolean findLove(RealUser target);
}

// 2 实现了相亲业务的真实对象 
@NoArgsConstructor
@AllArgsConstructor
@Data
public class RealUser implements FindLoveService {
    private int age;
    private String name;
    private String advantage;
    private String require;
    // 实现了相亲方法
    public boolean findLove(RealUser target) {
        System.out.println(this.name + "说: " + target.getName() + target.getAdvantage() + " 优点这么多,是我心目中的完美对象");
        return true;
    }
} 

/**
 * 3 媒婆代理类,实现了InvocationHandler
 */
public class MeiPoProxy implements InvocationHandler {
    private RealUser realUser;

    /**
     * 媒婆通过构造器持有真实对象的引用
     * @param realUser 真实对象(努力工作却没有时间相亲的用户)
     */
    public MeiPoProxy(RealUser realUser) {
        this.realUser = realUser;
    }

    /**
     * @param proxy  MeiPoProxy代理对象
     * @param method 要调用真实对象的目标方法
     * @param args   目标方法入参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("媒婆帮忙物色好对象");
        // 通过反射,调用真实对象的方法 method.invoke(真实对象)
        Object rsult = method.invoke(realUser, args);
        System.out.println("恭喜双方,没问题的话可以安排良辰吉日了 ");
        return rsult;
    }
}

public static void main(String[] args) {
  RealUser boy = new RealUser(28, "Axing", "帅气小伙,有房有车", "勤俭持家,温柔大方");

  //由JDK提供的工具类Proxy在运行时生成指定接口(FindLoveService)的代理实现类
  Class[] interfaces = {FindLoveService.class};//RealUser.class.getInterfaces()
  FindLoveService meiPoProxy = (FindLoveService) Proxy.newProxyInstance(JDKProxyTest.class.getClassLoader(), interfaces, new MeiPoProxy(boy));

  RealUser girl = new RealUser(18, "kk", "肤白貌美,贴心棉袄", "善良,有责任心");
  //因为meiPoProxy是实现InvocationHandler接口的代理类,最终的业务逻辑是在InvocationHandler实现类(MeiPoProxy)的invoke方法上,(在invoke方法上实现功能增强)
  meiPoProxy.findLove(girl);
}

最终输出结果如下: 
媒婆帮忙物色好对象
Axing说: kk肤白貌美,贴心棉袄 优点这么多,是我心目中的完美对象
恭喜双方,没问题的话可以安排良辰吉日了 
  • Proxy.newProxyInstance() 方法参数说明
/**
 *  @param loader 类加载器
 *  @param interfaces  目标对象接口,业务接口
 *  @param h  实现了 InvocationHandler接口的代理对象
 */
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,  
                                      												Class<?>[] interfaces, 
  																														InvocationHandler h)
                                                             
// 方法生成业务接口实现类预览 
public final class $proxyxx extends Proxy implements FindLoveService{
		public boolean findLove(RealUser target) {
       // 执行InvocationHandler 代理实现类的invoke方法
        return super.h.invock();
    }
}

Cglib动态代理编码示例

  • 代理模式涉及对象: 目标对象(真实用户),代理(中介)对象,代理创建类Proxy
// 1 真实对象 ,不要求实现接口
@NoArgsConstructor
@AllArgsConstructor
@Data
public class RealUser  {
    private int age;
    private String name;
    private String advantage;
    private String require;
    // 实现了相亲方法
    public boolean findLove(RealUser target) {
        System.out.println(this.name + "说: " + target.getName() + target.getAdvantage() + " 优点这么多,是我心目中的完美对象");
        return true;
    }
} 


/**
 * 代理对象(媒婆代理类) 要求实现 MethodInterceptor
 */
public class MeiPoCglibProxy implements MethodInterceptor {
    private RealUser realUser;

    /**
     * 媒婆通过构造器持有真实对象的引用
     *
     * @param realUser 真实对象(努力工作却没有时间相亲的用户)
     */
    public MeiPoProxy(RealUser realUser) {
        this.realUser = realUser;
    }

    /**
     * @param proxy  MeiPoProxy代理对象 自己
     * @param methodProxy 代理类执行的方法
     * @param args   方法入参
     * @throws Throwable
     */
     // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("媒婆帮忙物色好对象");
        //调用真实对象的方法 method.invoke(真实对象)
        Object rsult = methodProxy.invoke(realUser, args);
        System.out.println("恭喜双方,没问题的话可以安排良辰吉日了 ");
        return rsult;
    }
}

public static void main(String[] args) {
  //打印我们运行时生成的代理类的class文件,到指定路径
  System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\");
  RealUser boy = new RealUser(28, "Axing", "帅气小伙,有房有车", "勤俭持家,温柔大方");

  //创建代理对象(代理对象通过构造器持有了 目标对象的引用)
  RealUser realUserProxy = (RealUser) Enhancer.create(RealUser.class, new MeiPoCglibProxy(boy));

  RealUser girl = new RealUser(18, "kk", "肤白貌美,贴心棉袄", "善良,有责任心");
  //因为meiPoProxy是实现MethodInterceptor接口的代理类,会先调用到代理类的intercept()方法
  realUserProxy.findLove(girl);
}
  • Enhancer.create() 创建代理实现类方法参数说明
//type: 目标类的类型, callback:代理类的类型
public static Object create(Class type, Callback callback) {
        Enhancer e = new Enhancer();
        e.setSuperclass(type);//设置父类 (将目标对象类型,作为代理类的父类)
        e.setCallback(callback);
        return e.create();
}

Cglib生成的动态代理,实现类预览

  • 打印我们运行时生成的代理类的class文件,到指定路径 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\");
//打印我们运行时生成的代理类的class文件,到指定路径
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\");

// 1 生成的代理类是RealUser的子类
// 实现net.sf.cglib.proxy.Factory的目的是提供一些创建代理对象实例的工厂方法,这些方法会比反射创建对象快【clazz.newInstance()】
public class RealUser$EnhancerByCglib$234234 extends RealUser implements Factory {
    private static final Method CGLIB$findLove$2$Method;
    private static final MethodProxy CGLIB$findLove$2$Proxy;

    static void CGLIB$STATICHOOK1() {
        Class var0 = Class.forName("cn.javabus.proxy.RealUser$EnhancerByCGLIB$cb212fe7");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$findLove$2$Proxy = MethodProxy.create(var1, var0, "(Lcn/javabus/proxy/RealUser;)Z", "findLove", "CGLIB$findLove$2");
    }

    final boolean CGLIB$findLove$2(RealUser var1) {
        return super.findLove(var1);
    }
    public final boolean findLove(RealUser var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            //
            Object var2 = var10000.intercept(this, CGLIB$findLove$2$Method, new Object[]{var1}, CGLIB$findLove$2$Proxy);
            return var2 == null ? false : (Boolean) var2;
        } else {
            return super.findLove(var1);
        }
    }
    
 执行流程分析
1 生成代理类 RealUser realUserProxy = (RealUser) Enhancer.create(RealUser.class, new MeiPoCglibProxy(boy));
2 然后调用代理对象的findLove()
3 然后通过反编译代理类,查看findLove方法,发现在findLove中会调用拦截器intercept()方法。
4 然后在拦截器方法中会通过MethodProxy调用invokeSuper方法。

1 cglib动态代理,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑

2 CGLib性能比JDK创建后的动态代理对象的性能高,但CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例对象,因无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。

3 同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。

更多参考cglib 底层实现 : CGLIB动态代理底层实现原理 - SegmentFault 思否

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表