一、代理模式的作用
- 功能增强: 基于某个功能,再增加一些功能。
(比如目标类只负责核心功能,其他附属功能通过代理类完成。代理类的方法名与目标类的方法相同,内容不同,在核心功能外加了一些额外逻辑) - 控制访问: 防止直接访问目标。
代理类代理目标类
目标类是实现目标功能的类
二、实现代理的方式
-
动态代理:
- 特点:
- 在程序执行过程中,自动使用
jdk
反射机制创建代理对象(而不需要写.java源文件),并且动态指定要代理的目标类。
- 在程序执行过程中,自动使用
- 优点:
- 缺点
- 特点:
-
静态代理:
- 特点:
- 代理类是自己手动实现的。自己创建一个类作为代理类
- 要代理的目标类是确定的。
- 优点:
- 实现简单,容易理解
- 在不改变原目标对象代码的情况下,对目标的功能进行拓展
- 缺点:
- 当目标类增加了,代理类可能要成倍的增加,代理类过多
- 当接口的功能增加或修改,会影响所有实现类,修改工作量大,不好管理维护 。
- 特点:
三、动态代理的实现
3.1 jdk动态代理
基于接口实现代理
使用java
反射包中的接口和类实现动态代理,要求 代理类和工具类实现同一个接口。
其中反射包是java.lang,reflect
,里面有三个类:InvocationHandler
、Method
、Proxy
① 创建目标接口
public interface Star{
String sing(String name);
void dance();
}
② 创建目标类实现目标接口
public class BigStar implements Star {
private String name;
public BigStar(String name) {
this.name = name;
}
public String sing(String name) {
System.out.println(this.name + "正在唱歌儿:" + name);
return "谢谢儿!谢谢儿!";
}
public void dance() {
System.out.println(this.name + "正在跳舞儿");
}
}
③ 创建对应的处理器实现InvocationHandler
接口,重写invoke
方法(里面写增强业务)
public class StarInvocationHandler<T> implements java.lang.reflect.InvocationHandler {
private final T target; // 存储目标对象
public StarInvocationHandler(T target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + "方法执行前增强");
Object result = method.invoke(target, args);
System.out.println(method.getName() + "方法执行后增强");
return result;
}
}
④ 创建工厂类,为了简化创建代理对象的过程(这里用了泛型约束,可以不用)
public class ProxyHandlerFactory {
/**
* 创建 Star 接口的代理对象
*/
public static <T extends Star> Star createStarProxy(T target) {
return (Star) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new StarInvocationHandler<>(target));
}
}
⑤ 测试调用
public class Main {
public static void main(String[] args) {
BigStar bigstar = new BigStar("迪丽热巴");
Star starProxy = ProxyHandlerFactory.createStarProxy(bigstar);
String result = starProxy.sing("老公老公我爱你");
System.out.println(result);
}
}
结果:
3.2 cglib动态代理
基于继承实现代理
cglib
是一个第三方工具库,能够创建代理对象。
通过继承的方式实现动态代理,通过继承目标类,创建它的子类,在子类中重写父类的同名方法,实现功能的修改。(因为是继承重写的方式实现代理,因此要求目标类不能是final
修饰,要重写的方法也不能是final
修饰的)。
① 导入cglib
依赖
② 创建目标类
public class BigStar {
private String name;
public BigStar() {}
public BigStar(String name) {
this.name = name;
}
public String sing(String name) {
System.out.println(this.name + "正在唱歌儿:" + name);
return "谢谢儿!谢谢儿!";
}
public void dance() {
System.out.println(this.name + "正在跳舞儿");
}
}
③ 创建拦截器
public class BigStarMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(method.getName() + "方法增强前");
Object result = methodProxy.invokeSuper(o, objects); // 调用目标类方法
System.out.println(method.getName() + "方法增强前");
return result;
}
}
④ 创建代理对象,测试
public class TestMain {
@Test
public void test() {
// 创建与目标对象对应的拦截器
BigStarMethodInterceptor bigStarMethodInterceptor = new BigStarMethodInterceptor();
// 创建一个增强器对象
Enhancer enhancer = new Enhancer();
// 设置目标类
enhancer.setSuperclass(BigStar.class);
// 设置拦截处理器
enhancer.setCallback(bigStarMethodInterceptor);
// 创建代理对象(根据参数选择对应的 Constructor)
BigStar bigStarProxy = (BigStar) enhancer.create(new Class[]{String.class}, new Object[]{"胖迪"});
// 调用方法
String s = bigStarProxy.sing("猪之歌");
System.out.println(s);
}
}
运行结果:
cglib
的代理效率高于jdk
代理。
如果想看看生成的代理对象长什么养,可以通过设置一个系统属性并指定路径,CgLib会把生成的代理对象写出到该位置:
// CgLib提供的代理类生成器
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\idea_workspace\\ruoyi\\wipinfo_bigscreen_manager\\admin\\src");
参考资料:
- 哔哩哔哩 黑马程序员磊哥 【黑马磊哥】Java动态代理深入剖析,真正搞懂Java核心设计模式:代理设计模式
https://www.bilibili.com/video/BV1ue411N7GX?share_source=copy_web
- 哔哩哔哩 动力节点 Java-JDK动态代理(AOP)使用及实现原理分析
https://www.bilibili.com/video/BV1HZ4y1p7F1?share_source=copy_web
- 哔哩哔哩 上云_云哥 jdk动态代理,cglib动态代理,代理设计模式,aop切面编程
https://www.bilibili.com/video/BV1tY411Z799?p=6&share_source=copy_web