- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
代理模式(Proxy Design Pattern)是为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。
在结束创建型模式的讲解后,从这一篇开始就进入到了结构型模式,结构型模式主要是总结一些类和或对象组合在一起的结构。代理模式在不改变原始代理类的情况下,通过引入代理类来给原始类附加功能。
代理模式的主要结构如下:
Subject
:抽象主题类,通过接口或抽象类声明主题和代理对象实现的业务方法RealSubject
:真实主题类,实现Subject
中的具体业务,是代理对象所代表的真实对象Proxy
:代理类,其内部含有对真实主题的引用,它可以访问、控制或扩展RealSubject
的功能Client
:客户端,通过使用代理类来访问真实的主题类按照上面的类图,可以实现如下代码:
//主题类接口
public interface Subject {
void Request();
}
//真实的主题类
public class RealSubject implements Subject{
@Override
public void Request() {
System.out.println("我是真实的主题类");
}
}
//代理类
public class Proxy implements Subject{
private RealSubject realSubject;
@Override
public void Request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
realSubject.Request();
}
}
//客户端
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.Request();
}
}
代理模式有比较广泛的使用,比如Spring AOP
、RPC
、缓存等。在 Java 中,根据代理的创建时期,可以将代理模式分为静态代理和动态代理,下面就来分别阐述。
动态代理和静态代理的区分就是语言类型是在运行时检查还是在编译期检查。
静态代理是指在编译期,也就是在JVM运行之前就已经获取到了代理类的字节码信息。即Java源码生成.class
文件时期:
由于在JVM运行前代理类和真实主题类已经是确定的,因此也被称为静态代理。
在实际使用中,通常需要定义一个公共接口及其方法,被代理对象(目标对象)与代理对象一起实现相同的接口或继承相同的父类。其实现代码就是第一节中的代码。
动态代理,也就是在JVM运行时期动态构建对象和动态调用代理方法。
常用的实现方式是反射。反射机制是指程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力,使用反射我们可以调用任意一个类对象,以及其中包含的属性及方法。比如JDK Proxy。
此外动态代理也可以通过ASM(Java 字节码操作框架)来实现。比如CGLib。
这种方式是JDK自身提供的一种方式,它的实现不需要引用第三方类,只需要实现InvocationHandler
接口,重写invoke()
方法即可。代码实现如下所示:
public class ProxyExample {
static interface Car {
void running();
}
static class Bus implements Car {
@Override
public void running() {
System.out.println("bus is running");
}
}
static class Taxi implements Car {
@Override
public void running() {
System.out.println("taxi is runnig");
}
}
//核心部分 JDK Proxy 代理类
static class JDKProxy implements InvocationHandler {
private Object target;
public Object getInstance(Object target) {
this.target = target;
//获得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
return result;
}
}
public static void main(String[] args) {
JDKProxy jdkProxy = new JDKProxy();
Car instance = (Car) jdkProxy.getInstance(new Taxi());
instance.running();
}
}
动态代理的核心是实现Invocation
接口,我们再看看Invocation
接口的源码:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
实际上是通过invoke()
方法来触发代理的执行方法。最终使得实现Invocation
接口的类具有动态代理的能力。
动态代理的好处在于不需要和静态代理一样提前写好公共的代理接口,只需要实现Invocation
接口就可拥有动态代理能力。下面我们再来看看 CGLib 是如何实现的
CGLib 动态代理采取的是创建目标类的子类的方式,通过子类化,我们可以达到近似使用被调用者本身的效果。其实现代码如下所示:
public class CGLibExample {
static class car {
public void running() {
System.out.println("car is running");
}
}
static class CGLibProxy implements MethodInterceptor {
private Object target;
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
//设置父类为实例类
enhancer.setSuperclass(this.target.getClass());
//回调方法
enhancer.setCallback(this);
//创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
}
public static void main(String[] args) {
CGLibProxy cgLibProxy = new CGLibProxy();
car instance = (car) cgLibProxy.getInstance(new car());
instance.running();
}
}
从代码可以看出CGLib 也是通过实现代理器的接口,然后再调用某个方法完成动态代理,不同的是CGLib在初始化被代理类时,是通过Enhancer
对象把代理对象设置为被代理类的子类来实现动态代理:
Enhancer enhancer = new Enhancer();
//设置父类为实例类
enhancer.setSuperclass(this.target.getClass());
//回调方法
enhancer.setCallback(this);
//创建代理对象
return enhancer.create();
在MyBatis 中,也存在着代理模式的使用,比如MapperProxyFactory
。其中的newInstance()
方法就是生成一个具体的代理来实现功能,代码如下:
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return methodCache;
}
// 创建代理类
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
代理模式最常使用的一个应用场景就是在业务系统中开发一些非功能性需求,比如监控、统计、鉴权、限流、事务、日志等。将这些附加功能与业务功能解耦,放在代理类中统一处理,让程序员只需要关注业务方面的开发。而Spring AOP 的切面实现原理就是基于动态代理
Spring AOP 的底层通过上面提到的 JDK Proxy 和 CGLib动态代理机制,为目标对象执行横向织入。当Bean实现了接口时, Spring就会使用JDK Proxy,在没有实现接口时就会使用 CGLib。也可以在配置中强制使用 CGLib:
<aop:aspectj-autoproxy proxy-target-class="true"/>
RPC 框架的实现可以看作成是一种代理模式,通过远程代理、将网络同步、数据编解码等细节隐藏起来,让客户端在使用 RPC 服务时,不必考虑这些细节。
http://c.biancheng.net/view/1359.html
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=59#/detail/pc?id=1768
https://time.geekbang.org/column/article/7489
https://time.geekbang.org/column/article/201823
《Java 重学设计模式》
《大话设计模式》
OkHttp的作用 OkHttp is an HTTP client。 如果是HTTP的方式想得到数据,就需要我们在页面上输入网址,如果网址没有问题,就有可能返回对应的String字符串,如果这个地址
Record 一个重要的字符串算法,这是第三次复习。 通过总结我认为之所以某个算法总是忘记,是因为大脑始终没有认可这种算法的逻辑(也就是脑回路)。 本篇主要讲解从KMP的应用场景,
SQL 注入基础 【若本文有问题请指正】 有回显 回显正常 基本步骤 1. 判断注入类型 数字型 or 字符型 数字型【示例】:
标签: #Prompt #LLM 创建时间:2023-04-28 17:05:45 链接: 课程(含JupyterNotebook) , 中文版 讲师: An
Swift是供iOS和OS X应用编程的新编程语言,基于C和Objective-C,而却没有C的一些兼容约束。Swift采用了安全的编程模式和添加现代的功能来是的编程更加简单、灵活和有趣。界面则基于
红日靶机(一)笔记 概述 域渗透靶机,可以练习对域渗透的一些知识,主要还是要熟悉 powershell 语法,powershell 往往比 cmd 的命令行更加强大,而很多渗透开源的脚本都是 po
八大绩效域详细解析 18.1 干系人绩效域 跟干系人所有相关的活动. 一、预期目标 ①与干系人建立高效的工作关系 ②干系人认同项目目标 ③支持项目的干系人提高
18.3 开发方法和生命周期绩效域 跟开发方法,项目交付节奏和生命周期相关的活动和职能. 一、预期目标: ①开发方法与项目可交付物相符合; ②将项目交付与干系人价值紧密
18.7 度量绩效域 度量绩效域涉及评估项目绩效和采取应对措施相关的活动和职能度量是评估项目绩效,并采取适当的应对措施,以保持最佳项目绩效的过程。 一、 预期目标: ①对项目状况
pygraphviz 安装,windows系统: 正确的安装姿势: Prebuilt-Binaries/PyGraphviz at master · CristiFati/Prebuilt-Binar
今天给大家介绍IDEA开发工具如何配置devtools热加载工具。 1、devtools原理介绍 spring-boot-devtools是spring为开发者提供的热加载
一 什么是正则表达式 // 正则表达式(regular expression)是一个描述字符模式的对象; // JS定义RegExp类表示正则表达式; // String和RegExp都定义了使用
目前是2022-04-25 23:48:03,此篇博文分享到互联网上估计是1-2个月后的事了,此时的OpenCV3最新版是3.4.16 这里前提是gcc,g++,cmake都需要安装好。 没安装好的,
一、概述 1、Flink 是什么 Apache Flink is a framework and distributed processing engine for stateful comput
一、window 概述 Flink 通常处理流式、无限数据集的计算引擎,窗口是一种把无限流式数据集切割成有限的数据集进行计算。window窗口在Flink中极其重要。 二、window 类型 w
一、触发器(Trigger) 1.1、案例一 利用global window + trigger 计算单词出现三次统计一次(有点像CountWindow) 某台虚拟机或者mac 终端输入:nc -
一、时间语义 在Flink 中涉及到三个重要时间概念:EventTime、IngestionTime、ProcessingTime。 1.1、EventTime EventTime 表示日志事
一、概述 以wordcount为例,为什么每次输入数据,flink都能统计每个单词的总数呢?我们都没有显示保存每个单词的状态值,但是每来一条数据,都能计算单词的总数。事实上,flink在底层维护了每
一、概述 checkpoint机制是Flink可靠性的基石,可以保证Flink集群在某个算子因为某些原因(如 异常退出)出现故障时,能够将整个应用流图的状态恢复到故障之前的某一状态,保 证应用流图状
一、standalone 部署模式 1、下载安装包 下载安装包地址 有两种安装包类型: 第一种是带 Hadoop依赖的(整合YARN) 第二种是不带 Hadoop依赖的(Standalone模式)
我是一名优秀的程序员,十分优秀!