目 录CONTENT

文章目录

动态代理基础知识

FatFish1
2025-01-13 / 0 评论 / 0 点赞 / 77 阅读 / 0 字 / 正在检测是否收录...

动态代理概述

什么是代理?

比如我要去租房,中介代替房东来跟我谈,中介就是房东的代理,中介是为服务提供方做代理,这种就叫正向代理

反之,如果我是租客,中介代替我找房东谈,这就叫反向代理

为什么需要代理?

比如租房的过程,一般是带租客看房,签合同,拿钱等,如果想象成一个java方法,那就是将入参闲置房转换成返回值租金,其中的流程就是方法的代码行

10个房东可以分别完成这些事情,但这是一种开销。如果有中介,10个房东只需要说我要出租,把房(入参)给中介,等中介把出参返回来就好了,这样的代码就显得十分简单

在java开发中,往往把一些日志输出、性能打点等作为代理的实现

什么是动态代理?

静态代理是指代理类在编译期就已经确定,即需要事先手动编写一个代理类。动态代理则是在运行时动态生成代理类。举个开发例子:

  • 如果我要开发一个租房的静态代理,那就房东作为原始类,实现出租方法,同时编写中介类,也实现出租方法,调用时调用中介类

  • 如果我要开发一个租房的动态代理,可以参考下面JDK动态代理和CGLib流程

静态代理和动态代理各有各的好处和问题:

  • 静态代理更快,代码更清晰,但如果访问多了,每一次执行代理都生成一个代理对象,内存开销更大

  • 动态代理可以减少重复代码开发,内存开销小,但是代码更隐晦,更复杂,且访问流程会变慢

动态代理分类

常用有jdk动态代理和CGLib代理

补链接图二者差异

动态代理编码思路

JDK动态代理

JDK动态代理的代理对象必须是某个接⼝的实现,它是通过在运⾏期间创建⼀个接⼝的实现类来完成对⽬标对象的代理

JdkDynamicAopProxy实现了InvocationHandler接口的invoke方法,这个接口是就是真正实现拦截逻辑的方法

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxyMain {

    public static void main(String[] args) {
        JDKProxyTestInterface target = new JDKProxyTestInterfaceImpl();
        // 获取代理方法
        JDKProxyTestInterface proxy =
         (JDKProxyTestInterface) Proxy
         .newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new JDKProxyTestInvocationHandler(target));
        // 调用代理对象方法
        proxy.testProxy();
    }

    interface JDKProxyTestInterface {
        void testProxy();
    }

    static class JDKProxyTestInterfaceImpl
                    implements JDKProxyTestInterface {
        @Override
        public void testProxy() {
            System.out.println("testProxy");
        }
    }

   static class JDKProxyTestInvocationHandler implements InvocationHandler {
        private Object target;
        
        // 编写构造函数
        public JDKProxyTestInvocationHandler(Object target){
            this.target=target;
        }

        // 实现invoke方法
        @Override
        public Object invoke(Object proxy, Method method,
                             Object[] args) throws Throwable {
            System.out.println("执行前");
            Object result=  method.invoke(this.target,args);
            System.out.println("执行后");
            return result;
        }
    }
}

在整个创建过程中,对于InvocationHandler的创建是最为核⼼的,在⾃定义的InvocationHandler中需要重写3个函数:

  • 构造函数:将代理的对象传⼊。

  • InvocationHandler#invoke⽅法,此⽅法中实现了动态代理。

  • 获取代理⽅法,此⽅法千篇⼀律,但是必不可少,只是Proxy#newProxyInstance在哪里被调用的问题

CGLib动态代理

CGLIB代理的实现原理类似于JDK动态代理,只是它在运⾏期间⽣成的代理对象是针对⽬标类扩展的⼦类。

CGLIB是⾼效的代码⽣成包,底层是依靠ASM(开源的Java字节码编辑类库)操作字节码实现的,性能⽐JDK强

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyTest {

   static class CglibProxyService {
        public  CglibProxyService(){
        }
        void sayHello(){
            System.out.println(" hello !");
        }
    }

    static class CglibProxyInterceptor implements MethodInterceptor{
        @Override
        public Object intercept(Object sub, Method method,
             Object\[\] objects, MethodProxy methodProxy)
                                          throws Throwable {
            System.out.println("before hello");
            Object object = methodProxy.invokeSuper(sub, objects);
            System.out.println("after hello");
            return object;
        }
    }

    public static void main(String\[\] args) {
        // 通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        // 设置enhancer对象的父类
        enhancer.setSuperclass(CglibProxyService.class);
        // 设置enhancer的回调对象
        enhancer.setCallback(new CglibProxyInterceptor());
        // 创建代理对象
        CglibProxyService proxy= (CglibProxyService)enhancer.create();
        System.out.println(CglibProxyService.class);
        System.out.println(proxy.getClass());
        // 通过代理对象调用目标方法
        proxy.sayHello();
    }
}

main方法中先构造了一个Callback数组,有两个代理切面

动态代理enhancer使用的callbackfilter方法是如果方法名为play,返回1,否则返回0

这对应了callback数组中两个代理切面的顺序。即代理对象person执行的是play方法,调用切面1 (MyApiInterceptorForPlay)进行代理操作,否则调用切面2。

在CGLib中核心是MethodInterceptor,实现MethodInterceptor 接口,在调用目标对象的方法时,就可以实现在调用方法之前、调用方法过程中、调用方法之后对其进行控制

使用spring体系+MethodInterceptor也可以实现

public class TestMethodInterceptor  {

    public static void main(String[] args) {
        ProxyFactory proxyFactory=new ProxyFactory();
        proxyFactory.setTarget(new TestMethodInterceptor());
        proxyFactory.addAdvice(new adviseMethodInterceptor());
        Object proxy = proxyFactory.getProxy();
        TestMethodInterceptor methodInterceptor = (TestMethodInterceptor) proxy;
        methodInterceptor.doSomeThing("通过代理工厂设置代理对象,拦截代理方法");
    }

    public static class adviseMethodInterceptor implements MethodInterceptor{
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {

            Object result=null;
            try{
                System.out.println("方法执行之前:"+methodInvocation.getMethod().toString());
                result= methodInvocation.proceed();
                System.out.println("方法执行之后:"+methodInvocation.getMethod().toString());
                System.out.println("方法正常运行结果:"+result);
                return result;
            }catch (Exception e){
                System.out.println("方法出现异常:"+e.toString());
                System.out.println("方法运行Exception结果:"+result);
                return result;
            }
        }
    }

    public String doSomeThing(String someThing){
        //int i=5/0;
        return "执行被拦截的方法:"+someThing;
    }
}

// 输出结果:
// 方法执行之前:public java.lang.String com.blog.test.aop.TestMethodInterceptor.doSomeThing(java.lang.String)
// 方法执行之后:public java.lang.String com.blog.test.aop.TestMethodInterceptor.doSomeThing(java.lang.String)
// 方法正常运行结果:执行被拦截的方法:通过代理工厂设置代理对象,拦截代理方法

0

评论区