016 Java中的动态代理

代理

代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

代理可以实现过滤请求、插入横切逻辑等功能,应用场景丰富多彩。

代理的方式分为静态代理和动态代理两种。

静态代理

程序运行前代理类的字节码文件依然存在,需要程序员编写源文件。

  • 缺点:要针对于每一个类撰写代理类;对于单个被代理的类,如果需要被代理的方法很多,又加大了工作量。
  • 优点:直观,可读性较强。

动态代理

程序运行时动态生成代理类的字节码文件,不需要程序员编写代理类java文件。

  • 缺点:由于是运行时动态生成的,因此可读性不是很强;而且受限于被代理类自身的属性(jdk需要提供接口,cglib需要是非私有类)。
  • 优点:代码更加简洁,解放了无谓的编码工作。

实现方式

让你来实现一个代理类,需要哪些上下文,有哪些解决方案?

jdk和cglib两种解决方案。

要生产一个类A的代理类,唯一需要了解的就是生成一个什么类,因此就有了基于该类的接口构造一个“A”。

至于如何生成一个class文件,在既定规则下你当然可以先生产java文件,再编译成class文件。而最好的做法是直接操作字节码文件,jdk和cglib生成字节码文件分别用了sun的ProxyGenerator和开源项目ASM字节码框架。

通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时可以实现自定义的控制策略(Spring的AOP机制),设计上获得更大的灵活性。

JDK的动态代理实现

jdk的动态代理,依赖的是反射包下的InvocationHandler接口,我们的代理类实现InvocationHandler,重写invoke()方法,每当我们的代理类调用方法时,都会默认先经过invoke()方法。

UserService接口

public interface UserService {
    public String getName(int id);
    public Integer getAge(int id);
}

接口的实现类UserServiceImpl

public class UserServiceImpl implements UserService {

    public String getName(int id) {
        System.out.println("------getName------");
        return "Tom";
    }

    public Integer getAge(int id) {
        System.out.println("------getAge------");
        return 10;
    }
}

UserInvocationHandler.java

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

public class UserInvocationHandler implements InvocationHandler {

    private Object target;

    UserInvocationHandler() {
        super();
    }

    UserInvocationHandler(Object target) {
        super();
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if("getName".equals(method.getName())){
            System.out.println("++++++before " + method.getName() + "++++++");
            Object result = method.invoke(target, args);
            System.out.println("++++++after " + method.getName() + "++++++");
            return result;
        }else{
            Object result = method.invoke(target, args);
            return result;
        }
    }
}

测试类TestUserInvocationHandler.java

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

public class TestUserInvocationHandler {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        InvocationHandler invocationHandler = new UserInvocationHandler(userService);

        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                invocationHandler);

        System.out.println(userServiceProxy.getName(1));
        System.out.println(userServiceProxy.getAge(1));
    }
}

运行结果:

cglib的动态代理实现

cglib需要的jar包:cglib.jarasm.jar

cglib依赖的是cglib包下的MethodInterceptor接口,每调用代理类的方法,都会调用intercept方法

CglibMethodInterceptor.java

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("------before " + methodProxy.getSuperName() + "------");
        Object o1 = methodProxy.invokeSuper(o, args);
        System.out.println("------after " + methodProxy.getSuperName() + "------");
        return o1;
    }
}

TestCglibMethodInterceptor.java

import net.sf.cglib.proxy.Enhancer;

public class TestCglibMethodInterceptor {
    public static void main(String[] args) {
        CglibMethodInterceptor cglibProxy = new CglibMethodInterceptor();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);
        enhancer.setCallback(cglibProxy);

        UserService o = (UserService) enhancer.create();
        o.getName(1);
        o.getAge(1);
    }
}

运行结果:

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏xingoo, 一个梦想做发明家的程序员

AngularJS API之copy深拷贝

angular提供了一个可以复制对象的api——copy(source,destination),它会对source对象执行深拷贝。 使用时需要注意下面几...

22160
来自专栏微信公众号:Java团长

Java面试宝典

允许不同类对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用).主要有以下优点:

25020
来自专栏微信公众号:Java团长

Java堆和栈的区别

在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。当在一段代码块中定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作...

27730
来自专栏Java爬坑系列

【Java入门提高篇】Day11 Java代理——JDK动态代理

  今天来看看Java的另一种代理方式——JDK动态代理   我们之前所介绍的代理方式叫静态代理,也就是静态的生成代理对象,而动态代理则是在运行时创建代理对象。...

21970
来自专栏Brian

Python进阶教程(二)

概述 在上一篇博客中,我们介绍了Python进阶教程(一),还有一些新的技巧没有翻译完,我们下面来继续我们的翻译。 Intermediate Python 中译...

50180
来自专栏爱撒谎的男孩

Struts2之Action类的实现方式

42040
来自专栏闪电gogogo的专栏

【数据结构(C语言版)系列三】 队列

队列是一种先进先出的线性表,它只允许在表的一端进行插入,而在另一端删除元素。这和我们日常生活中的排队是一致的,最早进入队列的元素最早离开。在队列中,允许插入的一...

35120
来自专栏java一日一条

一个Java对象到底占用多大内存

大家可以用这个代码边看边验证,注意的是,运行这个程序需要通过javaagent注入Instrumentation,具体可以看原博客。我今天主要是总结下手动计算J...

8110
来自专栏武军超python专栏

2018年7月24日初次接触面向对象

昨天io模块知识的回顾补充: 用json模块可以把程序中的数据转换为字符串类型存储到文件中,但是字符串类型不安全,可以用记事本 直接打开查看里面的的所有内容

13630
来自专栏JavaEdge

HotSPot虚拟机对象探秘1 对象的创建过程2 对象的内存布局3 访问对象的过程

403160

扫码关注云+社区

领取腾讯云代金券