从使用Java代理的极简代码看Java代理逻辑

KevinQ 发表于 2022/04/20 20:52:04 2022/04/20
【摘要】 问题起源最近受到注解启发,学习了Java注解的实现方式,以及Java注解执行器中涉及到的Java反射的过程,但是笔者本人更关心的拦截器、过滤器等涉及到的Java AOP还没有涉及到。网上搜索到的很多关于AOP的实现都与Spring做了深度绑定,然而,我有个小癖好,不太喜欢与框架深度绑定的,所以,我们只是简简单单的学习一下AOP的实现方式,而不是Spring AOP的实现方式,因为“我们要做...

问题起源

最近受到注解启发,学习了Java注解的实现方式,以及Java注解执行器中涉及到的Java反射的过程,但是笔者本人更关心的拦截器、过滤器等涉及到的Java AOP还没有涉及到。网上搜索到的很多关于AOP的实现都与Spring做了深度绑定,然而,我有个小癖好,不太喜欢与框架深度绑定的,所以,我们只是简简单单的学习一下AOP的实现方式,而不是Spring AOP的实现方式,因为“我们要做Java Developer,而不是Spring Developer”。

代理

想要实现AOP,先要学习Java中的代理。代理模式的含义是:

给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

代理分为静态代理,动态代理。

静态代理

静态代理的例子代码如下所示,我们不做过多的叙述,具体可以看文中注释。(其实,自己把代码敲一遍就知道能够理解静态代理了。)

public interface ApiCall {
    void call();
}

/**
* 具体的执行类
*/
public class SaleApiCall implements ApiCall {
    private String name;


    public SaleApiCall(String name) {
        this.name = name;
    }

    public void call() {
        System.out.println(name + "被调用");
    }
}

public class SaleApiCallProxy implements ApiCall {
    SaleApiCall saleApiCall;

    public SaleApiCallProxy(ApiCall api) {
        if (api.getClass() == SaleApiCall.class) {
            this.saleApiCall = (SaleApiCall) api;
        }
    }

    //代理执行,调用被代理接口的行为
    public void call() {
        // before 这里可以添加方法执行器的逻辑
        saleApiCall.call();
        // after 这里可以添加方法执行后的逻辑
    }
}

public class StaticProxyTest {
    public static void main(String[] args) {
        ApiCall qw = new SaleApiCall("订单接口");
        ApiCall proxy = new SaleApiCallProxy(qw);
        proxy.call();
    }
}

输出结果:

订单接口被调用

动态代理

动态代理分为JDK动态代理与使用cglib的动态代理,前者需要被代理对象实现接口,而后者不需要。我们先来看看如何使用JDK来完成动态代理。

理解下面代码代码“动态”的核心观念在于,我们没有修改所要代理的对象内部,而是通过外部代理类来获取对象与执行方法。

例如,原本有一个接口Person与一个实现接口的类Programmer

public interface Person {
    void work();
}

public class Programmer implements Person{
    String name;

    public Programmer(String name) {
        this.name = name;
    }

    @Override
    public void work() {
        System.out.println(name + "进行编程工作");
    }
}

现在,我们希望在不改变原内容的情况下,通过代理方式生成Programmer对象并对其执行前后插入指定的行为:

public class ProgrammerInvocationHandler<T> implements InvocationHandler {
    T target;

    public ProgrammerInvocationHandler(T target) {
        this.target = target;
    }

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

上述类实现了接口InvocationHandler,这个接口中有一个方法invoke,我们可以从invoke的内部代码大致猜测到其执行逻辑,尤其是method.invoke(...)这一句。

public class DynamicProxyTest {
    public static void main(String[] args) {
        Person realPerson = new Programmer("洛叶飘");
        InvocationHandler programmerInvocationHandler = new ProgrammerInvocationHandler<Person>(realPerson);
        Person lyp = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, programmerInvocationHandler);
        lyp.work();
    }
}

如上所示,便通过Invocation与Proxy生成了监听的对象lyp,这个对象的所有方法执行,都会触发ProgrammerInvocationHandler中的invoke方法。

上述方法的执行结果如下:

work方法执行前
洛叶飘进行编程工作
work方法执行后

这种方法需要我们的类Programmer来实现一个接口Person,那么又没有更简单的方式呢?

有的,那就是使用cglib包。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:cloudbbs@huaweicloud.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。