• [技术干货] springboot获取当前服务ip_服务注册与发现组件 Eureka 应用实战
    在传统的单体应用中,组件之间的调用通过有规范约束的接口进行,实现不同模块间良好协作。在微服务架构中,原本的’巨石’应用按照业务被分割成相对独立的、提供特定功能的服务,每一个微服务都可以通过集群或者其他方式进行动态的扩展,每一个微服务实例的网络地址都可能动态变化,这使得原本通过硬编码地址的调用方式失去了适用性。微服务架构中,服务跨度之大,数量之多,迫切需要架构建立一个去中心化的组件对各个微服务实例的信息进行登记和管理,同时提供能力让各个服微务实例之间能够互相发现,从而达到互相调用的结果。  通常来说服务注册与发现包括两部分,一个是Server端,另一个是Client。Server是一个公用组件,为Client提供服务注册和发现的功能,维护着注册到自身的Client的相关信息,同时提供接口给Client获取到注册表中其他服务的信息,使得动态变化的Client在服务地址稳定的时间节点能够进行服务间调用。Client将自己的服务信息通过一定的方式登记到Server上,并在正常范围内维护自己信息的一致性,方便其他服务发现自己,同时可以通过Server获取到自己依赖的其他服务信息,完成服务调用。  Eureka简介 Eureka这个词来源于古希腊语,意为“我找到了!我发现了!”,据传,阿基米德在洗澡时发现浮力原理,高兴得来不及穿上裤子,跑到街上大喊:“Eureka(我找到了)!”。  在Netflix中,Eureka是一个REST风格的服务注册与发现的基础服务组件,主要是应用在AWS中定位中间层服务的负载均衡和故障转移。Eureka由两部分组成,一个是Eureka Server,提供服务注册和发现的功能,就是我们上面所说的Server端;另一个是Java客户端,称为Eureka Client,是为了使得与服务端交互更加简单,Eureka Client会定时将自己的信息注册到Eureka Server中,并从Server中发现其他服务。客户端中有一个内置的负载均衡器,用来进行基本的循环负载均衡。 Spring Cloud的版本基于Finchley.M9,spring-cloud-netflix的版本基于2.0.0.M8,Eureka的版本基于v1.8.7。  搭建Eureka服务注册中心 可以通过IDEA快速搭建包含Eurake Server依赖的SpringBoot项目 主要依赖   // eureka-client相关依赖org.springframework.cloudspring-cloud-starter-netflix-eureka-server // actuator相关依赖org.springframework.bootspring-boot-starter-actuator // Spring Web MVC相关依赖org.springframework.bootspring-boot-starter-web 在启动类中的注解 @EnableEurekaServer  @SpringBootApplication// `@EnableEurekaServer`注解会为项目自动配置必须的配置类,标识该服务为注册中心@EnableEurekaServer public class Chapter4EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(Chapter2EurekaServerApplication.class, args);}} 在 application.yml 配置文件中添加以下配置,配置注册中心的端口和标识自己为Eureka Server。  server: port: 8761eureka: instance: hostname: localhost client: register-with-eureka: false // 表明该服务不会向Eureka Server注册自己的信息 fetch-registry: false // 表明该服务不会向Eureka Server获取注册信息 service-url: // Eureka Server注册中心的地址,用于client与server进行交流 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ Eureka Server既可以独立部署,也可以集群部署,在集群部署的条件下,Eureka Server间会进行注册表信息同步的操作,这时被同步注册表信息的Eureka Server将会被同步注册表信息的Eureka Server称为 peer 。  请注意上述配置中service-url其实指向的注册中心为实例本身,通常来讲,每一个Eureka Server也是一个Eureka Client,它会尝试注册自己,所以需要最少一个注册中心的URL来定位对等点 peer 。如果不提供这样一个注册端点,注册中心也能工作,但是会在日志中打出无法向 peer 注册自己。在独立(Standalone)Eureka Server的模式下,Eureka Server一般会关闭作为client端注册自己的行为,注册中心将无注册到它的 peer 中。  Eureka Server与Eureka Client之间的维系主要通过心跳的方式实现,心跳(Heartbeat)即Eureka Client定时向Eureka Server汇报当前的本服务实例的状态。  Eureka Server需要随时维持最新的服务实例信息,所以在注册表中的每个服务实例都需要定期发送心跳到Server中以使自己的注册保持最新的状态(数据一般直接保存在内存中)。这意味着Eureka Client不需要为每次服务间请求都向注册中心请求依赖服务实例信息,Eureka Client将定时从Eureka Server中拉取Eureka Server中的注册表中的所有信息,并将注册表信息缓存到本地,用于服务发现。  启动Eureka Server后,应用会有一个主页面用来展示当前注册表中的服务实例信息和暴露一些基于HTTP协议的的endpoint在 /eureka 路径下被Eureka Client用于注册自身、获取注册表信息以及发送心跳等。 搭建Eureka服务提供者 可以通过IDEA快速搭建包含Eurake Client依赖的SpringBoot项目。 主要依赖有: // eureka-server相关依赖org.springframework.cloudspring-cloud-starter-netflix-eureka-clientorg.springframework.bootspring-boot-starter-web 启动类 @SpringBootApplicationpublic class Chapter4EurekaClientApplication {public static void main(String[] args) {SpringApplication.run(Chapter2EurekaClientApplication.class, args);}} 在 Finchley 版本中的Spring Cloud中,只要引入 spring-cloud-starter-netflix-eureka-client 的依赖,应用就会自动注册到Eureka Server,但是需要在配置文件中添加Eureka Server的地址。 在 application.yml 添加以下配置: eureka: instance: prefer-ip-address: true client: service-url: // Eureka Server注册中心的地址,用于client与server进行交流 defaultZone: http://localhost:8761/eureka/ server: port: 8765spring: application:  name: eureka-client 查看Eureka服务注册中心  搭建好上述的两个的Eureka应用后,依次启动Server和Client。  Eureka Server主页 访问地址Eureka Server的主页 http://localhost:8761 ,可以看到以下界面: 当前注册到Eureka Server上的服务实例信息 Eureka Server运行环境的通用信息 Eureka Server实例的信息 与服务注册中心交换信息 DiscoveryClient 来源于spring-cloud-client-discovery,是SpringCloud中定义的用来服务发现的顶级接口,在SpringCloud的各类服务发现组件中(如Netflix Eureka或者Consul)都有相应的实现。它提供从服务注册中心中根据serviceId的获取到对应的服务实例信息的能力。 当一个服务实例拥有 DiscoveryClient 的具体实现时,就可以从Eureka Server中发现其他的服务实例。 在Eureka Client中注入 DiscoveryClient ,并从Eureka Server获取服务实例的信息。 在 chapter2-eureka-client 添加一个 ServiceInstanceRestController 的controller  @RestControllerpublic class ServiceInstanceRestController { @Autowired private DiscoveryClient discoveryClient; @RequestMapping("/service-instances/{applicationName}") public List serviceInstancesByApplicationName( @PathVariable String applicationName) { return this.discoveryClient.getInstances(applicationName); }} 启动应用后,访问地址 http://localhost:8765/service-instances/eureka-client ,获取应用名为 eureka-client 的(服务本身)服务实例的元数据,结果如下: [ { "host":"192.168.1.168 ————————————————          原文链接:https://blog.csdn.net/weixin_42356829/article/details/112202396 
  • [技术干货] spring boot 最全配置说明
    # 端口号server.port=8000# Web URLserver.context-path=/config# 服务器ip地址server.address=# 设置http header大小   注意此处tomcat6-7 中默认是8192  即8k   并且每一个连接都会开辟一个8k的cache 修改配置一定注意server.max-http-header-size= # 设置返回http头 server.server-header= # 设置servlet path 默认为/ server.servlet-path=# 设置超时时间,单位为毫秒,如不设置将使用容器默认时间,-1表示不设置 server.connection-timeout= # 设置ServletContext parameter server.context-parameters.MyDefinition=我的定义 # 设置应用名 server.display-name=app # 是否启用相应压缩,支持text/html, text/xml, text/plain,text/css, text/javascript, application/javascriptserver.compression.enabled=false# 指定压缩类型,默认text/html, text/xml, text/plain,text/css, text/javascript, application/javascriptserver.compression.mime-types=# 指定不压缩的user-agent,多个以逗号分隔,默认值为:text/html,text/xml,text/plain,text/cssserver.compression.excluded-user-agents=# 超过该值将被压缩,默认是2048server.compression.min-response-size=1024# 错误地址,默认是/errorserver.error.path=# 是否包含异常的堆栈信息,默认是NEVER,其他ALWAYS,ON_TRACE_PARAMserver.error.include-stacktrace=# 当服务器错误是,是否显示错误信息,默认为trueserver.error.whitelabel.enabled=Jetty# Jetty 启用处理请求的线程数,设置值小于或等于处理器的2倍server.jetty.acceptors=# 设置Jetty post content 大小,单位为byteserver.jetty.max-http-post-size=# 设置Jetty selectors 线程数server.jetty.selectors=Jsp# 设置Jsp servlet class,默认为:org.apache.jasper.servlet.JspServlet,其他如:org.springframework.web.servlet.DispatcherServletserver.jsp-servlet.class-name=org.springframework.web.servlet.DispatcherServlet# 设置Jsp初始化参数server.jsp-servlet.init-parameters.MyDefinition=我的定义# 是否将Jsp servlet 注册到servlet 容器中server.jsp-servlet.registered=Cookie# 设置cookie描述server.session.cookie.comment=# 设置cookie作用域server.session.cookie.domain=# 设置cookie是否只读server.session.cookie.http-only=# 设置cookie最大有效时间server.session.cookie.max-age=# 设置cookie名称server.session.cookie.name=# 设置cookie生效路径server.session.cookie.path=# 如果使用SSL,设置为trueserver.session.cookie.secure=# 是否在重启时持久化sessionserver.session.persistent=# 存储路径server.session.store-dir=# session过期时间server.session.timeout=# 设置session跟踪模式(可以设置为"cookie", "url", "ssl")server.session.tracking-modes=SSL# 是否支持SSL ciphersserver.ssl.ciphers=# 设定client验证方式是wanted或neededserver.ssl.client-auth=# 开启ssl支持server.ssl.enabled=# 是否开启ssl协议server.ssl.enabled-protocols=# 存储区的密钥名称server.ssl.key-alias=# 存储区的密钥密码server.ssl.key-password=# ssl证书地址server.ssl.key-store=# 访问密钥的密码server.ssl.key-store-password=# 设置密钥存储的提供者server.ssl.key-store-provider=# 设置密钥存储的类型server.ssl.key-store-type=# 设置ssl协议 默认TLSserver.ssl.protocol=# 持有ssl证书的信任存储库server.ssl.trust-store=# 持有ssl证书存储库的密码server.ssl.trust-store-password=# 持有ssl证书存储库的提供者server.ssl.trust-store-provider=# 持有ssl证书存储类型server.ssl.trust-store-type=Tomcat# 请求队列的最大长度server.tomcat.accept-count=# 是否刷新日志缓冲区server.tomcat.accesslog.buffered=# 日志存储路径,默认logsserver.tomcat.accesslog.directory=# 是否存储log,默认为falseserver.tomcat.accesslog.enabled=# 日志文件后缀名,默认.yyyy-MM-ddserver.tomcat.accesslog.file-date-format=# 设定access logs的格式,默认: commonserver.tomcat.accesslog.pattern=# log文件前缀,默认是access_logserver.tomcat.accesslog.prefix=# 延迟将时间戳包含在文件命中当日志循环时server.tomcat.accesslog.rename-on-rotate=# 是否设置请求的IP地址、主机名、协议和端口的请求属性server.tomcat.accesslog.request-attributes-enabled=# 是否启用日志循环server.tomcat.accesslog.rotate=# 日志文件后缀名,默认.logserver.tomcat.accesslog.suffix=# 忽略文件字符server.tomcat.additional-tld-skip-patterns=# 在调用backgroundProcess方法的时间延迟。默认30秒server.tomcat.background-processor-delay=# tomcat根目录,默认是临时目录server.tomcat.basedir=# 正则表达式匹配内部代理server.tomcat.internal-proxies=# 服务器接受的最大连接数server.tomcat.max-connections=# 服务器接受最大的post内容请求server.tomcat.max-http-post-size=# 最大工作线程server.tomcat.max-threads=# 最少空闲工作线程server.tomcat.min-spare-threads=# 用于覆盖http头名称server.tomcat.port-header=# header包含协议名,通常命名格式为X-Forwarded-Protoserver.tomcat.protocol-header=# 表示请求使用ssl协议,默认为httpsserver.tomcat.protocol-header-https-value=# 是否使用/作为请求重定向的根路径server.tomcat.redirect-context-root=# 提取远程ip地址的头信息名称server.tomcat.remote-ip-header=# 配置uri字符编码server.tomcat.uri-encoding=#---------------------------------------- #CORE PROPERTIES #----- ----------------------------------- debug = false #启用调试日志。trace = false #启用跟踪日志。#logGING logging.config = #日志配置文件的位置。例如,Logback的`classpath:logback.xml`。logging.exception-conversion-word =%wEx #记录异常时使用的转换字。logging.file = #日志文件名(例如,`myapp.log`)。名称可以是精确位置或相对于当前目录。logging.file.max-history = 0 #要保留的归档日志文件的最大值。仅支持默认的logback设置。logging.file.max-size = 10MB #最大日志文件大小。仅支持默认的logback设置。logging.level。* =#日志级别严重等级映射。例如,`logging.level.org.springframework = DEBUG`。logging.path = #日志文件的位置。例如,`/ var / log`。logging.pattern.console = #用于输出到控制台的Appender模式。仅支持默认的Logback设置。logging.pattern.dateformat = yyyy-MM-dd HH:mm:ss.SSS #日志日期格式的Appender模式。仅支持默认的Logback设置。logging.pattern.file = #用于输出到文件的Appender模式。仅支持默认的Logback设置。logging.pattern.level =%5p #日志级别的Appender模式。仅支持默认的Logback设置。logging.register-shutdown-hook = false #在日志记录系统初始化时为其注册一个shutdown hook。#AOP spring.aop.auto =真#添加@EnableAspectJAutoProxy。spring.aop.proxy-target-class = true #是否要创建基于子类的(CGLIB)代理(true),而不是基于标准Java接口的代理(false)。#IDENTITY (ContextIdApplicationContextInitializer)spring.application.name = #应用程序名称。#ADMIN (SpringApplicationAdminJmxAutoConfiguration)spring.application.admin.enabled = false #是否为应用程序启用管理功能。spring.application.admin.jmx-name = org.springframework.boot:type = Admin,name = SpringApplication #JMX 应用程序管理员MBean的名称。#AUTO-CONFIGURATION spring.autoconfigure.exclude = #要排除的自动配置类。#BANNER spring.banner.charset = UTF-8 #横幅文件编码。spring.banner.location = classpath:banner.txt #横幅文本资源位置。spring.banner.image.location = classpath:banner.gif #横幅图像文件位置(也可以使用jpg或png)。spring.banner.image.width = 76 #字符中的横幅图像的宽度。spring.banner.image.height = #crs 中横幅图像的高度(默认基于图像高度)。spring.banner.image.margin = 2 #字符中的左手图像边距。spring.banner.image.invert = false #是否应针对暗终端主题反转图像。#SPRING CORE spring.beaninfo.ignore = true #是否跳过BeanInfo类的搜索。#SPRING CACHE(CacheProperties)spring.cache.cache-names = #Cmama 分隔的要创建的缓存名称列表(如果底层缓存管理器支持)。spring.cache.caffeine.spec = #用于创建缓存的规范。有关规格格式的更多详细信息,请参阅CaffeineSpec。spring.cache.couchbase.expiration = 0ms #条目到期。默认情况下,条目永不过期。请注意,此值最终会转换为秒。spring.cache.ehcache.config = #用于初始化EhCache的配置文件的位置。spring.cache.infinispan.config = #用于初始化Infinispan的配置文件的位置。spring.cache.jcache.config = #用于初始化缓存管理器的配置文件的位置。spring.cache.jcache.provider = #CachingProvider实现的完全限定名称,用于检索符合JSR-107的缓存管理器。仅当类路径上有多个JSR-107实现时才需要。spring.cache.redis.cache-null-values = true #允许缓存空值。spring.cache.redis.key-prefix = #键前缀。spring.cache.redis.time-to-live = 0ms #条目到期。默认情况下,条目永不过期。spring.cache.redis.use-key-prefix = true#写入Redis时是否使用密钥前缀。spring.cache.type = #Cache 类型。默认情况下,根据环境自动检测。#SPRING CONFIG - 仅使用环境属性(ConfigFileApplicationListener)spring.config.additional-location = #配置除默认值之外使用的文件位置。spring.config.location = #配置替换默认值的文件位置。spring.config.name = application #配置文件名。#HAZELCAST(HazelcastProperties)spring.hazelcast.config = #用于初始化Hazelcast的配置文件的位置。#PROJECT INFORMATION(ProjectInfoProperties)spring.info.build.location = classpath:META-INF / build-info.properties #生成的build-info.properties文件的位置。spring.info.git.location =类路径:git.properties 生成的git.properties文件#所在。#JMX spring.jmx.default域 = #JMX域名。spring.jmx.enabled = true #将管理bean公开给JMX域。spring.jmx.server = mbeanServer #MBeanServer bean name。#Email (MailProperties)spring.mail.default-encoding = UTF-8 #默认MimeMessage编码。spring.mail.host = #SMTP 服务器主机。例如,`smtp.example.com`。spring.mail.jndi-name = #会话JNDI名称。设置时,优先于其他会话设置。spring.mail.password = #SMTP 服务器的登录密码。spring.mail.port = #SMTP 服务器端口。spring.mail.properties。* = #其他JavaMail会话属性。spring.mail.protocol = smtp #SMTP服务器使用的协议。spring.mail.test-connection = false#是否在启动时测试邮件服务器是否可用。spring.mail.username = #STP 服务器的登录用户。#APICING SETTINGS(SpringApplication)spring.main.banner-mode = console #模式用于在应用程序运行时显示横幅。spring.main.sources = 要包含在ApplicationContext中的#Sources (类名,包名或XML资源位置)。spring.main.web-application-type = #用于显式请求特定类型的Web应用程序的标志。如果未设置,则根据类路径自动检测。#FILE ENCODING(FileEncodingApplicationListener)spring.mandatory-file-encoding = #应用程序必须使用的预期字符编码。#INTEREMENTIZATION (MessageSourceProperties)spring.messages.always-use-message-format = false #是否始终应用MessageFormat规则,甚至解析不带参数的消息。spring.messages.basename = messages #逗号分隔的basenames列表(本质上是一个完全限定的类路径位置),每个都遵循ResourceBundle约定,轻松支持基于斜杠的位置。spring.messages.cache-duration = #加载的资源包文件缓存持续时间。未设置时,捆绑包将永久缓存。如果未指定持续时间后缀,则将使用秒。spring.messages.encoding = UTF-8 #消息包编码。spring.messages.fallback-to-system-locale = true #如果找不到特定区域设置的文件,是否回退到系统区域设置。spring.messages.use-code-as-default-message = false #是否使用消息代码作为默认消息而不是抛出“NoSuchMessageException”。仅在开发期间推荐。#OUTPUT spring.output.ansi.enabled =检测#配置的ANSI输出。#PID FILE(ApplicationPidFileWriter)spring.pid.fail-on-write-error = #如果使用ApplicationPidFileWriter但它无法写入PID文件,则失败。spring.pid.file = #要写入的PID文件的位置(如果使用ApplicationPidFileWriter)。#PROFILES spring.profiles.active = #逗号分隔的有源配置文件列表。可以通过命令行开关覆盖。spring.profiles.include = #无条件地激活指定的逗号分隔的配置文件列表(如果使用YAML,则激活配置文件列表)。#Quartz调度器(QuartzProperties)spring.quartz.jdbc.comment前缀 = - #前缀在SQL初始化脚本单行注释。spring.quartz.jdbc.initialize-schema = embedded #数据库模式初始化模式。spring.quartz.jdbc.schema = classpath:org / quartz / impl / jdbcjobstore / tables_ @ @ platform @@ .sql #用于初始化数据库模式的SQL文件的路径。spring.quartz.job-store-type = memory #Quartz作业存储类型。spring.quartz.properties。* = #其他Quartz Scheduler属性。#REACTOR (ReactorCoreProperties)spring.reactor.stacktrace -mode.enabled = false #Reactor 是否应该在运行时收集堆栈跟踪信息。#SENDGRID(SendGridAutoConfiguration)spring.sendgrid.api-key = #SendGrid API密钥。spring.sendgrid.proxy.host = #SendGrid代理主机。spring.sendgrid.proxy.port = #SendGrid代理端口。#---------------------------------------- #WEB PROPERTIES #----- -----------------------------------#EmbEDDED SERVER CONFIGURATION(ServerProperties)server.address = #服务器应绑定到的网络地址。server.compression.enabled = false #是否启用了响应压缩。server.compression.excluded-user-agents = #要从压缩中排除的用户代理列表。server.compression.mime-types = text / html,text / xml,text / plain,text / css,text / javascript,application / javascript #应该压缩的以逗号分隔的MIME类型列表。server.compression.min-response-size = 2048 #执行压缩所需的最小“Content-Length”值。server.connection超时= #连接器在关闭连接之前等待另一个HTTP请求的时间。未设置时,将使用连接器的特定于容器的默认值。使用值-1表示没有(即无限)超时。server.error.include-exception = false #包含“exception”属性。server.error.include-stacktrace = never #何时包含“stacktrace”属性。server.error.path = / error #错误控制器的路径。server.error.whitelabel.enabled = true #是否在服务器出错时启用浏览器中显示的默认错误页面。server.http2.enabled = false#是否启用HTTP / 2支持,如果当前环境支持它。server.jetty.acceptors = #要使用的接受者线程数。server.jetty.accesslog.append = false #追加到日志。server.jetty.accesslog.date-format = dd / MMM / yyyy:HH:mm:ss Z #请求日志的时间戳格式。server.jetty.accesslog.enabled = false #启用访问日志。server.jetty.accesslog.extended-format = false #启用扩展NCSA格式。server.jetty.accesslog.file-date-format = #日期格式放在日志文件名中。server.jetty.accesslog.filename =#Log filename。如果未指定,则日志重定向到“System.err”。server.jetty.accesslog.locale = #请求日志的区域设置。server.jetty.accesslog.log-cookies = false #启用请求cookie的记录。server.jetty.accesslog.log-latency = false #启用请求处理时间的记录。server.jetty.accesslog.log-server = false #启用请求主机名的日志记录。server.jetty.accesslog.retention-period = 31 #删除轮换日志文件之前的天数。server.jetty.accesslog.time-zone = GMT #请求日志的时区。server.jetty.max-http-post-size = 0#HTTP post或put内容的最大大小(以字节为单位)。server.jetty.selectors = #要使用的选择器线程数。server.max-http-header-size = 0 #HTTP消息头的最大大小(以字节为单位)。server.port = 8080 #服务器HTTP端口。server.server-header = #用于Server响应头的值(如果为空,则不发送头)。server.use-forward-headers = #是否应将X-Forwarded- *头应用于HttpRequest。server.servlet.context-parameters。* = #Servlet context init参数。server.servlet.context-path = #应用程序的上下文路径。server.servlet.application-display-name = application #显示应用程序的名称。server.servlet.jsp.class-name = org.apache.jasper.servlet.JspServlet #JSP servlet的类名。server.servlet.jsp.init-parameters。* = #用于配置JSP servlet的Init参数。server.servlet.jsp.registered = true #是否已注册JSP servlet。server.servlet.path = / #主调度程序servlet的路径。server.servlet.session.cookie.comment = #会话cookie的评论。server.servlet.session.cookie.domain = #会话cookie的域名。server.servlet.session.cookie.http-only = 会话cookie的#“HttpOnly”标志。server.servlet.session.cookie.max-age = #会话cookie的最大年龄。如果未指定持续时间后缀,则将使用秒。server.servlet.session.cookie.name = #会话cookie名称。server.servlet.session.cookie.path = #会话cookie的路径。server.servlet.session.cookie.secure = 会话cookie的#“Secure”标志。server.servlet.session.persistent = false #是否在重新启动之间保留会话数据。server.servlet.session.store-dir = #用于存储会话数据的目录。server.servlet.session.timeout = #会话超时。如果未指定持续时间后缀,则将使用秒。server.servlet.session.tracking-modes = #会话跟踪模式(以下一项或多项:“cookie”,“url”,“ssl”)。server.ssl.ciphers = #支持的SSL密码。server.ssl.client-auth = #是否需要客户端身份验证(“想要”)或需要(“需要”)。需要信任存储。server.ssl.enabled = #启用SSL支持。server.ssl.enabled-protocols = #启用SSL协议。server.ssl.key-alias = #标识密钥库中密钥的别名。server.ssl.key-password = #用于访问密钥库中密钥的密码。server.ssl.key-store = #保存SSL证书的密钥库的路径(通常是jks文件)。server.ssl.key-store-password = #用于访问密钥库的密码。server.ssl.key-store-provider = #密钥库的提供者。server.ssl.key-store-type = #密钥库的类型。server.ssl.protocol = TLS #要使用的SSL协议。server.ssl.trust-store = #持有SSL证书的信任存储。server.ssl.trust-store-password = #用于访问信任库的密码。server.ssl.trust-store-provider = #信任存储的提供者。server.ssl.trust-store-type = #信任库的类型。server.tomcat.accept-count = 0 #当所有可能的请求处理线程都在使用时,传入连接请求的最大队列长度。server.tomcat.accesslog.buffered = true #是否缓冲输出以使其仅定期刷新。server.tomcat.accesslog.directory = logs #创建日志文件的目录。可以是绝对的或相对于Tomcat基础目录。server.tomcat.accesslog.enabled = false #启用访问日志。server.tomcat.accesslog.file最新格式= .yyyy-MM-dd #要放在日志文件名中的日期格式。server.tomcat.accesslog.pattern = common #访问日志的格式模式。server.tomcat.accesslog.prefix = access_log #日志文件名前缀。server.tomcat.accesslog.rename-on-rotate = false #是否延迟在文件名中包含日期戳,直到旋转时间。server.tomcat.accesslog.request-attributes-enabled = false #设置用于请求的IP地址,主机名,协议和端口的请求属性。server.tomcat.accesslog.rotate = true #是否启用访问日志轮换。server.tomcat.accesslog.suffix = .log#日志文件名后缀。server.tomcat.additional-tld-skip-patterns = #逗号分隔的其他模式列表,这些模式匹配要忽略的TLD扫描的jar。server.tomcat.background-processor-delay = 30s #调用backgroundProcess方法之间的延迟。如果未指定持续时间后缀,则将使用秒。server.tomcat.basedir = #Tomcat 基目录。如果未指定,则使用临时目录。server.tomcat.internal-proxies = 10 \\。\\ d {1,3} \\。\\ d {1,3} \\。\\ d {1,3} | \\。192 \\ 168 \\ d {1,3} \\ d {1,3} | \\。169 \\ 254 \\ d {1,3} \\ d {1,3} | \\。127 \\ d {1,3} \\ d {1,3} \\ d {1,3} | \\172 \\ 1 [6-9] {1} \\ d {1,3} \\ d {1,3} |。。\\172 \\ 2 [0-9] {1} \\ d {1,3} \\ d {1,3} |。。\\172 \\。3 [0-1] {1} \\。\\ d {1,3} \\。\\ d {1,3} #正则表达式匹配可信IP地址。server.tomcat.max-connections = 0 #服务器在任何给定时间接受和处理的最大连接数。server.tomcat.max-http-header-size = 0 #HTTP消息头的最大大小(以字节为单位)。server.tomcat.max-http-post-size = 0 #HTTP 帖子内容的最大大小(以字节为单位)。server.tomcat.max-threads = 0 #最大工作线程数。server.tomcat.min-spare-threads = 0 #最小工作线程数。server.tomcat.port-header = X-Forwarded-Port#用于覆盖原始端口值的HTTP头的名称。server.tomcat.protocol-header = #包含传入协议的标头,通常命名为“X-Forwarded-Proto”。server.tomcat.protocol-header-https-value = https #协议标头的值,指示传入请求是否使用SSL。server.tomcat.redirect-context-root = #是否应通过在路径中附加/来重定向对上下文根的请求。server.tomcat.remote-ip-header = #从中提取远程IP的HTTP头的名称。例如,`X-FORWARDED-FOR`。server.tomcat.resource.cache-ttl = #静态资源缓存的生存时间。server.tomcat.uri-encoding = UTF-8 #用于解码URI的字符编码。server.tomcat.use-relative-redirects = #通过调用sendRedirect生成的HTTP 1.1和更高版本的位置标头是使用相对还是绝对重定向。server.undertow.accesslog.dir = #Undertow 访问日志目录。server.undertow.accesslog.enabled = false #是否启用访问日志。server.undertow.accesslog.pattern = common #访问日志的格式模式。server.undertow.accesslog.prefix = access_log。#日志文件名前缀。server.undertow.accesslog.rotate = true#是否启用访问日志轮换。server.undertow.accesslog.suffix = log #日志文件名后缀。server.undertow.buffer-size = #每个缓冲区的大小,以字节为单位。server.undertow.direct-buffers = #是否在Java堆外部分配缓冲区。server.undertow.io-threads = #为worker创建的I / O线程数。server.undertow.eager-filter-init = true #是否应在启动时初始化servlet过滤器。server.undertow.max-http-post-size = 0 #HTTP 帖子内容的最大大小(以字节为单位)。server.undertow.worker-threads = #工作线程数。#FREEMARKER(FreeMarkerProperties)spring.freemarker.allow-request-override = false #是否允许HttpServletRequest属性覆盖(隐藏)控制器生成的同名模型属性。spring.freemarker.allow-session-override = false #是否允许HttpSession属性覆盖(隐藏)控制器生成的同名模型属性。spring.freemarker.cache = false #是否启用模板缓存。spring.freemarker.charset = UTF-8 #模板编码。spring.freemarker.check-template-location = true #是否检查模板位置是否存在。spring.freemarker.content-type = text / html #Content-Type value。spring.freemarker.enabled = true #是否为此技术启用MVC视图分辨率。spring.freemarker.expose-request-attributes = false #是否应在与模板合并之前将所有请求属性添加到模型中。spring.freemarker.expose-session-attributes = false #是否应在与模板合并之前将所有HttpSession属性添加到模型中。spring.freemarker.expose-spring-macro-helpers = true #是否公开一个RequestContext供Spring的宏库使用,名称为“springMacroRequestContext”。spring.freemarker.prefer-file-system-access = true #是否更喜欢文件系统访问以进行模板加载。文件系统访问可以热检测模板更改。spring.freemarker.prefix = #在构建URL时添加前缀以查看名称的前缀。spring.freemarker.request-context-attribute = #所有视图的RequestContext属性的名称。spring.freemarker.settings。* = #众所周知的FreeMarker密钥,传递给FreeMarker的配置。spring.freemarker.suffix = .ftl #在构建URL时附加到视图名称的后缀。spring.freemarker.template-loader-path = classpath:/ templates /#逗号分隔的模板路径列表。spring.freemarker.view-names = #可以解析的视图名称的白名单。#GLOVY TEMPLATES(GroovyTemplateProperties)spring.groovy.template.allow-request-override = false #是否允许HttpServletRequest属性覆盖(隐藏)控制器生成的同名模型属性。spring.groovy.template.allow-session-override = false #是否允许HttpSession属性覆盖(隐藏)控制器生成的同名模型属性。spring.groovy.template.cache = false #是否启用模板缓存。spring.groovy.template.charset = UTF-8 #模板编码。spring.groovy.template.check-template-location = true#是否检查模板位置是否存在。spring.groovy.template.configuration。* = #请参阅GroovyMarkupConfigurer spring.groovy.template.content-type = text / html #Content-Type value。spring.groovy.template.enabled = true #是否为此技术启用MVC视图分辨率。spring.groovy.template.expose-request-attributes = false #是否应在与模板合并之前将所有请求属性添加到模型中。spring.groovy.template.expose-session-attributes = false #在与模板合并之前是否应将所有HttpSession属性添加到模型中。spring.groovy.template.expose-spring-macro-helpers = true #是否公开一个RequestContext供Spring的宏库使用,名称为“springMacroRequestContext”。spring.groovy.template.prefix = #在构建URL时添加前缀以查看名称的前缀。spring.groovy.template.request-context-attribute = #所有视图的RequestContext属性的名称。spring.groovy.template.resource-loader-path = classpath:/ templates / #Template path。spring.groovy.template.suffix = .tpl #在构建URL时附加到视图名称的后缀。spring.groovy.template.view-names =#可以解析的视图名称的白名单。#SPRING HATEOAS(HateoasProperties)spring.hateoas.use-hal-as-default-json-media-type = true #是否应将application / hal + json响应发送给接受application / json的请求。#HTTP 消息转换spring.http.converters.preferred-json-mapper = #用于HTTP消息转换的首选JSON映射器。默认情况下,根据环境自动检测。#HTTP 编码(HttpEncodingProperties)spring.http.encoding.charset = UTF-8 #HTTP 请求和响应的字符集。如果未明确设置,则添加到“Content-Type”标头。spring.http.encoding.enabled = true #是否启用http编码支持。spring.http.encoding.force = #是否在HTTP请求和响应上强制编码到配置的字符集。spring.http.encoding.force-request = #是否在HTTP请求中强制编码到配置的字符集。未指定“force”时,默认为true。spring.http.encoding.force-response =#是否在HTTP响应中强制编码到配置的字符集。spring.http.encoding.mapping = #用于编码映射的Locale。#MULTIPART (MultipartProperties)spring.servlet.multipart.enabled = true #是否启用对分段上传的支持。spring.servlet.multipart.file-size-threshold = 0 #将文件写入磁盘的阈值。值可以使用后缀“MB”或“KB”分别表示兆字节或千字节。spring.servlet.multipart.location = #上传文件的中间位置。spring.servlet.multipart.max-file-size = 1MB #最大文件大小。值可以使用后缀“MB”或“KB”分别表示兆字节或千字节。spring.servlet.multipart.max-request-size = 10MB#最大请求大小。值可以使用后缀“MB”或“KB”分别表示兆字节或千字节。spring.servlet.multipart.resolve-lazily = false #是否在文件或参数访问时懒惰地解析多部分请求。#JACKSON (JacksonProperties)spring.jackson.date-format = #日期格式字符串或完全限定的日期格式类名。例如,`yyyy-MM-dd HH:mm:ss`。spring.jackson.default-property-inclusion = #控制序列化期间包含的属性。配置了Jackson的JsonInclude.Include枚举中的一个值。spring.jackson.deserialization。* = #Jackon on / off功能会影响Java对象的反序列化方式。spring.jackson.generator。* = #Jackson开/关功能,适用于发电机。spring.jackson.joda-date-time-format =#Joda日期时间格式字符串。如果未配置,如果使用格式字符串配置,则使用“date-format”作为后备。spring.jackson.locale = #用于格式化的区域设置。spring.jackson.mapper。* = #Jackson 通用开/关功能。spring.jackson.parser。* = #Jackson开启/关闭解析器的功能。spring.jackson.property-naming-strategy = #Jackson PropertyNamingStrategy的常数之一。也可以是PropertyNamingStrategy子类的完全限定类名。spring.jackson.serialization。* = #Jacker on / off功能会影响Java对象的序列化方式。spring.jackson.time-zone =#格式化日期时使用的时区。例如,“America / Los_Angeles”或“GMT + 10”。#GSON(GsonProperties)spring.gson.date-format = #序列化Date对象时使用的格式。spring.gson.disable-html-escaping = #是否禁用转义HTML字符,如'<','>'等.chring.gson.disable-inner-class-serialization = #是否在排除内部类时序列化。spring.gson.enable-complex-map-key-serialization = #是否启用复杂映射键(即非基元)的序列化。spring.gson.exclude-fields-without-expose-annotation = #是否排除所有不考虑序列化或反序列化但没有“Expose”注释的字段。spring.gson.field-naming-policy = #在序列化和反序列化期间应该应用于对象字段的命名策略。spring.gson.generate-non-executable-json = #是否通过在输出前添加一些特殊文本来生成不可执行的JSON。spring.gson.lenient = #是否宽容解析不符合RFC 4627的JSON.chring.gson.long-serialization-policy = #长和长类型的序列化策略。spring.gson.pretty-printing = #是否输出适合页面的序列化JSON以进行漂亮的打印。spring.gson.serialize-nulls = #是否序列化空字段。#JERSEY (JerseyProperties)spring.jersey.application-path = #作为应用程序基URI的路径。如果指定,则覆盖“@ApplicationPath”的值。spring.jersey.filter.order = 0 #Jersey过滤链顺序。spring.jersey.init。* = #通过servlet或过滤器传递给Jersey的Init参数。spring.jersey.servlet.load-on-startup = -1 #加载Jersey servlet的启动优先级。spring.jersey.type = servlet #Jersey集成类型。#SPRING LDAP(LdapProperties)spring.ldap.anonymous-read-only = false #只读操作是否应使用匿名环境。spring.ldap.base = #Base 后缀,所有操作都应该从中发起。spring.ldap.base-environment。* = #LDAP 规范设置。spring.ldap.password = #服务器的登录密码。spring.ldap.urls = #服务器的LDAP URL。spring.ldap.username = #登录服务器的用户名。#EmbEDDED LDAP(EmbeddedLdapProperties#EMBEDDED)spring.ldap.embedded.base-dn = #基本DN列表。spring.ldap.embedded.credential.username = #嵌入式LDAP用户名。spring.ldap.embedded.credential.password = #嵌入式LDAP密码。spring.ldap.embedded.ldif = classpath:schema.ldif #Schema (LDIF)脚本资源引用。spring.ldap.embedded.port = 0 #嵌入式LDAP端口。spring.ldap.embedded.validation.enabled = true #是否启用LDAP模式验证。spring.ldap.embedded.validation.schema = #自定义架构的路径。#MUSTACHE TEMPLATES(MustacheAutoConfiguration)spring.mustache.allow-request-override = false #是否允许HttpServletRequest属性覆盖(隐藏)控制器生成的同名模型属性。spring.mustache.allow-session-override = false #是否允许HttpSession属性覆盖(隐藏)控制器生成的同名模型属性。spring.mustache.cache = false #是否启用模板缓存。spring.mustache.charset = UTF-8 #模板编码。spring.mustache.check-template-location = true #是否检查模板位置是否存在。spring.mustache.content-type = text / html #Content-Type value。spring.mustache.enabled = true #是否为此技术启用MVC视图分辨率。spring.mustache.expose-request-attributes = false #是否应在与模板合并之前将所有请求属性添加到模型中。spring.mustache.expose-session-attributes = false #是否应在与模板合并之前将所有HttpSession属性添加到模型中。spring.mustache.expose-spring-macro-helpers = true #是否公开一个RequestContext供Spring的宏库使用,名称为“springMacroRequestContext”。spring.mustache.prefix= classpath:/ templates / #适用于模板名称的前缀。spring.mustache.request-context-attribute = #所有视图的RequestContext属性的名称。spring.mustache.suffix = .mustache #应用于模板名称的后缀。spring.mustache.view-names = #可以解析的视图名称的白名单。#SPRING MVC(WebMvcProperties)spring.mvc.async.request-timeout = #异步请求处理超时之前的时间。spring.mvc.contentnegotiation.favor-parameter = false #是否应使用请求参数(默认情况下为“format”)来确定请求的媒体类型。spring.mvc.contentnegotiation.favor-path-extension = false #是否应使用URL路径中的路径扩展来确定请求的媒体类型。spring.mvc.contentnegotiation.media-types。* = #映射内容协商的媒体类型的文件扩展名。例如,yml到text / yaml。spring.mvc.contentnegotiation.parameter-name =#启用“favor-parameter”时要使用的查询参数名称。spring.mvc.date-format = #要使用的日期格式。例如,`dd / MM / yyyy`。spring.mvc.dispatch-trace-request = false #是否将TRACE请求分派给FrameworkServlet doService方法。spring.mvc.dispatch-options-request = true #是否将OPTIONS请求分派给FrameworkServlet doService方法。spring.mvc.favicon.enabled = true #是否启用favicon.ico的解析。spring.mvc.formcontent.putfilter.enabled = true #是否启用Spring的HttpPutFormContentFilter。spring.mvc.ignore-default-model-on-redirect = true#在重定向场景中是否应忽略“默认”模型的内容。spring.mvc.locale = #要使用的语言环境。默认情况下,“Accept-Language”标头会覆盖此区域设置。spring.mvc.locale-resolver = accept-header #定义应如何解析语言环境。spring.mvc.log-resolved-exception = false #是否启用由“HandlerExceptionResolver”解析的异常的警告日志记录。spring.mvc.message-codes-resolver-format = #格式化消息代码的策略。例如,`PREFIX_ERROR_CODE`。spring.mvc.pathmatch.use-registered-suffix-pattern = false#后缀模式匹配是否仅适用于使用“spring.mvc.contentnegotiation.media-types。*”注册的扩展。spring.mvc.pathmatch.use-suffix-pattern = false #在将模式与请求匹配时是否使用后缀模式匹配(“。*”)。spring.mvc.servlet.load-on-startup = -1 #加载调度程序servlet的启动优先级。spring.mvc.static-path-pattern = / ** #用于静态资源的路径模式。spring.mvc.throw-exception-if-no-handler-found = false #如果没有找到Handler来处理请求,是否应该抛出“NoHandlerFoundException”。spring.mvc.view.prefix = #Spring MVC视图前缀。spring.mvc.view.suffix = #Spring MVC视图后缀。#SPRING RESOURCES HANDLING(ResourceProperties)spring.resources.add-mappings = true #是否启用默认资源处理。spring.resources.cache.cachecontrol.cache-private = #指示响应消息是针对单个用户的,并且不能由共享高速缓存存储。spring.resources.cache.cachecontrol.cache-public = #表示任何缓存都可以存储响应。spring.resources.cache.cachecontrol.max-age = #应该缓存响应的最长时间,如果未指定持续时间后缀,则以秒为单位。spring.resources.cache.cachecontrol.must-revalidate =#表示一旦它变得陈旧,缓存不得使用响应而不用服务器重新验证它。spring.resources.cache.cachecontrol.no-cache = #表示只有在与服务器重新验证时才能重用缓存的响应。spring.resources.cache.cachecontrol.no-store = #表示在任何情况下都不缓存响应。spring.resources.cache.cachecontrol.no-transform = #指示不应转换响应内容的中介(缓存和其他)。spring.resources.cache.cachecontrol.proxy-revalidate = #与“must-revalidate”指令的含义相同,不同之处在于它不适用于私有缓存。spring.resources.cache.cachecontrol.s-max-age = #共享缓存应缓存响应的最长时间,如果未指定持续时间后缀,则以秒为单位。spring.resources.cache.cachecontrol.stale-if-error = #遇到错误时可以使用响应的最长时间,如果未指定持续时间后缀,则以秒为单位。spring.resources.cache.cachecontrol.stale-while-revalidate = #响应变为失效后可以响应的最长时间,如果未指定持续时间后缀,则以秒为单位。spring.resources.cache.period = #资源处理程序所服务资源的缓存周期。如果未指定持续时间后缀,则将使用秒。spring.resources.chain.cache= true #是否在资源链中启用缓存。spring.resources.chain.enabled = #是否启用Spring资源处理链。默认情况下,禁用,除非至少启用了一个策略。spring.resources.chain.gzipped = false #是否启用已解压缩资源的解析。spring.resources.chain.html-application-cache = false #是否启用HTML5应用程序缓存清单重写。spring.resources.chain.strategy.content.enabled = false #是否启用内容版本策略。spring.resources.chain.strategy.content.paths = / **#逗号分隔的模式列表,应用于内容版本策略。spring.resources.chain.strategy.fixed.enabled = false #是否启用固定版本策略。spring.resources.chain.strategy.fixed.paths = / ** #以逗号分隔的模式列表应用于固定版本策略。spring.resources.chain.strategy.fixed.version = #用于固定版本策略的版本字符串。spring.resources.static-locations = classpath:/ META-INF / resources /,classpath:/ resources /,classpath:/ static /,classpath:/ public / #静态资源的位置。#SPRING SESSION(SessionProperties)spring.session.store-type = #会话存储类型。spring.session.timeout = #会话超时。如果未指定持续时间后缀,则将使用秒。spring.session.servlet.filter-order = -2147483598 #会话存储库过滤顺序。spring.session.servlet.filter-dispatcher-types = async,error,request #会话存储库过滤器调度程序类型。#SPRING SESSION HAZELCAST(HazelcastSessionProperties)spring.session.hazelcast.flush-mode = on-save #sessions flush mode。spring.session.hazelcast.map-name = spring:session:sessions #用于存储会话的地图的名称。#SPRING SESSION JDBC(JdbcSessionProperties)spring.session.jdbc.cleanup-cron = 0 * * * * * #cron 表达式用于过期的会话清理作业。spring.session.jdbc.initialize-schema = embedded #数据库模式初始化模式。spring.session.jdbc.schema = classpath:org / springframework / session / jdbc / schema- @@ platform @ @ .SQL #的路径SQL文件,以用于初始化数据库架构。spring.session.jdbc.table-name = SPRING_SESSION #用于存储会话的数据库表的名称。#春节会议MONGODB( MongoSessionProperties)spring.session.mongodb.collection-name = sessions #用于存储会话的集合名称。#SPRING SESSION REDIS(RedisSessionProperties)spring.session.redis.cleanup-cron = 0 * * * * * #Cron 表达式用于过期的会话清理作业。spring.session.redis.flush-mode = on-save #sessions flush mode。spring.session.redis.namespace = spring:session #用于存储会话的密钥的命名空间。#THYMELEAF(ThymeleafAutoConfiguration)spring.thymeleaf.cache = true #是否启用模板缓存。spring.thymeleaf.check-template = true #在呈现模板之前是否检查模板是否存在。spring.thymeleaf.check-template-location = true #是否检查模板位置是否存在。spring.thymeleaf.enabled = true #是否为Web框架启用Thymeleaf视图解析。spring.thymeleaf.enable-spring-el-compiler = false #在SpringEL表达式中启用SpringEL编译器。spring.thymeleaf.encoding = UTF-8 #模板文件编码。spring.thymeleaf.excluded-view-names = #逗号分隔的视图名称列表(允许的模式)应从分辨率中排除。spring.thymeleaf.mode = HTML #要应用于模板的模板模式。另请参见Thymeleaf的TemplateMode枚举。spring.thymeleaf.prefix = classpath:/ templates / #在构建URL时添加前缀以查看名称的前缀。spring.thymeleaf.reactive.chunked-mode-view-names = #逗号分隔的视图名称列表(允许的模式)应该是设置最大块大小时在CHUNKED模式下执行的唯一视图名称。spring.thymeleaf.reactive.full-mode-view-names =#逗号分隔的视图名称列表(允许的模式),即使设置了最大块大小,也应该在FULL模式下执行。spring.thymeleaf.reactive.max-chunk-size = 0 #用于写入响应的数据缓冲区的最大大小(以字节为单位)。spring.thymeleaf.reactive.media-types = #视图技术支持的媒体类型。spring.thymeleaf.servlet.content-type = text / html #Content-Type写入HTTP响应的值。spring.thymeleaf.suffix = .html #在构建URL时附加到视图名称的后缀。spring.thymeleaf.template-resolver-order = #链中模板解析器的顺序。spring.thymeleaf.view-名= #逗号分隔的视图名称列表(允许的模式),可以解析。#SPRING WEBFLUX(WebFluxProperties)spring.webflux.date-format = #要使用的日期格式。例如,`dd / MM / yyyy`。spring.webflux.static路径图案 = / ** #用于静态资源的路径模式。#SPRING WEB SERVICES(WebServicesProperties)spring.webservices.path = / services #作为服务基URI的路径。spring.webservices.servlet.init = #Servlet init参数传递给Spring Web Services。spring.webservices.servlet.load-on-startup = -1 #加载Spring Web Services servlet的启动优先级。spring.webservices.wsdl-locations =#逗号分隔的WSDL位置列表以及要作为bean公开的随附XSD。#---------------------------------------- #SECURITY PROPERTIES #----- ----------------------------------- #SECURITY(SecurityProperties)spring.security.filter.order = -100 #安全过滤器链顺序。spring.security.filter.dispatcher-types = async,error,request #安全过滤器链调度程序类型。spring.security.user.name = user #默认用户名。spring.security.user.password = #默认用户名的密码。spring.security.user.roles = #授予默认用户名的角色。#SECURITY OAUTH2 CLIENT(OAuth2ClientProperties)spring.security.oauth2.client.provider。* = #OAuth提供商详细信息。spring.security.oauth2.client.registration。* = #OAuth客户注册。#---------------------------------------- #DATA PROPERTIES#----- -----------------------------------#FLYWAY (FlywayProperties)spring.flyway.baseline-description = #spring.flyway.baseline-on-migrate = #spring.flyway.baseline-version = 1 #开始迁移的版本spring.flyway.check-location = true #是否检查迁移脚本位置是否存在。spring.flyway.clean-disabled = #spring.flyway.clean-on-validation-error = #spring.flyway.dry-run-output = #spring.flyway.enabled = true #是否启用flyway。spring.flyway.encoding = #spring.flyway.error-handlers = #= #spring.flyway.password =spring.flyway.group = #spring.flyway.ignore-future-migrations = #spring.flyway.ignore-missing-migrations = #spring.flyway.init-sqls = #在获取连接后立即执行以初始化连接的SQL语句。spring.flyway.installed-by = #spring.flyway.locations = classpath:db / migration #迁移脚本的位置。spring.flyway.mixed = #spring.flyway.out-of-order #如果你想让Flyway创建自己的DataSource,可以使用JDBC密码。spring.flyway.placeholder-prefix = #spring.flyway.placeholder-replacement = #spring.flyway.placeholder-suffix = #spring.flyway.placeholders。* = #spring.flyway.repeatable-sql-migration-prefix = #spring.flyway.schemas = #schemas to update spring.flyway.skip-default-callbacks = #spring.flyway.skip-default-resolvers = #spring.flyway.sql-migration-prefix = V #spring.flyway.sql-migration -separator =#spring.flyway.sql-migration-suffix = .sql #spring.flyway.sql-migration-suffixes = #spring.flyway.table = #spring.flyway.target = #spring.flyway.undo-sql-migration-prefix = #spring.flyway.url = 要迁移的数据库的JDBC url。如果未设置,则使用主要配置的数据源。spring.flyway.user = #要迁移的数据库的登录用户。spring.flyway.validate-on-migrate = ##LIQUIBASE(LiquibaseProperties)spring.liquibase.change-log = classpath:/db/changelog/db.changelog-master.yaml# 更改日志配置路径。spring.liquibase.check-change-log-location = true #是否检查更改日志位置是否存在。spring.liquibase.contexts = #逗号分隔的运行时上下文列表。spring.liquibase.default-schema = #默认数据库模式。spring.liquibase.drop-first = false #是否首先删除数据库模式。spring.liquibase.enabled = true #是否启用Liquibase支持。spring.liquibase.labels =#要使用的以逗号分隔的运行时标签列表。spring.liquibase.parameters。* =#更改日志参数。spring.liquibase.password = #要迁移的数据库的登录密码。spring.liquibase.rollback-file = #执行更新时写入回滚SQL的文件。spring.liquibase.url = #JDBC要迁移的数据库的URL。如果未设置,则使用主要配置的数据源。spring.liquibase.user = #要迁移的数据库的登录用户。#COUCHBASE(CouchbaseProperties)spring.couchbase.bootstrap-hosts = #Couchbase节点(主机或IP地址)来自bootstrap。spring.couchbase.bucket.name = default #要连接的存储桶的名称。spring.couchbase.bucket.password = #桶的密码。spring.couchbase.env.endpoints.key-value = 1 #每个节点对密钥/值服务的套接字数。spring.couchbase.env.endpoints.queryservice.min-endpoints = 1 #每个节点的最小套接字数。spring.couchbase.env.endpoints.queryservice.max-endpoints = 1 #每个节点的最大套接字数。spring.couchbase.env.endpoints.viewservice.min-endpoints = 1 #每个节点的最小套接字数。spring.couchbase.env.endpoints.viewservice.max-endpoints = 1 #每个节点的最大套接字数。spring.couchbase.env.ssl.enabled = #是否启用SSL支持。除非另有说明,否则在提供“keyStore”时自动启用。spring.couchbase.env.ssl.key-store = #保存证书的JVM密钥库的路径。spring.couchbase.env.ssl.key-store-password = #用于访问密钥库的密码。spring.couchbase.env.timeouts.connect = 5000ms #Bucket连接超时。spring.couchbase.env.timeouts.key-value = 2500ms #阻止对特定密钥超时执行的操作。spring.couchbase.env.timeouts.query = 7500ms #N1QL查询操作超时。spring.couchbase.env.timeouts.socket-connect = 1000ms #Socket 连接超时。spring.couchbase.env.timeouts.view = 7500ms #常规和地理空间视图操作超时。#DAO (PersistenceExceptionTranslationAutoConfiguration)spring.dao.exceptiontranslation.enabled = true #是否启用PersistenceExceptionTranslationPostProcessor。#CASSANDRA (CassandraProperties)spring.data.cassandra.cluster-name = #Cassandra 集群的名称。spring.data.cassandra.compression = none #Cassandra二进制协议支持的压缩。spring.data.cassandra.connect-timeout = #Socket 选项:连接超时。spring.data.cassandra.consistency-level = #查询一致性级别。spring.data.cassandra.contact-points = localhost #群集节点地址。spring.data.cassandra.fetch-size = #查询默认提取大小。spring.data.cassandra.keyspace-name = #要使用的Keyspace名称。spring.data.cassandra.load-balancing-policy =#负载均衡策略的类名称。spring.data.cassandra.port = #Cassandra 服务器的端口。spring.data.cassandra.password = #服务器的登录密码。spring.data.cassandra.pool.heartbeat-interval = 30s #Heartbeat interval ,在空闲连接上发送消息以确保它仍然存在。如果未指定持续时间后缀,则将使用秒。spring.data.cassandra.pool.idle-timeout = 120s #删除空闲连接之前的空闲超时。如果未指定持续时间后缀,则将使用秒。spring.data.cassandra.pool.max-queue-size = 256#如果没有可用的连接,则排队的最大请求数。spring.data.cassandra.pool.pool-timeout = 5000ms #尝试从主机池获取连接时的池超时。spring.data.cassandra.read超时=选项:读取超时。spring.data.cassandra.reconnection-policy = #重新连接策略类。spring.data.cassandra.repositories.type = auto #要启用的Cassandra存储库的类型。spring.data.cassandra.retry-policy = #重试策略的类名。spring.data.cassandra.serial-consistency-level = #查询串行一致性级别。spring.data.cassandra.schema-action = none #启动时要采取的架构操作。spring.data.cassandra.ssl = false #启用SSL支持。spring.data.cassandra.username = #服务器的登录用户。#DATA COUCHBASE(CouchbaseDataProperties)spring.data.couchbase.auto-index = false #自动创建视图和索引。spring.data.couchbase.consistency = read-your-own-writes #在生成的查询中默认应用的一致性。spring.data.couchbase.repositories.type = auto #要启用的Couchbase存储库的类型。原文链接:https://blog.csdn.net/sinat_39308893/article/details/127882791
  • [技术干货] Servlet相关
    基于web.xml <?xml version="1.0" encoding="utf-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml">     <display-name>manage</display-name>     <welcome-file-list>         <welcome-file>index.html</welcome-file>         <welcome-file>index.htm</welcome-file>         <welcome-file>index.jsp</welcome-file>         <welcome-file>default.html</welcome-file>         <welcome-file>default.htm</welcome-file>         <welcome-file>default.jsp</welcome-file>     <welcome-file-list>     <servlet>         <description></description>         <display-name>user</display-name>         <servlet-name>user</servlet-name>         <servlet-class>com.sec.servlet.UserServlet</servlet-class>     </servlet>     <servlet-mapping>         <servlet-name>user</servlet-name>         <url-pattern>/user</url-pattern>     </servlet-mapping> </web-app> 在web.xml中,Servlet的配置在Servlet标签中,Servlet标签是由Servlet和Servlet-mapping标签组成,两者通过在Servlet和Servlet-mapping标签中相同的Servlet-name名称实现关联,标签的含义如下:  <servlet>:声明Servlet配置入口 <description>:声明Servlet描述信息 <display-name>:定义Web应用的名字 <servlet-name>:声明Servlet名称以便在后面的映射时使用 <servlet-class>:指定当前servlet对应的类的路径 <servlet-mapping>:注册组件访问设置的路径入口 <servlet-name>:指定上文配置的Servlet的名称 <url-pattern>:指定配置这个组件的访问路径 基于注解方式 在Servlet3.0以上的版本中,开发者无须在web.xml里面配置Servlet,只需要添加@WebServlet注解即可修改Servlet的属性  基于注解方式的注解参数 属性名    类型    描述 name    String    指定Servlet的name属性,等价于servlet-name value    String[]    等价于urlPatterns属性 urlPatterns    String[]    指定一组Servlet的URL匹配模式,等价于url-pattern标签 loadOnStartup    int    指定Servlet的加载顺序,等价于load-on-startup标签 asyncSupported    boolean    声明Servlet是否支持异步操作模式,等价于async-supported标签 initParams    WebInitParam[]    指定一组Servlet初始化参数,等价于init-param标签 description    String    Servlet的描述信息,等价于description标签 displayName    String    Servlet的显示名,通常配合工具使用,等价于display-name标签 Servlet的访问流程 首先在浏览器地址栏中输入user,即访问url-pattern标签中的值;然后浏览器发起请求,服务器通过servlet-mapping标签中找到文件名为user的url-pattern,通过其对应的servlet-name寻找servlet标签中servlet-name相同的servlet;再通过servlet标签中的servlet-name,获取servlet-class参数;最后得到具体的class文件路径,继而执行servlet-class标签中class文件的逻辑。  从上述过程中可以看出,servlet和servlet-mapping中都含有<servlet-name></servlet-name>标签,其主要原因是通过servlet-name作为纽带,将servlet-class和url-pattern构成联系,从而使URL映射到servlet-class所指定的类中执行相应逻辑。 Servlet的接口方法 HTTP有8种请求方法,分别为GET、POST、HEAD、OPTIONS、PUT、DELETE、TRACE以及CONNECT方法。Servlet接口中也对应着相应的请求接口:GET、POST、HEAD、OPTIONS、PUT、DELETE以及TRACE,这些接口对应着请求类型,service()方法会检查HTTP请求类型,然后在适当的时候调用doGet、doPost、doPut、doDelete等方法。  init()接口 在Servlet实例化后,Servlet容器会调用init()方法来初始化该对象,主要是使Servlet对象在处理客户请求前可以完成一些初始化工作。init()方法在第一次创建Servlet时被调用,在后续每次用户请求时不再被调用。  public void init() throws ServletException{     //此处内容为开发者定义的初始化代码 } service()接口 service()方法是执行实际任务的主要方法。Servlet容器调用service()方法来处理来自客户端的请求,并将格式化的响应写回给客户端,每次服务器接收到一个Servlet请求时,服务器都会产生一个新的线程并调用服务。在service()方法被Servlet容器调用之前,必须确保init()方法正确完成。  public void service(ServletRequest request,ServletResponse response) throws ServletException, IOException {     //此处内容为开发者处理客户请求的代码 } doGet()/doPost()等接口 doGet()等方法根据HTTP的不同请求调用不同的方法。如果HTTP得到一个来自URL的GET请求,就会调用doGet()方法;如果得到的是一个POST请求,就会调用doPost()方法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {     //此处内容为开发者处理GET请求的代码     //以此类推,若是POST请求,则调用public void doPost方法 } destroy()接口 当Servlet容器检测到一个Servlet对象应该从服务中被移除时,会调用该对象的destroy()方法,以便Servlet对象释放它所使用的资源,保存数据到持久存储设备中。destroy()方法与init()方法相同,只会被调用一次。  public void destroy() {     //此处内容为开发者进行终止操作的代码 } getServletConfig()接口 getServletConfig()方法返回Servlet容器调用init()方法时传递给Servlet对象的ServletConfig对象,ServletConfig对象包含Servlet的初始化参数。开发者可以在Servlet的配置文件web.xml中,使用<init-param>标签为Servlet配置一些初始化参数。  <servlet>     <servlet-name>servlet</servlet-name>     <servlet-class>org.test.TestServlet</servlet-class>     <init-param>         <param-name>userName</param-name>         <param-value>panda</param-value>     </init-param>     <init-param>         <param-name>E-mail</param-name>         <param-value>test@test.net</param-value>     </init-param> </servlet> getServletInfo()接口 getServletInfo()方法会返回一个String类型的字符串,包括关于Servlet的信息,如作者、版本及版权等。  Servlet生命周期 Sevlet生命周期指的是Servlet从创建知道销毁的整个过程。在一个生命周期中,Servlet经历了被加载、初始化、接收请求、响应请求以及提供服务的过程。 当用户第一次向服务器发起请求时,服务器会解析用户的请求,此时容器会加载Servlet,然后创建Servlet实例,再调用init()方法初始化Servlet,紧接着调用服务的service()方法处理用户GET、POST或者其他类型的请求。当执行完Servlet中对应class文件的逻辑后,将结果返回给服务器,服务器再响应用户请求。当服务器不再需要Servlet实例或重新载入Servlet时,会调用destroy()方法,释放掉所有在init()方法中申请的资源。 ————————————————                      原文链接:https://blog.csdn.net/weixin_45007073/article/details/120020495 
  • [技术干货] Springboot 整合RabbitMq
    该篇文章内容较多,包括有rabbitMq相关的一些简单理论介绍,provider消息推送实例,consumer消息消费实例,Direct、Topic、Fanout的使用,消息回调、手动确认等。 (但是关于rabbitMq的安装,就不介绍了) 在安装完rabbitMq后,输入http://ip:15672/ ,是可以看到一个简单后台管理界面的。  在这个界面里面我们可以做些什么? 可以手动创建虚拟host,创建用户,分配权限,创建交换机,创建队列等等,还有查看队列消息,消费效率,推送效率等等。 以上这些管理界面的操作在这篇暂时不做扩展描述,我想着重介绍后面实例里会使用到的。 首先先介绍一个简单的一个消息推送到接收的流程,提供一个简单的图: RabbitMq -JCccc 黄色的圈圈就是我们的消息推送服务,将消息推送到 中间方框里面也就是 rabbitMq的服务器,然后经过服务器里面的交换机、队列等各种关系(后面会详细讲)将数据处理入列后,最终右边的蓝色圈圈消费者获取对应监听的消息。 常用的交换机有以下三种,因为消费者是从队列获取信息的,队列是绑定交换机的(一般),所以对应的消息推送/接收模式也会有以下几种:  Direct Exchange  直连型交换机,根据消息携带的路由键将消息投递给对应队列。 大致流程,有一个队列绑定到一个直连交换机上,同时赋予一个路由键 routing key 。 然后当一个消息携带着路由值为X,这个消息通过生产者发送给交换机时,交换机就会根据这个路由值X去寻找绑定值也是X的队列。 Fanout Exchange 扇型交换机,这个交换机没有路由键概念,就算你绑了路由键也是无视的。 这个交换机在接收到消息后,会直接转发到绑定到它上面的所有队列。 Topic Exchange 主题交换机,这个交换机其实跟直连交换机流程差不多,但是它的特点就是在它的路由键和绑定键之间是有规则的。 简单地介绍下规则:  *  (星号) 用来表示一个单词 (必须出现的) #  (井号) 用来表示任意数量(零个或多个)单词 通配的绑定键是跟队列进行绑定的,举个小例子 队列Q1 绑定键为 *.TT.*          队列Q2绑定键为  TT.# 如果一条消息携带的路由键为 A.TT.B,那么队列Q1将会收到; 如果一条消息携带的路由键为TT.AA.BB,那么队列Q2将会收到;  主题交换机是非常强大的,为啥这么膨胀? 当一个队列的绑定键为 "#"(井号) 的时候,这个队列将会无视消息的路由键,接收所有的消息。 当 * (星号) 和 # (井号) 这两个特殊字符都未在绑定键中出现的时候,此时主题交换机就拥有的直连交换机的行为。 所以主题交换机也就实现了扇形交换机的功能,和直连交换机的功能。  另外还有 Header Exchange 头交换机 ,Default Exchange 默认交换机,Dead Letter Exchange 死信交换机,这几个该篇暂不做讲述。 好了,一些简单的介绍到这里为止,  接下来我们来一起编码。 本次实例教程需要创建2个springboot项目,一个 rabbitmq-provider (生产者),一个rabbitmq-consumer(消费者)。 首先创建 rabbitmq-provider, pom.xml里用到的jar依赖:          <!--rabbitmq-->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-amqp</artifactId>         </dependency>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency> 然后application.yml:  server:   port: 8021 spring:   #给项目来个名字   application:     name: rabbitmq-provider   #配置rabbitMq 服务器   rabbitmq:     host: 127.0.0.1     port: 5672     username: root     password: root     #虚拟host 可以不设置,使用server默认host     virtual-host: JCcccHost ps:里面的虚拟host配置项不是必须的,我自己在rabbitmq服务上创建了自己的虚拟host,所以我配置了;你们不创建,就不用加这个配置项(默认写 :virtual-host: /)。 一定要注意 要注意 要注意!!!!! 那么怎么建一个单独的host呢? 假如我就是想给某个项目接入,使用一个单独host,顺便使用一个单独的账号,就好像我文中配置的 root 这样。 其实也很简便: virtual-host的创建:  账号user的创建:  然后记得给账号分配权限,指定使用某个virtual host: 其实还可以特定指定交换机使用权等等:  回归正题,继续继续。 接着我们先使用下direct exchange(直连型交换机),创建DirectRabbitConfig.java(对于队列和交换机持久化以及连接使用设置,在注释里有说明,后面的不同交换机的配置就不做同样说明了):  import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;   /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/ @Configuration public class DirectRabbitConfig {       //队列 起名:TestDirectQueue     @Bean     public Queue TestDirectQueue() {         // durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效         // exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable         // autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。         //   return new Queue("TestDirectQueue",true,true,false);           //一般设置一下队列的持久化就好,其余两个就是默认false         return new Queue("TestDirectQueue",true);     }       //Direct交换机 起名:TestDirectExchange     @Bean     DirectExchange TestDirectExchange() {       //  return new DirectExchange("TestDirectExchange",true,true);         return new DirectExchange("TestDirectExchange",true,false);     }       //绑定  将队列和交换机绑定, 并设置用于匹配键:TestDirectRouting     @Bean     Binding bindingDirect() {         return BindingBuilder.bind(TestDirectQueue()).to(TestDirectExchange()).with("TestDirectRouting");     }     @Bean     DirectExchange lonelyDirectExchange() {         return new DirectExchange("lonelyDirectExchange");     } } 然后写个简单的接口进行消息推送(根据需求也可以改为定时任务等等,具体看需求),SendMessageController.java:  import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; import java.util.UUID;   /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/ @RestController public class SendMessageController {       @Autowired     RabbitTemplate rabbitTemplate;  //使用RabbitTemplate,这提供了接收/发送等等方法       @GetMapping("/sendDirectMessage")     public String sendDirectMessage() {         String messageId = String.valueOf(UUID.randomUUID());         String messageData = "test message, hello!";         String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));         Map<String,Object> map=new HashMap<>();         map.put("messageId",messageId);         map.put("messageData",messageData);         map.put("createTime",createTime);         //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange         rabbitTemplate.convertAndSend("TestDirectExchange", "TestDirectRouting", map);         return "ok";     }     } 把rabbitmq-provider项目运行,调用下接口: 因为我们目前还没弄消费者 rabbitmq-consumer,消息没有被消费的,我们去rabbitMq管理页面看看,是否推送成功: 再看看队列(界面上的各个英文项代表什么意思,可以自己查查哈,对理解还是有帮助的): 很好,消息已经推送到rabbitMq服务器上面了。 接下来,创建rabbitmq-consumer项目: pom.xml里的jar依赖:          <!--rabbitmq-->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-amqp</artifactId>         </dependency>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter</artifactId>         </dependency> 然后是 application.yml:    server:   port: 8022 spring:   #给项目来个名字   application:     name: rabbitmq-consumer   #配置rabbitMq 服务器   rabbitmq:     host: 127.0.0.1     port: 5672     username: root     password: root     #虚拟host 可以不设置,使用server默认host     virtual-host: JCcccHost 然后一样,创建DirectRabbitConfig.java(消费者单纯的使用,其实可以不用添加这个配置,直接建后面的监听就好,使用注解来让监听器监听对应的队列即可。配置上了的话,其实消费者也是生成者的身份,也能推送该消息。):  import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;   /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/ @Configuration public class DirectRabbitConfig {       //队列 起名:TestDirectQueue     @Bean     public Queue TestDirectQueue() {         return new Queue("TestDirectQueue",true);     }       //Direct交换机 起名:TestDirectExchange     @Bean     DirectExchange TestDirectExchange() {         return new DirectExchange("TestDirectExchange");     }       //绑定  将队列和交换机绑定, 并设置用于匹配键:TestDirectRouting     @Bean     Binding bindingDirect() {         return BindingBuilder.bind(TestDirectQueue()).to(TestDirectExchange()).with("TestDirectRouting");     } } 然后是创建消息接收监听类,DirectReceiver.java:  @Component @RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue public class DirectReceiver {       @RabbitHandler     public void process(Map testMessage) {         System.out.println("DirectReceiver消费者收到消息  : " + testMessage.toString());     }   } 然后将rabbitmq-consumer项目运行起来,可以看到把之前推送的那条消息消费下来了: 然后可以再继续调用rabbitmq-provider项目的推送消息接口,可以看到消费者即时消费消息: 那么直连交换机既然是一对一,那如果咱们配置多台监听绑定到同一个直连交互的同一个队列,会怎么样? 可以看到是实现了轮询的方式对消息进行消费,而且不存在重复消费。 接着,我们使用Topic Exchange 主题交换机。 在rabbitmq-provider项目里面创建TopicRabbitConfig.java:  import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.TopicExchange; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;   /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/   @Configuration public class TopicRabbitConfig {     //绑定键     public final static String man = "topic.man";     public final static String woman = "topic.woman";       @Bean     public Queue firstQueue() {         return new Queue(TopicRabbitConfig.man);     }       @Bean     public Queue secondQueue() {         return new Queue(TopicRabbitConfig.woman);     }       @Bean     TopicExchange exchange() {         return new TopicExchange("topicExchange");     }         //将firstQueue和topicExchange绑定,而且绑定的键值为topic.man     //这样只要是消息携带的路由键是topic.man,才会分发到该队列     @Bean     Binding bindingExchangeMessage() {         return BindingBuilder.bind(firstQueue()).to(exchange()).with(man);     }       //将secondQueue和topicExchange绑定,而且绑定的键值为用上通配路由键规则topic.#     // 这样只要是消息携带的路由键是以topic.开头,都会分发到该队列     @Bean     Binding bindingExchangeMessage2() {         return BindingBuilder.bind(secondQueue()).to(exchange()).with("topic.#");     }   } 然后添加多2个接口,用于推送消息到主题交换机:      @GetMapping("/sendTopicMessage1")     public String sendTopicMessage1() {         String messageId = String.valueOf(UUID.randomUUID());         String messageData = "message: M A N ";         String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));         Map<String, Object> manMap = new HashMap<>();         manMap.put("messageId", messageId);         manMap.put("messageData", messageData);         manMap.put("createTime", createTime);         rabbitTemplate.convertAndSend("topicExchange", "topic.man", manMap);         return "ok";     }       @GetMapping("/sendTopicMessage2")     public String sendTopicMessage2() {         String messageId = String.valueOf(UUID.randomUUID());         String messageData = "message: woman is all ";         String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));         Map<String, Object> womanMap = new HashMap<>();         womanMap.put("messageId", messageId);         womanMap.put("messageData", messageData);         womanMap.put("createTime", createTime);         rabbitTemplate.convertAndSend("topicExchange", "topic.woman", womanMap);         return "ok";     } } 生产者这边已经完事,先不急着运行,在rabbitmq-consumer项目上,创建TopicManReceiver.java: import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; import java.util.Map;   /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/ @Component @RabbitListener(queues = "topic.man") public class TopicManReceiver {       @RabbitHandler     public void process(Map testMessage) {         System.out.println("TopicManReceiver消费者收到消息  : " + testMessage.toString());     } } 再创建一个TopicTotalReceiver.java:  package com.elegant.rabbitmqconsumer.receiver;   import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; import java.util.Map;   /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/   @Component @RabbitListener(queues = "topic.woman") public class TopicTotalReceiver {       @RabbitHandler     public void process(Map testMessage) {         System.out.println("TopicTotalReceiver消费者收到消息  : " + testMessage.toString());     } } 同样,加主题交换机的相关配置,TopicRabbitConfig.java(消费者一定要加这个配置吗? 不需要的其实,理由在前面已经说过了。):  import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.TopicExchange; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;   /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/   @Configuration public class TopicRabbitConfig {     //绑定键     public final static String man = "topic.man";     public final static String woman = "topic.woman";       @Bean     public Queue firstQueue() {         return new Queue(TopicRabbitConfig.man);     }       @Bean     public Queue secondQueue() {         return new Queue(TopicRabbitConfig.woman);     }       @Bean     TopicExchange exchange() {         return new TopicExchange("topicExchange");     }     //将firstQueue和topicExchange绑定,而且绑定的键值为topic.man     //这样只要是消息携带的路由键是topic.man,才会分发到该队列     @Bean     Binding bindingExchangeMessage() {         return BindingBuilder.bind(firstQueue()).to(exchange()).with(man);     }       //将secondQueue和topicExchange绑定,而且绑定的键值为用上通配路由键规则topic.#     // 这样只要是消息携带的路由键是以topic.开头,都会分发到该队列     @Bean     Binding bindingExchangeMessage2() {         return BindingBuilder.bind(secondQueue()).to(exchange()).with("topic.#");     }   }  然后把rabbitmq-provider,rabbitmq-consumer两个项目都跑起来,先调用/sendTopicMessage1  接口: 然后看消费者rabbitmq-consumer的控制台输出情况: TopicManReceiver监听队列1,绑定键为:topic.man TopicTotalReceiver监听队列2,绑定键为:topic.# 而当前推送的消息,携带的路由键为:topic.man   所以可以看到两个监听消费者receiver都成功消费到了消息,因为这两个recevier监听的队列的绑定键都能与这条消息携带的路由键匹配上。 接下来调用接口/sendTopicMessage2: 然后看消费者rabbitmq-consumer的控制台输出情况: TopicManReceiver监听队列1,绑定键为:topic.man TopicTotalReceiver监听队列2,绑定键为:topic.# 而当前推送的消息,携带的路由键为:topic.woman 所以可以看到两个监听消费者只有TopicTotalReceiver成功消费到了消息。 接下来是使用Fanout Exchang 扇型交换机。 同样地,先在rabbitmq-provider项目上创建FanoutRabbitConfig.java: import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/   @Configuration public class FanoutRabbitConfig {       /**      *  创建三个队列 :fanout.A   fanout.B  fanout.C      *  将三个队列都绑定在交换机 fanoutExchange 上      *  因为是扇型交换机, 路由键无需配置,配置也不起作用      */         @Bean     public Queue queueA() {         return new Queue("fanout.A");     }       @Bean     public Queue queueB() {         return new Queue("fanout.B");     }       @Bean     public Queue queueC() {         return new Queue("fanout.C");     }       @Bean     FanoutExchange fanoutExchange() {         return new FanoutExchange("fanoutExchange");     }       @Bean     Binding bindingExchangeA() {         return BindingBuilder.bind(queueA()).to(fanoutExchange());     }       @Bean     Binding bindingExchangeB() {         return BindingBuilder.bind(queueB()).to(fanoutExchange());     }       @Bean     Binding bindingExchangeC() {         return BindingBuilder.bind(queueC()).to(fanoutExchange());     } } 然后是写一个接口用于推送消息,        @GetMapping("/sendFanoutMessage")     public String sendFanoutMessage() {         String messageId = String.valueOf(UUID.randomUUID());         String messageData = "message: testFanoutMessage ";         String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));         Map<String, Object> map = new HashMap<>();         map.put("messageId", messageId);         map.put("messageData", messageData);         map.put("createTime", createTime);         rabbitTemplate.convertAndSend("fanoutExchange", null, map);         return "ok";     } 接着在rabbitmq-consumer项目里加上消息消费类,  FanoutReceiverA.java:  import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; import java.util.Map; /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/ @Component @RabbitListener(queues = "fanout.A") public class FanoutReceiverA {       @RabbitHandler     public void process(Map testMessage) {         System.out.println("FanoutReceiverA消费者收到消息  : " +testMessage.toString());     }   } FanoutReceiverB.java:  import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; import java.util.Map; /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/ @Component @RabbitListener(queues = "fanout.B") public class FanoutReceiverB {       @RabbitHandler     public void process(Map testMessage) {         System.out.println("FanoutReceiverB消费者收到消息  : " +testMessage.toString());     }   } FanoutReceiverC.java:  import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; import java.util.Map;   /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/ @Component @RabbitListener(queues = "fanout.C") public class FanoutReceiverC {       @RabbitHandler     public void process(Map testMessage) {         System.out.println("FanoutReceiverC消费者收到消息  : " +testMessage.toString());     }   } 然后加上扇型交换机的配置类,FanoutRabbitConfig.java(消费者真的要加这个配置吗? 不需要的其实,理由在前面已经说过了):  import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/ @Configuration public class FanoutRabbitConfig {       /**      *  创建三个队列 :fanout.A   fanout.B  fanout.C      *  将三个队列都绑定在交换机 fanoutExchange 上      *  因为是扇型交换机, 路由键无需配置,配置也不起作用      */         @Bean     public Queue queueA() {         return new Queue("fanout.A");     }       @Bean     public Queue queueB() {         return new Queue("fanout.B");     }       @Bean     public Queue queueC() {         return new Queue("fanout.C");     }       @Bean     FanoutExchange fanoutExchange() {         return new FanoutExchange("fanoutExchange");     }       @Bean     Binding bindingExchangeA() {         return BindingBuilder.bind(queueA()).to(fanoutExchange());     }       @Bean     Binding bindingExchangeB() {         return BindingBuilder.bind(queueB()).to(fanoutExchange());     }       @Bean     Binding bindingExchangeC() {         return BindingBuilder.bind(queueC()).to(fanoutExchange());     } } 最后将rabbitmq-provider和rabbitmq-consumer项目都跑起来,调用下接口/sendFanoutMessage : 然后看看rabbitmq-consumer项目的控制台情况: 可以看到只要发送到 fanoutExchange 这个扇型交换机的消息, 三个队列都绑定这个交换机,所以三个消息接收类都监听到了这条消息。 到了这里其实三个常用的交换机的使用我们已经完毕了,那么接下来我们继续讲讲消息的回调,其实就是消息确认(生产者推送消息成功,消费者接收消息成功)。 在rabbitmq-provider项目的application.yml文件上,加上消息确认的配置项后:  ps: 本篇文章使用springboot版本为 2.1.7.RELEASE ;  如果你们在配置确认回调,测试发现无法触发回调函数,那么存在原因也许是因为版本导致的配置项不起效, 可以把publisher-confirms: true 替换为  publisher-confirm-type: correlated  server:   port: 8021 spring:   #给项目来个名字   application:     name: rabbitmq-provider   #配置rabbitMq 服务器   rabbitmq:     host: 127.0.0.1     port: 5672     username: root     password: root     #虚拟host 可以不设置,使用server默认host     virtual-host: JCcccHost       #确认消息已发送到交换机(Exchange)     #publisher-confirms: true     publisher-confirm-type: correlated       #确认消息已发送到队列(Queue)     publisher-returns: true 然后是配置相关的消息确认回调函数,RabbitConfig.java:  import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.connection.CorrelationData; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;     /**  * @Author : JCccc  * @CreateTime : 2019/9/3  * @Description :  **/ @Configuration public class RabbitConfig {       @Bean     public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){         RabbitTemplate rabbitTemplate = new RabbitTemplate();         rabbitTemplate.setConnectionFactory(connectionFactory);         //设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数         rabbitTemplate.setMandatory(true);           rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {             @Override             public void confirm(CorrelationData correlationData, boolean ack, String cause) {                 System.out.println("ConfirmCallback:     "+"相关数据:"+correlationData);                 System.out.println("ConfirmCallback:     "+"确认情况:"+ack);                 System.out.println("ConfirmCallback:     "+"原因:"+cause);             }         });           rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {             @Override             public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {                 System.out.println("ReturnCallback:     "+"消息:"+message);                 System.out.println("ReturnCallback:     "+"回应码:"+replyCode);                 System.out.println("ReturnCallback:     "+"回应信息:"+replyText);                 System.out.println("ReturnCallback:     "+"交换机:"+exchange);                 System.out.println("ReturnCallback:     "+"路由键:"+routingKey);             }         });           return rabbitTemplate;     }   } 到这里,生产者推送消息的消息确认调用回调函数已经完毕。 可以看到上面写了两个回调函数,一个叫 ConfirmCallback ,一个叫 RetrunCallback; 那么以上这两种回调函数都是在什么情况会触发呢?  先从总体的情况分析,推送消息存在四种情况:  ①消息推送到server,但是在server里找不到交换机 ②消息推送到server,找到交换机了,但是没找到队列 ③消息推送到sever,交换机和队列啥都没找到 ④消息推送成功  那么我先写几个接口来分别测试和认证下以上4种情况,消息确认触发回调函数的情况:  ①消息推送到server,但是在server里找不到交换机 写个测试接口,把消息推送到名为‘non-existent-exchange’的交换机上(这个交换机是没有创建没有配置的):      @GetMapping("/TestMessageAck")     public String TestMessageAck() {         String messageId = String.valueOf(UUID.randomUUID());         String messageData = "message: non-existent-exchange test message ";         String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));         Map<String, Object> map = new HashMap<>();         map.put("messageId", messageId);         map.put("messageData", messageData);         map.put("createTime", createTime);         rabbitTemplate.convertAndSend("non-existent-exchange", "TestDirectRouting", map);         return "ok";     } 调用接口,查看rabbitmq-provuder项目的控制台输出情况(原因里面有说,没有找到交换机'non-existent-exchange'):  2019-09-04 09:37:45.197 ERROR 8172 --- [ 127.0.0.1:5672] o.s.a.r.c.CachingConnectionFactory       : Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'non-existent-exchange' in vhost 'JCcccHost', class-id=60, method-id=40) ConfirmCallback:     相关数据:null ConfirmCallback:     确认情况:false ConfirmCallback:     原因:channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'non-existent-exchange' in vhost 'JCcccHost', class-id=60, method-id=40)     结论: ①这种情况触发的是 ConfirmCallback 回调函数。   ②消息推送到server,找到交换机了,但是没找到队列   这种情况就是需要新增一个交换机,但是不给这个交换机绑定队列,我来简单地在DirectRabitConfig里面新增一个直连交换机,名叫‘lonelyDirectExchange’,但没给它做任何绑定配置操作:      @Bean     DirectExchange lonelyDirectExchange() {         return new DirectExchange("lonelyDirectExchange");     } 然后写个测试接口,把消息推送到名为‘lonelyDirectExchange’的交换机上(这个交换机是没有任何队列配置的):      @GetMapping("/TestMessageAck2")     public String TestMessageAck2() {         String messageId = String.valueOf(UUID.randomUUID());         String messageData = "message: lonelyDirectExchange test message ";         String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));         Map<String, Object> map = new HashMap<>();         map.put("messageId", messageId);         map.put("messageData", messageData);         map.put("createTime", createTime);         rabbitTemplate.convertAndSend("lonelyDirectExchange", "TestDirectRouting", map);         return "ok";     } 调用接口,查看rabbitmq-provuder项目的控制台输出情况:  ReturnCallback:     消息:(Body:'{createTime=2019-09-04 09:48:01, messageId=563077d9-0a77-4c27-8794-ecfb183eac80, messageData=message: lonelyDirectExchange test message }' MessageProperties [headers={}, contentType=application/x-java-serialized-object, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0]) ReturnCallback:     回应码:312 ReturnCallback:     回应信息:NO_ROUTE ReturnCallback:     交换机:lonelyDirectExchange ReturnCallback:     路由键:TestDirectRouting ConfirmCallback:     相关数据:null ConfirmCallback:     确认情况:true ConfirmCallback:     原因:null 可以看到这种情况,两个函数都被调用了; 这种情况下,消息是推送成功到服务器了的,所以ConfirmCallback对消息确认情况是true; 而在RetrunCallback回调函数的打印参数里面可以看到,消息是推送到了交换机成功了,但是在路由分发给队列的时候,找不到队列,所以报了错误 NO_ROUTE 。   结论:②这种情况触发的是 ConfirmCallback和RetrunCallback两个回调函数。  ③消息推送到sever,交换机和队列啥都没找到  这种情况其实一看就觉得跟①很像,没错 ,③和①情况回调是一致的,所以不做结果说明了。   结论: ③这种情况触发的是 ConfirmCallback 回调函数。   ④消息推送成功 那么测试下,按照正常调用之前消息推送的接口就行,就调用下 /sendFanoutMessage接口,可以看到控制台输出:  ConfirmCallback:     相关数据:null ConfirmCallback:     确认情况:true ConfirmCallback:     原因:null 结论: ④这种情况触发的是 ConfirmCallback 回调函数。 以上是生产者推送消息的消息确认 回调函数的使用介绍(可以在回调函数根据需求做对应的扩展或者业务数据处理)。  接下来我们继续, 消费者接收到消息的消息确认机制。  和生产者的消息确认机制不同,因为消息接收本来就是在监听消息,符合条件的消息就会消费下来。 所以,消息接收的确认机制主要存在三种模式:  ①自动确认, 这也是默认的消息确认情况。  AcknowledgeMode.NONE RabbitMQ成功将消息发出(即将消息成功写入TCP Socket)中立即认为本次投递已经被正确处理,不管消费者端是否成功处理本次投递。 所以这种情况如果消费端消费逻辑抛出异常,也就是消费端没有处理成功这条消息,那么就相当于丢失了消息。 一般这种情况我们都是使用try catch捕捉异常后,打印日志用于追踪数据,这样找出对应数据再做后续处理。  ② 根据情况确认, 这个不做介绍 ③ 手动确认 , 这个比较关键,也是我们配置接收消息确认机制时,多数选择的模式。 消费者收到消息后,手动调用basic.ack/basic.nack/basic.reject后,RabbitMQ收到这些消息后,才认为本次投递成功。 basic.ack用于肯定确认  basic.nack用于否定确认(注意:这是AMQP 0-9-1的RabbitMQ扩展)  basic.reject用于否定确认,但与basic.nack相比有一个限制:一次只能拒绝单条消息   消费者端以上的3个方法都表示消息已经被正确投递,但是basic.ack表示消息已经被正确处理。 而basic.nack,basic.reject表示没有被正确处理: 着重讲下reject,因为有时候一些场景是需要重新入列的。 channel.basicReject(deliveryTag, true);  拒绝消费当前消息,如果第二参数传入true,就是将数据重新丢回队列里,那么下次还会消费这消息。设置false,就是告诉服务器,我已经知道这条消息数据了,因为一些原因拒绝它,而且服务器也把这个消息丢掉就行。 下次不想再消费这条消息了。 使用拒绝后重新入列这个确认模式要谨慎,因为一般都是出现异常的时候,catch异常再拒绝入列,选择是否重入列。 但是如果使用不当会导致一些每次都被你重入列的消息一直消费-入列-消费-入列这样循环,会导致消息积压。 顺便也简单讲讲 nack,这个也是相当于设置不消费某条消息。 channel.basicNack(deliveryTag, false, true); 第一个参数依然是当前消息到的数据的唯一id; 第二个参数是指是否针对多条消息;如果是true,也就是说一次性针对当前通道的消息的tagID小于当前这条消息的,都拒绝确认。 第三个参数是指是否重新入列,也就是指不确认的消息是否重新丢回到队列里面去。 同样使用不确认后重新入列这个确认模式要谨慎,因为这里也可能因为考虑不周出现消息一直被重新丢回去的情况,导致积压。 看了上面这么多介绍,接下来我们一起配置下,看看一般的消息接收 手动确认是怎么样的。 ​​​在消费者项目里, 新建MessageListenerConfig.java上添加代码相关的配置代码: import com.elegant.rabbitmqconsumer.receiver.MyAckReceiver; import org.springframework.amqp.core.AcknowledgeMode; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;   /**  * @Author : JCccc  * @CreateTime : 2019/9/4  * @Description :  **/ @Configuration public class MessageListenerConfig {       @Autowired     private CachingConnectionFactory connectionFactory;     @Autowired     private MyAckReceiver myAckReceiver;//消息接收处理类       @Bean     public SimpleMessageListenerContainer simpleMessageListenerContainer() {         SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);         container.setConcurrentConsumers(1);         container.setMaxConcurrentConsumers(1);         container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认消息         //设置一个队列         container.setQueueNames("TestDirectQueue");         //如果同时设置多个如下: 前提是队列都是必须已经创建存在的         //  container.setQueueNames("TestDirectQueue","TestDirectQueue2","TestDirectQueue3");         //另一种设置队列的方法,如果使用这种情况,那么要设置多个,就使用addQueues         //container.setQueues(new Queue("TestDirectQueue",true));         //container.addQueues(new Queue("TestDirectQueue2",true));         //container.addQueues(new Queue("TestDirectQueue3",true));         container.setMessageListener(myAckReceiver);           return container;     } } 对应的手动确认消息监听类,MyAckReceiver.java(手动确认模式需要实现 ChannelAwareMessageListener): //之前的相关监听器可以先注释掉,以免造成多个同类型监听器都监听同一个队列。  import com.rabbitmq.client.Channel; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener; import org.springframework.stereotype.Component;   import java.io.ByteArrayInputStream; import java.io.ObjectInputStream; import java.util.Map;   @Component   public class MyAckReceiver implements ChannelAwareMessageListener {       @Override     public void onMessage(Message message, Channel channel) throws Exception {         long deliveryTag = message.getMessageProperties().getDeliveryTag();         try {             byte[] body = message.getBody();             ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(body));             Map<String,String> msgMap = (Map<String,String>) ois.readObject();             String messageId = msgMap.get("messageId");             String messageData = msgMap.get("messageData");             String createTime = msgMap.get("createTime");             ois.close();             System.out.println("  MyAckReceiver  messageId:"+messageId+"  messageData:"+messageData+"  createTime:"+createTime);             System.out.println("消费的主题消息来自:"+message.getMessageProperties().getConsumerQueue());             channel.basicAck(deliveryTag, true); //第二个参数,手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息 //            channel.basicReject(deliveryTag, true);//第二个参数,true会重新放回队列,所以需要自己根据业务逻辑判断什么时候使用拒绝         } catch (Exception e) {             channel.basicReject(deliveryTag, false);             e.printStackTrace();         }     }      } 这时,先调用接口/sendDirectMessage, 给直连交换机TestDirectExchange 的队列TestDirectQueue 推送一条消息,可以看到监听器正常消费了下来: 到这里,我们其实已经掌握了怎么去使用消息消费的手动确认了。 但是这个场景往往不够! 因为很多伙伴之前给我评论反应,他们需要这个消费者项目里面,监听的好几个队列都想变成手动确认模式,而且处理的消息业务逻辑不一样。 没有问题,接下来看代码 场景: 除了直连交换机的队列TestDirectQueue需要变成手动确认以外,我们还需要将一个其他的队列 或者多个队列也变成手动确认,而且不同队列实现不同的业务处理。 那么我们需要做的第一步,往SimpleMessageListenerContainer里添加多个队列: 然后我们的手动确认消息监听类,MyAckReceiver.java 就可以同时将上面设置到的队列的消息都消费下来。 但是我们需要做不用的业务逻辑处理,那么只需要  根据消息来自的队列名进行区分处理即可,如:  import com.rabbitmq.client.Channel; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener; import org.springframework.stereotype.Component; import java.io.ByteArrayInputStream; import java.io.ObjectInputStream; import java.util.Map;     @Component public class MyAckReceiver implements ChannelAwareMessageListener {       @Override     public void onMessage(Message message, Channel channel) throws Exception {         long deliveryTag = message.getMessageProperties().getDeliveryTag();         try {             byte[] body = message.getBody();             ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(body));             Map<String,String> msgMap = (Map<String,String>) ois.readObject();             String messageId = msgMap.get("messageId");             String messageData = msgMap.get("messageData");             String createTime = msgMap.get("createTime");             ois.close();               if ("TestDirectQueue".equals(message.getMessageProperties().getConsumerQueue())){                 System.out.println("消费的消息来自的队列名为:"+message.getMessageProperties().getConsumerQueue());                 System.out.println("消息成功消费到  messageId:"+messageId+"  messageData:"+messageData+"  createTime:"+createTime);                 System.out.println("执行TestDirectQueue中的消息的业务处理流程......");                              }               if ("fanout.A".equals(message.getMessageProperties().getConsumerQueue())){                 System.out.println("消费的消息来自的队列名为:"+message.getMessageProperties().getConsumerQueue());                 System.out.println("消息成功消费到  messageId:"+messageId+"  messageData:"+messageData+"  createTime:"+createTime);                 System.out.println("执行fanout.A中的消息的业务处理流程......");               }                          channel.basicAck(deliveryTag, true); //            channel.basicReject(deliveryTag, true);//为true会重新放回队列         } catch (Exception e) {             channel.basicReject(deliveryTag, false);             e.printStackTrace();         }     } } ok,这时候我们来分别往不同队列推送消息,看看效果: 调用接口/sendDirectMessage  和 /sendFanoutMessage , 如果你还想新增其他的监听队列,也就是按照这种方式新增配置即可(或者完全可以分开多个消费者项目去监听处理)。  好,这篇Springboot整合rabbitMq教程就暂且到此。 ————————————————                       原文链接:https://blog.csdn.net/qq_35387940/article/details/100514134 
  • [技术干货] Spring boot之@EnableAutoConfiguration
    一、@EnableAutoConfiguration的作用 简单点说就是Spring Boot根据依赖中的jar包,自动选择实例化某些配置,配置类必须有@Configuration注解。 说白了,还是实例化对象,只是实例化的是依赖包中的类。 另外,我们也可以按照自动装配的规范自己定义装配的类。 二、@EnableAutoConfiguration和 @Configuration 的区别 @Configuration:表示作用的类是个配置类。我们平时也会写配置类,比如我们系统中的DataSourceConfig类,但是由于这个类是在Starter对应的子目录下,会自动加载,所以@EnableAutoConfiguration就作用不到了。 @EnableAutoConfiguration:是一个加载Starter目录包之外的需要Spring自动生成bean对象(是否需要的依据是"META-INF/spring.factories"中org.springframework.boot.autoconfigure.EnableAutoConfiguration后面是有能找到那个bean)的带有@Configuration注解的类。一般就是对各种引入的spring-boot-starter依赖包指定的(spring.factories)类进行实例化。 整体流程: 1、我们启动类中会加上@SpringBootApplication 2、@SpringBootApplication包含了@EnableAutoConfiguration,告知应用启动的时候需要扫描依赖包中需要实例化的类 3、Springboot启动的时候会去扫描META-INF/spring.factories,查看具体是哪些类需要实例化 4、扫描哪些需要实例化的类,看下是否有@Configuration注解,如果有,则实例化 5、实例化的时候可能需要一些配置属性,一般这些类都会加上@EnableConfigurationProperties(RocketMQProperties.class) 6、RocketMQProperties会将属性映射为bean类 三、@EnableAutoConfiguration案例 1、Kafka自动装配 我们会发现,当我们在application.properties中配置spring.kafka.xxx,就可以在程序中直接使用了,说明Kafka已经被实例化了,问题来了,是什么时候实例化的?其实就是@EnableAutoConfiguration注解自动实例化的。实现类:KafkaAutoConfiguration 2、JMX自动装配 当我们引入Spring Boot Actuator相关包之后就能自动采集到Spring boot应用的相关运行指标了,怎么做到的? 其实这些指标都是通过JMX标准采集的,而Spring boot的自动装配也包含了JMX,即SpringApplicationAdminJmxAutoConfiguration。 四、spring-boot-starter与@EnableAutoConfiguration的关系 spring-boot-starter:自动导入组件对应的maven包,目的主要是简单开发人员手动去配置maven各种依赖包,不仅麻烦,碰到版本匹配问题还很容易出错。 @EnableAutoConfiguration:需要依赖已经加载好的包进行bean实例化。但不一定需要依赖spring-boot-starter,自动手动把包加载也可以 五、关于实例化 在Jfinal中我们需要通过Plugin的规范去生成对象到上下文中。 而在Spring boot中,我们可以通过@Configuration以及@Service,@Component等注解生成对象到IOC容器中。  未必所有实例化都是通过@EnableAutoConfiguration,@Configuration以及@Service,@Component等注解,也可以通过其他方式(说白了怎么加载类的问题,因为实例化最终就是找到类,然后通过反射生成对象而已)。  其他实例化方式其实可以从META-INF/spring.factories中看出来,里面不仅有org.springframework.boot.autoconfigure.EnableAutoConfiguration,还有org.springframework.context.ApplicationContextInitializer(初始化的时候实例化),org.springframework.context.ApplicationListener(初始化监听的时候实例化),org.springframework.boot.diagnostics.FailureAnalyzer(应用启动失败分析类)等等。  但是对于这些实例化方式有个共同点,都需要在META-INF/spring.factories中配置。  六、默认自动装配的类 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\ org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\ org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\ org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\ org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\ org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration ———————————————— 原文链接:https://blog.csdn.net/w2009211777/article/details/122609547 
  • [技术干货] SpringBoot集成RabbitMQ
    spring boot版本  <parent>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-parent</artifactId>     <version>2.2.2.RELEASE</version>     <relativePath/> <!-- lookup parent from repository --> </parent> 首先创建一个生产者rabbitmq-provider,yml配置如下:  server:   port: 8021   servlet:     application-display-name: rabbitmq-provider     context-path: /rabbitmq-provider     session:       timeout: 30m spring:   application:     name: ${server.servlet.application-display-name}   rabbitmq:     sys-name: ${server.servlet.application-display-name}     host: 127.0.0.1     port: 5672     username: guest     password: guest     virtual-host: /     publisher-confirms: true #支持发布确认     publisher-returns: true  #支持发布返回     listener:       simple:         acknowledge-mode: manual #采用手动应答         retry:           enabled: true #是否支持重试           initial-interval: 1000 # 第一次和第二次 尝试发布或交付(此处是消费者交付,template.retry中是消费者发布)的 间隔时间           max-interval: 10000   # 两次重试的最大间隔           max-attempts: 1  # 尝试发布或交付的 最大次数(重试次数)           multiplier: 1.0  # 每次重试都比上一次重试间隔时长大x倍           stateless: true  # 重试是无状态还是有状态的 pom.xml:  <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-web</artifactId> </dependency> java版本:  ```java <properties>     <java.version>1.8</java.version> </properties> 创建一个消费者 rabbitmq-consumer: yml文件  server:   port: 8022   servlet:     application-display-name: rabbitmq-consumer     context-path: /rabbitmq-consumer     session:       timeout: 30m spring:   application:     name: ${server.servlet.application-display-name}   rabbitmq:     sys-name: ${server.servlet.application-display-name}     host: 127.0.0.1     port: 5672     username: guest     password: guest     virtual-host: /     publisher-confirms: true #支持发布确认     publisher-returns: true  #支持发布返回     listener:       simple:         acknowledge-mode: manual #采用手动应答         retry:           enabled: true #是否支持重试           initial-interval: 1000 # 第一次和第二次 尝试发布或交付(此处是消费者交付,template.retry中是消费者发布)的 间隔时间           max-interval: 10000   # 两次重试的最大间隔           max-attempts: 1  # 尝试发布或交付的 最大次数(重试次数)           multiplier: 1.0  # 每次重试都比上一次重试间隔时长大x倍           stateless: true  # 重试是无状态还是有状态的 pom文件与jdk与生产者相同。  Direct Exchange例子: 创建配置文件:  import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  /**  * 方法名:          DirectRabbitConfig  * 方法功能描述:  *  * @param:  * @return:  * @Author:   * @Create Date:   2019/12/19  */ @Configuration public class DirectRabbitConfig {     //队列 起名:TestDirectQueue     @Bean     public Queue TestDirectQueue() {         return new Queue("TestDirectQueue",true);  //true 是否持久     }      //Direct交换机 起名:TestDirectExchange     @Bean     DirectExchange TestDirectExchange() {         return new DirectExchange("TestDirectExchange");     }      //绑定  将队列和交换机绑定, 并设置用于匹配键:TestDirectRouting     @Bean     Binding bindingDirect() {         return BindingBuilder.bind(TestDirectQueue()).to(TestDirectExchange()).with("TestDirectRouting");     }  } 创建发送消息的Rest  import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;  import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; import java.util.UUID;  /**  * 方法名:          SendMessageController  * 方法功能描述:  *  * @param:  * @return:  * @Author: 陈超  * @Create Date:   2019/12/19  */ @RestController public class SendMessageController {      @Autowired     RabbitTemplate rabbitTemplate;  //使用RabbitTemplate,这提供了接收/发送等等方法      @GetMapping("/sendDirectMessage")     public String sendDirectMessage() {         String messageId = String.valueOf(UUID.randomUUID());         String messageData = "test message, hello!";         String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));         Map<String, Object> map = new HashMap<>();         map.put("messageId", messageId);         map.put("messageData", messageData);         map.put("createTime", createTime);         //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange         rabbitTemplate.convertAndSend("TestDirectExchange", "TestDirectRouting", map);         return "ok";     } } 启动项目运行,返回OK说明发送消息成功 打开http://localhost:15672/#/queues 队列TestDirectQueue已经加入到消息队列中。 现在使用消费者来消费消息,同样的创建DirectRabbitConfig类,与生产者的相同。 创建接收消息的类:  import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component;  import java.util.Map;  /**  * 方法名:          DirectReceiver  * 方法功能描述:  *  * @param:  * @return:  * @Author: cc  * @Create Date:   2019/12/20  */  @Component @RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue public class DirectReceiver {      @RabbitHandler     public void process(Map testMessage) {         System.out.println("DirectReceiver消费者收到消息  : " + testMessage.toString());     }  }  现在已经将生产者与消费者都准备好,启动消费者rabbitmq-consumer,RabbitListener监听器会监听名称为“TestDirectQueue”的队列,将消息接收打印。 同时,生产者发送消息,消费者也会显示发送的消息。  Topic Exchange举例: 首先回顾,topic是按照通配符的方式来进行匹配的。 在生产者中加入配置文件TopicRabbitConfig:  import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.TopicExchange; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  /**  * 方法名:          TopicRabbitConfig  * 方法功能描述:  *  * @param:  * @return:  * @Author: 陈超  * @Create Date:   2019/12/20  */ @Configuration public class TopicRabbitConfig {      //绑定键     public static final String red = "topic.red";     public static final String green = "topic.green";      @Bean     public Queue firstQueue() {         return new Queue(TopicRabbitConfig.red);     }      @Bean     public Queue secondQueue() {         return new Queue(TopicRabbitConfig.green);     }      @Bean     TopicExchange exchange() {         return new TopicExchange("topicExchange");     }      //将firstQueue和topicExchange绑定,而且绑定的键值为topic.red     //这样只要是消息携带的路由键是topic.red,才会分发到该队列     @Bean     Binding bindingExchangeMessage() {         return BindingBuilder.bind(firstQueue()).to(exchange()).with(red);     }      //将secondQueue和topicExchange绑定,而且绑定的键值为用上通配路由键规则topic.#     // 这样只要是消息携带的路由键是以topic.开头,都会分发到该队列     @Bean     Binding bindingExchangeMessage2() {         return BindingBuilder.bind(secondQueue()).to(exchange()).with("topic.#");     }  }  也就是secondQueue绑定了"topic.green",而secondQueue绑定了bindingExchangeMessage2。那么green就可以按照topic的规则进行适配。 创建发送机:  @GetMapping("/sendTopicMessageRed") public String sendTopicMessageRed() {     String messageId = String.valueOf(UUID.randomUUID());     String messageData = "红灯消息 RED";     String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));     Map<String, Object> redMap = new HashMap<>();     redMap.put("messageId", messageId);     redMap.put("messageData", messageData);     redMap.put("createTime", createTime);     rabbitTemplate.convertAndSend("topicExchange", "topic.red", redMap);     return "ok"; }  @GetMapping("/sendTopicMessageGreen") public String sendTopicMessageGreen() {     String messageId = String.valueOf(UUID.randomUUID());     String messageData = "绿灯与红灯消息 ";     String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));     Map<String, Object> greenMap = new HashMap<>();     greenMap.put("messageId", messageId);     greenMap.put("messageData", messageData);     greenMap.put("createTime", createTime);     rabbitTemplate.convertAndSend("topicExchange", "topic.green", greenMap);     return "ok"; } 接下来在消费者中创建同样的配置类TopicRabbitConfig。创建接收机 @Component @RabbitListener(queues = "topic.red") public class TopicRedReceiver {      @RabbitHandler     public void process(Map testMessage) {         System.out.println("TopicRedReceiver  : " + testMessage.toString());     } }  @Component @RabbitListener(queues = "topic.green") public class TopicGreenReceiver {      @RabbitHandler     public void process(Map testMessage) {         System.out.println("TopicGreenReceiver  : " + testMessage.toString());     } } 接下来发送消息: 生产者发送红灯消息,调用 消费者接收到 可以看出绿灯的接收者也接受到红灯消息,这是因为我们在TopicRabbitConfig 配置中的 //将secondQueue和topicExchange绑定,而且绑定的键值为用上通配路由键规则topic.#     // 这样只要是消息携带的路由键是以topic.开头,都会分发到该队列     @Bean     Binding bindingExchangeMessage2() {         return BindingBuilder.bind(secondQueue()).to(exchange()).with("topic.#");     } 发挥了作用。 现在发送绿灯消息  只接收到了绿灯消息,因为红灯的接收机没有进行适配。 Fanout Exchang 适配器 创建配置文件:  import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  /**  * 方法名:          FanoutRabbitConfig  * 方法功能描述:  *  * @param:  * @return:  * @Author:   * @Create Date:   2019/12/20  */  @Configuration public class FanoutRabbitConfig {      /**      *  创建三个队列 :fanout.A   fanout.B  fanout.C      *  将三个队列都绑定在交换机 fanoutExchange 上      *  因为是扇型交换机, 路由键无需配置,配置也不起作用      */     @Bean     public Queue queueA() {         return new Queue("fanout.A");     }      @Bean     public Queue queueB() {         return new Queue("fanout.B");     }      @Bean     public Queue queueC() {         return new Queue("fanout.C");     }      @Bean     FanoutExchange fanoutExchange() {         return new FanoutExchange("fanoutExchange");     }      @Bean     Binding bindingExchangeA() {         return BindingBuilder.bind(queueA()).to(fanoutExchange());     }      @Bean     Binding bindingExchangeB() {         return BindingBuilder.bind(queueB()).to(fanoutExchange());     }      @Bean     Binding bindingExchangeC() {         return BindingBuilder.bind(queueC()).to(fanoutExchange());     } } 在生产者中创建发送方法: @GetMapping("/sendFanoutMessage") public String sendFanoutMessage() {     String messageId = String.valueOf(UUID.randomUUID());     String messageData = "message: testFanoutMessage ";     String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));     Map<String, Object> map = new HashMap<>();     map.put("messageId", messageId);     map.put("messageData", messageData);     map.put("createTime", createTime);     rabbitTemplate.convertAndSend("fanoutExchange", null, map);     return "ok"; } 交换机指向了创建的交换机fanoutExchange。 接下来是消费者 同样创建配置文件FanoutRabbitConfig ,与生产者的相同 创建接受类  @Component @RabbitListener(queues = "fanout.A") public class FanoutReceiverA {      @RabbitHandler     public void process(Map testMessage) {         System.out.println("FanoutReceiverA消费者收到消息  : " +testMessage.toString());     } } @Component @RabbitListener(queues = "fanout.B") public class FanoutReceiverB {      @RabbitHandler     public void process(Map testMessage) {         System.out.println("FanoutReceiverB消费者收到消息  : " +testMessage.toString());     }  } @Component @RabbitListener(queues = "fanout.C") public class FanoutReceiverC {      @RabbitHandler     public void process(Map testMessage) {         System.out.println("FanoutReceiverC消费者收到消息  : " +testMessage.toString());     }  } 创建好之后调用生产者中的发送消息的方法sendFanoutMessage。 三个方法都接受到,说明扇形交换器成功 RabbirMQ消息的回调与消息的确认 在application.yml添加配置项  rabbitmq:   sys-name: ${server.servlet.application-display-name}   host: 127.0.0.1   port: 5672   username: guest   password: guest   virtual-host: /   publisher-confirm-type: correlated #确认消息已发送到交换机   publisher-returns: true  #确认消息已发送到队列(Queue) 在生产者中添加RabbitConfig配置  import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.connection.CorrelationData; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  /**  * 方法名:          RabbitConfig  * 方法功能描述:  *  * @param:  * @return:  * @Author:   * @Create Date:   2019/12/23  */  @Configuration public class RabbitConfig {      @Bean     public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory) {         RabbitTemplate rabbitTemplate = new RabbitTemplate();         rabbitTemplate.setConnectionFactory(connectionFactory);         //设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数         rabbitTemplate.setMandatory(Boolean.TRUE);          rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {             @Override             public void confirm(CorrelationData correlationData, boolean b, String s) {                 System.out.println("ConfirmCallback:     " + "相关数据:" + correlationData);                 System.out.println("ConfirmCallback:     " + "确认情况:" + b);                 System.out.println("ConfirmCallback:     " + "原因:" + s);             }         });          rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {             @Override             public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {                 System.out.println("ReturnCallback:     " + "消息:" + message);                 System.out.println("ReturnCallback:     " + "回应码:" + replyCode);                 System.out.println("ReturnCallback:     " + "回应信息:" + replyText);                 System.out.println("ReturnCallback:     " + "交换机:" + exchange);                 System.out.println("ReturnCallback:     " + "路由键:" + routingKey);             }         });         return rabbitTemplate;     } } 到这里,生产者推送消息的消息确认调用回调函数已经完毕。 可以看到上面写了两个回调函数,一个叫 ConfirmCallback ,一个叫 RetrunCallback; 那么以上这两种回调函数都是在什么情况会触发呢? 先从总体的情况分析,推送消息存在四种情况: ①消息推送到server,但是在server里找不到交换机 ②消息推送到server,找到交换机了,但是没找到队列 ③消息推送到sever,交换机和队列啥都没找到 ④消息推送成功  测试①消息推送到server,但是在server里找不到交换机这种情况: 写个测试接口,把消息推送到名为‘non-existent-exchange’的交换机上(这个交换机是没有创建没有配置的)  @GetMapping("/TestMessageAck") public String TestMessageAck() {     String messageId = String.valueOf(UUID.randomUUID());     String messageData = "message: non-existent-exchange test message ";     String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));     Map<String, Object> map = new HashMap<>();     map.put("messageId", messageId);     map.put("messageData", messageData);     map.put("createTime", createTime);     rabbitTemplate.convertAndSend("non-existent-exchange", "TestDirectRouting", map);     return "ok"; }  调用接口: 返回消息 报错未找到该交换机,ConfirmCallback发挥作用。  测试②消息推送到server,找到交换机了,但是没找到队列 这种情况 这种情况就是需要新增一个交换机,但是不给这个交换机绑定队列,我来简单地在DirectRabitConfig里面新增一个直连交换机,名叫‘lonelyDirectExchange’,但没给它做任何绑定配置操作:  @Bean DirectExchange lonelyDirectExchange() {     return new DirectExchange("lonelyDirectExchange"); } 然后写个测试接口,把消息推送到名为‘lonelyDirectExchange’的交换机上(这个交换机是没有任何队列配置的):  @GetMapping("/TestMessageAckLoneLy") public String TestMessageAckLoneLy() {     String messageId = String.valueOf(UUID.randomUUID());     String messageData = "message: lonelyDirectExchange test message ";     String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));     Map<String, Object> map = new HashMap<>();     map.put("messageId", messageId);     map.put("messageData", messageData);     map.put("createTime", createTime);     rabbitTemplate.convertAndSend("lonelyDirectExchange", "TestDirectRouting", map);     return "ok"; } 调用接口: 发挥了作用。这种情况下,消息是推送成功到服务器了的,所以ConfirmCallback对消息确认情况是true; 而在RetrunCallback回调函数的打印参数里面可以看到,消息是推送到了交换机成功了,但是在路由分发给队列的时候,找不到队列,所以报了错误 NO_ROUTE 。 结论:②这种情况触发的是 ConfirmCallback和RetrunCallback两个回调函数。 测试③消息推送到sever,交换机和队列啥都没找到 这种情况其实一看就觉得跟①很像,没错 ,③和①情况回调是一致的,所以不做结果说明了。 结论: ③这种情况触发的是 ConfirmCallback 回调函数。 测试④消息推送成功 结论: ④这种情况触发的是 ConfirmCallback 回调函数。 以上是生产者推送消息的消息确认 回调函数的使用介绍(可以在回调函数根据需求做对应的扩展或者业务数据处理)。 接下来我们测试消费者接到消息的消息确认机制 和生产者的消息确认机制不同,因为消息接收本来就是在监听消息,符合条件的消息就会消费下来。 所以,消息接收的确认机制主要存在三种模式:  ①自动确认, 这也是默认的消息确认情况。 AcknowledgeMode.NONE RabbitMQ成功将消息发出(即将消息成功写入TCP Socket)中立即认为本次投递已经被正确处理,不管消费者端是否成功处理本次投递。 所以这种情况如果消费端消费逻辑抛出异常,也就是消费端没有处理成功这条消息,那么就相当于丢失了消息。 一般这种情况我们都是使用try catch捕捉异常后,打印日志用于追踪数据,这样找出对应数据再做后续处理。  ② 不确认, 这个不做介绍 ③ 手动确认 , 这个比较关键,也是我们配置接收消息确认机制时,多数选择的模式。 消费者收到消息后,手动调用basic.ack/basic.nack/basic.reject后,RabbitMQ收到这些消息后,才认为本次投递成功。 basic.ack用于肯定确认 basic.nack用于否定确认(注意:这是AMQP 0-9-1的RabbitMQ扩展) basic.reject用于否定确认,但与basic.nack相比有一个限制:一次只能拒绝单条消息 消费者端以上的3个方法都表示消息已经被正确投递,但是basic.ack表示消息已经被正确处理,但是basic.nack,basic.reject表示没有被正确处理,但是RabbitMQ中仍然需要删除这条消息。  之前介绍用了很多个交换,现在我们就先给直连型交换机添加消息接收确认机制, 新建MessageListenerConfig.java上添加代码相关的配置代码(可以看到注释掉的代码,就是给扇型交换机配置消息确认,只用在这个里面继续添加对应的队列和对应的接收类即可,当然对应的接收类也需要跟后面介绍一样加上对应方法):  import com.cc.mq.client.DirectReceiver; import org.springframework.amqp.core.AcknowledgeMode; import org.springframework.amqp.core.MessageListener; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  /**  * 方法名:          MessageListenerConfig  * 方法功能描述:  *  * @param:  * @return:  * @Author:   * @Create Date:   2019/12/23  */ @Configuration public class MessageListenerConfig {      @Autowired     private CachingConnectionFactory connectionFactory;     @Autowired     private DirectReceiver directReceiver;//Direct消息接收处理类     @Autowired     DirectRabbitConfig directRabbitConfig;     @Bean     public SimpleMessageListenerContainer simpleMessageListenerContainer() {         SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);         container.setConcurrentConsumers(1);         container.setMaxConcurrentConsumers(1);         container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认消息         container.setQueues(directRabbitConfig.TestDirectQueue());         container.setMessageListener(directReceiver);         return container;     }  } 这里的DirectReceiver 是我们之前创建的接受类。 然后在直连型交换机的消息接收处理类上需要添加相关的消息手动确认代码DirectReceiver.java: @Component @RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue public class DirectReceiver implements ChannelAwareMessageListener {      @RabbitHandler     public void process(Map testMessage) {         System.out.println("DirectReceiver消费者收到消息  : " + testMessage.toString());     }      @Override     public void onMessage(Message message, Channel channel) throws Exception {         long deliveryTag = message.getMessageProperties().getDeliveryTag();         try {             //因为传递消息的时候用的map传递,所以将Map从Message内取出需要做些处理             String msg = message.toString();             String[] msgArray = msg.split("'");//可以点进Message里面看源码,单引号直接的数据就是我们的map消息数据             Map<String, String> msgMap = this.mapStringToMap(msgArray[1].trim());             String messageId=msgMap.get("messageId");             String messageData=msgMap.get("messageData");             String createTime=msgMap.get("createTime");             System.out.println("messageId:"+messageId+"  messageData:"+messageData+"  createTime:"+createTime);             channel.basicAck(deliveryTag, true); //       channel.basicReject(deliveryTag, true);//为true会重新放回队列         } catch (Exception e) {             channel.basicReject(deliveryTag, false);             e.printStackTrace();         }      }          //这种解析方式是有问题的,在生产的时候不要这样做     private Map<String, String> mapStringToMap(String str) {         str = str.substring(1, str.length() - 1);         String[] strs = str.split(",");         Map<String, String> map = new HashMap<String, String>();         for (String string : strs) {             String key = string.split("=")[0].trim();             String value = string.split("=")[1];             map.put(key, value);         }         return map;     }  } 然后现在将rabbitmq-provider 、rabbitmq-consumer两个项目跑起来,调用下/sendDirectMessage接口往直连型交换机推送一条消息,看看监听到的消息结果: 手动的确认模式的投递效率略低于自动,但是可以弥补自动确认模式的不足,更加准确地去记录消息消费情况。 那么如果需要有些消息接收类设置自动确认,有些消息接收类设置手动确认的话,那只需要将需要设置手动确认的相关队列加到之前的MessageListenerConfig的SimpleMessageListenerContainer里面就行。 ———————————————— 原文链接:https://blog.csdn.net/qq_24848931/article/details/104468398 
  • [互动交流] CodeArts IDE for java怎么默认maven配置
    请问默认设置maven的路径,现在每次都要去配置,有点麻烦。并且新的版本如何去运行springboot项目
  • [技术干货] 探索Java中最常用的框架:Spring、Spring MVC、Spring Boot、MyBatis和Netty -转载
     Spring MVC框架  Spring MVC是Spring框架的一部分,用于构建Web应用程序。它提供了一种MVC(Model-View-Controller)的架构,使得Web应用的开发更有组织和易于维护。  模型(Model):模型代表应用程序的数据和业务逻辑。  视图(View):视图负责显示模型的数据。  控制器(Controller):控制器接受来自用户的请求,处理它们并选择适当的视图来响应请求。   Spring MVC的优点:  1.松耦合:Spring MVC使用了松耦合的设计,允许将控制器、模型和视图分开开发,从而提高了代码的可维护性。 2.高度可扩展:Spring MVC支持自定义视图解析器、拦截器等,使得定制化开发变得容易。 3.强大的数据绑定:Spring MVC可以将请求参数绑定到控制器方法的参数,大大减少了开发工作。 4.REST支持:Spring MVC支持构建RESTful Web服务,通过注解和配置来定义REST端点。     Spring Boot框架 Spring Boot是Spring的扩展,旨在简化Spring应用程序的创建和开发。它通过提供默认配置、自动配置和快速开发功能,大大减少了开发者的工作量。  Spring Boot的特点:   1.自动配置:Spring Boot根据项目中使用的库和类自动配置应用程序。如果你需要自定义配置,只需覆盖默认配置即可。 2.嵌入式Web服务器:Spring Boot集成了嵌入式的Web服务器,如Tomcat、Jetty等,无需额外配置。 3.生产就绪特性:Spring Boot内置了用于监控、度量、健康检查的功能,便于生产环境的部署。 4.开箱即用:Spring Boot提供了一系列的“Starter”依赖,可以快速构建特定类型的应用程序,如Web应用、数据访问应用、消息队列等。  MyBatis框架  MyBatis是一种优秀的持久层框架,它简化了数据库访问操作。与其他ORM框架不同,MyBatis使用XML或注解配置SQL语句,提供了更灵活的SQL编写方式。  MyBatis的特点:  1.SQL分离:MyBatis将SQL语句与Java代码分离,提供了更好的可读性和维护性。 2.参数映射:MyBatis可以将Java对象和数据库表之间的字段映射自动处理。 3.高性能:MyBatis执行SQL语句的性能很高,支持懒加载、缓存等特性。 4.灵活性:MyBatis支持自定义类型处理器、插件等扩展功能。  Netty框架 Netty是一个基于事件驱动的网络应用程序框架,用于快速开发高性能的网络服务器和客户端。它支持各种传输协议,如TCP、UDP、HTTP等。  Netty的特点:  1.高性能:Netty的事件驱动架构使得它在高负载情况下表现出色。 2.可扩展性:Netty的组件是可扩展的,可以轻松添加自定义的处理器。 3.多协议支持:Netty支持多种协议,使其适用于各种应用,包括Web服务、实时通信等。 4.成熟的生态系统:Netty有一个活跃的社区,提供了丰富的扩展和文档资源。  适用场景  1.使用Spring来构建企业级应用,特别是那些需要控制反转和面向切面编程的应用。 2.开发Web应用程序时,可以使用Spring MVC来处理Web请求。 3.使用Spring Boot来快速创建独立的Spring应用程序,减少配置工作。 4.需要数据库持久化操作时,可以选择MyBatis作为ORM框架。 5.需要构建高性能的网络应用程序时,可以使用Netty。   结语  Spring、Spring MVC、Spring Boot、MyBatis和Netty是Java开发中最常用的框架之一,每个框架都有自己的优点和适用场景。选择合适的框架取决于你的项目需求和技术栈。熟练掌握这些框架将有助于提高你的Java开发技能,加速项目开发,并提高应用程序的性能和质量。希望本文对你更好地理解这些框架提供了帮助。  关于探索Java中最常用的框架:Spring、Spring MVC、Spring Boot、MyBatis和Netty,懒大王就先分享到这里了,如果你认为这篇文章对你有帮助,请给懒大王点个赞点个关注吧,如果发现什么问题,欢迎评论区留言!!💕💕         ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/weixin_58070962/article/details/135497866 
  • [专题汇总] 新一年的汇总来啦,2024年1月技术干货合集
    本次给大家带来的是CodeArts最新的干货合集25篇,涵盖了,python,java,springboot,kafka,vue,pinia等多个干货,快来看吧。 1.idea一直indexing无法操作的问题解决【转】 https://bbs.huaweicloud.com/forum/thread-0294142071416308014-1-1.html  2.VSCode打开Json文件格式化的简单步骤【转】 https://bbs.huaweicloud.com/forum/thread-0243142071705276018-1-1.html  3.PHP8.3更新内容新特性及支持版本探究【转】 https://bbs.huaweicloud.com/forum/thread-0271142072065293019-1-1.html  4.springboot如何配置多kafka【转】 https://bbs.huaweicloud.com/forum/thread-0243142072165273019-1-1.html  5.java中@NotBlank限制属性不能为空【转】 https://bbs.huaweicloud.com/forum/thread-0297142073070924013-1-1.html  6.正则去除中括号(符号)及里面包含的内容(最新推荐)【转】 https://bbs.huaweicloud.com/forum/thread-0243142071315437017-1-1.html  7.python Dtale库交互式数据探索分析和可视化界面【转】 https://bbs.huaweicloud.com/forum/thread-0255142071244033020-1-1.html  8.python dataprep库简化加速数据科学操作【转】 https://bbs.huaweicloud.com/forum/thread-0294142071061284012-1-1.html  9.python kornia计算机视觉库实现图像变化【转】 https://bbs.huaweicloud.com/forum/thread-0255142070801280018-1-1.html  10.基于Python实现n-gram文本生成的示例代码【转】 https://bbs.huaweicloud.com/forum/thread-0271142070636778018-1-1.html  11.python第三方模块xmltodict库优雅处理xml格式为json【转】 https://bbs.huaweicloud.com/forum/thread-0259142068233301011-1-1.html  12.python Stanza处理NLP任务使用详解(多语言处理工具)【转】 https://bbs.huaweicloud.com/forum/thread-0271142068138833017-1-1.html  13.Python Arrow处理时间数据使用详解(标准库之外另一种选择)【转】 https://bbs.huaweicloud.com/forum/thread-0297142067851262012-1-1.html  14.python list 切片倒着取的实现示例【转】 https://bbs.huaweicloud.com/forum/thread-0243142067708249015-1-1.html  15.Python Fire中两种命令行参数灵活设置方式详解【转】 https://bbs.huaweicloud.com/forum/thread-0271142067433292016-1-1.html  16.pinia初学习 https://bbs.huaweicloud.com/forum/thread-0202141875876618017-1-1.html  17.为什么需要使用$nextTick?【转】 https://bbs.huaweicloud.com/forum/thread-0231141875789608020-1-1.html  18.Vue自动生成组件名【转】 https://bbs.huaweicloud.com/forum/thread-0296141875621746009-1-1.html  19.工程化第一步这个package.json要真的搞明白才行【转】 https://bbs.huaweicloud.com/forum/thread-0267141875153948017-1-1.html  20.关于vite的跨域问题【转】 https://bbs.huaweicloud.com/forum/thread-0231141874969502019-1-1.html  21.前端无感知刷新token & 超时自动退出【转】 https://bbs.huaweicloud.com/forum/thread-02118141874791175015-1-1.html  22.按钮防连点终极解决方案 【转】 https://bbs.huaweicloud.com/forum/thread-0224141874429208008-1-1.html  23.5分钟搞定vue3函数式弹窗【转】 https://bbs.huaweicloud.com/forum/thread-0202141874275399016-1-1.html  24.有了Composition API后,有些场景或许你不需要pinia了【转】 https://bbs.huaweicloud.com/forum/thread-02118141874125748014-1-1.html  25.Vue中的$attrs你真的会用吗?【转】 https://bbs.huaweicloud.com/forum/thread-0296141873968992008-1-1.html 
  • [技术干货] java中@NotBlank限制属性不能为空【转】
    在实体类的对应属性上添加 @NotBlank 注解,可以实现对空置的限制。除了 @NotBlank 外,还有 @NotNull 和 @NotEmpty ,它们的区别如下所示: 1.String name = null; @NotNull: false @NotEmpty:false  @NotBlank:false   2.String name = ""; @NotNull:true @NotEmpty: false @NotBlank: false  3.String name = " "; @NotNull: true @NotEmpty: true @NotBlank: false  4.String name = "Great answer!"; @NotNull: true @NotEmpty:true @NotBlank:true 需要注意的是 @NotNull 一般作用在 Integer 类型上,其还可以配合 @size、@Max、@Min 对字段的数值大小进行控制。而 @NotEmpty 表示不能为空,且长度必须大于 0 ,一般用在集合类或数组上。 @NotBlank 只能作用在 String 类型上,并且调用 trim() 后,长度必须大于 0 。同时,使用 @NotBlank 等注解时,一定要和 @valid 一起使用,不然@NotBlank不起作用,如:、    @PostMapping("/save")     @ApiOperation(value = "新增", notes = "传入supplementaryMaterial")     public ActionResult save(@Valid @RequestBody SupplementaryMaterial supplementaryMaterial) {         return new ActionResult().status(supplementaryMaterialService.save(supplementaryMaterial));     } @NotEmpty、@NotBlank、@NotNull三种注解的区别1. @NotEmpty@NotEmpty 注解用于验证一个字符串、集合或数组是否为空。它检查目标对象是否为 null,并且长度是否为零。换句话说,被 @NotEmpty 注解标注的字段必须至少包含一个非空字符、元素或项。 public class User {     @NotEmpty     private String username;      // Getter and Setter } 在上面的例子中,username 字段被标注为 @NotEmpty,这意味着在验证时,username 必须既不为 null,也不为空字符串。2. @NotBlank@NotBlank 注解也用于验证字符串是否为空,但它比 @NotEmpty 更严格。它会去除字符串两端的空格,并验证处理后的字符串是否为空。因此,@NotBlank 只适用于字符串类型的字段。 public class Product {     @NotBlank     private String productName;      // Getter and Setter } 在上述示例中,productName 字段被标注为 @NotBlank,这要求字段既不为 null,也不为空字符串,且去除两端空格后也不能为空。3. @NotNull相对于前两者,@NotNull 注解更加简单,它只检查目标字段是否为 null。这意味着它适用于所有类型的字段,包括字符串、对象、基本数据类型等。 public class Order {     @NotNull     private Long orderId;      // Getter and Setter } 在上述代码中,orderId 字段被标注为 @NotNull,这表示在处理时,orderId 不能为 null。区别总结@NotEmpty 验证字段不为 null,且长度不为零。适用于字符串、集合和数组。@NotBlank 验证字段不为 null,长度不为零,且去除两端空格后也不为空。仅适用于字符串。@NotNull 验证字段不为 null。适用于所有类型的字段。
  • [技术干货] idea一直indexing无法操作的问题解决【转】
    今天主要分享一下在使用idea 2020.3版本开发maven项目的时候,一直出现有效件index, 有时候是scaning indexing, 有时候是update indexing, indexing的时候,idea基本上就没办法操作了,连跳入到类或方法里都跳不了。不厌其烦。于是开始在网上找解决方法: 得到的90%及以上的结果,都是让我点击: File-invalid cache/Restart, ,简直了,看到这我就想骂人,一看就是天下文章一大抄,应该根本就没有验证过,我试了一点也不管用。这样的作者 和文章真的是误导人。于是我就开始自己找方法,在stackoverflow上,有一个解决方案,说是在设置里,把下面红框千面的勾选去掉,试了,也不管用后来仔细观察了一下,每次它indexing都是在index jdk或者是 maven仓库,然后我就抱着试试看的态度,在设置里直接搜索index,果然又发现。把对应的jdk和maven改为不下载,使用本地索引。完美解决。补充: 其实做了如上的设置后,indexing的情况还是时有发生,只不过频率降低了一些,但是其实还是没有从根本上解决问题。后来无奈体验了各个版本的idea,发现在升级到了2021.3.2以后的版本,该问题再也没有出现过。所以大家如果一直被这个问题困扰,建议升级一下。
  • [技术干货] 顶级Javaer必知的常用类库,你都用过哪些【转】
    1.日志库2.JSON解析库3.单元测试库4.通用库5.HTTP 库6.XML 解析库7.Excel 阅读库8.字节码库9.数据库连接池库10.消息库11.PDF 库12.日期和时间库13.集合库14.电子邮件 API15.HTML 解析库16.密码库17.嵌入式 SQL 数据库库18.JDBC 故障排除库19.序列化库20.网络库优秀且经验丰富的 Java 开发人员的特点之一是对 API 的广泛了解,包括 JDK 和第三方库。如何使用现有的 API 进行开发,而不是为常见的东西编写新的代码。是提升开发效率必选之路。一般来说,我会为日常项目提供有用的库,包括 Log4j 等日志库、Jackson 等 JSON 解析库以及 JUnit 和 Mockito 等单元测试 API。如果您需要在项目中使用它们,则可以在项目的类路径中包含这些库的 JAR 以开始使用它们,也可以使用Maven进行依赖管理。对 Java 程序员有用的开源库下面是收集的一些有用的第三方库,Java 开发人员可以在他们的应用程序中使用它们来完成很多有用的任务。为了使用这些库,Java 开发人员应该熟悉这一点,这就是本文的重点。如果您有一个想法,那么您可以研究该库并使用它。1. 日志库日志库非常常见,因为您在每个项目中都需要它们。它们对于服务器端应用程序来说是最重要的,因为日志只放置在您可以看到应用程序正在发生什么的地方。尽管 JDK 附带了自己的日志库,但仍有更好的替代方案可用,例如 Log4j、SLF4j 和 LogBack。Java 开发人员应该熟悉日志库的优缺点,并知道为什么使用 SLF4j 比普通的 Log4j 更好。2. JSON解析库在当今的 Web 服务和物联网世界中,JSON 已成为将信息从客户端传输到服务器的首选协议。它们已取代 XML,成为以独立于平台的方式传输信息的首选方式。不幸的是,JDK 没有JSON 库。但是,有许多优秀的第三方库可以让您解析和创建 JSON 消息,例如 Jackson 和 Gson。Java Web 开发人员应该至少熟悉这些库中的一个。3. 单元测试库单元测试是将普通开发人员与优秀开发人员区分开来的最重要的事情。程序员经常得到不编写单元测试的借口,但避免单元测试的最常见借口是缺乏流行单元测试库的经验和知识,包括 JUnit、Mockito 和 PowerMock。图片4. 通用库Java 开发人员可以使用一些优秀的通用第三方库,例如 Apache Commons 和 Google Guava。我总是在我的项目中包含这些库,因为它们简化了很多任务。重新发明轮子是没有意义的。我们应该更喜欢使用久经考验的库,而不是时不时地编写我们自己的例程。图片Java 开发人员最好熟悉 Google Guava 和 Apache Commons 库。5. HTTP 库我不喜欢 JDK 的一件事是它们缺乏对 HTTP 的支持。虽然您可以使用包中的类建立 HTTP 连接 http://java.net,但使用开源第三方库(如 Apache HttpClient 和 HttpCore)并不容易或无缝。图片尽管 JDK 9 带来了对 HTTP 2.0 的支持以及对 HTTP 的更好支持,但我强烈建议所有 Java 开发人员熟悉流行的 HTTP 客户端库,包括 HttpClient 和 HttpCore。6. XML 解析库有许多 XML 解析库,包括 Xerces、JAXB、JAXP、Dom4j 和 Xstream。Xerces2 是 Apache Xerces 系列中的下一代高性能、完全兼容的 XML 解析器。这个新版本的 Xerces 引入了 Xerces Native Interface (XNI),这是一个用于构建解析器组件和配置的完整框架,它非常模块化且易于编程。图片Apache Xerces2 解析器是 XNI 的参考实现,但其他解析器组件、配置和解析器可以使用 Xerces Native Interface 编写。Dom4j 是另一个用于 Java 应用程序的灵活 XML 框架。7. Excel 阅读库信不信由你——所有现实世界的应用程序都必须以某种形式与 Microsoft Office 交互。许多应用程序需要提供在 Excel 中导出数据的功能,如果您必须从 Java 应用程序中执行相同操作,则需要 Apache POI API。这是一个非常丰富的库,允许您 从 Java 程序读取和写入 XLS 文件。您可以查看该链接以获取在核心 Java 应用程序中读取 Excel 文件的工作示例。8. 字节码库如果您正在编写生成代码或与字节码交互的框架或库,那么您需要一个字节码库。它们允许您读取和修改应用程序生成的字节码。Java 世界中一些流行的字节码库是 javassist 和 Cglib Nodep。图片Javassist(JAVA 编程助手)使 Java 字节码操作变得非常简单。它是一个用于在 Java 中编辑字节码的类库。ASM 是另一个有用的字节码编辑库。9. 数据库连接池库如果您从 Java 应用程序与数据库进行交互,但不使用数据库连接池库,那么,您会丢失一些东西。推荐程序员摸鱼地址:https://www.yoodb.com/slack-off/home.html由于在运行时创建数据库连接需要时间并且使请求处理速度变慢,因此始终建议使用数据库连接库。一些流行的是 Commons Pool 和 DBCP。在 Web 应用程序中,它的 Web 服务器通常提供这些功能,但在核心 Java 应用程序中,您需要将这些连接池库包含到您的类路径中才能使用数据库连接池。10. 消息库与日志记录和数据库连接类似,消息传递也是许多实际 Java 应用程序的共同特征。Java 提供 JMS 或 Java 消息传递服务,它不是 JDK 的一部分。对于此组件,您需要包含一个单独的 jms.jar图片同样,如果您使用第三方消息传递协议,例如 Tibco RV,那么您需要 tibrv.jar 在应用程序类路径中使用第三方 JAR 。11. PDF 库与 Microsoft Excel 类似,PDF 库是另一种普遍存在的格式。如果您需要在应用程序中支持 PDF 功能,例如 在 PDF 文件中导出数据,您可以使用 iText 和 Apache FOP 库。两者都提供有用的 PDF 相关功能,但 iText 更丰富更好。图片12. 日期和时间库在 Java 8 之前,JDK 的数据和时间库有很多缺陷,因为它们不是线程安全的、不可变的和容易出错的。许多 Java 开发人员依靠 JodaTime 来实现他们的日期和时间要求。从 JDK 8 开始,没有理由使用 Joda,因为您可以在 JDK 8 的新日期和时间 API中获得所有这些功能,但是如果您使用的是较旧的 Java 版本,那么 JodaTime 是一个值得学习的库。图片13. 集合库尽管 JDK 拥有丰富的集合库,但也有一些第三方库提供了更多选项,例如 Apache Commons 集合、Goldman Sachs 集合、Google 集合和 Trove。Trove 库特别有用,因为它为 Java 提供了高速的常规和原始集合。图片FastUtil 是另一个类似的 API。它通过提供特定类型的映射、集合、列表和优先级队列来扩展 Java 集合框架,这些映射、集合、列表和优先级队列具有较小的内存占用、快速访问和插入;它还提供大(64 位)数组、集合和列表,以及用于二进制和文本文件的快速、实用的 I/O 类。14. 电子邮件 APIjavax.mail 和 Apache Commons Email 都提供了用于从 Java 发送电子邮件的 API 。它建立在 JavaMail API 之上,旨在简化它。图片15. HTML 解析库与JSON和XML类似,HMTL 是我们许多人必须处理的另一种常见格式。值得庆幸的是,我们有 JSoup,它极大地简化了在 Java 应用程序中使用 HTML。您可以使用JSoup不仅解析 HTML,还可以创建 HTML 文档图片它提供了一个非常方便的 API 用于提取和操作数据,使用最好的DOM、CSS 和类似 jquery 的方法。JSoup 实现了 WHATWG HTML5 规范并将HTML解析为与现代浏览器相同的 DOM。16.密码库Apache Commons Codec 包包含各种格式的简单编码器和解码器,例如Base64和 Hexadecimal。除了这些广泛使用的编码器和解码器之外,编解码器包还维护了一组语音编码实用程序。图片17. 嵌入式 SQL 数据库库我真的很喜欢像 H2 这样的内存数据库,你可以将它嵌入到你的 Java 应用程序中。它们非常适合测试您的 SQL 脚本和运行需要数据库的单元测试。但是,H2 不是唯一的 DB,您还可以选择 Apache Derby 和 HSQL。图片18. JDBC 故障排除库有一些很好的 JDBC 扩展库可以让调试更容易,比如 P6spy。这是一个库,可以无缝拦截和记录数据库数据,而无需更改应用程序的代码。您可以使用它们来记录 SQL 查询及其时间。例如,如果您在代码中使用PreparedStatment和CallableStatement,这些库可以记录带有参数的准确调用以及执行所需的时间。图片19. 序列化库Google 协议缓冲区是一种以高效且可扩展的格式对结构化数据进行编码的方法。它是Java 序列化的更丰富和更好的替代方案。我强烈建议有经验的 Java 开发人员学习 Google Protobuf。图片20. 网络库一些有用的网络库是 Netty 和 Apache MINA。如果您正在编写需要执行低级网络任务的应用程序,请考虑使用这些库。图片以上就是今天小编分享给大家的一些工作中常用的库,了解并熟练的运用他们,不仅可以大大提高你的开发效率,也可以学习优秀代码的设计,提高自己的编码能力。
  • [技术干货] 高并发场景下的 HttpClient 优化方案,QPS大大提升!【转】
    1 背景我们有个业务,会调用其他部门提供的一个基于http的服务,日调用量在千万级别。使用了httpclient来完成业务。之前因为qps上不去,就看了一下业务代码,并做了一些优化,记录在这里。先对比前后:优化之前,平均执行时间是250ms;优化之后,平均执行时间是80ms,降低了三分之二的消耗,容器不再动不动就报警线程耗尽了,清爽~2 分析项目的原实现比较粗略,就是每次请求时初始化一个httpclient,生成一个httpPost对象,执行,然后从返回结果取出entity,保存成一个字符串,最后显式关闭response和client。我们一点点分析和优化:2.1 httpclient反复创建开销httpclient是一个线程安全的类,没有必要由每个线程在每次使用时创建,全局保留一个即可。2.2 反复创建tcp连接的开销tcp的三次握手与四次挥手两大裹脚布过程,对于高频次的请求来说,消耗实在太大。试想如果每次请求我们需要花费5ms用于协商过程,那么对于qps为100的单系统,1秒钟我们就要花500ms用于握手和挥手。又不是高级领导,我们程序员就不要搞这么大做派了,改成keep alive方式以实现连接复用!2.3 重复缓存entity的开销原本的逻辑里,使用了如下代码:HttpEntityentity=httpResponse.getEntity();String response = EntityUtils.toString(entity);这里我们相当于额外复制了一份content到一个字符串里,而原本的httpResponse仍然保留了一份content,需要被consume掉,在高并发且content非常大的情况下,会消耗大量内存。并且,我们需要显式的关闭连接,ugly。3 实现按上面的分析,我们主要要做三件事:一是单例的client,二是缓存的保活连接,三是更好的处理返回结果。一就不说了,来说说二。提到连接缓存,很容易联想到数据库连接池。httpclient4提供了一个PoolingHttpClientConnectionManager 作为连接池。接下来我们通过以下步骤来优化:3.1 定义一个keep alive strategy关于keep-alive,本文不展开说明,只提一点,是否使用keep-alive要根据业务情况来定,它并不是灵丹妙药。还有一点,keep-alive和time_wait/close_wait之间也有不少故事。在本业务场景里,我们相当于有少数固定客户端,长时间极高频次的访问服务器,启用keep-alive非常合适再多提一嘴,http的keep-alive 和tcp的KEEPALIVE不是一个东西。回到正文,定义一个strategy如下:ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() { @Override public long getKeepAliveDuration(HttpResponse response, HttpContext context) { HeaderElementIterator it = new BasicHeaderElementIterator (response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { HeaderElement he = it.nextElement(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase ("timeout")) { return Long.parseLong(value) * 1000; } } return 60 * 1000;//如果没有约定,则默认定义时长为60s }};3.2 配置一个PoolingHttpClientConnectionManagerPoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();connectionManager.setMaxTotal(500);connectionManager.setDefaultMaxPerRoute(50);//例如默认每路由最高50并发,具体依据业务来定也可以针对每个路由设置并发数。3.3 生成httpclienthttpClient = HttpClients.custom() .setConnectionManager(connectionManager) .setKeepAliveStrategy(kaStrategy) .setDefaultRequestConfig(RequestConfig.custom().setStaleConnectionCheckEnabled(true).build()) .build();注意:使用setStaleConnectionCheckEnabled方法来逐出已被关闭的链接不被推荐。更好的方式是手动启用一个线程,定时运行closeExpiredConnections 和closeIdleConnections方法,如下所示。public static class IdleConnectionMonitorThread extends Thread { private final HttpClientConnectionManager connMgr; private volatile boolean shutdown; public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) { super(); this.connMgr = connMgr; } @Override public void run() { try { while (!shutdown) { synchronized (this) { wait(5000); // Close expired connections connMgr.closeExpiredConnections(); // Optionally, close connections // that have been idle longer than 30 sec connMgr.closeIdleConnections(30, TimeUnit.SECONDS); } } } catch (InterruptedException ex) { // terminate } } public void shutdown() { shutdown = true; synchronized (this) { notifyAll(); } } }3.4 使用httpclient执行method时降低开销这里要注意的是,不要关闭connection。推荐程序员摸鱼地址:https://www.yoodb.com/slack-off/home.html一种可行的获取内容的方式类似于,把entity里的东西复制一份:res = EntityUtils.toString(response.getEntity(),"UTF-8");EntityUtils.consume(response1.getEntity());但是,更推荐的方式是定义一个ResponseHandler,方便你我他,不再自己catch异常和关闭流。在此我们可以看一下相关的源码:public <T> T execute(final HttpHost target, final HttpRequest request, final ResponseHandler<? extends T> responseHandler, final HttpContext context) throws IOException, ClientProtocolException { Args.notNull(responseHandler, "Response handler"); final HttpResponse response = execute(target, request, context); final T result; try { result = responseHandler.handleResponse(response); } catch (final Exception t) { final HttpEntity entity = response.getEntity(); try { EntityUtils.consume(entity); } catch (final Exception t2) { // Log this exception. The original exception is more // important and will be thrown to the caller. this.log.warn("Error consuming content after an exception.", t2); } if (t instanceof RuntimeException) { throw (RuntimeException) t; } if (t instanceof IOException) { throw (IOException) t; } throw new UndeclaredThrowableException(t); } // Handling the response was successful. Ensure that the content has // been fully consumed. final HttpEntity entity = response.getEntity(); EntityUtils.consume(entity);//看这里看这里 return result;}可以看到,如果我们使用resultHandler执行execute方法,会最终自动调用consume方法,而这个consume方法如下所示:public static void consume(final HttpEntity entity) throws IOException { if (entity == null) { return; } if (entity.isStreaming()) { final InputStream instream = entity.getContent(); if (instream != null) { instream.close(); } }}可以看到最终它关闭了输入流。4 其他通过以上步骤,基本就完成了一个支持高并发的httpclient的写法,下面是一些额外的配置和提醒:4.1 httpclient的一些超时配置CONNECTION_TIMEOUT是连接超时时间,SO_TIMEOUT是socket超时时间,这两者是不同的。连接超时时间是发起请求前的等待时间;socket超时时间是等待数据的超时时间。HttpParams params = new BasicHttpParams();//设置连接超时时间Integer CONNECTION_TIMEOUT = 2 * 1000; //设置请求超时2秒钟 根据业务调整Integer SO_TIMEOUT = 2 * 1000; //设置等待数据超时时间2秒钟 根据业务调整 //定义了当从ClientConnectionManager中检索ManagedClientConnection实例时使用的毫秒级的超时时间//这个参数期望得到一个java.lang.Long类型的值。如果这个参数没有被设置,默认等于CONNECTION_TIMEOUT,因此一定要设置。Long CONN_MANAGER_TIMEOUT = 500L; //在httpclient4.2.3中我记得它被改成了一个对象导致直接用long会报错,后来又改回来了 params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, CONNECTION_TIMEOUT);params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, SO_TIMEOUT);params.setLongParameter(ClientPNames.CONN_MANAGER_TIMEOUT, CONN_MANAGER_TIMEOUT);//在提交请求之前 测试连接是否可用params.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, true); //另外设置http client的重试次数,默认是3次;当前是禁用掉(如果项目量不到,这个默认即可)httpClient.setHttpRequestRetryHandler(newDefaultHttpRequestRetryHandler(0,false));4.2 如果配置了nginx的话,nginx也要设置面向两端的keep-alive现在的业务里,没有nginx的情况反而比较稀少。nginx默认和client端打开长连接而和server端使用短链接。注意client端的keepalive_timeout和keepalive_requests参数,以及upstream端的keepalive参数设置,这三个参数的意义在此也不再赘述。以上就是我的全部设置。通过这些设置,成功地将原本每次请求250ms的耗时降低到了80左右,效果显著。JAR包如下:<!-- httpclient --><dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.6</version></dependency>代码如下://Basic认证private static final CredentialsProvider credsProvider = new BasicCredentialsProvider();//httpClientprivate static final CloseableHttpClient httpclient;//httpGet方法private static final HttpGet httpget;//private static final RequestConfig reqestConfig;//响应处理器private static final ResponseHandler<String> responseHandler;//jackson解析工具private static final ObjectMapper mapper = new ObjectMapper(); static { System.setProperty("http.maxConnections","50"); System.setProperty("http.keepAlive", "true"); //设置basic校验 credsProvider.setCredentials( new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM), new UsernamePasswordCredentials("", "")); //创建http客户端 httpclient = HttpClients.custom() .useSystemProperties() .setRetryHandler(new DefaultHttpRequestRetryHandler(3,true)) .setDefaultCredentialsProvider(credsProvider) .build(); //初始化httpGet httpget = new HttpGet(); //初始化HTTP请求配置 reqestConfig = RequestConfig.custom() .setContentCompressionEnabled(true) .setSocketTimeout(100) .setAuthenticationEnabled(true) .setConnectionRequestTimeout(100) .setConnectTimeout(100).build(); httpget.setConfig(reqestConfig); //初始化response解析器 responseHandler = new BasicResponseHandler();}/* * 功能:返回响应 * @author zhangdaquan * @param [url] * @return org.apache.http.client.methods.CloseableHttpResponse * @exception */public static String getResponse(String url) throws IOException { HttpGet get = new HttpGet(url); String response = httpclient.execute(get,responseHandler); return response;} /* * 功能:发送http请求,并用net.sf.json工具解析 * @author zhangdaquan * @param [url] * @return org.json.JSONObject * @exception */public static JSONObject getUrl(String url) throws Exception{ try { httpget.setURI(URI.create(url)); String response = httpclient.execute(httpget,responseHandler); JSONObject json = JSONObject.fromObject(response); return json; } catch (IOException e) { e.printStackTrace(); } return null;} /* * 功能:发送http请求,并用jackson工具解析 * @author zhangdaquan * @param [url] * @return com.fasterxml.jackson.databind.JsonNode * @exception */public static JsonNode getUrl2(String url){ try { httpget.setURI(URI.create(url)); String response = httpclient.execute(httpget,responseHandler); JsonNode node = mapper.readTree(response); return node; } catch (IOException e) { e.printStackTrace(); } return null;}/* * 功能:发送http请求,并用fastjson工具解析 * @author zhangdaquan * @param [url] * @return com.fasterxml.jackson.databind.JsonNode * @exception */public static com.alibaba.fastjson.JSONObject getUrl3(String url){ try { httpget.setURI(URI.create(url)); String response = httpclient.execute(httpget,responseHandler); com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(response); return jsonObject; } catch (IOException e) { e.printStackTrace(); } return null;}
  • [技术干货] MVC小知识
    (1) 什么是MVC?       MVC是一种设计思想,根据职责不同将程序中的组件分成以下3个部分。       V(View视图):负责与用户交互。将数据展现,或者是接收数据       M(Model模型):负责业务处理。业务模型,数据模型       C(Controller控制器):负责协同模型和视图工作。视图有请求调用模型处理,模型处理完毕调用视图响应。(2)为什么使用MVC?      MVC是一个非常优秀的设计思想,基于该思想架构程序,可以提高程序的结构灵活性,便于日后维护、扩展和升级。注意:下面内容助于理解:1)  一个模型可以被多个视图共享模型只负责输出数据,不关心数据的表现形式,同一仹数据,可以使用多个不同的视图展现给用户。模型只负责处理数据,不关心是谁在调用,可以使用多种不同的界面来调用模型。2)  方便测试    模型一般使用java 类来开发,在开发完成之后,可以立即测试。如果业务逻辑直接写在servlet里面,则需要部署在服务器上面才能测试,比较麻烦。3)  组件复用    控制器可以做成一个通用的模块。4)  代码好维护,利于分工协作。    按照 mvc 的思想,可以对程序迚行分层,一般划分成表示层(包括 v,c)、业务层(m中的业务逻辑部分)、持久层(m中的数据访问逻辑部分)。下一层的代码发生改变,只要接口不变,不会影响到上一层的代码。mvc的缺点1)  采用 mvc 以后,会增加代码量,相应的开发周期以及开发的成本会相应增加。2)  使用 mvc,需要良好的设计。如果设计不当,会增加开发的难度。在表示层Servlet中调用业务层代码的接口,当业务层发生改变时不影响Servelt ;在业务层Service中调用DAO的接口,DAO发生改变不影响Service和其上层 结论一般来说,如果一个程序需要良好的架构,需要良好的代码的可维护性及可扩展性,需要使用mvc思想来架构。反之,则不必使用。
  • [技术干货] AJAX开发小知识
    AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。AJAX 不是新的编程语言,而是一种使用现有标准的新方法。AJAX 是与服务器交换数据并更新部分网页的艺术,在不重新加载整个页面的情况下。AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。AJAX编程步骤?1) 获得 XmlHttpRequest对象2) 使用 XmlHttpRequest向服务器发请求。                  a.发送get请求:                  /* open(请求方式,请求地址,同步/异步)  * 请求方式: get/post   * 请求地址:如果是get请求,请求参数添加到地址之后。                 * 比如  check_user.do?username=zs                 * 同步/异步:true 表示异步。*/xhr.open('get','check_user.do',true);                  b. 发送 post 请求:xhr.open('post','check_username.do',true); //#必须添加一个消息头content-type     xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");3). 在服务器端,处理请求。4).在监听器当中,处理服务器返回的响应。xhr.onreadystatechange=function(){  //编写相应的处理代码  if(xhr.readyState == 4){    //只有 readyState 等亍 4,xhr 才完整地接收到了服务器返回的数据。    //获得文本数据    var txt = xhr.responseText;    //获得一个xml dom对象。    var xml = xhr.responseXML;    //dom操作、更新页面  }   };5.xhr.send(null)AJAX技术的优点?   1.页面无刷新   2.不打断用户的操作,用户的体验好   3.按需获取数据,浏览器和服务器之间数据的传输量减少   4.是一个标准技术,不需要下载任何的插件   5.可以利用客户端(浏览器)的计算能力
总条数:739 到第
上滑加载中