Java代理技术

前言

Java代理分为静态代理与动态代理(jdk、cglib、javaassit等),它是整个java技术中最为重要的技术之一。其中,动态代理更是在很多java框架中得到广泛地使用,比如spring aop,mybatis,hibernate等等。

使用场景

代理模式是常用的java设计模式之一,它的理论依据就是java代理技术。
代理模式使用的场景很多,常见的有以下几种:

  • 因安全原因屏蔽客户端直接访问真实对象;
  • 在远程调用中使用代理类处理远程方法调用的技术细节(RMI);
  • 为提升性能,对真实对象进行封装,从而达到延迟加载的目的;
  • 为关键的业务类记录方法执行日志;
  • 为频繁访问的数据增加缓存功能;
  • 为系统中分散在各处的业务添加通用的逻辑。

代理的实现方式

静态代理

代理类和目标类需要实现相同的接口,对静态代理类方法的调用都是通过委托给目标对象实现的。
缺点:对不同的目标类,需要定义不同的静态代理类,会造成大量的样板式重复代码;目标接口新增方法,目标类与代理类都需要修改,增加了代码维护的复杂度。
基于上述原因,通常都采用动态代理进行解决。

/**
 * 静态代理
 * @author wxyh
 * @date 2018/02/28
 */
public class StaticProxy implements HelloService {

    /**
     * 目标对象
     */
    private final HelloService target;

    public StaticProxy(HelloService target) {
        super();
        this.target = target;
    }

    @Override
    public void sayHello(String name) {
        System.out.println("Before execute StaticProxy.sayHello...");
        if (new Random().nextBoolean()) {
            // 控制对目标对象的访问
            target.sayHello(name);
            System.out.println("Say hello successfully!");
        } else {
            System.out.println(name + " absence. Say hello failed!");
        }
        System.out.println("After execute StaticProxy.sayHello...");
    }

}

JDK动态代理

JDK自带的动态代理,不需要添加其他依赖,代理类和目标类实现同一个接口。
缺点:代理类继承自Proxy类,所以只能代理实现了接口的类;代理类通过反射机制调用目标对象方法,性能有一定损耗。

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

/**
 * JDK动态代理工厂
 * @author wxyh
 * @date 2018/02/28
 */
public class JdkProxyFactory {

    /**
     * 获取代理对象
     * @param target 目标对象
     * @return 代理对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T getProxyObject(Object target) {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), new JdkDynamicProxy(target));
    }

    /**
     * JDK动态代理<br>
     * @author wxyh
     * @date 2018/02/28
     */
    public static class JdkDynamicProxy implements InvocationHandler {

        /**
         * 目标对象
         */
        private final Object target;

        public JdkDynamicProxy(Object target) {
            super();
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String fullMethodName = method.getDeclaringClass().getName() + "." + method.getName();
            // 前置逻辑
            System.out.println("JdkDynamicProxy::Before execute " + fullMethodName);
            System.out.println("Jdk Proxy Object::" + proxy.getClass().getName());
            // 反射调用目标对象方法
            Object result = method.invoke(this.target, args);
            // 后置逻辑
            System.out.println("JdkDynamicProxy::After execute " + fullMethodName);
            return result;
        }

    }

}

CGLIB动态代理

代理类对被代理类是否实现接口没有强制要求,适用范围更广,方法调用性能好。

import java.lang.reflect.Method;

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

/**
 * CGLIB动态代理工厂
 * @author wxyh
 * @date 2018/02/28
 */
public class CglibProxyFactory {

    /**
     * 获取代理对象
     * @param target 目标对象
     * @return 代理对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T getProxyObject(Object target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new CglibDynamicProxy(target));
        return (T) enhancer.create();
    }

    /**
     * CGLIB动态代理<br>
     * 动态生成的代理类为目标类的子类,在代理类中植入增强
     * @author wxyh
     * @date 2018/02/28
     */
    public static class CglibDynamicProxy implements MethodInterceptor {

        /**
         * 目标对象
         */
        @SuppressWarnings("unused")
        private final Object target;

        public CglibDynamicProxy(Object target) {
            super();
            this.target = target;
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            String fullMethodName = method.getDeclaringClass().getName() + "." + method.getName();
            // 前置逻辑
            System.out.println("CglibDynamicProxy::Before execute " + fullMethodName);
            System.out.println("Cglib Proxy Object::" + obj.getClass().getName());
            // 调用代理对象方法
            Object result = proxy.invokeSuper(obj, args);
            // 后置逻辑
            System.out.println("CglibDynamicProxy::After execute " + fullMethodName);
            return result;
        }

    }

}

HelloService接口及其实现类

/**
 * @author wxyh
 * @date 2018/02/28
 */
public interface HelloService {
    void sayHello(String name);
}
/**
 * @author wxyh
 * @date 2018/02/28
 */
public class HelloServiceImpl implements HelloService {

    @Override
    public void sayHello(String name) {
        System.out.println(String.format("Hello %s!", StringUtils.defaultIfBlank(name, "World")));
    }

}
/**
 * @author wxyh
 * @date 2018/02/28
 */
public class HelloServiceImpl2 {

    public void sayHello(String name) {
        System.out.println(String.format("Hello %s!", StringUtils.defaultIfBlank(name, "World")));
    }

}

测试代码

/**
 * @author wxyh
 * @date 2018/02/28
 */
public class ProxyTest {

    /**
     * 测试静态代理
     */
    @Test
    public void testStaticProxy() {
        HelloService helloServcie = new StaticProxy(new HelloServiceImpl());
        helloServcie.sayHello("xyz");
    }

    /**
     * 测试JDK动态代理
     */
    @Test
    public void testJdkProxy() {
        long start = System.currentTimeMillis();
        // 目标对象实现HelloService接口
        HelloService helloServcie = JdkProxyFactory.getProxyObject(new HelloServiceImpl());
        System.out.println("Create jdkProxy took " + (System.currentTimeMillis() - start) + "ms.");
        helloServcie.sayHello("xyz");
    }

    /**
     * 测试CGLIB动态代理
     */
    @Test
    public void testCglibProxy() {
        long start = System.currentTimeMillis();
        // 目标对象实现HelloService接口
        HelloService helloServcie = CglibProxyFactory.getProxyObject(new HelloServiceImpl());
        System.out.println("Create cglibProxy took " + (System.currentTimeMillis() - start) + "ms.");
        helloServcie.sayHello("xyz");

        System.out.println();

        // 目标对象没有实现接口
        HelloServiceImpl2 helloServcie2 = CglibProxyFactory.getProxyObject(new HelloServiceImpl2());
        helloServcie2.sayHello("xyz");
    }

}

测试结果

Create jdkProxy took 1ms.
JdkDynamicProxy::Before execute com.wxyh.springbootproject.common.service.HelloService.sayHello
Jdk Proxy Object::com.sun.proxy.$Proxy4
Hello xyz!
JdkDynamicProxy::After execute com.wxyh.springbootproject.common.service.HelloService.sayHello

Before execute StaticProxy.sayHello...
xyz absence. Say hello failed!
After execute StaticProxy.sayHello...

Create cglibProxy took 77ms.
CglibDynamicProxy::Before execute com.wxyh.springbootproject.common.service.HelloServiceImpl.sayHello
Cglib Proxy Object::com.wxyh.springbootproject.common.service.HelloServiceImpl$$EnhancerByCGLIB$$3b897022
Hello xyz!
CglibDynamicProxy::After execute com.wxyh.springbootproject.common.service.HelloServiceImpl.sayHello

CglibDynamicProxy::Before execute com.wxyh.springbootproject.common.service.HelloServiceImpl2.sayHello
Cglib Proxy Object::com.wxyh.springbootproject.common.service.HelloServiceImpl2$$EnhancerByCGLIB$$967091cc
Hello xyz!
CglibDynamicProxy::After execute com.wxyh.springbootproject.common.service.HelloServiceImpl2.sayHello

从测试结果可知,JDK动态代理创建过程快,这是因为创建代理对象时,调用了Proxy$ProxyClassFactory.apply方法,该方法通过调用本地方法defineClass0创建的代理对象,故其性能要高于其他实现。但JDK动态代理类的函数调用是通过反射调用目标方法的,其性能不如CGLIB动态代理。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值