Java基础系列:代理Proxy及源码分析

1 简介

代理:

序号代理方式备注
1静态代理完全1:1实现目标对象
2动态代理通过newProxyInstance代理目标接口的实现类
3Cglib代理通过Enhancer代理目标类(创建接口实现类)

2 Usage

2.1 IUserService

统一服务接口.

package basic.datatype.proxy;

/**
 * Target object: interface.
 * @author xindaqi
 * @since 2020-10-13
 */

public interface IUserService {

    Integer add();

    Integer delete(); 
    
}

2.2 统一接口实现

目标类实现统一接口.

package basic.datatype.proxy.impl;

import basic.datatype.proxy.IUserService;

/**
 * Target object implements. 
 * @author xindaqi
 * @since 2020-10-13
 */

public class UserServiceImpl implements IUserService{

    public Integer add() {
        System.out.println("Implements function: add");
        return 1;
    }

    public Integer delete() {
        System.out.println("Implements function: delete");
        return 1;
    }
 
}

2.1 静态代理

静态代理类实现统一接口.
在这里插入图片描述

图2.1 静态代理

2.1.0 接口实现

package basic.datatype.proxy.staticproxy.impl;

import basic.datatype.proxy.IUserService;

/**
 * Proxy implements.
 * @author xindaqi
 * @since 2020-10-13
 */

public class UserServiceProxyImpl implements IUserService{

    private IUserService userService;

    public UserServiceProxyImpl(IUserService userService) {
        this.userService = userService;
    }

    public Integer add() {
        System.out.println("Implements function: proxy add");
        userService.add();
        return 1;
    }

    public Integer delete() {
        System.out.println("Implements function: proxy delete");
        userService.delete();
        return 1;

    }
    
}

2.1.2 测试

package basic.datatype.proxy.staticproxy;

import basic.datatype.proxy.*;
import basic.datatype.proxy.impl.*;
import basic.datatype.proxy.staticproxy.impl.*;

/**
 * Static proxy test. 
 * @author xindaqi
 * @since 2020-10-13
 */

public class StaticProxyTest {

    public static void main(String[] args) {
        // Target object.
        IUserService userService = new UserServiceImpl();
        // Proxy object.
        UserServiceProxyImpl proxy = new UserServiceProxyImpl(userService);
        proxy.add();
        System.out.println("==我是分割线==");
        proxy.delete();

    }

    
}
  • 结果
Implements function: proxy add
Implements function: add
==我是分割线==
Implements function: proxy delete
Implements function: delete

2.2 动态代理

动态代理实现InvocationHandler.

在这里插入图片描述

图2.2 动态代理

2.2.1 InvocationHandler实现

package basic.datatype.proxy.dynamicproxy.impl;

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

/**
 * InvocationHandler implements. 
 * @author xindaqi
 * @since 2020-10-13
 */

public class UserServiceInvocationImpl implements InvocationHandler {

    private Object target;

    public UserServiceInvocationImpl(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Implements function: invoke");
        Object obj = method.invoke(target);
        System.out.println("Implements funciton: invoke object");
        return obj;
    }
    
}
  • InvocationHandler源码
    通过invoke创建引用句柄.
public interface InvocationHandler {

    /**
     * Processes a method invocation on a proxy instance and returns
     * the result.  This method will be invoked on an invocation handler
     * when a method is invoked on a proxy instance that it is
     * associated with.
     *
     * @param   proxy the proxy instance that the method was invoked on
     *
     * @param   method the {@code Method} instance corresponding to
     * the interface method invoked on the proxy instance.  The declaring
     * class of the {@code Method} object will be the interface that
     * the method was declared in, which may be a superinterface of the
     * proxy interface that the proxy class inherits the method through.
     *
     * @param   args an array of objects containing the values of the
     * arguments passed in the method invocation on the proxy instance,
     * or {@code null} if interface method takes no arguments.
     * Arguments of primitive types are wrapped in instances of the
     * appropriate primitive wrapper class, such as
     * {@code java.lang.Integer} or {@code java.lang.Boolean}.
     *
     * @return  the value to return from the method invocation on the
     * proxy instance.  If the declared return type of the interface
     * method is a primitive type, then the value returned by
     * this method must be an instance of the corresponding primitive
     * wrapper class; otherwise, it must be a type assignable to the
     * declared return type.  If the value returned by this method is
     * {@code null} and the interface method's return type is
     * primitive, then a {@code NullPointerException} will be
     * thrown by the method invocation on the proxy instance.  If the
     * value returned by this method is otherwise not compatible with
     * the interface method's declared return type as described above,
     * a {@code ClassCastException} will be thrown by the method
     * invocation on the proxy instance.
     *
     * @throws  Throwable the exception to throw from the method
     * invocation on the proxy instance.  The exception's type must be
     * assignable either to any of the exception types declared in the
     * {@code throws} clause of the interface method or to the
     * unchecked exception types {@code java.lang.RuntimeException}
     * or {@code java.lang.Error}.  If a checked exception is
     * thrown by this method that is not assignable to any of the
     * exception types declared in the {@code throws} clause of
     * the interface method, then an
     * {@link UndeclaredThrowableException} containing the
     * exception that was thrown by this method will be thrown by the
     * method invocation on the proxy instance.
     *
     * @see     UndeclaredThrowableException
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

2.2.2 newProxyInstance新建实例

package basic.datatype.proxy.dynamicproxy;

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

public class UserServiceDynamicProxy {

    private Object target;

    private InvocationHandler invocationHandler;

    public UserServiceDynamicProxy(Object target, InvocationHandler invocationHandler) {
        this.target = target;
        this.invocationHandler = invocationHandler;
    }

    public Object getProxy() {
        Object obj = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), invocationHandler);
        return obj;
    }
    
}
  • newProxyInstance源码
    通过Constructor实例化对象,通过向上转型为接口,直接代理目标对象(接口实现),不需要实现目标对象,更加灵活,返回InvocationHandler对象,即可访问目标对象的方法.
/**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     *
     * <p>{@code Proxy.newProxyInstance} throws
     * {@code IllegalArgumentException} for the same reasons that
     * {@code Proxy.getProxyClass} does.
     *
     * @param   loader the class loader to define the proxy class
     * @param   interfaces the list of interfaces for the proxy class
     *          to implement
     * @param   h the invocation handler to dispatch method invocations to
     * @return  a proxy instance with the specified invocation handler of a
     *          proxy class that is defined by the specified class loader
     *          and that implements the specified interfaces
     * @throws  IllegalArgumentException if any of the restrictions on the
     *          parameters that may be passed to {@code getProxyClass}
     *          are violated
     * @throws  SecurityException if a security manager, <em>s</em>, is present
     *          and any of the following conditions is met:
     *          <ul>
     *          <li> the given {@code loader} is {@code null} and
     *               the caller's class loader is not {@code null} and the
     *               invocation of {@link SecurityManager#checkPermission
     *               s.checkPermission} with
     *               {@code RuntimePermission("getClassLoader")} permission
     *               denies access;</li>
     *          <li> for each proxy interface, {@code intf},
     *               the caller's class loader is not the same as or an
     *               ancestor of the class loader for {@code intf} and
     *               invocation of {@link SecurityManager#checkPackageAccess
     *               s.checkPackageAccess()} denies access to {@code intf};</li>
     *          <li> any of the given proxy interfaces is non-public and the
     *               caller class is not in the same {@linkplain Package runtime package}
     *               as the non-public interface and the invocation of
     *               {@link SecurityManager#checkPermission s.checkPermission} with
     *               {@code ReflectPermission("newProxyInPackage.{package name}")}
     *               permission denies access.</li>
     *          </ul>
     * @throws  NullPointerException if the {@code interfaces} array
     *          argument or any of its elements are {@code null}, or
     *          if the invocation handler, {@code h}, is
     *          {@code null}
     */
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }
  • Method invoke源码
    调用参数(Object,对象)中的底层方法,运行时调用(反射),返回对象,该对象可以访问参数Object的方法.
/**
     * Invokes the underlying method represented by this {@code Method}
     * object, on the specified object with the specified parameters.
     * Individual parameters are automatically unwrapped to match
     * primitive formal parameters, and both primitive and reference
     * parameters are subject to method invocation conversions as
     * necessary.
     *
     * <p>If the underlying method is static, then the specified {@code obj}
     * argument is ignored. It may be null.
     *
     * <p>If the number of formal parameters required by the underlying method is
     * 0, the supplied {@code args} array may be of length 0 or null.
     *
     * <p>If the underlying method is an instance method, it is invoked
     * using dynamic method lookup as documented in The Java Language
     * Specification, Second Edition, section 15.12.4.4; in particular,
     * overriding based on the runtime type of the target object will occur.
     *
     * <p>If the underlying method is static, the class that declared
     * the method is initialized if it has not already been initialized.
     *
     * <p>If the method completes normally, the value it returns is
     * returned to the caller of invoke; if the value has a primitive
     * type, it is first appropriately wrapped in an object. However,
     * if the value has the type of an array of a primitive type, the
     * elements of the array are <i>not</i> wrapped in objects; in
     * other words, an array of primitive type is returned.  If the
     * underlying method return type is void, the invocation returns
     * null.
     *
     * @param obj  the object the underlying method is invoked from
     * @param args the arguments used for the method call
     * @return the result of dispatching the method represented by
     * this object on {@code obj} with parameters
     * {@code args}
     *
     * @exception IllegalAccessException    if this {@code Method} object
     *              is enforcing Java language access control and the underlying
     *              method is inaccessible.
     * @exception IllegalArgumentException  if the method is an
     *              instance method and the specified object argument
     *              is not an instance of the class or interface
     *              declaring the underlying method (or of a subclass
     *              or implementor thereof); if the number of actual
     *              and formal parameters differ; if an unwrapping
     *              conversion for primitive arguments fails; or if,
     *              after possible unwrapping, a parameter value
     *              cannot be converted to the corresponding formal
     *              parameter type by a method invocation conversion.
     * @exception InvocationTargetException if the underlying method
     *              throws an exception.
     * @exception NullPointerException      if the specified object is null
     *              and the method is an instance method.
     * @exception ExceptionInInitializerError if the initialization
     * provoked by this method fails.
     */
    @CallerSensitive
    public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }

2.2.3 测试

动态代理:

  • 实现InvocationHandler,重写invoke
  • 通过newProxyInstance创建实例
package basic.datatype.proxy.dynamicproxy;

import basic.datatype.proxy.*;
import basic.datatype.proxy.impl.*;
import basic.datatype.proxy.dynamicproxy.impl.*;

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

/**
 * Dynaminc proxy test. 
 * @author xindaqi
 * @since 2020-10-13
 */

public class DynamicProxyTest {

    public static void main(String[] args) {
        IUserService userService = new UserServiceImpl();
        System.out.println("Target object: " + userService.getClass());
        System.out.println("Target clasloader: " + userService.getClass().getClassLoader());
        System.out.println("Target interface: " + userService.getClass().getInterfaces());

        UserServiceInvocationImpl userServiceInvocationImpl = new UserServiceInvocationImpl(userService);

        IUserService proxy = (IUserService) new UserServiceDynamicProxy(userService, userServiceInvocationImpl).getProxy();
        System.out.println("Proxy object: " + proxy.getClass());
        System.out.println("==我是分割线:1==");
        proxy.add();
        proxy.delete();
        System.out.println("==我是分割线:2==");
        IUserService target = new UserServiceImpl();
        UserServiceInvocationImpl invocation = new UserServiceInvocationImpl(userService);
        IUserService proxy2 = (IUserService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), invocation);
        proxy2.add();
        proxy2.delete();

        // Implements Handler
        System.out.println("==我是分割线:3==");
        final IUserService target3 = new UserServiceImpl();
        IUserService proxy3 = (IUserService) Proxy.newProxyInstance(target3.getClass().getClassLoader(), target3.getClass().getInterfaces(), new InvocationHandler() {

            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("Implements function: invoke");
                Object obj = method.invoke(target);
                System.out.println("Implements funciton: invoke object");
                return obj;
            }
        });

        proxy3.add();
        proxy3.delete();

    }
    
}
  • 结果
Target object: class basic.datatype.proxy.impl.UserServiceImpl
Target clasloader: sun.misc.Launcher$AppClassLoader@6d06d69c
Target interface: [Ljava.lang.Class;@70dea4e
Proxy object: class com.sun.proxy.$Proxy0
==我是分割线:1==
Implements function: invoke
Implements function: add
Implements funciton: invoke object
Implements function: invoke
Implements function: delete
Implements funciton: invoke object
==我是分割线:2==
Implements function: invoke
Implements function: add
Implements funciton: invoke object
Implements function: invoke
Implements function: delete
Implements funciton: invoke object
==我是分割线:3==
Implements function: invoke
Implements function: add
Implements funciton: invoke object
Implements function: invoke
Implements function: delete
Implements funciton: invoke object

2.3 Cglib代理

先清除类,再新建类,最后通过Method反射获取对象。

在这里插入图片描述

图2.3 Cglib代理
  • 引入依赖
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.10</version>
</dependency>

2.3.1 目标类

package basic.datatype.proxy;

/**
 * @author xindaqi
 * @since 2020-10-13
 */

public class UserService {

    public Integer add() {
        System.out.println("Class function: add");
        return 1;
    }

    public Integer delete() {
        System.out.println("Class function: delete");
        return 1;
    }
    
}

2.3.2 代理类

package basic.datatype.proxy.cglibproxy;

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

import java.lang.reflect.Method;

/**
 * Code generate library. 
 * @author xindaqi
 * @since 2020-10-13
 */

public class UserServiceCglibProxy implements MethodInterceptor{

    private Object target;

    public UserServiceCglibProxy(Object target) {
        this.target = target;
    }

    public Object getProxyInstance() {
        Enhancer en = new Enhancer();
        en.setSuperclass(target.getClass());
        en.setCallback(this);
        return en.create();
    }

    public Object intercept(Object object, Method method, Object[] arg2, MethodProxy proxy) throws Throwable {
        System.out.println("Cglib function: invoke");
        Object obj = method.invoke(target);
        System.out.println("Cglib function: invoke object");
        return obj;
    }
    
}
  • setSuperclass
    入参为类对象,清空类.
/**
     * Set the class which the generated class will extend. As a convenience,
     * if the supplied superclass is actually an interface, <code>setInterfaces</code>
     * will be called with the appropriate argument instead.
     * A non-interface argument must not be declared as final, and must have an
     * accessible constructor.
     * @param superclass class to extend or interface to implement
     * @see #setInterfaces(Class[])
     */
    public void setSuperclass(Class superclass) {
        if (superclass != null && superclass.isInterface()) {
            setInterfaces(new Class[]{ superclass });
        } else if (superclass != null && superclass.equals(Object.class)) {
            // affects choice of ClassLoader
            this.superclass = null;
        } else {
            this.superclass = superclass;
        }
    }
  • setCallback
/**
     * Set the single {@link Callback} to use.
     * Ignored if you use {@link #createClass}.
     * @param callback the callback to use for all methods
     * @see #setCallbacks
     */
    public void setCallback(final Callback callback) {
        setCallbacks(new Callback[]{ callback });
    }
  • create
    清空类后,新建接口实例.
/**
     * Generate a new class if necessary and uses the specified
     * callbacks (if any) to create a new object instance.
     * Uses the no-arg constructor of the superclass.
     * @return a new instance
     */
    public Object create() {
        classOnly = false;
        argumentTypes = null;
        return createHelper();
    }

  • createHelper
private Object createHelper() {
        preValidate();
        Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
                ReflectUtils.getNames(interfaces),
                filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
                callbackTypes,
                useFactory,
                interceptDuringConstruction,
                serialVersionUID);
        this.currentKey = key;
        Object result = super.create(key);
        return result;
    }

2.3.3 测试

package basic.datatype.proxy.cglibproxy;

import basic.datatype.proxy.UserService;

/**
 * Code generate library proxy test. 
 * @author xindaqi
 * @since 2020-10-13
 */

public class CglibProxyTest {

    public static void main(String[] args) {
        UserService target = new UserService();
        UserServiceCglibProxy proxyFactory = new UserServiceCglibProxy(target);
        UserService proxy = (UserService) proxyFactory.getProxyInstance();

        System.out.println("Proxy object: " + proxy.getClass());
        proxy.add();
        proxy.delete();
    }
    
}
  • 结果
Proxy object: class basic.datatype.proxy.UserService$$EnhancerByCGLIB$$4052db64
Cglib function: invoke
Class function: add
Cglib function: invoke object
Cglib function: invoke
Class function: delete
Cglib function: invoke object

[参考文献]
[1]https://www.cnblogs.com/leeego-123/p/10995975.html
[2]https://www.jianshu.com/p/8ccdbe00ff06

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
首先声明,这段源代码不是我编写的,让我们感谢这位名叫Carl Harris的大虾,是他编写了这段代码并将其散播到网上供大家学习讨论。这段代码虽然只是描述了最简单的proxy操作,但它的确是经典,它不仅清晰地描述了客户机/服务器系统的概念,而且几乎包括了Linux网络编程的方方面面,非常适合Linux网络编程的初学者学习。   这段Proxy程序的用法是这样的,我们可以使用这个proxy登录其它主机的服务端口。假如编译后生成了名为Proxy的可执行文件,那么命令及其参数的描述为:    ./Proxy   其中参数proxy_port是指由我们指定的代理服务器端口。参数remote_host是指我们希望连接的远程主机的主机名,IP地址也同样有效。这个主机名在网络上应该是唯一的,如果您不确定的话,可以在远程主机上使用uname -n命令查看一下。参数service_port是远程主机可提供的服务名,也可直接键入服务对应的端口号。这个命令的相应操作是将代理服务器的proxy_port端口绑定到remote_host的service_port端口。然后我们就可以通过代理服务器的proxy_port端口访问remote_host了。例如一台计算机,网络主机名是legends,IP地址为10.10.8.221,如果在我的计算机上执行:    [root@lee /root]#./proxy 8000 legends telnet   那么我们就可以通过下面这条命令访问legends的telnet端口。 ----------------------------------------------------------------- [root@lee /root]#telnet legends 8000 Trying 10.10.8.221... Connected to legends(10.10.8.221). Escape character is '^]' Red Hat Linux release 6.2(Zoot) Kernel 2.2.14-5.0 on an i686 Login: -----------------------------------------------------------------

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天然玩家

坚持才能做到极致

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值