什么是代理模式
代理模式就是不对原始类进行修改的情况下,
对该类中方法进行增强的一种解决方案。
以下代码已上传码云-------->代理模式源代码
静态代理
举个例子,小王有个业务需要获取到当前所有登录用户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类获取代理类对象,我们先来看下该类的结构
我们可以看到该类中存在一个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接口的子实现类的实例了,
我们先看下该接口的结构
我们发现该接口是一个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类获取代理类对象。
我们先来看下该类的结构
我们可以看到该类中存在一个接收两个参数的静态方法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接口的结构
该接口居然是个空接口,一个方法也没有,那如何实现方法的拦截呢?
别急,我们先来看看该接口在哪些地方被实现过
我们从该接口的实现中看到一个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对象创建,该方式感兴趣的可自行百度。