**意图:**为其他对象提供一种代理以控制对这个对象的访问。
主要解决:
在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
**何时使用:**想在访问一个类时做一些控制。
**如何解决:**增加中间层。
**关键代码:**实现与被代理类组合。
UML图示:
我们将创建一个 Image 接口和实现了 Image 接口的实体类。ProxyImage 是一个代理类,减少 RealImage 对象加载的内存占用。
ProxyPatternDemo 类使用 ProxyImage 来获取要加载的 Image 对象,并按照需求进行显示。(来自菜鸟教程)
无论是哪种代理,核心都是介绍中提到的,代理对象和被代理对象实现共同的接口,并且代理对象只是进行增强方法(前置处理和后置处理),核心方法还是调用被代理对象的方法。
静态代理非常简单,就是创建一个
代理类
持有被被代理类对象
的引用,并实现共同接口,重写方法,在方法中进行前置处理
和后置处理
。
共同接口
public interface IUserService {
void add();
void update();
}
被代理对象
public class UserServiceImpl implements IUserService{
@Override
public void add() {
System.out.println("---------add方法执行----------");
}
@Override
public void update() {
System.out.println("---------update方法执行----------");
}
}
代理对象
public class StaticProxy implements IUserService {
//需要持有被代理对象的引用
IUserService userService;
public StaticProxy(IUserService userService) {
this.userService = userService;
}
@Override
public void add() {
//前置处理
System.out.println("事务开启了......");
//代理对象调用方法
userService.add();
//后置处理
System.out.println("事务提交了......");
}
@Override
public void update() {
System.out.println("事务开启了......");
userService.update();
System.out.println("事务提交了......");
}
}
在静态代理中,代理对象是程序员通过代码实实在在编写的一个类,所以称之为
静态
,这种硬编码的方式并不可取,我们理想的方式应该是 只需要输入被
代理类的接口方法
以及
前置处理、后置处理(我们需要添加的逻辑)
等信息,就能动态地生成一个代理对象供我们使用,这才是程序员正确的思维方式,所以动态代理
应用而生。
JDK动态代理是JDK提供的生成代理对象的方法。
public class DynamicProxy {
public static IUserService getProxyInstance() {
//动态代理 (固定流程)
/**
* 第一个参数:被代理类的类加载器 这个是有固定的写法
* 被代理的类.class.getClassLoader
* 第二个参数:就是我们被代理的类实现的接口
* 如果被代理的是类:被代理的类.class.getInterfaces();
* 如果被代理的是接口:new Class[]{被代理的接口.class}
* 第三个参数:就是对这个代理的类或者接口中方法执行的监听
* new InvacationHandler(){}
*/
ClassLoader classLoader=UserServiceImpl.class.getClassLoader();
Class<?>[] interfaces = UserServiceImpl.class.getInterfaces();
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("事务开启了......");
method.invoke(new UserServiceImpl(),args);
System.out.println("事务提交了......");
return null;
}
};
IUserService proxyInstance = (IUserService) java.lang.reflect.Proxy.newProxyInstance(classLoader, interfaces, handler);
return proxyInstance;
}
}
JDK动态代理
生成的代理对象是这样的(省略部分代码)
public class UserServiceProxy implements IUserService {
private InvocationHandler invocationHandler;
public UserServiceProxy(InvocationHandler invocationHandler) {
this.invocationHandler = invocationHandler;
}
public void add() throws Throwable {
//获取父类中的add 方法的method对象
Method method = IUserService.class.getMethod("add");
invocationHandler.invoke(this, method, null);
}
public void update() throws Throwable {
//获取父类中的add 方法的method对象
Method method = IUserService.class.getMethod("update");
invocationHandler.invoke(this, method, null);
}
}
当我们使用代理对象,调用add()方法时
首先会通过反射获取该方法的Method对象
;
将Method对象
作为参数,调用invocationHandler.invoke
方法
invoke
方法是我们重写的逻辑(前置处理,后置处理)
UserServiceProxy userServiceProxy = new UserServiceProxy(new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//就能监控到这个方法在什么时候执行
String name = method.getName();
System.out.println("当前执行的方法是:"+name);
System.out.println("打开事务.....");
Object invoke = method.invoke(new UserService(), args);
System.out.println("提交事务.....");
return invoke;
}
});
由于Method对象
是该方法的参数,所以能够在该方法中通过反射调用被代理对象的方法
Object invoke = method.invoke(new UserService(), args);
分析完了代理对象,接下来看看这个代理对象是如何生成的
通过字符串拼接 和 Java编译 生成代理对象…(具体过程和代码请移步另一篇文章)
JDK动态代理的前提是
被代理对象
必须实现接口核心在于利用
多态
,将生成的代理对象
,即接口实现类
赋值给接口
通过调用接口方法间接地调用接口实现类中重写的方法。
但不妨思考思考,多态只有这一种用法吗?
多态还有另外一种使用,就是子类赋值给父类,通过调用父类方法间接地调用子类的方法,这就是CGLib的由来。
public class CGLibDynamicProxy implements MethodInterceptor {
private UserServiceImpl userService;
public CGLibDynamicProxy(UserServiceImpl userService){
this.userService = userService;
}
public UserServiceImpl getCGLibProxyInstance(){
Enhancer enhancer=new Enhancer();
//给生成的这个类 搞个爹
enhancer.setSuperclass(UserServiceImpl.class);
//指定拦截的接口实例
enhancer.setCallback(this);
return (UserServiceImpl) enhancer.create();
}
/**
* 在这里来进行拦截
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("打开事务.....");
Object invoke = method.invoke(this.userService, objects);
System.out.println("提交事务.....");
return invoke;
}
}
CGLib生成的代理对象和JDK的非常类似, 核心不同在于
实现 ------------- 继承
InvocationHandler ------------- methodInterceptor
invoke ------------- intercept
public class UserServiceProxy extends UserService {
private MethodInterceptor methodInterceptor;
//父类的class对象
private Class superClass;
public UserServiceProxy(MethodInterceptor methodInterceptor, Class superClass) {
this.methodInterceptor = methodInterceptor;
this.superClass = superClass;
}
@Override
public void add() throws Throwable {
//这里需要找到父类中的方法
Method method = this.superClass.getMethod("add");
this.methodInterceptor.intercept(this, method, null, null);
}
@Override
public void update() throws Throwable {
//这里需要找到父类中的方法
Method method = this.superClass.getMethod("update");
this.methodInterceptor.intercept(this, method, null, null);
}
}
代理对象的生成的步骤和JDK动态代理如出一辙,不在进行冗余讲述。