代理模式入门

什么是代理模式

代理模式就是不对原始类进行修改的情况下,
对该类中方法进行增强的一种解决方案。
以下代码已上传码云-------->代理模式源代码

静态代理

举个例子,小王有个业务需要获取到当前所有登录用户ID,所以小王有个方法getUsers

public class UserServiceImpl implements IUserService{

    public void getUsers(){
        //获取所有登录用户ID
    }
}

由于业务的变更,公司要求小王在获取用户ID后,还需要对其进行排序,于是小王重写了getUsers

public class UserServiceImpl implements IUserService{

    public void getUsers(){
        //获取所有登录用户ID
        //所有登录用户ID排序
    }
}

以上代码虽然可以完成业务需求,但却违反了开闭原则,
所以我们一般思路都是将其对象交给另一个类维护,
并在另一个类中进行增强,以下为其代码实现:

public class UserServiceImplPlus {

    //维护的对象
    private UserServiceImpl userServiceImpl;

    public UserServiceImplPlus(UserServiceImpl userServiceImpl ) {
        this.userServiceImpl = userServiceImpl;
    }

    public void getUsers(){
        //增强代码
        //需要增强的方法
        userServiceImpl.getUsers();
        //增强代码
    }
}

这就是静态代理最简单的写法,将被代理类对象交给代理类维护,
外部通过调用代理类对象的方法完成被代理类方法的间接调用,
但我们为了代理类的扩展性,一般都是将被代理类的接口作为维护对象,
以下为其代码实现:

public class UserServiceImplPlus implements IUserService{

    //维护的对象
    private IUserService iUserService;

    public UserServiceImplPlus(IUserService iUserService) {
        this.iUserService = iUserService;
    }

    public void getUsers(){
        //StaticPro增强代码
        //需要增强的方法
        iUserService.getUsers();
        //StaticPro增强代码
    }
}

动态代理

动态代理是静态代理基础上的进一步封装,其隐藏了代理类的可见性,代理类编写及创建的权利交给程序本身,常见的动态代理方式有JDK代理及CGlib代理。

JDK代理

通过java.lang.reflect.Proxy类获取代理类对象,我们先来看下该类的结构
Proxy类结构
我们可以看到该类中存在一个newProxyInstance的静态方法,该方法名翻译过来就是新的代理实例的意思,
以下是该方法的定义:

/**
 * 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

从以上定义中我们可以知道,该方法执行成功时,会返回一个代理类对象,
第一个参数是被代理类的类加载器,第二个参数是被代理类实现的接口列表,用于创建代理类
第三个参数是需要一个InvocationHandler接口的子实现类的实例,用于拦截被代理的方法,
第一个和第二个参数可以通过被代理的字节码对象获取,
剩下的就只有InvocationHandler接口的子实现类的实例了,
我们先看下该接口的结构
InvocationHandler结构
我们发现该接口是一个SAM接口,只有一个invoke方法,所以可以使用Lambda表达式简化其实例化,
以下是该方法的定义

/**
 * 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;

从以上定义中我们可以知道,该方法返回值就是外部调用代理类方法时会获取到的返回值,
且该值只能为对象,当方法返回值为基本类型或null时都会向上抛出空指针异常,
第一个参数是当前代理类的实例,第二个参数为被拦截的方法对象,
第三个方法为被拦截方法的参数,而我们需要使用的就只有后两个参数:method和args,
我们可以通过method.invoke(new UserServiceImpl(), args),完成被代理类方法的调用,并将其返回值作为本方法的返回值返回,同时在该方法的前后对进行增强,
以下为其代码实现:

public class UserServiceImplPlusJDKPro {

    public static IUserService getUserServiceImplPro(){
        IUserService iUserService = (IUserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), (x, y, z) -> {
            //JDKPro增强代码
            Object invoke = y.invoke(new UserServiceImpl(), z);
            //JDKPro增强代码
            return invoke;
        });
        return iUserService;
    }
}

CGlib代理

使用CGlib代理前需安装jar包,非maven项目需先将jar包下载到本地,并添加到项目,
maven项目直接导入其依赖。
CGlib代理是通过该jar包中的net.sf.cglib.proxy.Enhancer类获取代理类对象。
我们先来看下该类的结构
Enhancer类结构
我们可以看到该类中存在一个接收两个参数的静态方法create,以下是其方法定义:

/**
 * Helper method to create an intercepted object.
 * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
 * instead of this static method.
 * @param type class to extend or interface to implement
 * @param callback the callback to use for all methods
 */
public static Object create(Class type, Callback callback) {

该方法执行成功后会返回一个代理类对象,
第一个参数为被代理类的字节码对象,第二个参数为Callback的子实现类对象,
那我们看一下Callback接口的结构
Callback结构
该接口居然是个空接口,一个方法也没有,那如何实现方法的拦截呢?
别急,我们先来看看该接口在哪些地方被实现过
Callback的实现
我们从该接口的实现中看到一个MethodInterceptor的接口,该接口从语义上来讲好像正好和我们的需求一致,
我们再看下MethodInterceptor接口的结构
MethodInterceptor结构
是不是有种熟悉的感觉,该方法是不是和JDK代理时使用的InvocationHandler的invoke方法很像!
我们再来看下该方法的定义

/**
 * All generated proxied methods call this method instead of the original method.
 * The original method may either be invoked by normal reflection using the Method object,
 * or by using the MethodProxy (faster).
 * @param obj "this", the enhanced object
 * @param method intercepted Method
 * @param args argument array; primitive types are wrapped
 * @param proxy used to invoke super (non-intercepted method); may be called
 * as many times as needed
 * @throws Throwable any exception may be thrown; if so, super method will not be invoked
 * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
 * @see MethodProxy
 */    
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                           MethodProxy proxy) throws Throwable;

该方法与InvocationHandler的invoke使用基本一致,只是多了一个方法代理对象的参数,
以下为其代码实现:

public class UserServiceImplPlusCGlibPro {

    public static IUserService getUserServiceImplPro(){
        Enhancer enhancer = new Enhancer();
        Object obj = Enhancer.create(UserServiceImpl.class, new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                //CGlibPro增强代码
                Object invoke = method.invoke(new UserServiceImpl(), args);
                //CGlibPro增强代码
                return invoke;
            }
        });
        return (IUserService) obj;
    }
}

以上只是Enhandcer较粗粒度的创建代理类对象方式,还有一种更为细粒度的创建方式,
通过Enhandcer对象创建,该方式感兴趣的可自行百度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

早川不爱吃香菜

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值