Spring之AOP源码(一)

文章目录

    • 一、动态代理
      • 1. 概念
      • 2. Cglib动态代理的使用
      • 3. JDK动态代理的使用
    • 二、SpringAOP
      • 1. 简介
      • 2. Spring AOP使用

一、动态代理

1. 概念

动态代理(Dynamic Proxy)是一种在运行时动态生成代理对象的技术。它是一种设计模式,用于在不修改原始对象的情况下,通过代理对象来间接访问原始对象,并在访问前后执行额外的操作。

在Spring中实现动态代理无非就两种方式,分别是Cglib和Jdk动态代理。

2. Cglib动态代理的使用

CGLIB(Code Generation Library)是一个功能强大且高性能的代码生成库,它是用Java编写的,用于在运行时生成字节码并创建新的类。CGLIB广泛用于AOP(面向切面编程)和其他需要在运行时生成类的场景。以下是CGLIB的一些主要特点和用法:

继承式代理: CGLIB使用继承方式生成代理类,而不是像JDK动态代理那样要求目标类必须实现接口。这意味着你可以代理没有实现任何接口的类。

字节码生成: CGLIB通过使用ASM库来生成字节码,实现了在运行时创建新类的功能。这使得可以在运行时修改类的行为,例如添加新的方法、修改字段等。

性能高效: 由于CGLIB是直接生成字节码,相对于反射,它的性能通常更高。这使得它在一些要求高性能的场景中得到广泛应用。

AOP支持: CGLIB常用于实现AOP,通过在运行时生成代理类,在目标方法的前后添加额外的逻辑。

Spring框架中的应用: Spring框架的AOP模块就使用了CGLIB,当目标对象没有实现接口时,Spring会默认使用CGLIB来创建代理类。

下面是Cglib使用的一个案例

UserService target = new UserService();
//构建增强器对象
Enhancer enhancer = new Enhancer(); // cglib
//设置要代理的那个类
enhancer.setSuperclass(UserService.class);
//设置代理逻辑
enhancer.setCallbacks(new Callback[]{new MethodInterceptor() { 
@Override
      public Object intercept(Object o, Method method, Object[] objects, MethodProxy
  methodProxy) throws Throwable {
          System.out.println("before...");
          //执行被代理对象的方法
          Object result = methodProxy.invoke(target, objects);
          System.out.println("after...");
          return result;
}
}});
//获得一个代理对象
UserService userService = (UserService) enhancer.create(); UserService
userService.test();

在这里插入图片描述
cglib底层是操作ASM码的,这部分源码在我的这篇博客详细分析了,现在我们来理解上的代码,首先我们知道Object result = methodProxy.invoke(target, objects);,target是我们实际被代理的对象,Object o就是我们生成的代理对象。下面对于这句代码,我提供3种写法,来分析一下:

//1. 
Object result = method.invoke(target, objects);
//2.
Object result = method.invoke(o, objects);
//3. 
Object result = methodProxy.invokeSuper(o, objects);

对于写法一其实和案例中正常写法一样,也是执行代理目标对象的方法,写法二这个会报错,它会执行代理对象的方法也就是,它会执行new MethodInterceptor() { 后面的代码,由于内部一直调用自己会陷入死循环,最后一个就是执行代理对象的父类的方法,这个是没问题的,我们前面说过cglib动态代理是基于继承的机制,这里想代理的目标对象是UserService类,所以执行它父类的方法就等同于执行UserService方法。

下面讲解一下cgblib其它的一些功能,我们知道如果我们代理了一个类,那么通常这个类中的所有的方法都会被代理,现在我们修改Userservice类。

public class UserService {

	public void test(){
		System.out.println("执行UserService的test方法");
	}
	public void a(){
		System.out.println("执行UserService的a方法");
	}
}

现在提出一个需求,只需要代理test方法而不需要代理a方法,或者执行test方法是一段代理逻辑,执行a方法是另一段代理逻辑。思路是每个方法对应一个执行器:

public class Test {

	public static void main(String[] args) {
		UserService target = new UserService();
		Enhancer enhancer = new Enhancer(); // cglib
		enhancer.setSuperclass(UserService.class);
		enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
			@Override
			public Object intercept(Object o, Method method, Object[] objects, MethodProxy
					methodProxy) throws Throwable {
				System.out.println("before...");
				Object result = methodProxy.invoke(target, objects);
				System.out.println("after...");
				return result;
			}
		}, NoOp.INSTANCE});
		enhancer.setCallbackFilter(new CallbackFilter() {
			@Override
			public int accept(Method method) {
			//返回值对应Callback中的方法处理器的下标
				if(method.getName().equals("test"))
				  return 0;
				else{
					return 1;
				}
			}
		});

		UserService userService = (UserService) enhancer.create();
		userService.test();
		userService.a();
	}
}

NoOp.INSTANCE你可以理解为啥事也没干就行了。我们看一下输出结果:
在这里插入图片描述

3. JDK动态代理的使用

JDK动态代理是Java提供的一种实现动态代理的机制。它是通过Java反射机制来实现的,主要用于在运行时动态生成代理类和对象。动态代理使得我们可以在不事先知道目标对象的情况下,通过代理类来调用目标对象的方法。以下是JDK动态代理的一些关键概念和使用方式:

接口必须实现: JDK动态代理要求目标对象必须实现一个或多个接口。代理类会实现这些接口,并且在调用方法时将请求委托给实际的目标对象。

Proxy类和InvocationHandler接口: JDK动态代理通过Proxy类和InvocationHandler接口实现。Proxy类提供了创建代理对象的静态方法,而InvocationHandler接口中有一个方法invoke,该方法在代理对象的方法被调用时执行。

newProxyInstance方法: Proxy类的newProxyInstance方法用于创建代理对象。该方法接受三个参数:ClassLoader(用于加载代理类的类加载器)、Class数组(目标对象实现的接口列表)、和InvocationHandler接口的实现类实例。

public class Test {

	public static void main(String[] args) {
		UserService target = new UserService();
		Aservice aservice = (Aservice) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{Aservice.class}, new InvocationHandler() {
			@Override
			public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
				System.out.println("before");
				//执行被Userservice的方法
				method.invoke(target,objects);
				System.out.println("after");
				return null;
			}
		});
		aservice.test();
	}
}

注意JDK动态代理被代理的对象其实是那个接口类型。

二、SpringAOP

1. 简介

上面介绍了两种常用的动态代理方式,而Spring底层就对这两种代理方式进行了封装,关键对象是ProxyFactory。下面介绍一下这个类的使用:

public static void main(String[] args) {
		UserService target = new UserService();
		//创建代理工厂
		ProxyFactory proxyFactory=new ProxyFactory();
		//设置代理对象
		proxyFactory.setTarget(target);
		//设置代理逻辑
		proxyFactory.addAdvice(new MethodBeforeAdvice() {
			@Override
			public void before(Method method, Object[] args, Object target) throws Throwable {
				System.out.println("before");
			}
		});
		//返回代理对象
		UserService proxy = (UserService) proxyFactory.getProxy();
		proxy.test();
	}

ProxyFactory默认是使用Cglib动态代理技术
在这里插入图片描述

我们稍微改一下,它就会使用jdk动态代理技术

public static void main(String[] args) {
		UserService target = new UserService();
		//创建代理工厂
		ProxyFactory proxyFactory=new ProxyFactory();
		//设置代理对象
		proxyFactory.setTarget(target);
		//设置接口
		proxyFactory.setInterfaces(Aservice.class);
		//设置代理逻辑
		proxyFactory.addAdvice(new MethodBeforeAdvice() {
			@Override
			public void before(Method method, Object[] args, Object target) throws Throwable {
				System.out.println("before");
			}
		});
		//返回代理对象
		Aservice proxy = (Aservice) proxyFactory.getProxy();
		proxy.test();
	}

在这里插入图片描述
此时我们可以发现,我们压根就不需要了解我们到底使用的是哪种代理模式,我们只需要知道通过这个代理工厂我们就可以拿到我们所需要的代理对象。

关于通知(Advice)的逻辑我们上面只使用了MethodBeforeAdvice,即方法执行前被调用,当然还有很多种,这里我们先稍微了解一下:

  • AfterReturningAdvice:方法执行完之后执行的代理逻辑
  • ThrowsAdvice:被代理方法执行抛异常之后执行的代理逻辑(这个接口没有提供重写方法,我们可以自己写方法逻辑,关键点是方法中的参数要和抛出的异常匹配才会调用这个方法)

在这里插入图片描述

  • MethodInterceptor(环绕通知):这个灵活性就很强我们可以收到去实现自己的代理逻辑下面用代码介绍一下这个通知的使用
public class Test {

	public static void main(String[] args) {
		UserService target = new UserService();
		//创建代理工厂
		ProxyFactory proxyFactory=new ProxyFactory();
		//设置代理对象
		proxyFactory.setTarget(target);
		//设置接口
		proxyFactory.setInterfaces(Aservice.class);
		//设置代理逻辑
		proxyFactory.addAdvice(new MethodInterceptor() {
			@Nullable
			@Override
			public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
				System.out.println("before1");
				Object proceed = invocation.proceed();
				System.out.println("after1");
				return null;
			}
	    });
		proxyFactory.addAdvice(new MethodInterceptor() {
			@Nullable
			@Override
			public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
				System.out.println("before2");
				Object proceed = invocation.proceed();
				System.out.println("after2");
				return null;
			}
		});
		//返回代理对象
		Aservice proxy = (Aservice) proxyFactory.getProxy();
		proxy.test();
	}
}

上面代码我给UserService加了两段代理逻辑,然后我们看一下输出:
在这里插入图片描述
分析输出我们大致能猜想出上面代理逻辑的执行顺序:

  1. 执行顺序是按照添加顺序来的
  2. 在执行前面一个代理逻辑的Object proceed = invocation.proceed();时候,它里面会执行后面添加的代理逻辑,就像一个洋葱一样,后面的代理逻辑被前面的代理逻辑包住,这也是环绕通知这一名字的由来。

还是回到上面提到的一个问题,如果我们需要一些方法被代理,而其它方法不被代理或被其它代理逻辑代理,那么在AOP中该怎么做?其实就是AOP中的切点:

public static void main(String[] args) {
		UserService target = new UserService();
		//创建代理工厂
		ProxyFactory proxyFactory=new ProxyFactory();
		//设置代理对象
		proxyFactory.setTarget(target);
		//设置接口
		proxyFactory.setInterfaces(Aservice.class);
		//添加切点
		proxyFactory.addAdvisor(new PointcutAdvisor() {
			//获得切点
			@Override
			public Pointcut getPointcut() {
				return new StaticMethodMatcherPointcut() {
					@Override
					public boolean matches(Method method, Class<?> targetClass) {
						return method.getName().equals("test");
					}
				};
			}

			@Override
			public Advice getAdvice() {
				return new MethodBeforeAdvice() {
					@Override
					public void before(Method method, Object[] args, Object target) throws Throwable {
						System.out.println("before test ");
					}
				};
			}

			@Override
			public boolean isPerInstance() {
				return false;
			}
		});
		//添加切点
		proxyFactory.addAdvisor(new PointcutAdvisor() {
			//获得切点
			@Override
			public Pointcut getPointcut() {
				return new StaticMethodMatcherPointcut() {
					@Override
					public boolean matches(Method method, Class<?> targetClass) {
						return method.getName().equals("a");
					}
				};
			}

			@Override
			public Advice getAdvice() {
				return new MethodBeforeAdvice() {
					@Override
					public void before(Method method, Object[] args, Object target) throws Throwable {
						System.out.println("before a ");
					}
				};
			}

			@Override
			public boolean isPerInstance() {
				return false;
			}
		});
		//返回代理对象
		Aservice proxy = (Aservice) proxyFactory.getProxy();
		proxy.test();
		proxy.a();
	}

在这里插入图片描述

Advisor=切点+通知

2. Spring AOP使用

前面介绍了ProxyFactory来创建代理对象的过程,但前面我们始终没有和Spring联系起来,都是我们收动的在创建自己的代理对象,添加自己的代理逻辑,这一部分我们将Spring和AOP紧密联系起来进行分析。

  • 方式一:

使用FactoryBean将代理对象作为一个bean存到AOP容器中,底层类型是ProxyFactoryBean

    @Bean
	public ProxyFactoryBean userService(){
		ProxyFactoryBean proxyFactoryBean=new ProxyFactoryBean();
		proxyFactoryBean.addAdvice(new MethodBeforeAdvice() {
			@Override
			public void before(Method method, Object[] args, Object target) throws Throwable {
				System.out.println("before");
			}
		});
		proxyFactoryBean.setTarget(new UserService());
		return proxyFactoryBean;
	}

在这里插入图片描述

  • 方式二

先说需求,如果我们要被代理的对象UserService是一个Bean,然后我们的代理逻辑也是一个Bean,那么怎么将这两个bean联系起来,生成UserService的代理对象。首先我们先将通知(Advice)封装成一个bean。

@Component
public class Aservice implements MethodBeforeAdvice {

	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("方法执行前的逻辑");
	}
}

@Component
public class UserService {


	public void test() {
		System.out.println("test");
	}

	public void a() {
		System.out.println("a");
	}
}

要达到上面的需求我们要使用到一个Bean,为BeanNameAutoProxyCreator

	@Bean
	public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){
		BeanNameAutoProxyCreator beanNameAutoProxyCreator=new BeanNameAutoProxyCreator();
		//要被代理的Bean的名称前缀
		beanNameAutoProxyCreator.setBeanNames("User*");
		//设置代理逻辑的bean
		beanNameAutoProxyCreator.setInterceptorNames("AService");
		return beanNameAutoProxyCreator;
	}

其实BeanNameAutoProxyCreator本质上就是一个BeanPostProcessor。通过BeanNameAutoProxyCreator可以对批量的Bean进行AOP,并且指定了代理逻辑,指定了一个 InterceptorName,也就是一个Advise,前提条件是这个Advise也得是一个Bean,这样Spring才能找到的,但是BeanNameAutoProxyCreator的缺点很明显,它只能根据beanName来指定想要代理 的Bean。

  • 方式三

除了使用BeanNameAutoProxyCreator,还可以使用DefaultAdvisorAutoProxyCreator

@ComponentScan("com.zhouyu")
@EnableScheduling
@PropertySource("classpath:spring.properties")
@EnableTransactionManagement
@Configuration
public class AppConfig {
	@Bean
	public DefaultPointcutAdvisor defaultPointcutAdvisor(){
		NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
		pointcut.addMethodName("test");
		DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
		defaultPointcutAdvisor.setPointcut(pointcut);
		defaultPointcutAdvisor.setAdvice(new AService());
		return defaultPointcutAdvisor;
	}
	@Bean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new
				DefaultAdvisorAutoProxyCreator();
		return defaultAdvisorAutoProxyCreator;
	}
}

上面DefaultAdvisorAutoProxyCreator也是一个BeanPostProcessor在bean的生命周期中,调用DefaultAdvisorAutoProxyCreator,然后判断当前创建bean有没有test方法,如果有就执行AService代理逻辑。

OOP表示面向对象编程,是一种编程思想,AOP表示面向切面编程,也是一种编程思想,而我们上 面所描述的就是Spring为了让程序员更加方便的做到面向切面编程所提供的技术支持,换句话说,就 是Spring提供了一套机制,可以让我们更加容易的来进行AOP,所以这套机制我们也可以称之为 Spring AOP。但是值得注意的是,上面所提供的注解的方式来定义Pointcut和Advice,Spring并不是首创,首创是 AspectJ,而且也不仅仅只有Spring提供了一套机制来支持AOP,还有比如 JBoss 4.0、aspectwerkz 等技术都提供了对于AOP的支持。而刚刚说的注解的方式,Spring是依赖了AspectJ的,或者说, Spring是直接把AspectJ中所定义的那些注解直接拿过来用,自己没有再重复定义了,不过也仅仅只 是把注解的定义赋值过来了,每个注解具体底层是怎么解析的,还是Spring自己做的,所以我们在用 Spring时,如果你想用@Before、@Around等注解,是需要单独引入aspecj相关jar包的。

意思是,AOP中的这些概念不是Spring特有的,不幸的是,AOP中的概念不是特别直观的,但是, 如果Spring重新定义自己的那可能会导致更加混乱

  1. Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、 Advice等等
  2. Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一 个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。
  3. Advice:表示通知,表示在一个特定连接点上所采取的动作。Advice分为不同的类型,后面详 细讨论,在很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接 点周围维护一个Interceptor链
  4. Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的, Advice将会执行在和切点表达式所匹配的连接点上
  5. Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
  6. Target object:目标对象,被代理对象
  7. AOP proxy:表示代理工厂,用来创建代理对象的,在Spring Framework中,要么是JDK动态 代理,要么是CGLIB代理
  8. Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如 Aspejctj),或者运行时,比如Spring AOP

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/317280.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Web实战丨基于django+html+css+js的电子商务网站

文章目录 写在前面实验目标需求分析实验内容安装依赖库1.登陆界面2.注册界面3.电子商城界面4.其他界面 运行结果写在后面 写在前面 本期内容&#xff1a;基于DjangoHTMLCSSJS的电子商务网站 实验环境&#xff1a; vscode或pycharmpython(3.11.4)django 代码下载地址&#x…

Web自动化测试,一定得掌握的 8 个核心知识点

使用 cypress 进行端对端测试&#xff0c;和其他的一些框架有一个显著不同的地方&#xff0c;它使用 javascript 作为编程语言。传统主流的 selenium 框架是支持多语言的&#xff0c;大多数 QA 会的python 和 java 语言都可以编写 selenium 代码&#xff0c;遇到需要编写 js 代…

距离美赛还有20天,不要忘了阅读往年获奖论文(附04-23年美赛获奖论文)

之前发了很多数模相关的知识&#xff0c;受到了一些人的关注&#xff0c;也有很多人私下问我&#xff0c;距离美赛还有20几天了&#xff0c;还来不来得及。 对此我想说&#xff0c; 来不来得及重要吗&#xff1f; 你名都报了&#xff0c;钱也交了&#xff0c;还是笔不小的钱…

猫狗大战(猫狗识别)

1.问题简介 1.1问题描述 在这个问题中&#xff0c;你将面临一个经典的机器学习分类挑战——猫狗大战。你的任务是建立一个分类模型&#xff0c;能够准确地区分图像中是猫还是狗。 1.2预期解决方案 你的目标是通过训练一个机器学习模型&#xff0c;使其在给定一张图像时能够准…

【模块系列】STM32BMP280

前言 最进想练习下I2C的应用&#xff0c;手上好有BMP280也没用过&#xff0c;就看着机翻手册和原版手册&#xff0c;开始嘎嘎写库函数了。库的命名应该还1是比较规范了吧&#xff0c;就是手册对于最终值的计算方式很迷糊&#xff0c;所以现在也不能保证有可靠性啊&#xff0c;大…

关于运维·关于数据库面试题

目录 一、数据库类型 二、数据库引擎 三、mysql数据库类型 四、mysql的约束添加 五、主从复制原理 六、主从方式有几种 七、mysql主从数据不一致的原因 八、mysql的优化 九、什么是事务的特征 十、数据库读写分离的好处 十一、怎样优化sql语句 十二、mysql的同步方…

基于MAP算法的Turbo译码 -- 公式推导

到此为止&#xff0c;讲完了turbo译码器的子译码器基于MAP算法的译码过程。但在实际使用中&#xff0c;很少直接使用MAP算法进行译码。而是使用改进的LOG-MAP和MAX-LOG-MAP算法进行译码&#xff0c;因此译码的整体流程&#xff0c;包括外信息的计算以及先验信息的获取等。都在后…

openai自定义API操作 API (openai.custom)

OpenAI 提供了一个自定义 API&#xff0c;允许开发者通过编程方式与 OpenAI 的 AI 模型进行交互。使用这个 API&#xff0c;你可以执行各种任务&#xff0c;例如文本生成、推理和翻译等。 以下是使用 OpenAI 自定义 API 的基本步骤&#xff1a; 创建 API 密钥&#xff1a;首先…

谷粒商城-商品服务-品牌管理-阿里云云存储+JSR303数字校验+统一异常处理

阿里云云存储OSS 分布式系统上传文件 分布式系统上传文件 单体应用上传&#xff1a;上传文件到服务器&#xff0c;想获取文件时再向服务器发请求获取文件。 分布式系统上传&#xff1a; 因为有多台服务器&#xff0c;为防止负载均衡导致获取文件时没找到对应的服务器&#xf…

数字信号处理教程学习笔记1-第2章时域中的离散信号和系统

信号处理的任务示意方框图 模拟信号和数字信号分别是啥样的,有啥区别

MySQL数据库设计原则

0.简单的处理逻辑 一.MySQL完整性约束 主键约束 primary key 自增键约束 auto_increment 唯一键约束 unique 非空约束 not null 默认值约束 default 外键约束 foreign key 下面是一个sql语句创建一个表,可以看出来了使用了哪几个约束吗? create table user( id int…

vue前端开发自学,借助KeepAlive标签保持组件的存活

vue前端开发自学,借助KeepAlive标签保持组件的存活&#xff01;如果不想让组件在切换的时候&#xff0c;被默认操作&#xff08;卸载掉了&#xff09;。他们需要使用这个这个表情哦。 下面给大家看看代码情况。 <template><h3>ComA</h3><p>{{ messag…

Arduino开发实例-AS608光学指纹传感器驱动

AS608光学指纹传感器驱动 文章目录 AS608光学指纹传感器驱动1、AS608光学指纹传感器介绍2、硬件准备及接线3、代码实现3.1 指纹录入3.2 指纹匹配验证1、AS608光学指纹传感器介绍 AS608 光学指纹传感器可用于扫描指纹,它也可以通过串行通信将处理后的数据发送到微控制器。 所有…

鸿蒙(HarmonyOS)应用开发指南

1. 概述 1.1 简介 鸿蒙&#xff08;即 HarmonyOS &#xff0c;开发代号 Ark&#xff0c;正式名称为华为终端鸿蒙智能设备操作系统软件&#xff09;是华为公司自 2012 年以来开发的一款可支持鸿蒙原生应用和兼容 AOSP 应用的分布式操作系统。该系统利用“分布式”技术将手机、电…

好用的便签有哪些?windows便签工具在哪打开?

每当我8点准时上班&#xff0c;在等待电脑开机的过程&#xff0c;我都会习惯性地思考整理今天要晚上的任务&#xff0c;列出所要完成的待办事项。随着每一项任务的清晰呈现&#xff0c;我的心情也逐渐明朗起来。当然了&#xff0c;这个时候&#xff0c;我迫切需要一款好用的便签…

【LeetCode】142. 环形链表 II(中等)——代码随想录算法训练营Day04

题目链接&#xff1a;142. 环形链表 II 题目描述 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了…

springcloud stream消息驱动

简介 Spring Cloud Stream是一个用于构建基于事件驱动的微服务应用程序的框架&#xff0c;其核心目标是简化开发过程&#xff0c;降低消息通信的复杂性&#xff0c;从而使开发人员能够专注于编写业务逻辑。Spring Cloud Stream通过提供Binder抽象&#xff0c;将应用程序与消息…

使用 Docker 进行 Go 应用程序引导指南

为在 Docker 中部署的 Go 应用程序做准备 在使用 Go 开发 Web 应用程序时&#xff0c;无论是用于 HTTP 还是其他类型的服务&#xff0c;部署到不同的阶段或环境&#xff08;本地开发、生产环境等&#xff09;都是一个常见的考虑因素。在本文中&#xff0c;我们将探讨在 Docker …

利用低代码技术,企业怎样开拓数字化转型新路径?

近年来&#xff0c;随着技术的发展和市场竞争的加剧&#xff0c;企业数字化转型已成为一种趋势。许多企业已经完成了线上协作办公的初步转型&#xff0c;这主要得益于像钉钉、企微等发展完善的平台&#xff0c;只需将员工全部拉入这些平台&#xff0c;就能实现线上协作办公。 然…

js逆向第17例:猿人学第13题入门级cookie

文章目录 一、前言二、定位关键参数三、代码实现一、前言 任务十三:还是抓取这5页的数字,计算加和并提交结果 二、定位关键参数 通过题名已经知道需要破解cookie值,控制台查看请求数据,接口https://match.yuanrenxue.cn/api/match/13?page=2中的yuanrenxue_cookie值应该…