最新要闻
- 好作品应如实呈现时代的偏见吗?——《漫长的季节》中的女性角色塑造 焦点
- 【速看料】国产办公软件WPS出现服务故障 官微道歉:正在紧急排查修复
- 全球实时:红魔电竞平板官宣:首款骁龙8 Gen2+内置风扇平板
- 《博德之门3》反向跳票!不会为主机版质量向30帧妥协 全球快讯
- 你退了吗?网易暴雪国服游戏退款申请今天截止:过期视为自动放弃 全球快消息
- 男生长胖20斤被公司解约 官方回应:如举报会责令整改_当前短讯
- 微软只保证《使命召唤》停留三年 索尼官方回应
- 新资讯:携程正式宣布员工生孩后补贴5万元:每年1万 连领5年
- 超值优惠手慢无:真维斯Z+冰丝短裤29元、长裤/Polo衫39元
- 特斯拉充电中被临车女车主强行拔枪:哨兵模式拍个正着-焦点信息
- 你能坚持多久?小伙在没信号的原始森林隐居4年火了:中国最神秘部落揭秘_环球今热点
- 每日消息!特斯拉人形机器人将亮相上海:预计未来售价不超过15万元
- 【世界热闻】马价十倍说明了什么道理_马价十倍
- 我们该怎么帮助年轻人就业
- 2.5万元的Vision Pro问世 荣耀赵明:整个行业都应该感谢苹果_最新快讯
- 焦点资讯:帕萨特提车第二天就严重抖动 车主:车子像是仿佛在蹦
手机
光庭信息跌4.57% 2021上市超募11亿2022扣非降74% 时快讯
搜狐汽车全球快讯 | 大众汽车最新专利曝光:仪表支持拆卸 可用手机、平板替代-环球关注
- 光庭信息跌4.57% 2021上市超募11亿2022扣非降74% 时快讯
- 搜狐汽车全球快讯 | 大众汽车最新专利曝光:仪表支持拆卸 可用手机、平板替代-环球关注
- 视点!美国首位女总统即将诞生?拜登恐怕要提前下岗,美政坛迎来变局?
- 当前速递!用理想仪器实现更好的颗粒 德国新帕泰克亮相CPHI & PMEC China获好评
- 微粒贷怎么申请开通 开通方法如下
- 焦点简讯:心疼!这位40岁的云南缉毒警,已是满头白发
家电
Spring事件监听在业务使用中的优化
事件驱动的开发方式在业务系统开发中扮演着关键角色,若能妥善运用,将对系统的拓展性产生至关重要的影响。Spring框架对事件驱动进行了出色的封装,然而在实际业务应用中,我们发现了一些可优化的领域,因此针对这些问题进行了封装和优化工作。
Spring事件驱动的使用
在电商订单创建的场景中,一旦订单成功创建,必须执行一系列附加的业务操作。这些操作包括向用户发送相关信息,并将订单信息同步到相关系统中。考虑到此场景的特点,事件驱动的开发方式被广泛认为是最合适的选择,因其能够在不对业务系统造成负面影响的前提下进行处理。
【资料图】
事件定义
在订单创建事件中,可以定义包含订单对象以及与订单相关的属性,例如订单ID等。通过此事件,能够有效地传递订单信息,并为其他业务操作提供必要的数据依据。
@Data@AllArgsConstructorpublic class CreateTradeEvent{ // 可以在此定义订单创建事件中的相关属性 private Trade trade; }
事件发布
在订单成功创建之后,我们可以获取到订单实体或其他相关属性。基于这些内容,我们可以创建一个订单创建事件的对象,并利用Spring框架提供的事件发布机制进行订单创建事件的发布。通过这种方式,我们能够有效地将订单创建事件传递出去,以便其他业务逻辑可以相应地对该事件进行处理。
public class TradeServiceImpl{ @Autowired private ApplicationEventPublisher applicationEventPublisher; public void createTrade(CreateTradeCmd crateTradeCmd){ // 一些业务操作后得到订单实体 Trade trade = new Trade(); // 发布订单创建的事件 applicationEventPublisher.publishEvent(new CreateTradeEvent(trade)); }}
事件监听
一旦订单创建事件被发布,我们需要对其进行监听进行一些额外的业务操作。Spring框架提供了出色的事件监听机制,只需在方法上添加@EventListener
注解即可实现对特定事件的监听。这种方式使得对订单创建事件的监听和处理变得便捷,无需繁琐的配置和编写冗余代码。
@Componentpublic class TradeEventProcessor { @EventListener public void handleCreateTradeEventForNotify(CreateTradeEvent createTradeEvent) { // 处理订单创建事件,并将订单信息发送到通知系统中 } @EventListener public void handleCreateTradeEventForEs(CreateTradeEvent createTradeEvent) { // 处理订单创建事件,并将订单信息同步到Es中 }}
需要注意的是,尽管上述示例在代码结构上实现了业务的分离,但监听者仍然是在同一线程中按顺序执行的。如果我们需要自定义多个监听者的执行顺序,可以使用@Order
注解来指定优先级。通过为不同的监听者添加不同的优先级值,可以控制它们的执行顺序。这种方式允许我们精确地定义监听者的执行顺序,以满足特定的业务需求。
@Componentpublic class TradeEventProcessor { @EventListener @Order(1) public void handleCreateTradeEventForNotify(CreateTradeEvent createTradeEvent) { // 处理订单创建事件,并将订单信息发送到通知系统中 } @EventListener @Order(2) public void handleCreateTradeEventForEs(CreateTradeEvent createTradeEvent) { // 处理订单创建事件,并将订单信息同步到Es中 }}
针对那些在订单创建完成后并不影响主要流程的次要操作,例如向用户发送通知信息,我们倾向于使用异步线程进行执行,以提高系统的响应速度。
在Spring框架中,我们可以通过添加@EnableAsync
注解来启用异步操作功能,并在监听者方法上使用@Async
注解来指定任务的异步执行。通过这种方式,我们能够将次要操作与主要流程分离,异步执行它们,从而实现更快的系统响应速度。
需要注意的是,在使用@Async
注解时,最好自定义一个线程池进行注入。默认情况下,Spring会使用一个简单的线程池来处理异步方法调用。通过注入自定义线程池,我们能够更精确地控制并发执行的线程数量以及线程的生命周期。
@Component@EnableAsyncpublic class TradeEventProcessor { @EventListener @Order(1) @Async("customThreadPool") public void handleCreateTradeEventForNotify(CreateTradeEvent createTradeEvent) { // 处理订单创建事件,并将订单信息发送到通知系统中 } @EventListener @Order(2) public void handleCreateTradeEventForEs(CreateTradeEvent createTradeEvent) { // 处理订单创建事件,并将订单信息同步到Es中 }}
使用过程中的思考
在事件驱动的开发方式中,通常有许多监听者处理的是不影响主要业务流程的业务。针对这种情况,我们倾向于将这些监听者的执行异步化,以提高系统的响应性能。然而,在使用@Async
注解将这些监听者方法异步执行时,可能会遇到一些问题需要注意。
尽管我们可以在事件中定义一些与事件相关的属性,但在监听者进行业务操作时,这些属性往往不足以满足需求,需要在业务监听者中进行数据查询。然而,这带来了一个问题:在异步执行期间,如果主线程在对相关数据进行操作时被阻塞(例如锁表),而事务尚未成功提交,那么异步线程读取的可能是操作之前的数据状态,这将对后续监听者的操作产生错误的影响。
在异步任务执行过程中,由于网络问题或其他因素的影响,监听者的执行可能会失败。尽管这些操作并不影响主流程业务,但我们仍然希望能够尽最大努力确保监听者的成功执行,并在发生错误时进行相应的通知。为了实现这一目标,我们需要对这些业务监听进行持久化,以便可以进行重试操作。如果没有进行持久化,还可能因为重启等操作导致任务丢失。
通过将业务监听持久化,我们能够提供可靠的任务重试机制,保证监听者的执行成功性。这样即使出现了故障或异常情况,系统能够在恢复正常运行后重新执行未成功的任务。这种持久化机制可以保证任务的可靠性和一致性,从而提供更可靠的系统行为。
在项目开发中,通常会将一些公共属性(例如当前用户信息)放入线程变量中,并在接口调用结束后清除线程变量的数据,以避免内存溢出。然而,在异步执行时,我们无法直接获取主线程中的线程变量数据,这给业务代码编写带来了一些挑战。
对于异步线程执行任务的场景下,我们需要能够做到以下几点:
为了解决异步线程中的查询问题,我们采用了一种策略:只有在主线程事务成功提交后,才执行异步线程中的业务操作。
通过这样的处理方式,确保了在主线程执行相关数据操作并成功提交后,再触发异步线程的执行。这样可以避免异步线程在查询数据时读取到未提交的数据状态,从而保证了查询结果的准确性和一致性。
这种异步处理策略有效地解决了异步线程中的查询问题,并确保了数据的正确性。同时,它不会对Spring框架原有的功能产生任何负面影响,保持了系统的可靠性和稳定性。
在任务执行出错的情况下,我们采用持久化操作来确保任务不会丢失,并为后续重试提供支持。通过将执行的任务进行持久化,我们可以将其记录到持久存储系统(如数据库或消息队列)中。
通过这一策略,我们能够确保任务的持久性和可靠性,在发生错误时能够尽最大努力地进行重试,从而提高系统的稳定性和可靠性,并为任务的重试提供了可行的手段。
针对错误任务的持久化,我们需要支持多种持久化方法,包括常见的数据库持久化,并能够兼容不同的持久化框架(如JDBC、MyBatis、JPA等)。
通过这样的设计,我们可以在持久化任务时根据需求选择合适的持久化方法和框架。无论是使用关系型数据库、非关系型数据库,还是其他持久化方案,都能够进行任务的持久化操作。
这种灵活性和扩展性的设计使得我们能够适应各种数据存储需求,并与不同的持久化框架协同工作,为任务的持久化提供了广泛的支持和可选性。
我们需要支持多种线程变量数据的传递,无论业务系统在线程变量中存放什么类型的数据,我们都需要能够支持。
技术分析
通过以上的问题分析以及业务要求,我们如果想要优化Spring的监听者在异步执行下场景,那么会遇到下面的开发问题
- 如何对相应的事件监听进行增强
- 如何支持不同的持久化方案,如数据库,消息队列等
- 如何提供公用的补偿机制
- 如何向异步线程中传递数据问题,不同系统线程变量不同,如何处理?
初步分析考虑对Spring的EventListener
创建一个切面进行增强,考虑到我们需要支持同步和异步的调用方式,考虑创建一个自定义的注解,然后对这个自定义注解进行增强。那么这个自定义的注解需要支持原本EventListener
所有的功能,并且能够支持对任务的排序,异步执行以及同步执行。
其次,我们需要将业务监听者的类名、方法名以及参数等进行持久化,并且支持不同的持久化方案。
从业务使用场景下要求我们能够支持不同的持久化方案,我们可以考虑定义统一的接口,然后由不同的业务系统去定义实现,业务系统如何持久化由个业务系统自行处理。如果这样处理,我们在对注解增强的地方如何通过接口去调用实现,完成持久化呢?初步有两个方案,一是使用Java的SPI
机制,二是通过Spring容器直接获取相应的实现类完成持久化操作。
完成持久化后,如何提供公用的补偿机制?首先能够查询出来,然后通过反射调用的方式能够实现对监听者的调用。查询可以和持久化一样,通过定义接口,各业务系统定义实现的方式进行。
最后还有线程变量的问题,向异步线程中传递数据相对简单,可以在主线程中先获取出数据,然后传递到子线程中,子线程结束时清除子线程中的数据,这样看的话,也是有三步,获取数据,设置数据,清除数据。那么我们也是可以定义公共的接口,由各业务系统去完成这三个实现,以支持各业务系统线程变量不同的要求。
功能实现
通过以上分析,我们决定创建一个项目来扩展原有的Spring事件监听功能。幸运的是,Spring的starter机制使得集成类似的功能变得非常简便。
基于这个决策,我们可以利用Spring的starter机制,创建一个自定义的starter项目。该项目将提供一套扩展的事件监听功能,包括异步执行、错误处理和持久化等特性。
通过创建自定义的starter项目,我们能够轻松地将这些功能集成到现有的Spring应用程序中。开发者只需添加我们自定义的starter依赖,并进行适当的配置即可使用这些扩展功能。
自定义注解
为了确保不影响原有的Spring功能,我们计划自定义一个注解,以实现与原有Spring事件监听相同的功能和配置。
通过自定义注解并实现原有Spring事件监听的全部功能和配置,我们能够在不影响原有Spring功能的前提下,提供更多的定制化选项和增强功能,以满足特定应用场景的需求。
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@EventListener@Orderpublic @interface EventEnhanceListener { /** * 是否异步执行,默认同步执行 */ boolean async() default true; /** * 自定义线程池,用于异步执行任务 */ String threadPool() default ""; /** * 执行顺序 */ @AliasFor(annotation = Order.class, attribute = "value") int order() default Ordered.LOWEST_PRECEDENCE; /** * 持久化接口 */ String persistenceService() default ""; // EventListener的属性 @AliasFor(annotation = EventListener.class, attribute = "classes") Class>[] value() default {}; @AliasFor(annotation = EventListener.class, attribute = "value") Class>[] classes() default {}; @AliasFor(annotation = EventListener.class, attribute = "condition") String condition() default "";}
对自定义注解的增强
创建注解类对象的后置处理器
@Configuration@Role(BeanDefinition.ROLE_SUPPORT)public class EventEnhanceConfiguration implements ApplicationContextAware { private ApplicationContext applicationContext; @Nullable protected Supplier executor; @Nullable protected Supplier exceptionHandler; @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public EventEnhanceListenerBeanPostProcessor advisor(){ EventEnhanceListenerBeanPostProcessor bpp = new EventEnhanceListenerBeanPostProcessor(); bpp.configure(this.executor, this.exceptionHandler, applicationContext); return bpp; } @Autowired(required = false) void setConfigurers(Collection configurers) { if (CollectionUtils.isEmpty(configurers)) { return; } if (configurers.size() > 1) { throw new IllegalStateException("Only one AsyncConfigurer may exist"); } AsyncConfigurer configurer = configurers.iterator().next(); this.executor = configurer::getAsyncExecutor; this.exceptionHandler = configurer::getAsyncUncaughtExceptionHandler; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }}
后置处理器中创建一个切面类
public class EventEnhanceListenerBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor { @Nullable private Supplier executor; @Nullable private Supplier exceptionHandler; private ApplicationContext applicationContext; public EventEnhanceListenerBeanPostProcessor() { setBeforeExistingAdvisors(true); } public void configure(@Nullable Supplier executor, @Nullable Supplier exceptionHandler, ApplicationContext applicationContext) { this.executor = executor; this.exceptionHandler = exceptionHandler; this.applicationContext = applicationContext; } @Override public void setBeanFactory(BeanFactory beanFactory) { super.setBeanFactory(beanFactory); EventEnhanceListenerAdvisor advisor = new EventEnhanceListenerAdvisor(this.executor, this.exceptionHandler, this.applicationContext); advisor.setBeanFactory(beanFactory); this.advisor = advisor; }}
切面类中通过EventEnhanceListenerExecutionInterceptor
对自定义注解进行增强
public class EventEnhanceListenerAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware { private Advice advice; private Pointcut pointcut; private ApplicationContext applicationContext; public EventEnhanceListenerAdvisor(@Nullable Supplier executor, @Nullable Supplier exceptionHandler, ApplicationContext applicationContext) { Set> annotationTypes = new LinkedHashSet<>(2); annotationTypes.add(EventEnhanceListener.class); this.applicationContext = applicationContext; this.advice = buildAdvice(executor, exceptionHandler); this.pointcut = buildPointcut(annotationTypes); } protected Advice buildAdvice(@Nullable Supplier executor, @Nullable Supplier exceptionHandler) { EventEnhanceListenerExecutionInterceptor interceptor = new EventEnhanceListenerExecutionInterceptor(null, this.applicationContext); interceptor.configure(executor, exceptionHandler); return interceptor; } protected Pointcut buildPointcut(Set> asyncAnnotationTypes) { ComposablePointcut result = null; for (Class extends Annotation> annotationType : asyncAnnotationTypes) { Pointcut cpc = new AnnotationMatchingPointcut(annotationType, true); Pointcut mpc = new AnnotationMatchingPointcut(null, annotationType, true); if (result == null) { result = new ComposablePointcut(cpc); } else { result.union(cpc); } result = result.union(mpc); } return (result != null ? result : Pointcut.TRUE); } @Override public Pointcut getPointcut() { return this.pointcut; } @Override public Advice getAdvice() { return this.advice; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { if (this.advice instanceof BeanFactoryAware) { ((BeanFactoryAware) this.advice).setBeanFactory(beanFactory); } }}
public class EventEnhanceListenerExecutionInterceptor extends AsyncExecutionInterceptor { private ApplicationContext applicationContext; private AsyncTaskDataTransfer asyncTaskDataTransfer; public EventEnhanceListenerExecutionInterceptor(@Nullable Executor defaultExecutor, ApplicationContext applicationContext) { super(defaultExecutor); this.applicationContext = applicationContext; } @Override public Object invoke(MethodInvocation invocation) throws Throwable { EventEnhanceListener eventEnhanceListener = AnnotatedElementUtils.findMergedAnnotation(invocation.getMethod(), EventEnhanceListener.class); Assert.notNull(eventEnhanceListener, "method @CustomEventListener is null"); if (eventEnhanceListener.async()) { return asyncSubmitTask(invocation, eventEnhanceListener); } else { return invocation.proceed(); } } private Object asyncSubmitTask(MethodInvocation invocation, EventEnhanceListener eventEnhanceListener) { Class> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass); final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod); if (executor == null) { throw new IllegalStateException( "No executor specified and no default executor set on AsyncExecutionInterceptor either"); } Object mainThreadData = getMainThreadData(); // 将执行的任务持久化 EventListenerAsyncTask eventListenerAsyncTask = buildAsyncTask(invocation, targetClass, userDeclaredMethod, mainThreadData); String persistenceServiceName = eventEnhanceListener.persistenceService(); eventListenerAsyncTask.getData().setPersistenceService(persistenceServiceName); AsyncTaskPersistenceService asyncTaskPersistenceService = applicationContext.getBean(persistenceServiceName, AsyncTaskPersistenceService.class); asyncTaskPersistenceService.saveForTask(eventListenerAsyncTask); Callable
通用的补偿接口
考虑对外提供通用的补偿http
接口,如何调度由业务系统自行决定。
public class AsyncCompensateJob implements ApplicationContextAware { private ApplicationContext applicationContext; private AsyncTaskDataTransfer asyncTaskDataTransfer; public void execute() { Map asyncTaskPersistenceServiceMap = applicationContext.getBeansOfType(AsyncTaskPersistenceService.class); if (CollectionUtils.isEmpty(asyncTaskPersistenceServiceMap)) { return; } if (asyncTaskDataTransfer == null) { ServiceLoader serviceLoader = ServiceLoader.load(AsyncTaskDataTransfer.class); if (serviceLoader.findFirst().isPresent()) { asyncTaskDataTransfer = serviceLoader.findFirst().get(); } } for (Map.Entry entry : asyncTaskPersistenceServiceMap.entrySet()) { AsyncTaskPersistenceService asyncTaskPersistenceService = entry.getValue(); List taskList = asyncTaskPersistenceService.findTaskList(); for (EventListenerAsyncTask eventListenerAsyncTask : taskList) { EventTaskInfo eventTaskInfo = eventListenerAsyncTask.getData(); Object threadContext = eventTaskInfo.getThreadContext(); String className = eventTaskInfo.getClassName(); String methodName = eventTaskInfo.getMethod(); String argumentString = eventTaskInfo.getArguments(); if (Objects.nonNull(asyncTaskDataTransfer)) { asyncTaskDataTransfer.setAsyncThreadData(threadContext); } try { Class> clazz = Class.forName(className); Method method = getMethod(clazz, methodName); Assert.notNull(method, "根据类限定名以及方法名未反射找到对应的方法"); Class>[] parameterTypes = method.getParameterTypes(); Object[] actualArgumentObjects = generateArguments(parameterTypes, argumentString); // 反射调用方法执行 Object targetObject = applicationContext.getBean(clazz); ReflectionUtils.invokeMethod(method, targetObject, actualArgumentObjects); // 执行成功删除任务 asyncTaskPersistenceService.removeForTask(eventListenerAsyncTask); } catch (Exception ex) { log.error("异步任务错误补偿执行过程报错,异步任务信息:{}", eventListenerAsyncTask, ex); } finally { if (Objects.nonNull(asyncTaskDataTransfer)) { asyncTaskDataTransfer.clear(); } } } } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }}
待完善处
- 为了实现接口参数的序列化和反序列化,我们采用
JSON
格式进行数据的转换。这要求业务接口的参数对象必须具备无参构造函数,并提供GETTER和SETTER方法,以便能够正确地进行序列化和反序列化操作。然而,这会对业务系统带来一定程度的侵入性。 - 通用的补偿任务完成后,当前实现中使用了删除持久化记录的方式,但是接口命名不应该是删除,而应该是后置处理,补偿任务完成后的具体处理方式应由业务系统自行决定。此外,在调用接口发生异常时,应定义一个异常处理接口,并将其提供给业务系统,以使其了解补偿失败的情况,并能够进行适当的后续处理,例如设定补偿失败的阈值等。
- 在通用的补偿任务中,我们通过反射调用需要补偿的接口,由于业务接口的类通常都会有些依赖,为方便构造反射对象,我们通过Spring的容器直接获取相应的对象,然后反射调用相应的方法。这会带来一个问题,由于通过容器调用会触发我们在该类上创建的切面增强。后续优化考虑通过对从容器中获取的对象创建一个代理,通过代理调用容器对象中的方法,使用这种方式避免触发切面相应的业务
- 目前对于线程变量数据的传递,采用的是定义公共接口,由业务系统实现具体逻辑的方式,目前在拦截器中使用的是Java的
SPI
机制,后续考虑在自定义注解上增加一个属性,定义线程变量传递实现的name,通过Spring容器获取实现的方式实现,这样业务系统在使用时会更加方便一些,和持久化方案一致,逻辑上更加统一。
思考
代码写到最后发现,其实实现的功能解决的是在异步线程中处理任务所遇到的问题,与其说是对@EventListener
的增强,还不如说是对异步线程任务的增强,后续考虑将功能的实现放在@Async
上,这样既不破坏原有Spring的事件监听机制,也增大了功能的适用范围。
关键词:
Spring事件监听在业务使用中的优化
我国首个万吨级光伏制氢项目投产-环球今亮点
好作品应如实呈现时代的偏见吗?——《漫长的季节》中的女性角色塑造 焦点
【速看料】国产办公软件WPS出现服务故障 官微道歉:正在紧急排查修复
全球实时:红魔电竞平板官宣:首款骁龙8 Gen2+内置风扇平板
《博德之门3》反向跳票!不会为主机版质量向30帧妥协 全球快讯
你退了吗?网易暴雪国服游戏退款申请今天截止:过期视为自动放弃 全球快消息
男生长胖20斤被公司解约 官方回应:如举报会责令整改_当前短讯
每日播报!Linux 中重置数据库的 root 密码的技巧
海外视频直播源码技术视频直播间的搭建
BabylonJS教程_编程入门自学教程_菜鸟教程-免费教程分享|动态焦点
海康威视产品命名规则
焦点快看:记录一次项目数据采集分析-NEWC数据泄漏
微软只保证《使命召唤》停留三年 索尼官方回应
新资讯:携程正式宣布员工生孩后补贴5万元:每年1万 连领5年
超值优惠手慢无:真维斯Z+冰丝短裤29元、长裤/Polo衫39元
特斯拉充电中被临车女车主强行拔枪:哨兵模式拍个正着-焦点信息
你能坚持多久?小伙在没信号的原始森林隐居4年火了:中国最神秘部落揭秘_环球今热点
每日消息!特斯拉人形机器人将亮相上海:预计未来售价不超过15万元
IAM风险CTF挑战赛
PC网站如何实现微信扫码登录
【世界热闻】马价十倍说明了什么道理_马价十倍
【新视野】7月新规来了
我们该怎么帮助年轻人就业
2.5万元的Vision Pro问世 荣耀赵明:整个行业都应该感谢苹果_最新快讯
焦点资讯:帕萨特提车第二天就严重抖动 车主:车子像是仿佛在蹦
荣耀赵明:面对最强对手苹果竞争 荣耀整合核心能力突破
荣耀Magic V2即将发布 赵明:折叠屏将是未来最优解
贵州6人吃野生菌4人被送云南抢救:误食亚稀褶红菇、严重可致死
呼和浩特市:69处党政机关、企事业单位内部车场共享开放
一天吃透MySQL面试八股文 环球快资讯
央企的无奈:假央企何以屡禁不止?
每日快报!高温持续驻守华北黄淮等地 长江中下游降雨增强
游客自驾游开车压草场牧民损失数万 无视警示牌:官方回应
世界简讯:建全自主半导体产业链几乎不可能!ASML找强援:加速推进新一代光刻机
全球聚焦:频繁被黑!警惕抹黑清华的舆论倾向 网友支持:严惩造谣者
特斯拉Model 3一头扎进农田 只是因为捡一个瓶子 全球滚动
行政拘留能暂缓执行吗
【天天报资讯】险资:权益市场在底部区域 下半年看好科技和消费的投资价值
时代的眼泪!Kindle中国电子书店正式停运:这下真要盖泡面了-天天即时看
华为搞定6G赫兹技术验证:可实现10Gbps下行速率_世界速看
售价超28万!Caviar推出定制苹果Vision Pro:3斤18K黄金闪瞎眼 微资讯
栗新小学教师志愿服务队_关于栗新小学教师志愿服务队概略
天天通讯!读发布!设计与部署稳定的分布式系统(第2版)笔记16_握手和考验机
【环球新要闻】无糖可乐不能喝了?或含致癌物 厂商纷纷回应撇清关系
确保数字孪生水利管用好用_播资讯
小屏旗舰最后的荣光!华硕Zenfone 10图赏
国际饮料协会回应阿斯巴甜争议:系误导
双头男孩上下串联:被当畸形参观 死后还被盗尸解剖
无糖可乐或含致癌物?不能喝了?要不咱先把猪肉给戒了吧
全球热资讯!马云说阿里巴巴永远不会做快递!结果 还是真香
Epic喜+1:《纳赫鲁博王国地下城:混沌护符》!
开不了口周杰伦在线试听_开不了口周杰伦-世界观热点
今日看点:怒斥温网官方!安迪穆雷为自己鸣不平:我比阿尔卡拉斯伟大!
浙江省衢州市出现北斗智慧综合灯杆 实现全自动道路巡检
电影《封神第一部》妲己首次亮相 由新人演员娜然扮演
美团成功收购光年之外 收购价约为20.65亿人民币
市民称上海外滩夜景灯光太土 有一种浓浓的乡镇味道
科学家发现“孤雌生殖”的案例 哥斯达黎加一鳄鱼采用独特“繁殖”方式
国内首款抑郁症诊断机器人正式亮相 通过智能传输分析患者是否有抑郁症
全国首位视障播音硕士董丽娜毕业 其坚韧性格和不屈精神感动数万人
获取波场(Tron)钱包TRX、USDT余额和剩余带宽、能量 - 笔记 世界快播报
计算机专业学生暑假要去看这些经典书籍! 环球新视野
焦点播报:微服务架构必读篇 - 网关
kratos http原理 每日看点
手机问题:努比亚z30pro续航怎么样
天天播报:RTX 4060今晚上市后 尴尬一幕出现:日本线下仅1人排队买首发
重点聚焦!女子疑坐公交不付钱:踩猫威胁司机
算法导论-第14章-数据结构的扩张-全球新消息
货币市场日报:6月29日
2023年上半年剧集报告发布:国产剧口碑上涨显著
微动态丨GDDR7显存明年就有了!RTX 50可能先吃螃蟹
800V高压架构+二代XNGP辅助驾驶上车!小鹏G6上市:20.99万起 滚动
iGame人气王!七彩虹iGame RTX 4060 Ultra W OC显卡图赏
文心一言 VS 讯飞星火 VS chatgpt (51)-- 算法导论6.2 3题|全球时讯
每日观察!《异世界》魔径—給异世界点颜色看看 楔子
世界看点:华为宣布2024年推出面向商用的5.5G全套网络设备
【全球速看料】山东龙凤四胞胎同圆大学梦 网友:四倍辛苦 四倍幸福
当前信息:可以换电的纯电MPV来了!全新上汽大通大家MIFA 9车电分离版上市
世界热推荐:Kubernetes 对象以及部署nginx服务示例(四)
记录--写一个高德地图巡航功能的小DEMO
旗袍发型教程_旗袍发型-天天即时看
世界实时:债市日报:6月29日
阿斯巴甜将被世卫列为“可能致癌物质”:元气森林、奈雪火速回应
丫丫解锁新玩具竹笼:一刻也没闲着
无糖可乐不能喝了?或含致癌物阿斯巴甜
天天观焦点:Steam将AI作图游戏拒之门外!除非能证明拥有版权
环球热讯:红米Redmi Pad 2平板跑分曝光:配骁龙680 或近期发布
新市街道开展“健康无毒 喜迎大运”6.26禁毒宣传日活动
天天观点:内爆致5人遇难!美国泰坦号观光潜艇失事残骸打捞上岸
1200mAh锂电池+三档风力调节:佳格手持小风扇8.9元狂促_全球今日讯
世界时讯:最好的Windows掌机!ROG Ally暴力魔改8TB SSD:容量陡增8倍
当前热讯:重要提醒!暴雪国服游戏退款申请明日将截止
【当前独家】张颂文北影毕业典礼演讲:希望你们每天都在进步
【世界速看料】不吝赐教的意思(赐教的意思)
Spring Cloud 如何引入云原生网关,创新微服务架构|天天最新
拈花云科基于 Apache DolphinScheduler 在文旅业态下的实践
.NET 个人博客-添加RSS订阅功能 世界快播
如何将视频文件.h264和音频文件.mp3复用为输出文件output.mp4?
天天热资讯!商品日报(6月29日):尿素再度拉涨超5% 供应利多刺激甲醇盘中涨超3%