GOF 结构型模式 系列文章
1. 概述
-
应用场景
- 安全代理:屏蔽对真实角色的直接访问。
- 远程代理:通过代理类处理远程方法调用(RMI)
- 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。
-
主要优点
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
-
主要缺点
- 代理模式会造成系统设计中类的数量增加
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度;
-
模式的结构
- 抽象主题(
Subject
)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。 - 真实主题(
Real Subject
)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。 - 代理(
Proxy
)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
- 抽象主题(
-
AOP实现的核心机制
-
分类
静态代理
:由程序员创建代理类或特定工具自动生成源代码再对其编译,在 程序运行前代理类的.class
文件就已经存在 了。- 动态代理:在程序运行时,运用反射机制动态创建而成
JDK动态代理
CGLib动态代理
- Javassist字节码操作库实现
- ASM(底层使用指令,可维护性较差)
2. 静态代理
2.1 抽象主题类
/* 声明 被代理类,抽象接口 的业务方法 */
public interface AbstractStar {
void sing(); /* 唱歌 */
}
2.2 真实主题类
/* 被代理类,业务方法的实现类 */
public class Star implements AbstractStar {
@Override
public void sing() { System.out.println("Star::sing()"); }
}
2.3 代理类
/* 代理类,调用被代理类的实现方法 */
public class StaticProxyStar implements AbstractStar {
private AbstractStar star=null;
public StaticProxyStar(AbstractStar star){ this.star = star; }
@Override
public void sing() {
if (star == null) { System.out.println("error!"); }
preSing();
star.sing();
afterSing();
}
public void preSing() { System.out.println("代理类 访问 被代理类 前的预处理"); }
public void afterSing() { System.out.println("代理类 访问 被代理类 后的后续处理"); }
}
2.4 测试
public class StaticProxyTest {
public static void main(String[] args) {
Star star1 = new Star();
ProxyStar proxy = new ProxyStar(star1);
proxy.sing();
}
}
测试结果如下
3. JDK代理
- 使用条件
- 必须实现
InvocationHandler
接口; - 使用
Proxy.newProxyInstance
产生代理对象; - 被代理的对象必须要实现接口;
- 必须实现
3.1 抽象主题类:使用2.1
中的AbstractStar
接口
3.2 真实主题类: 使用2.2
中的Star
实现类
3.3 代理类
- 五大步骤
- 通过实现
InvocationHandler
接口来自定义自己的InvocationHandler
; - 在
Proxy.newProxyInstance
方法内部通过Proxy.getProxyClass
获得动态代理类; - 在
Proxy.newProxyInstance
方法内部通过反射机制
获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)
; - 通过构造函数获得代理对象并将自定义的
InvocationHandler
实例对象传为参数传入; - 通过代理对象调用目标方法;
- 通过实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxyStar implements InvocationHandler {
private Object target = null; // 真实对象
/**
* 建立代理对象(代理类)、真实对象(被代理)的代理关系,并返回代理对象
* @param target 真实对象
* @return 代理对象
*/
public Object bind(Object target){
this.target = target;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 挂在哪些类接口下
this); // 定义实现方法逻辑的代理类
}
/**
* 代理方法逻辑
* @param proxy 代理对象
* @param method 当前调度方法
* @param args 当前调度方法的参数
* @return 代理结果返回
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理对象 访问 被代理对象 前的预处理");
if(method.getName().equals("sing")){
method.invoke(target, args);
}
System.out.println("代理对象 访问 被代理对象 后的后续处理");
return null;
}
}
3.4 测试
public class JDKProxyTest {
public static void main(String[] args) {
AbstractStar star = new Star();
JDKProxyStar jdkProxy = new JDKProxyStar();
// 绑定关系,挂在 AbstractStar 接口下,所以声明代理对象 AbstractStar proxy
AbstractStar proxy = (AbstractStar) jdkProxy.bind(star);
proxy.sing();
}
}
测试结果如下
4. CGLib代理
-
环境构建
- 使用jar:
cglib-2.2.2.jar
、asm-3.3.1.jar
- 使用maven
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
- 使用jar:
-
基本介绍
-
cglib是一个java字节码的生成工具,它动态生成一个被代理类的子类,
-
子类重写被代理类的所有不是final的方法。
即 cglib无法代理被final修饰的类或方法以及静态方法。
在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。 -
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
-
CGLIB基于ASM框架实现,是JDK动态代理的有效补充,可以用于AOP编程,比如日志打印、安全控制、统一鉴权等
-
4.1 抽象主题类:使用2.1
中的AbstractStar
接口
4.2 真实主题类: 使用2.2
中的Star
实现类
4.3 代理类
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/* 实现MethodInterceptor接口生成方法拦截器 */
public class CGLibProxyInterceptor implements MethodInterceptor {
/**
*
* @param obj 表示要进行增强的对象
* @param method 表示拦截的方法
* @param objects 数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double
* @param methodProxy 表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用
* @return 执行结果
* @throws Throwable 异常
*/
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
before(method.getName());
/* 注意这里是调用invokeSuper而不是invoke,否则死循环 */
// methodProxy.invokeSuper执行的是原始类的方法;
// method.invoke执行的是子类的方法;
Object result = methodProxy.invokeSuper(obj, objects);
after(method.getName());
return result;
}
/* 调用invoke方法之前执行 */
private void before(String methodName) { System.out.println("代理对象 访问 被代理对象 前的预处理"); }
/* 调用invoke方法之后执行 */
private void after(String methodName) { System.out.println("代理对象 访问 被代理对象 后的后续处理"); }
}
4.4 测试
import net.sf.cglib.proxy.Enhancer;
public class CGLibProxyTest {
public static void main(String[] args) {
// 通过CGLIB动态代理获取代理对象的过程
// 1. 创建Enhancer对象,类似于JDK动态代理的Proxy类
Enhancer enhancer = new Enhancer();
// 继承被代理类
// 2. 设置目标类的字节码文件
enhancer.setSuperclass(Star.class);
// 3. 设置回调函数
enhancer.setCallback(new CGLibProxyInterceptor());
// 4. 设置代理类对象
// create方法正式创建代理类
Star star = (Star)enhancer.create();
//在调用代理类中方法时会被我们实现的方法拦截器进行拦截
star.sing();
}
}
测试结果如下