什么是代理模式?
代理模式来自于现实中的中介或代理,一个事情你不去做,找别人代你去做,就叫代理模式。
适用场景
- 屏蔽调用者和提供者之间的联系,操作都由中间者代理实现。
- 使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
- 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
在代码中,一般代理会被理解为代码增强,实际上就是在原代码逻辑前后增加一些代码逻辑,而使调用者无感知。
根据代理的创建时期,代理模式分为静态代理和动态代理。
- 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
- 动态:在程序运行时,运用反射机制动态创建而成
静态代理
静态代理是指在程序运行前就已经存在的编译好的代理类是为静态代理。实现静态代理有四个步骤:
- 定义业务接口;
- 被代理类实现业务接口;
- 定义代理类并实现业务接口;
- 最后便可通过客户端进行调用。(这里可以理解成程序的main方法里的内容)
我们按照这个步骤去实现静态代理。需求:在向数据库添加一个用户时前后打印日志。
//抽象主题
interface Service {
void add(String name);
}
//真实主题 实现抽象主题
class ServiceImpl implements Service {
@Override
public void add(String name) {
System.out.println("添加用户:" + name);
}
}
/**
* 代理类 增强主题能力
* 在添加用户前后打印日志
*/
class Proxy implements Service {
// 被代理对象
private final Service target;
// 通过构造方法传入被代理对象
public Proxy(Service target) {
this.target = target;
}
@Override
public void add(String name) {
System.out.println("添加用户前置操作……");
target.add(name);
System.out.println("添加用户后预操作……");
}
}
//静态代理模式测试
public class ProxyPattern {
public static void main(String[] args) {
Service service = new ServiceImpl();
Proxy proxy = new Proxy(service);
proxy.add("张三");
//输出:
//添加用户前置操作……
//添加用户:张三
//添加用户后预操作……
}
}
动态代理
静态代理的一些缺点吧:
- 代理类和被代理类实现了相同的接口,导致代码的重复,如果接口增加一个方法,那么除了被代理类需要实现这个方法外,代理类也要实现这个方法,增加了代码维护的难度。
- 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。
动态代理是指:在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能,动态代理的优势:
- 代理类数量可以很少,
- 当你修改了接口中的方法时,不会影响代理类。
动态代理的实现:JDK动态代理 和 CGLIB动态代理
jdk动态代理(理解): 使用java反射包中的类和接口实现动态代理的功能。
反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method, Proxy.
cglib动态代理(了解): cglib是第三方的工具库, 创建代理对象。
cglib的原理是继承,cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法, 实现功能的修改。
要求目标类不能是final的, 方法也不能是final的。
cglib的要求目标类比较宽松, 只要能继承就可以了。cglib在很多的框架中使用,
比如 mybatis ,spring框架中都有使用。
JDK动态代理
JDK反射前置知识
Meathod类:封装Java的方法,方法对象
//获取Service的add(String name) 方法
Method method = Service.class.getMethod("add", String.class);
//执行ServiceImpl的add(String name) 方法,传参"张三"
method.invoke(new ServiceImpl(), "张三");
2.java.lang.reflect包下的InvocationHandler类,封装调用处理操作,就一个invoke()方法,表示代理对象要执行的功能代码,你的代理类要完成的功能就写在invoke()方法中。
//Object proxy: jdk创建的代理对象,无需赋值。
//Method method: 目标类中的方法,jdk提供method对象的
//Object[] args: 目标类中方法的参数, jdk提供的。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
Java动态代理实现
创建被代理的接口和类;
- 创建InvocationHandler接口的实现类,在invoke方法中实现代理逻辑;
- 通过Proxy的静态方法
newProxyInstance( ClassLoaderloader, Class[] interfaces, InvocationHandler h)
创建一个代理对象 - 使用代理对象。 ```java
//抽象主题 interface Service { void add(String name); }
//真实主题 实现抽象主题 class ServiceImpl implements Service { @Override public void add(String name) { System.out.println(“添加用户:” + name); } }
/**
- 动态代理类
- 实现java.lang.reflect.InvocationHandler接口
- 方法:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; *
@see InvocationHandler */ class ServiceInvocationHandler implements InvocationHandler { //被代理对象,Object类型 private Object target;
public ServiceInvocationHandler(Object target) {
this.target = target;
}
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("添加用户前置操作……");
Object ret = method.invoke(target, args);
System.out.println("添加用户后预操作……");
return ret;
}
}
//静态代理模式测试 public class ProxyPattern { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { //获取Service的add(String name) 方法 Method method = Service.class.getMethod(“add”, String.class); //执行ServiceImpl的add(String name) 方法,传参”张三” method.invoke(new ServiceImpl(), “张三”);
Service target = new ServiceImpl();
ServiceInvocationHandler handler = new ServiceInvocationHandler(target);
//第一个参数是指定代理类的类加载器(我们传入当前测试类的类加载器)
//第二个参数是代理类需要实现的接口(我们传入被代理类实现的接口,这样生成的代理类和被代理类就实现了相同的接口)
//第三个参数是invocation handler,用来处理方法的调用。这里传入我们自己实现的handler
Service proxyObject = (Service) Proxy.newProxyInstance(
ProxyPattern.class.getClassLoader(),
target.getClass().getInterfaces(),
handler
);
proxyObject.add("张三");
//输出:
//添加用户前置操作……
//添加用户:张三
//添加用户后预操作……
}
}
<a name="QEuIP"></a>
## CGLIB动态代理
cglib是一个java字节码的生成工具,它动态生成一个被代理类的子类,子类重写被代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
JDK代理要求被代理的类必须实现接口,有很强的局限性。而CGLIB动态代理则没有此类强制性要求。简单的说,CGLIB会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。但是如果被代理类被final修饰,那么它不可被继承,即不可被代理;同样,如果被代理类中存在final修饰的方法,那么该方法也不可被代理。
**CGLIB实现:**
1. 创建Enhancer实例
1. 通过setSuperclass方法来设置目标类
1. 通过setCallback 方法来设置拦截对象
1. create方法生成Target的代理类,并返回代理类的实例
```java
//抽象主题
interface Service {
void add(String name);
}
//真实主题 实现抽象主题
class ServiceImpl implements Service {
@Override
public void add(String name) {
System.out.println("添加用户:" + name);
}
}
/**
* 动态代理类
* 实现java.lang.reflect.InvocationHandler接口
* 方法:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
*
* @see InvocationHandler
*/
class ServiceInterceptor implements MethodInterceptor {
/**
* @param obj 表示要进行增强的对象
* @param method 表示拦截的方法
* @param objects 数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double
* @param methodProxy 表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用
* @return 执行结果
* @throws Throwable 异常
*/
@Override
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("添加用户前置操作……");
// 注意这里是调用invokeSuper而不是invoke,否则死循环;
// methodProxy.invokeSuper执行的是原始类的方法;
// method.invoke执行的是子类的方法;
Object result = methodProxy.invokeSuper(obj, objects);
System.out.println("添加用户后置操作……");
return result;
}
}
//静态代理模式测试
public class ProxyPattern {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 通过CGLIB动态代理获取代理对象的过程
// 创建Enhancer对象,类似于JDK动态代理的Proxy类
Enhancer enhancer = new Enhancer();
// 设置目标类的字节码文件
enhancer.setSuperclass(ServiceImpl.class);
// 设置回调函数
enhancer.setCallback(new ServiceInterceptor());
// create方法正式创建代理类
Service userDao = (Service) enhancer.create();
// 调用代理类的具体业务方法
userDao.add("张三");
//输出:
//添加用户前置操作……
//添加用户:张三
//添加用户后预操作……
}
}