第十一讲-AOP之动态代理增强JDK

第十一讲-AOP之动态代理增强JDK

JDK实现动态代理需要三个参数:

  • ClassLoader loader, 类加载器,JDK代理是在运行期间生成代理类的字节码,因此需要类加载器在运行时被加载
  • Class<?>[] interfaces, 需要实现的接口
  • InvocationHandler h 实现接口中的方法,规定方法中的行为,代理类将来被执行时就会调用该方法

使用JDK实现的动态代理简单示例:

package com.cherry.a12;

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

public class JdkProxyDemo {
    interface Foo{
        void foo();
    }

    static class Target implements Foo{

        @Override
        public void foo() {
            System.out.println("target foo");
        }
    }

    // 使用JDK代理实现功能增强 --> 只能针对接口代理
    // cglib代理既可以针对接口代理,也可以针对非接口代理
    public static void main(String[] args) {
        // 创建一个代理对象
        /**
         * ClassLoader loader,      类加载器
         * Class<?>[] interfaces,   需要实现的接口
         * InvocationHandler h      实现接口中的方法,规定方法中的行为,代理类将来被执行时就会调用该方法
         */
        // 获取类加载器
        ClassLoader loader = JdkProxyDemo.class.getClassLoader();
        Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, new InvocationHandler() {
            @Override           // 代理对象自己    正在执行的方法   方法中的参数
            public Object invoke(Object p, Method method, Object[] args) throws Throwable {
                System.out.println("before...");
                return null;
            }
        });

        // 调用代理对象中的方法(也就是实现的父接口中的方法)
        proxy.foo();
    }
}

这里代理对象为什么能够转为接口类型呢?因此JDK中Proxy.newInstance()方法是创建出来一个代理对象,而这个代理对象又实现了父接口,因此可以转为父接口类型。此外,JDK的代理实现就是对接口实现代理,生成的代理对象能够转为接口对象也是一个合理的解释。

运行结果如下:

before...

我们发现InvocationHandler中的方法被执行了,但是此时只能算是一个增强的功能(前置增强的功能),

但是待增强的目标类的原始逻辑却没有被增强,换句话说,就是该接口的实现类中的实现方法却没有被增强,所以呢,我们也要执行目标类中重写接口中的方法:

首先定义一个目标对象,并在并在InvocationHandler中使用反射调用该方法,并获取结果以及返回结果:

// 准备一个目标对象
Target target = new Target();

// ......  省略中间代码 ......
Object result = method.invoke(target, args);
return result;

运行测试如下:

before...
target foo

我们发现此时不仅对目标方法实现了功能的增强,也执行了目标类中重写了父类的目标方法!当然我们也可以实现后置增强,完整的代码如下:

package com.cherry.a12;

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

public class JdkProxyDemo {
    interface Foo{
        void foo();
    }

    static class Target implements Foo{

        @Override
        public void foo() {
            System.out.println("target foo");
        }
    }

    // 使用JDK代理实现功能增强 --> 只能针对接口代理
    // cglib代理既可以针对接口代理,也可以针对非接口代理
    public static void main(String[] args) {
        // 准备一个目标对象
        Target target = new Target();


        // 创建一个代理对象
        /**
         * ClassLoader loader,      类加载器
         * Class<?>[] interfaces,   需要实现的接口
         * InvocationHandler h      实现接口中的方法,规定方法中的行为,代理类将来被执行时就会调用该方法
         */
        // 获取类加载器
        ClassLoader loader = JdkProxyDemo.class.getClassLoader();
        Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}	, new InvocationHandler() {
            @Override           // 代理对象自己    正在执行的方法   方法中的参数
            public Object invoke(Object p, Method method, Object[] args) throws Throwable {
                System.out.println("before...");
                Object result = method.invoke(target, args);
                System.out.println("after...");
                return result;
            }
        });

        // 调用代理对象中的方法(也就是实现的父接口中的方法)
        proxy.foo();
    }
}

运行结果如下:

before...
target foo
after...

JDK实现的代理的特点如下:

  • JDK的动态代理和我们的目标类(接口实现类)是兄弟关系,因为它们都实现了共同的父接口!
  • 既然它们两个都是兄弟关系,它们两者之间可以相互转换吗?不能的!比如猫和狗都实现了‘动物’这个接口,两者之间是不能进行转换的,但是两者都能转为‘动物(父接口)’
  • 目标类可以使用final关键字修饰吗?可以的,目标类和代理类是兄弟关系,所有不存在继承关系,换句话说,目标类和代理类也仅仅是兄弟关系,并没有其它关系!

关于目标类和代理类仅仅是兄弟关系的演示(目标类使用final关键字修饰):

package com.cherry.a12;

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

public class JdkProxyDemo {
    interface Foo{
        void foo();
    }

    static final class Target implements Foo{

        @Override
        public void foo() {
            System.out.println("target foo");
        }
    }

    // 使用JDK代理实现功能增强 --> 只能针对接口代理
    // cglib代理既可以针对接口代理,也可以针对非接口代理
    public static void main(String[] args) {
        // 准备一个目标对象
        Target target = new Target();


        // 创建一个代理对象
        /**
         * ClassLoader loader,      类加载器
         * Class<?>[] interfaces,   需要实现的接口
         * InvocationHandler h      实现接口中的方法,规定方法中的行为,代理类将来被执行时就会调用该方法
         */
        // 获取类加载器
        ClassLoader loader = JdkProxyDemo.class.getClassLoader();
        Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, new InvocationHandler() {
            @Override           // 代理对象自己    正在执行的方法   方法中的参数
            public Object invoke(Object p, Method method, Object[] args) throws Throwable {
                System.out.println("before...");
                Object result = method.invoke(target, args);
                System.out.println("after...");
                return result;
            }
        });

        // 调用代理对象中的方法(也就是实现的父接口中的方法)
        proxy.foo();
    }
}

运行如下:

before...
target foo
after...
posted @   LilyFlower  阅读(10)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示