代理模式
静态代理
- 静态代理中,我们对目标对象的每个方法的增强都是手动完成的(后面会具体演示代码),非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)。 实际应用场景非常非常少,日常开发几乎看不到使用静态代理的场景。
上面我们是从实现和应用角度来说的静态代理,从 JVM 层面来说, 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。 - 实现步骤
- 定义一个接口及其实现类;
- 创建一个代理类同样实现这个接口
- 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情 ```java public interface SmsService { String send(String message); }
public class SmsServiceImpl implements SmsService { public String send(String message) { System.out.println(“send message:” + message); return message; } }
public class SmsProxy implements SmsService {
private final SmsService smsService;
public SmsProxy(SmsService smsService) {
this.smsService = smsService;
}
@Override
public String send(String message) {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method send()");
smsService.send(message);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method send()");
return null;
}
}
public class Main { public static void main(String[] args) { SmsService smsService = new SmsServiceImpl(); SmsProxy smsProxy = new SmsProxy(smsService); smsProxy.send(“java”); } }
结果: before method send() send message:java after method send()
<a name="39d8ec08"></a>
## 动态代理
- **从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。**
- 就 Java 来说,动态代理的实现方式有很多种,比如 **JDK 动态代理**、**CGLIB 动态代理**等等。
<a name="7ae25329"></a>
### **JDK动态代理**
-
```java
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
......
}
- 必须要实现InvocationHandler
public interface InvocationHandler {
/**
* 当你使用代理对象调用方法的时候实际会调用到这个方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
你通过
**Proxy**
类的**newProxyInstance()**
创建的代理对象在调用方法的时候,实际会调用到实现**InvocationHandler**
接口的类的**invoke()**
方法。 你可以在invoke()
方法中自定义处理逻辑,比如在方法执行前后做什么事情。- 步骤
- 定义一个接口及其实现类;
- 自定义
InvocationHandler
并重写invoke
方法,在invoke
方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑; - 通过
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
方法创建代理对象;
- JDK 动态代理代码实现
```java //目标接口 public interface xxService{ String send(String message); } //目标实现类 public class xxServiceimpl implements xxService{ @Overide
public String send(String message){
System.out.println("send message:" + message);
return message;
}
} //创建调用处理类 public class xxInvocationHandler implements InvocationHandler { //要代理的实现对象 private final Object target;
public xxInvocationHandler(Object target){this.target = target;}
//返回类型要和代理对象返回值相同(String),否则异常 public Object invoke(Object proxy, Method method, String[] args)throws InvocationTargetException, IllegalAccessException {
System.out.println("before method " + method.getName());
//
Object result = method.invoke(target, args);
System.out.println("after method " + method.getName());
return result;
} } //创建工厂代理类 public class ProxyFactory{ public static Object getProxy(Object object){
return Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new xxInvocationHandler(object)
);
} }
//使用xxService指针指向工厂代理类返回的代理实例(通过代理实现类的类加载和接口和调用处理对象绑定,真正的实现在调用处理对象)
xxService xxservice = (xxService) ProxyFactory.getProxy(xxServiceImpl);
xxservice.send();//xxservice指向的实际上是newProxyInstance,实际会调用到实现InvocationHandler
接口的类的 invoke()
方法。
<a name="3ca3a532"></a>
### GCLIB(Code Generation Library)动态代理
- 使用步骤
- 定义一个被代理类
- 定义一个`MethodInterceptor`方法拦截器。`intecept()`拦截被代理类的方法(实际上当代理类enhancer调用方法时,调用的是interceptor方法)
- 定义一个代理工厂`xxProxyFactory,getProxy()`创建了一个动态代理增强类enhancer。定义它的类加载器为被代理类的类加载器`classLoader`、它的父类是被加载类,并且创建enhancer的方法拦截器。
- 使用`proxyFactory`创建代理对象。
- 代码实现:
```java
package github.javaguide.dynamicProxy.cglibDynamicProxy;
public class AliSmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 自定义MethodInterceptor
*/
public class DebugMethodInterceptor implements MethodInterceptor {
/**
* @param o 被代理的对象(需要增强的对象)
* @param method 被拦截的方法(需要增强的方法)
* @param args 方法入参
* @param methodProxy 用于调用原始方法
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method " + method.getName());
Object object = methodProxy.invokeSuper(o, args);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method " + method.getName());
return object;
}
}
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyFactory {
public static Object getProxy(Class<?> clazz) {
// 创建动态代理增强类
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
// 设置被代理类
enhancer.setSuperclass(clazz);
// 设置方法拦截器
enhancer.setCallback(new DebugMethodInterceptor());
// 创建代理类
return enhancer.create();
}
}
AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("java");
输出:
before method send
send message:java
after method send
- JDK 动态代理只能只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
- 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。