多维柔性作业调用_摆脱困境:从预定作业中调用安全方法

多维柔性作业调用

假设我们已经实现了一个Spring支持的应用程序,并使用Spring Security的方法安全性表达式对其进行了保护 。

我们的下一个任务是使用安全方法实施计划作业。 更具体地说,我们必须实现一个计划的作业,该作业从我们的服务类中获取一条消息,并将接收到的消息写入日志。

让我们开始吧。

本博客文章中描述的计划作业使用在特定于配置文件的配置文件中配置的cron表达式。 如果您不知道如何执行此操作,建议您阅读我的博客文章,其中描述了如何使用带有@Scheduled批注的特定于环境的cron表达式 。

我们的第一次尝试

让我们创建一个计划的作业,该作业调用受保护的方法并找出执行作业时发生的情况。 让我们先来看一下示例应用程序的服务层。

服务层

安全服务类的方法在MessageService接口中声明。 它声明了一个称为getMessage()的方法,并指定只有具有角色ROLE_USER的用户才能调用它。

MessageService接口的源代码如下所示:

import org.springframework.security.access.prepost.PreAuthorize;public interface MessageService {@PreAuthorize("hasRole('ROLE_USER')")public String getMessage();
}

我们对MessageService接口的实现非常简单。 其源代码如下:

import org.springframework.stereotype.Service;@Service
public class HelloMessageService implements MessageService {@Overridepublic String getMessage() {return "Hello World!";}
}

让我们继续并创建调用getMessage()方法的计划作业。

创建计划的作业

我们可以按照以下步骤创建计划的作业:

  1. 创建一个ScheduledJob类,并使用@Component注释对其进行注释。 这样可以确保在类路径扫描期间找到我们的计划作业(只要将其放入要扫描的程序包中)。
  2. 将私有的Logger字段添加到创建的类中,并通过调用LoggerFactory类的静态getLogger()方法来创建Logger对象。 我们将使用Logger对象将从HelloMessageService对象收到的消息写入日志。
  3. 将私有MessageService字段添加到创建的类。
  4. 将一个构造函数添加到创建的类中,并使用@Autowired注释对其进行注释。 这确保了我们可以使用构造函数注入将MessageService bean注入MessageService字段。
  5. 向创建的类添加一个公共run()方法,并使用@Scheduled批注对其进行批注。 将其cron属性的值设置为'$ {scheduling.job.cron}' 。 这意味着cron表达式是从属性文件中读取的,其值是schedule.job.cron属性的值( 有关此内容的更多信息,请参阅此博客文章 )。
  6. 通过调用MessageService接口的getMessage()方法来实现run()方法。 将收到的消息写入日志。

我们计划的作业的源代码如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
public class ScheduledJob {private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledJob.class);private final MessageService messageService;@Autowiredpublic ScheduledJob(MessageService messageService) {this.messageService = messageService;}@Scheduled(cron = "${scheduling.job.cron}")public void run() {String message = messageService.getMessage();LOGGER.debug("Received message: {}", message);}
}

让我们看看调用ScheduledJob类的run()方法时会发生什么。

它不起作用

当执行我们的计划作业时,将抛出AuthenticationCredentialsNotFoundException ,并且我们看到以下堆栈跟踪:

2013-12-10 19:45:19,001 ERROR - kUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task.
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContextat org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:339)at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:198)at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:60)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)at com.sun.proxy.$Proxy31.getMessage(Unknown Source)at net.petrikainulainen.spring.trenches.scheduling.job.ScheduledJobTwo.run(ScheduledJobTwo.java:26)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:601)at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64)at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53)at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)at java.util.concurrent.FutureTask.run(FutureTask.java:166)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)at java.lang.Thread.run(Thread.java:722)

该stacktrace实际上非常有用。 它告诉我们安全方法无法调用,因为从SecurityContext中找不到Authentication对象。

我看到的两个最常见的解决方案是:

  • 创建一个与受保护的方法具有相同功能的单独方法,然后修改计划的作业以使用此方法。 此方法通常具有Javadoc注释,该注释指出只有计划的作业才能调用此方法。 这个解决方案有两个问题:1)它会使代码库混乱,并且2)最终无论如何都将调用该方法(除非真正需要,否则没人真正阅读Javadocs)。
  • 从计划作业调用的方法中删除方法安全注释。 由于明显的原因,这是一个非常糟糕的解决方案。 提示:该方法的安全是有充分理由的!

幸运的是,还有第三种方法可以解决此问题。 让我们开始查找计划作业使用的安全上下文的存储位置。

安全上下文从何而来?

我们的问题的解决方案很明确:我们必须创建一个Authentication对象,然后在调用安全方法之前将其添加到SecurityContext中

但是,在对示例应用程序进行必要的修改之前,我们必须了解SecurityContext对象的存储位置。

如果未进行其他配置,则将安全上下文存储到ThreadLocal 。 换句话说,每个线程都有其自己的安全上下文。 这意味着在同一线程中执行的所有计划作业均共享相同的安全上下文。

假设我们有三个预定的作业。 这些作业称为ABC。 另外,我们假设这些作业是按字母顺序执行的。

如果我们使用只有一个线程的默认线程池,则所有作业共享相同的安全上下文。 如果作业B身份验证对象设置为安全上下文,则执行计划的作业时会发生以下情况:

  • 作业A无法调用安全方法,因为它在作业B之前执行。 这意味着从安全上下文中找不到身份验证对象。
  • 作业B可以调用安全方法,因为作业B在尝试调用安全方法之前将Authentication对象设置为安全上下文。
  • 作业C可以调用安全方法,因为它是在将身份验证对象设置为安全上下文的作业B之后执行的。

如果我们使用具有多个线程的线程池,则每个线程都有其自己的安全上下文。 如果作业AAuthentication对象设置为安全上下文,则在同一线程中执行的所有作业都将使用相同的特权执行,只要它们在作业A之后执行即可。

让我们一步一步地完成每一项工作:

  • 作业A可以调用安全方法,因为作业A在尝试调用安全方法之前将Authentication对象设置为安全上下文。
  • 如果作业B 与作业A在同一线程中执行,则作业B可以调用安全方法。 如果未在同一线程中执行作业,则无法调用安全方法,因为无法从安全上下文中找到Authentication对象。
  • 如果作业C 与作业A在同一线程中执行,则作业C可以调用安全方法。 如果未在同一线程中执行作业,则无法调用安全方法,因为无法从安全上下文中找到Authentication对象。

显然,解决此问题的最佳方法是确保使用所需的特权执行每个计划的作业。 此解决方案有两个好处:

  • 我们可以按任何顺序执行工作。
  • 我们不必确保作业在“正确的”线程中执行。

让我们找出当我们的应用程序使用Spring Security 3.1时如何解决这个问题。

Spring Security 3.1:需要手动工作

如果我们的应用程序使用Spring Security 3.1,则解决问题的最简单方法是

  • 在我们的工作尝试调用安全方法之前,创建一个Authentication对象并将其设置为安全上下文。
  • 在作业完成之前,从安全上下文中删除身份验证对象。

让我们从创建提供所需方法的AuthenticationUtil类开始。

创建AuthenticationUtil类

我们可以按照以下步骤创建AuthenticationUtil类:

  1. 创建AuthenticationUtil类。
  2. AuthenticationUtil类添加一个私有构造函数。 这样可以确保无法实例化该类。
  3. 将静态clearAuthentication()方法添加到该类,并通过以下步骤实现该方法:
    1. 通过调用SecurityContextHolder类的静态getContext()方法来获取SecurityContext对象。
    2. 通过调用SecurityContext接口的setContext()方法删除身份验证信息。 将null作为方法参数传递。
  4. 将静态configureAuthentication()方法添加到该类。 此方法将用户的角色作为方法参数。 通过执行以下步骤来实现此方法:
    1. 通过调用AuthorityUtils类的静态createAuthorityList()方法来创建GrantedAuthority对象的集合 。 将用户角色作为方法参数传递。
    2. 创建一个新的UsernamePasswordAuthenticationToken对象,并将以下对象作为构造函数参数传递:
      1. 第一个构造函数参数是主体。 将字符串“ user”作为第一个构造函数参数传递。
      2. 第二个构造函数参数是用户的凭据。 将作为方法参数给出的角色作为第二个构造函数参数传递。
      3. 第三个构造函数参数包含用户的权限。 将创建的Collection <GrantedAuthority>对象作为第三个构造函数参数传递。
    3. 通过调用SecurityContextHolder类的静态getContext()方法来获取SecurityContext对象。
    4. 通过调用SecurityContext接口的setAuthentication()方法,将创建的Authentication对象设置为安全上下文。 将创建的UsernamePasswordAuthenticationToken作为方法参数传递。

AuthenticationUtil类的源代码如下所示:

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;import java.util.Collection;public final class AuthenticationUtil {//Ensures that this class cannot be instantiatedprivate AuthenticationUtil() {}public static void clearAuthentication() {SecurityContextHolder.getContext().setAuthentication(null);}public static void configureAuthentication(String role) {Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(role);Authentication authentication = new UsernamePasswordAuthenticationToken("user",role,authorities);SecurityContextHolder.getContext().setAuthentication(authentication);}
}

我们还没有完成。 我们仍然必须对我们的预定工作进行一些修改。 让我们找出如何进行这些修改。

修改计划的作业

我们必须对ScheduledJob类进行两次修改。 我们可以按照以下步骤进行修改:

  1. 启动作业时,调用AuthenticationUtil类的静态configureAuthentication()方法,并将字符串 'ROLE_USER'作为方法参数传递。 这样可以确保我们的计划作业可以执行与具有ROLE_USER角色的普通用户相同的方法。
  2. 在作业完成之前,调用AuthenticationUtil类的静态clearAuthentication()方法。 这从安全上下文中删除了身份验证信息。

ScheduledJob类的源代码如下所示:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
public class ScheduledJob {private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledJob.class);private final MessageService messageService;@Autowiredpublic ScheduledJob(MessageService messageService) {this.messageService = messageService;}@Scheduled(cron = "${scheduling.job.cron}")public void run() {AuthenticationUtil.configureAuthentication("ROLE_USER");String message = messageService.getMessage();LOGGER.debug("Received message: {}", message);AuthenticationUtil.clearAuthentication();}
}

让我们找出运行预定作业时会发生什么。

运行计划的作业

调用作业时,以下消息将写入日志:

2013-12-17 20:41:33,019 DEBUG - ScheduledJob            - Received message: Hello World!

当我们的应用程序使用Spring Security 3.1时,一切都将正常运行。 我们的解决方案不是那么优雅,但可以。 该解决方案的明显缺点是,我们必须记住在计划的作业中调用AuthenticationUtil类的configureAuthentication()clearAuthentication()方法。

Spring Security 3.2解决了这个问题。 让我们继续前进,找出当我们的应用程序使用Spring Security 3.2时如何解决这个问题。

Spring Security 3.2:几乎就像魔术一样!

Spring Security 3.2具有全新的并发支持 ,这使我们可以将安全上下文从一个线程转移到另一个线程。 让我们找出如何配置应用程序上下文以使用Spring Security 3.2提供的功能。

配置应用程序上下文

因为我们要使用Spring Security 3.2的新并发支持,所以我们必须对应用程序上下文配置类进行以下更改( 原始配置在此博客文章中进行了描述 ):

  1. 实现SchedulingConfigurer接口。 该接口可以由使用@EnableScheduling批注注释的应用程序上下文配置类来实现,并且通常用于配置使用的TaskScheduler bean或以编程方式配置执行的任务。
  2. 将私有createrSchedulerSecurityContext()方法添加到配置类。 此方法没有方法参数,它返回一个SecurityContext对象。 通过执行以下步骤来实现此方法:
    1. 通过调用SecurityContextHolder类的静态createEmptyContext()方法来创建新的SecurityContext对象。
    2. 通过调用AuthorityUtils类的静态createAuthorityList()方法来创建GrantedAuthority对象的集合 。 将字符串 “ ROLE_USER”作为方法参数传递。
    3. 创建一个新的UsernamePasswordAuthenticationToken对象,并将以下对象作为构造函数参数传递:
      1. 第一个构造函数参数是主体。 将字符串 “ user”作为第一个构造函数参数传递。
      2. 第二个构造函数参数是用户的凭据。 将字符串 “ ROLE_USER”作为第二个构造函数参数传递。
      3. 第三个构造函数参数包含用户的权限。 将创建的Collection <GrantedAuthority>对象作为第三个构造函数参数传递。
    4. 通过调用SecurityContext接口的setAuthentication()方法,将创建的UsernamePasswordAuthenticationToken对象设置为创建的安全上下文。
  3. 将公共taskExecutor()方法添加到配置类中,并使用@Bean注释对该方法进行注释。 此方法没有方法参数,并返回Executor对象。 通过执行以下步骤来实现此方法:
    1. 通过调用Executors类的静态newSingleThreadScheduledExecutor()方法来创建新的ScheduledExecutorService对象。 这将创建一个ScheduledExecutorService对象,该对象通过使用一个线程来运行所有作业。
    2. 通过调用私有的createSchedulerSecurityContext()方法来获取对SecurityContext对象的引用。
    3. 创建一个新的DelegatingSecurityContextScheduledExecutorService对象,并将以下对象作为构造函数参数传递:
      1. 第一个构造函数参数是ScheduledExecutorService对象。 该对象用于调用计划的作业。 将创建的ScheduledExecutorService对象作为第一个构造函数参数传递。
      2. 第二个构造函数参数是SecurityContext对象。 创建的DelegatingSecurityContextScheduledExecutorService对象确保每个调用的作业都使用此SecurityContext 。 将创建的SecurityContext对象作为第二个构造函数参数传递。
    4. 返回创建的DelegatingSecurityContextScheduledExecutorService对象。
  4. 实现SchedulingConfigurer接口的configureTasks()方法 。 此方法将ScheduledTaskRegistrar对象作为方法参数。 通过执行以下步骤来实现此方法:
    1. 通过调用taskExecutor()方法创建一个新的Executor对象。
    2. 通过调用ScheduledTaskRegistrar类的setScheduler()方法来设置使用的调度程序 ,并将Executor对象作为方法参数传递。

ExampleApplicationContext类的源代码如下所示(相关部分已突出显示):

import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.concurrent.DelegatingSecurityContextScheduledExecutorService;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;@Configuration
@EnableScheduling
@ComponentScan(basePackages = {"net.petrikainulainen.spring.trenches.scheduling"
})
@Import(ExampleSecurityContext.class)
@PropertySource("classpath:application.properties")
public class ExampleApplicationContext implements SchedulingConfigurer {@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {taskRegistrar.setScheduler(taskExecutor());}@Beanpublic Executor taskExecutor() {ScheduledExecutorService delegateExecutor = Executors.newSingleThreadScheduledExecutor();SecurityContext schedulerContext = createSchedulerSecurityContext();return new DelegatingSecurityContextScheduledExecutorService(delegateExecutor, schedulerContext);}private SecurityContext createSchedulerSecurityContext() {SecurityContext context = SecurityContextHolder.createEmptyContext();Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_USER");Authentication authentication = new UsernamePasswordAuthenticationToken("user","ROLE_USER",authorities);context.setAuthentication(authentication);return context;}@Beanpublic PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {PropertySourcesPlaceholderConfigurer properties = new PropertySourcesPlaceholderConfigurer();properties.setLocation(new ClassPathResource( "application.properties" ));properties.setIgnoreResourceNotFound(false);return properties;}
}

这就对了。 此配置确保每个计划的作业都可以访问由createSchedulerSecurityContext()方法创建的SecurityContext对象。 这意味着每个计划的作业都可以调用安全的方法,这些方法可以由角色为“ ROLE_USER”的用户调用。

让我们快速看一下我们的预定工作。

那预定的工作呢?

该解决方案的最好之处在于,我们不必对ScheduledJob类进行任何更改。 其源代码如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
public class ScheduledJob {private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledJob.class);private final MessageService messageService;@Autowiredpublic ScheduledJob(MessageService messageService) {this.messageService = messageService;}@Scheduled(cron = "${scheduling.job.cron}")public void run() {String message = messageService.getMessage();LOGGER.debug("Received message: {}", message);}
}

调用计划作业时,将以下行写入日志:

2013-12-17 21:12:14,012 DEBUG - ScheduledJob            - Received message: Hello World!

很酷 对?

摘要

现在,我们已经成功创建了可以调用安全方法的计划作业。 本教程教会了我们三件事:

  • 我们了解到,通常将SecurityContext对象存储到ThreadLocal中 ,这意味着在同一线程中执行的所有计划作业均共享相同的安全上下文
  • 我们了解到,如果我们的应用程序使用Spring Security 3.1,并且希望从计划的作业中调用安全方法,则最简单的方法是在每个计划的作业中配置使用的Authentication对象。
  • 我们学习了如何使用Spring Security 3.2的并发支持,以及如何将SecurityContext对象从一个线程转移到另一个线程。

您可以从Github( Spring Security 3.1和Spring Security 3.2 )获得此博客文章的示例应用程序。

注意: Spring Security 3.2示例的XML配置目前无法正常工作。 如果有时间,我会修复它。

参考: 从工作槽中跳出来:在Petri Kainulainen博客上从JCG合作伙伴 Petri Kainulainen 调用预定作业中的安全方法 。

翻译自: https://www.javacodegeeks.com/2014/01/spring-from-the-trenches-invoking-a-secured-method-from-a-scheduled-job.html

多维柔性作业调用

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HDMI高清光端机产品介绍

HDMI高清光端机对视频音频发布等起到传输作用&#xff0c;HDMI光端机由发送器和接收器组成&#xff0c;能通过单根光纤把计算机主机的音频&#xff0c;视频&#xff0c;USB延长到远端&#xff0c;用户可以在远端实时收听到电脑主机的图像和声音&#xff0c;并使用电脑控制。接下…

【渝粤教育】电大中专计算机职业素养 (14)作业 题库

1曾经的一项调查发现一些企业业绩不好&#xff0c;客户流失的最重要的原因是&#xff08;&#xff09;。 A企业产品价格不好 B企业员工素养达不到要求 C企业产品质量问题 D企业管理问题 错误 正确答案&#xff1a;左边查询 学生答案&#xff1a;A 2职业素养是人类在社会活动中要…

【渝粤教育】电大中专跨境电子商务理论与实务 (29)作业 题库

1在按照交易主体类型中&#xff0c;&#xff08; &#xff09;面对的最终客户为企业或集团客户&#xff0c;提供企业、产品、服务等相关信息。 AB2B跨境电商或平台 BB2C跨境电商或平台 CC2C跨境电商或平台 DO2O跨境电商或平台 正确 正确答案&#xff1a;左边查询 学生答案&…

全局变量求平均分最高分最低分_想去江苏读大学,2021届山东考生需要多少分?...

点击链接查看详情&#xff1a;https://mp.weixin.qq.com/s/A1-e97_D4jbC_BRJtPIN2A​mp.weixin.qq.com江苏省2020年在山东普通批次本科招生66所院校。1027个专业&#xff1b;共4310人&#xff0c;最低分南通理工学院的船舶与海洋工程专业&#xff0c;464分&#xff0c;位次2437…

Thymeleaf的Spring数据

介绍 今天&#xff0c;我将讨论更具体的问题。 这次没有设计模式或算法&#xff1a;-)。 我们并不总是从头开始设计软件组件。 通常&#xff0c;我们必须尝试使现有软件组件协同工作。 Spring Boot是Java世界上最好的免费软件之一。 它解决了Spring的许多配置问题。 它非常灵活…

如何有效维护PDH光端机的常见故障?

众所周知&#xff0c;在PDH光端机的使用过程中&#xff0c;出现的故障可分为永久性故障和间断性故障。而永久性故障是指反映告警一直保持&#xff1b;间断性故障则是指故障时有时无&#xff0c;有时还可自动恢复。但无论是哪种故障&#xff0c;都与传输网有着密不可分的关系。接…

【渝粤题库】陕西师范大学209018 现代城市管理 作业

一、单项选择题 1、从市政职能看&#xff0c;它主要研究城市政府对公共事务和&#xff08; &#xff09;。 A、城市规划 B、城市环境 C、公共事业 D、私人产品 2、根据有关资料&#xff0c;世界上第一个超过10万人口的城市&#xff0c;是公元前14世纪的&#xff08; &#xff0…

转为字符数组_数组的20种常用的方法?

1.shift 删除数组中的第一个元素2.pop 删除数组中的最后一个元素3.unshift 增加元素在数组的前面4.push 增加元素在数组的后面5.map 循环&#xff0c;并且返回新的数组6.forEach 循环&#xff0c;遍历7.filter 过滤&#xff0c;筛选出数组中的满足条件的&#xff0c;并且返回新…

Profibus-DP光端机产品功能特点及技术参数详解

Profibus DP总线光端机&#xff0c;符合Profibus DP协议&#xff0c;采用大规模FPGA设计&#xff0c;采用独创技术&#xff0c;可同时支持1路Profibus-DP到光纤中继&#xff0c;通信速率0-12Mpbs。支持多种光纤网络拓扑结构&#xff1a;点对点通讯、链型网络、星型网络及冗余环…

【渝粤题库】陕西师范大学800010 经济地理学

《经济地理学》作业 一、选择题 1、自然条件和自然资源不是产业布局的( )。 A.前提 B.基础 C.充分条件 D.必要条件 2、下列关于三次产业分类法&#xff0c;说法不正确的是( )。 A.三次产业分类法的依据和标准是产品的性质和生产过程的特征 B.费希尔明确提出了第三次产业的概念…

[渝粤教育] 西南科技大学 工程建设监理 在线考试复习资料

工程建设监理——在线考试复习资料 一、单选题 1. 监理工程师在施工现场发出的口头指令及要求,应采用( )。 A.联系单予以确认 B.变更单予以确认 C.通知单予以确认 D.回复单予以确认 2.主持制订监理规划并组织实施是( …

jenkins编译java_在Window上使用Jenkins自动发布Java工件

jenkins编译java这篇文章将展示如何使用Jenkins Continuous Integration自动执行Java Web应用程序&#xff08;使用MYSQL DB和基于Hibernate ORM在基于REST的Jersey2 Spring环境中开发的学生申请应用程序&#xff09;的发布过程-上载发布工件到发布存储库。 如上一篇文章《在Wi…

工业级光纤收发器九大技术优势介绍

光纤收发器主要是通过光纤来进行传输的那些100M以太网或1000M以太网&#xff0c;不过也被大家称之为光纤交换机&#xff0c;简而言之&#xff0c;可以将我们要发送的电信号转换成光信号发送出去&#xff0c;同时将接收到的光信号转换成电信号&#xff0c;输入到我们的接收端&am…

单片机c语言篮球比分_基于单片机的篮球比赛计时计分器的设计

摘 要&#xff1a;本系统是采用单片机AT89C51作为本设计的核心原件。利用7段共阴LED作为显示器件。在此设计中共接入了2个7段共阴LED显示器&#xff0c;用于记录得分和比赛时间&#xff0c;显示范围可以达到0到99分&#xff0c;基本满足赛程需求。计分按照倒计时&#xff0c;每…

[渝粤教育] 西南科技大学 建筑制图 在线考试复习资料(1)

建筑制图——在线考试复习资料 一、单选题 1.截平面通过锥顶完全截割圆锥时,截交线为( ) A.椭圆 B.抛三角形 C.四边形 D.圆 2.下列说法正确的是( ) A.投影面垂直线满足中心投影规律 B.投影面垂直线可以在两个投影面反映集聚性 C.直线垂直于一个投影面,必然平行于其余两个投影面…

[渝粤教育] 西南科技大学 形式逻辑 在线考试复习资料

形式逻辑——在线考试复习资料 一、单选题 1. 下列直言命题主项周延,谓项不周延的有( )。 A.阿根廷不是北美国家 B.有些作物不是栽培的 C.所有宗教都不是科学 D.没有一种事物不是运动变化的 E.不都是可以避免的 2. 这几年参加注册会计…

echart 折线从左到右动画效果_echarts之自动切换折线图

echarts有legend控件可以控制多个折线图之间的切换legend: {data: [财经,娱乐,社会],selectedMode: "single", //单选模式&#xff0c;每次出现一条折线图y: y, //由于需要自适应&#xff0c;此值用变量动态改变right: 10,inactiveColor: "#ADD9FF", //选中…

TellDontAsk的扩展

五年多来&#xff0c;Martin Fowler在他著名的TellDontAsk文章中指出了面向对象编程中的最大问题之一。 在他的著作中&#xff0c;他提醒程序员&#xff0c;他们应该信任自己的对象来为他们执行工作&#xff0c;而不是要求对象提供以后可以使用的数据。 我非常同意这一点&…

网管型光纤收发器产品功能特性详解

飞畅科技生产的网管型光纤收发器机架&#xff0c;吸收了国内外同类产品的优点&#xff0c;产品更具灵活性和实用性。网管光纤收发器采用结构灵活的机架模块化设计&#xff0c;十六槽2U机箱、电源模块及标准的热插拔模块组成&#xff0c;机箱内置双容错电源及无源背板总线&#…

[渝粤教育] 中国地质大学 大学英语(1) 复习题

《大学英语(1)》模拟题 一.单选题 — ______ is the man near the window? — Oh he is Tom. A. What B. Who C. How — I don’t get up late on Sundays.— _____ do I. A. Neither B. Either C. So — I have got a pain in my chest. — You _____ see the doctor. A. …