试试使用Spring Event组合@Async注解,轻松实现代码的解耦和异步

Spring注解 @EventListener 的介绍与使用示例以及异常处理 将一个方法标记为监听器,用于监听应用程序事件,事件可以是 ApplicationEvent实例,也可以是其他任意的对象。 阅读详情

一 前言

在我们写代码的时候,通常需要考虑到代码的耦合性,因为低耦合的代码有利于我们后续的维护和迭代,而Spring Event可以说是一个降低代码耦合度的神器,配合@Async注解更是能够轻松实现异步。今天我们就一起来了解一下Spring Event。

二:如何使用Spring Event

我们以一个简单的业务场景为例:

用户注册账号之后,我们需要赠送用户500积分

1.定义Event事件类和DTO传输数据对象

首先我们需要定义一个增加积分的事件,而这个类需要继承ApplicationEvent类。

public class AddCreditEvent extends ApplicationEvent {

    private static final long serialVersionUID = 303142463705896963L;

    public AddCreditEvent(Object source) {
        super(source);
    }
}

2.发布一个增加积分的事件

我们在用户进行注册之后,需要赠送用户积分,因此,我们在用户注册之后需要发布一个增加积分的事件。我们在UserServiceImpl中实现ApplicationEventPublisherAware接口,实现setApplicationEventPublisher()方法,获取到applicationEventPublisher对象,通过applicationEventPublisher对象的publishEvent()方法就可以发布对应的事件了。

public class UserServiceImpl implements UserService, ApplicationEventPublisherAware {
 
    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
    
    @Override
    public void register(RegisterReqDTO reqDTO) {
        //注册用户
        registerUser(reqDTO);

        //发布增加积分事件 新增积分
        AddCreditDTO addCreditDTO = new AddCreditDTO();
        addCreditDTO.setUserId(user.getId());
        addCreditDTO.setCreditAmount(500);
        applicationEventPublisher.publishEvent(new AddCreditEvent(addCreditDTO));
    }

}

3.创建监听类监听发布的事件

发布完事件之后,我们需要有对应的监听类去监听我们发布的事件,在监听类中执行我们对应的业务逻辑。监听类需要实现ApplicationListener类,并且设置泛型为我们发布的事件类型,同时我们需要将监听类交给Spring管理(所以我们加上@Component注解)。这样在onApplicationEvent()方法中就可以执行我们的业务逻辑了。

@Component
public class AddCreditEventListener implements ApplicationListener<AddCreditEvent> {

    @Override
    public void onApplicationEvent(AddCreditEvent event) {
        Object source = event.getSource();
        AddCreditDTO addCreditDTO = (AddCreditDTO)source;
        //新增积分业务代码
        ....
    }
}

三:配合@Async注解实现异步

1.启动类上添加@EnableAsync注解

在启动类上添加@EnableAsync注解

@SpringBootApplication
@EnableAsync
public class UserApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class,args);
    }
}

2.在onApplicationEvent()方法上添加@Async注解

在onApplicationEvent方法上添加@Async注解就可以轻松实现异步了,但是并不推荐直接使用@Async注解,可以配置一个自定义线程池,根据业务以及系统资源配置好最大线程数,核心线程数,阻塞队列等线程池参数。

注:为什么不推荐直接使用@Async?因为在Springboot环境中,@Async默认使用的线程池最大线程数是Integer.MAX,并且阻塞队列的大小也是Integer.MAX,这显然是不合理的,所以我们最好自己定义线程池,然后指定@Async的value属性。

@Component
public class AddCreditEventListener implements ApplicationListener<AddCreditEvent> {
    
    @Override
    @Async
    public void onApplicationEvent(AddCreditEvent event) {
        Object source = event.getSource();
        AddCreditDTO addCreditDTO = (AddCreditDTO)source;
        //新增积分业务代码
        ....
    }
}

四:利用事件机制完成业务逻辑有什么好处?

1.降低代码的耦合度

如果我需要新增积分,那么我就发布一个新增积分的事件,需要成为会员,那么我就发布一个成为会员的事件,通过不同的事件,将业务逻辑解耦,只需要发布事件,不需要关注具体的实现逻辑,代码的条理更清晰。方便以后的扩展与维护工作。

2.增强代码的复用性

例如注册用户可能需要新增积分,购买商品之后也需要新增积分,那么我们就都可以通过发布一个新增积分的事件来完成了。代码的复用性得到了很大的提高。

五:Spring Event的实现原理

1.首先在Spring容器启动时,在Application的refresh()方法中会调用prepareBeanFactory()方法,在prepareBeanFactory()方法中会注册一个ApplicationListenerDetector的类,ApplicationListenerDetector实现了BeanPostProcessor,在postProcessAfterInitialization()方法中保存所有的实现了ApplicationListener的类。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    ...
   // Register early post-processor for detecting inner beans as ApplicationListeners.
   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
   ....
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (bean instanceof ApplicationListener) {
			// potentially not detected as a listener by getBeanNamesForType retrieval
			Boolean flag = this.singletonNames.get(beanName);
			if (Boolean.TRUE.equals(flag)) {
				// singleton bean (top-level or inner): register on the fly
                                //保存所有实现了ApplicationListener的类
				this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
			}
			else if (Boolean.FALSE.equals(flag)) {
				if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
					// inner bean with other scope - can't reliably process events
					logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
							"but is not reachable for event multicasting by its containing ApplicationContext " +
							"because it does not have singleton scope. Only top-level listener beans are allowed " +
							"to be of non-singleton scope.");
				}
				this.singletonNames.remove(beanName);
			}
		}
		return bean;
	}

2.refresh()方法会调用initApplicationEventMulticaster()方法,initApplicationEventMulticaster()方法的作用是看Spring容器中是否存在ApplicationEventMulticaster广播器,如果不存在,那么就创建一个SimpleApplicationEventMulticaster的广播器,并且加入到Spring容器中。

	protected void initApplicationEventMulticaster() {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
		else {
                //如果不存在广播器 那么久创建一个SimpleApplicationEventMulticaster的广播器 并注册到Spring容器中
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
			beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
						"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
			}
		}
	}

3.refresh()方法中调用registerListeners()方法,registerListeners()方法作用就是将ApplicationEvent绑定到ApplicationEventMulticaster广播器中。

protected void registerListeners() {
		// Register statically specified listeners first.
		for (ApplicationListener<?> listener : getApplicationListeners()) {
			getApplicationEventMulticaster().addApplicationListener(listener);
		}

		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let post-processors apply to them!
		String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
		for (String listenerBeanName : listenerBeanNames) {
			getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
		}

		// Publish early application events now that we finally have a multicaster...
		Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
		this.earlyApplicationEvents = null;
		if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
			for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
				getApplicationEventMulticaster().multicastEvent(earlyEvent);
			}
		}
	}

4.有了前面的准备,接下来我们可以看applicationEventPublisher.publishEvent()方法了。publishEvent()方法的核心作用就是调用ApplicationEventMulticaster广播器的multicastEvent()方法。

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");

		// Decorate event as an ApplicationEvent if necessary
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
			}
		}

		// Multicast right now if possible - or lazily once the multicaster is initialized
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish event via parent context as well...
		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event);
			}
		}
	}

multicastEvent()

multicastEvent方法会找到所有监听对应事件的监听类,调用invokeListener(listener, event)方法(这里默认线程池为空,所以默认是同步执行的)。

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

invokeListener()

invokeListener()方法的作用就是调用doInvokeListener()方法,所以我们看doInvokeListener()方法。

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
		ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
			try {
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
				errorHandler.handleError(err);
			}
		}
		else {
			doInvokeListener(listener, event);
		}
	}

doInvokeListener()

doInvokeListener()方法就是调用对应监听器的onApplicationEvent方法。

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
					(event instanceof PayloadApplicationEvent &&
							matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				// -> let's suppress the exception.
				Log loggerToUse = this.lazyLogger;
				if (loggerToUse == null) {
					loggerToUse = LogFactory.getLog(getClass());
					this.lazyLogger = loggerToUse;
				}
				if (loggerToUse.isTraceEnabled()) {
					loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
				}
			}
			else {
				throw ex;
			}
		}
	}

至此,Spring Event事件的实现也就完成了。

六:最后

本文主要介绍了Spring Event的使用以及它的实现原理,看完这篇文章相信你对Spring Event已经有了一定的了解,不妨在我们的业务开发中尝试使用Spring Event来降低代码的耦合度吧。

Spring Event详解 Spring Event是Spring框架内建的一种发布/订阅(Publish-Subscribe)模式的实现,它允许应用内部不同组件之间通过事件进行通信。当某个特定事件发生时,系统中对这类事件感兴趣的监听器可以接收到通知并执行相应操作。 阅读详情

相关推荐

SpringBoot中使用SpringEvent业务解耦神器实现监听发布事件同步异步执行任务

开发过程中,业务逻辑可能非常复杂,核心业务 + N个子业务。如果都放到一块儿去做,代码可能会很长,耦合度不断攀升。还有一些业务场景不需要在一次请求中同步完成,比如邮件发送、短信发送等。MQ 可以解决这个问题,但 MQ 重,非必要不提升架构复杂度。针对这些问题,我们了解一下 Spring Event。Spring Event(Application Event)其实就是一个观察者设计模式,一个 Bean 处理完成任务后希望通知其它 Bean 或者说一个 Bean 想观察监听另一个Bean 的行为。

BADAO_LIUMANG_QIZHI的博客 1160

SpringBoot 实现异步事件Event

异步Event

ZULJ131的博客 3376

Android开发必备:TBS腾讯浏览服务静态集成完整指南(附Demo下载)

本文详细解析了Android开发中TBS腾讯浏览服务的静态集成方案,通过对比动态集成,深入剖析了其核心原理与适用场景。文章提供了从环境准备、资源获取、项目配置到代码初始化的完整指南,并重点介绍了WebView与文件预览等高级特性的实战应用。针对集成过程中的常见问题,提供了详尽的排查清单与性能调优建议,旨在帮助开发者构建极致稳定、不依赖网络的WebView与文件预览功能。文末附有经过验证的Demo项目可供下载参考。

earth的博客 369

Spring Event组合@Async注解,代码的解耦和异步最优解!

在我们写代码的时候,通常需要考虑到代码的耦合性,因为低耦合的代码有利于我们后续的维护和迭代,而Spring Event可以说是一个降低代码耦合度的神器,配合@Async注解更是能够轻松实现异步。今天我们就一起来了解一下Spring Event。

m0_45902146的博客 924

IEEE Transactions on Industrial Informatics(TII) 投稿须知- Initial Submission

请注意,您可以在作者中心查看稿件的当前状态,如:在EIC办公室、分配给AE、AE邀请审稿人、AE指定审稿人、正在审稿、等待AE决定、等待EIC决定。为了确保公正的评审过程,我们要求在稿件中没有任何作者的身份(不包括作者的姓名、传记、隶属关系、求职信、签署的版权表格、识别脚注、致谢等)。如果你的手稿有很大一部分已经在会议上发表过,论文必须隐藏所有作者的身份,这样会议论文就可以被引用,就好像它是由不同的作者写的一样。请注意,通常你的技术成就是根据被引用的次数来评估的,而不是根据发表的论文数量。

loinleeai的博客 4434

深度解析跨境支付之业务架构

详解跨境支付之业务架构

weixin_47493863的博客 1879

【SpringBoot】使用SpringEvent事件通知做异步消息处理

我们在项目中经常会碰见类似诸如用户支付完订单后,通知用户支付成功,并且商品库存对应减少这样的需求,也就是做完主业务流程之后,希望异步执行一些其他的操作。接下来我们来探究解决这个问题的几种方案来引出,这里的方案就先不讨论MQ队列。...............

分享记录开发中的问题,欢迎一起探讨! 3260

相场断裂模型中的各向异性机制:从裂纹密度到驱动力的数值实现

断裂力学是研究材料在载荷作用下裂纹萌生与扩展规律的基础学科,其核心在于理解材料失效的能量准则与路径选择。传统方法通常预设裂纹路径,而相场断裂模型通过引入连续的相场变量,将裂纹模拟转化为一个能量最小化问题,能够自发预测裂纹的萌生、扩展与分叉,在工程失效分析中具有重要价值。当材料具有显著的方向依赖性时,如单晶、复合材料或轧制金属板,各向同性假设会失效,此时需引入各向异性机制。该机制的核心在于改造两个关键部分:一是裂纹密度函数,通过引入各向异性张量来定义不同方向上形成裂纹微结构的能量成本;二是临界能量释放率(断裂

weixin_31421613的博客 298

2020-12-15:Spring框架中有哪些不同类型的事件

2020-12-15:Spring框架中有哪些不同类型的事件 前言es底层读写原理?倒排索引原理? 前言 每日一题专栏 es底层读写原理?倒排索引原理? Spring 提供了以下5种标准的事件: 1.上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。 2.上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的S

残冬十九 877

腾讯云CodeBuddy+微信小程序:5分钟开发番茄小闹钟

番茄钟是一种简单的时间管理方法。这个方法很简单:你专注工作25分钟,然后休息5分钟。这样可以让你更专心,不容易分心,还能防止过度疲劳。 很多人用番茄钟来提高工作效率,因为它能帮助你更好地管理时间。不过,市面上的一些免费番茄钟应用常常会有广告弹出来,影响使用体验。这就是为什么我们要开发一个没有广告的番茄钟小程序,让用户可以专心工作,不被打扰。

2201_75366661的博客 1689

完整IC设计流程解析&Makefile学习

本文介绍了decorder38项目的文件结构、自动化综合流程和Makefile使用方法。项目包含RTL到GDSII全流程文件,重点关注syn目录中的综合脚本。通过make dc命令运行综合流程,生成网表、约束和时序文件。关键报告如qor.rpt、area.rpt和min_vio.rpt用于分析综合质量、面积和时序。Makefile部分解释了其基本语法、IC设计中的典型结构,包括目标定义、命令执行和特殊声明,强调了其在自动化构建和依赖管理中的重要性。

m0_63352623的博客 856

spring 的 事件监听机制实现以及使用线程池异步处理监听事件

使用spring提供的事件监听机制: 为什么需要使用事件这种模式? spring中实现事件有几种方式? 上述两个问题可以参考: 链接博客 本文主要是解释如何通过注解简便实现事件监听以及让事件监听支持异步。 1.使用注解实现事件监听 涉及三个对象: 1)监听的事件(提供事件处理时的元数据) 2)监听器(事件处理者) 3)事件发布者(调用者) 事件: 实现 ApplicationEvent 类 import lombok.Getter; import org.springframework.context.Ap

lin1214000999的博客 6724

【Win10/11】Windows电脑WiFi显示小地球/断网的解决方法 Windows WiFi小地球、网络连接中断、网络重置、无线网卡驱动、Win10/Win11网络修复

最近经常有粉丝留言问我,Windows 电脑突然连不上 WiFi,右下角网络图标显示成了“小地球 🌐”,或者干脆提示“未连接 - 没有可用的连接”。明明其他设备网络都正常,唯独自己的电脑断了。作为在互联网大厂混迹多年的前端程序员,我自己也碰到过这个问题。总结几种比较常见、亲测有效的解决方法,写出来供大家参考,希望能帮你快速恢复上网!问题原因推荐解决方法系统设置错乱使用网络重置功能驱动异常或冲突卸载后重启自动安装驱动驱动版本过旧手动更新网卡驱动实在不行考虑使用系统还原点或重装系统这几个方法中,

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 3851

spring中@EventListener 的详解和使用

spring 监听器的使用 @EventListener

keanu20191101的博客 1678

OpenClaw 子代理配对失败问题分析与解决方案

OpenClaw子代理配对失败问题分析与解决方案 问题核心在于agent设备权限不足,仅有operator.approvals权限而缺少operator.admin权限,导致子代理启动时形成权限死锁。本文提供三种解决方案: 直接编辑设备配对文件(推荐):修改本地JSON文件,为agent设备添加完整operator.admin权限,操作简单快捷。 标准重新配对流程:删除现有agent设备后重新配对,通过正常审批流程获取完整权限,符合系统安全设计。 修改源码降低权限要求(仅限开发环境):调整子代理代码避免强制

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 151

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Spring的@Event事件默认同步线程,如何开启异步?

三、事件监听的异步已经大功告成,而且异步线程中的各种异常(捕获和未捕获),都不会影响到主线程的正常返回。一、首先在Application启动类上添加@EnableAsync开启异步注解;二、在监听方法上加上@Async注解。

qq_42811766的博客 1076

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

spring event事件(三)广播与异步问题

下面对同一个事件加上多个监听器,copy MyListenerOne为MyListenerTwo。触发event,监听器和调用处是同步执行的,调用处-->listen执行-->调用处;可以看到多个listener都监听到了,是广播的形式。在某一个Listener加入异常代码。调用接口到返回的时间很长,日志打印。日志打印这一个监听器报错。

w_t_y_y的博客 1286

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Spring ApplicationEvent 异步处理

在性能要求比较高的接口中,执行一个比较耗时而并不关键的操作(比如上报监控信息什么的),如果同步执行必然是没有意义的,因此一般我们会想去异步处理,通常会使用MQ之类的中间件,不过Spring也提供了事件相关的处理,就是ApplicationEvent,关于这个类这里不再多言,基础的知识可以网上搜索相关教程,不过有一个问题需要注意的是,默认这种事件机制是同步的,好处是如果有事务,发送事件的方法和事件处...

thekenofDIS的专栏 1万+

【Java】SpringBoot实现事件监听(异步执行)

SpringBoot实现事件监听(异步执行)-源码测试

m0_64210833的博客 6265

SpringBoot使用异步Spring Event事件

Spring Event发布事件,进行业务方法解耦

charm2014的博客 1105

Springboot event Listener 事件监听 异步处理

编写SpringContextHolder package com.cf.user.server.event; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.DisposableBean; import org.springframework.context.ApplicationContext; import org.springframew.

丶Melody 3014

@EventPublisher + @Async 异步事件流详解

本文主要介绍Spring事件流和`@Async`异步线程池处理,以及`@Async`默认线程池可能会导致的问题及解决方法。 在@Async注解中value参数使用自定义线程池,能让开发工程师更加明确线程池的运行规则,选取适合的线程策略,规避资源耗尽的风险

若明天不见 1969

spring事件发布监听EventListener

spring事件发布 监听

yang_zzu的博客 1493

Spring事件之注解@EventListener讲解

当我们观察方法的时候,会发现这个方法它主要是在对这个缓存在搞事情。这个缓存里面放的就是在项目启动过程中已经触发过的框架自带的listener对象:调用的时候,如果能从缓存中拿到对应的listener,则直接返回。而我们Demo中的自定义listener是第一次触发,所以肯定是没有的。因此关键逻辑就在方法里面:这个方法里面的逻辑较多,只说一下这个关键的 for 循环:这个 for 循环在干啥事呢?就是循环当前所有的listener,过滤出能处理当前这个事件的listener。

上善若泪 1万+

【Spring的事件监听模式异步配置】

最近在项目中,有个场景是有几个场景下的数据变更,需要同时执行一个逻辑。在这里我使用了Spring事件的模式对系统进行解耦,事件源发布一个事件,事件监听器(@EventLintener注解方法)可以消费这个事件,而事件源不用关注发布的事件有哪些监听器,这可以可以对系统进行解耦。但是,同事看到后说我以前用过,Spring的监听是异步执行的,监听器内的执行不会阻塞主线程。我一听不对啊,时间监听默认是线程同步的,并不是异步了,需要异步执行的话得进行配置,话不多说,直接上demo进行验证。通过追踪的。

qq_41784138的博客 2131
上一篇: Spring 源码阅读 04:BeanFactory 初始化
下一篇: mysql查询 limit 1000,10 和limit 10 速度一样快吗?如果我要分页,我该怎么办?
java_lujj
博客等级 码龄4年 18粉丝 152原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值