关于springboot:Spring-Boot导出PDF文件

骄阳似火的六月天,被迫在办公室吹着空调瑟瑟发抖。脑海中不停的考虑,什么才是程序员真正的宿命。 这次咱们要实现一个导出pdf文件的简略需要,基于Spring Boot 2.2.5.RELEASE。 咱们用到的依赖如下 <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13.2</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> <version>5.2.0</version> </dependency>spring mvc中曾经存在了一个AbstractPdfView的抽象类,然而应用到的依赖被网友吐槽太过老旧,所以咱们本人写一个,这个抽象类没什么特地,就是将原有的包都换成itext的包。 package com.jason.cloud.view;import com.itextpdf.text.Document;import com.itextpdf.text.DocumentException;import com.itextpdf.text.PageSize;import com.itextpdf.text.pdf.PdfWriter;import org.springframework.web.servlet.view.AbstractView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.ByteArrayOutputStream;import java.io.OutputStream;import java.util.Map;/** * 重写抽象类,将原有的AbstractPdfView由itext低版本替换为高版本 * create by Jason * Date: 2021/6/2 * Time: 11:52 */public abstract class AbstractTextPdfView extends AbstractView { public AbstractTextPdfView() { this.setContentType("application/pdf"); } protected boolean generatesDownloadContent() { return true; } protected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { ByteArrayOutputStream baos = this.createTemporaryOutputStream(); Document document = this.newDocument(); PdfWriter writer = this.newWriter(document, baos); this.prepareWriter(model, writer, request); this.buildPdfMetadata(model, document, request); this.buildPdfDocument(model, document, writer, request, response); this.writeToResponse(response, baos); } protected Document newDocument() { return new Document(PageSize.A4); } protected PdfWriter newWriter(Document document, OutputStream os) throws DocumentException { return PdfWriter.getInstance(document, os); } protected void prepareWriter(Map<String, Object> model, PdfWriter writer, HttpServletRequest request) throws DocumentException { writer.setViewerPreferences(this.getViewerPreferences()); } protected int getViewerPreferences() { return PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage; } protected void buildPdfMetadata(Map<String, Object> model, Document document, HttpServletRequest request) { } protected abstract void buildPdfDocument(Map<String, Object> var1, Document var2, PdfWriter var3, HttpServletRequest var4, HttpServletResponse var5) throws Exception;}咱们写一个实现了本人AbstractTextPdfView的类PdfView。 ...

June 7, 2021 · 3 min · jiezi

关于springboot:SpringBoot应用ELK日志收集

服务初始化相干的配置服务初始化相干的配置 Docker装置 装置yum-utils(装置docker-ce所需依赖)为yum源增加docker仓库地位装置docker启动dockerElasticsearch装置 应用如下命令启动Elasticsearch服务启动的时候会报错装置中文分词器IKAnalyzer,并重新启动开启防火墙拜访会返回版本信息:http://39.103.203.41:9200/Logstash装置 下载Logstash7.6.2的docker镜像如下配置文件logstash.conf创立/mydata/logstash目录,并将Logstash的配置文件logstash.conf拷贝到该目录应用如下命令启动Logstash服务在logstash中装置json_lines插件Kibana装置 下载Kibana7.6.2的docker镜像应用如下命令启动Kibana服务拜访地址进行测试 http://39.103.203.41:5601/SpringBoot利用集成Logstash增加配置文件logback-spring.xml让logback的日志输入到logstash运行SpringBoot利用在kibana中查看日志信息 创立index pattern调用该接口并查看日志Docker装置(不同的操作系统能够参考 https://yeasy.gitbook.io/dock...)装置yum-utils(装置docker-ce所需依赖) yum install -y yum-utils device-mapper-persistent-data lvm2为yum源增加docker仓库地位yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repoyum.repos.d# 仓库源寄存的地位cd /etc/yum.repos.d vim docker-ce.repo 装置docker⚠️留神 docker-ce与docker两个仓库版本有差别(自己爬坑比拟多 docker不反对文件的挂载比拟老的版本) yum install docker-ce# 如果报错以下信息errors during downloading metadata for repository 'base': - Curl error (28): Timeout was reached for http://mirrors.aliyuncs.com/centos/3/os/x86_64/repodata/repomd.xml [Connection timed out after 30001 milliseconds] - Status code: 404 for http://mirrors.cloud.aliyuncs.com/centos/3/os/x86_64/repodata/repomd.xml (IP: 100.100.2.148) - Status code: 404 for http://mirrors.aliyun.com/centos/3/os/x86_64/repodata/repomd.xml (IP: 39.96.118.193)Error: Failed to download metadata for repo 'base': Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried# 将/etc/yum.repos.d/docker-ce.repo的地址进行批改# 将数据源替换成版本7 不然下载的时候会报错 链接地址不存在.baseurl=https://download.docker.com/linux/centos/7/debug-$basearch/stable启动docker systemctl start dockerElasticsearch装置应用如下命令启动Elasticsearch服务docker run -p 9200:9200 -p 9300:9300 --name elasticsearch \-e "discovery.type=single-node" \-e "cluster.name=elasticsearch" \-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \-d elasticsearch:7.6.2启动的时候会报错OpenJDK 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.{"type": "server", "timestamp": "2021-06-03T11:24:02,468Z", "level": "ERROR", "component": "o.e.b.ElasticsearchUncaughtExceptionHandler", "cluster.name": "elasticsearch", "node.name": "4f5aaf7716c0", "message": "uncaught exception in thread [main]", "stacktrace": ["org.elasticsearch.bootstrap.StartupException: ElasticsearchException[failed to bind service]; nested: AccessDeniedException[/usr/share/elasticsearch/data/nodes];","at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:174) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:161) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:125) ~[elasticsearch-cli-7.6.2.jar:7.6.2]","at org.elasticsearch.cli.Command.main(Command.java:90) ~[elasticsearch-cli-7.6.2.jar:7.6.2]","at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:126) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:92) ~[elasticsearch-7.6.2.jar:7.6.2]","Caused by: org.elasticsearch.ElasticsearchException: failed to bind service","at org.elasticsearch.node.Node.<init>(Node.java:615) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.node.Node.<init>(Node.java:257) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.bootstrap.Bootstrap$5.<init>(Bootstrap.java:221) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:221) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:349) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:170) ~[elasticsearch-7.6.2.jar:7.6.2]","... 6 more","Caused by: java.nio.file.AccessDeniedException: /usr/share/elasticsearch/data/nodes","at sun.nio.fs.UnixException.translateToIOException(UnixException.java:90) ~[?:?]","at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111) ~[?:?]","at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:116) ~[?:?]","at sun.nio.fs.UnixFileSystemProvider.createDirectory(UnixFileSystemProvider.java:389) ~[?:?]","at java.nio.file.Files.createDirectory(Files.java:693) ~[?:?]","at java.nio.file.Files.createAndCheckIsDirectory(Files.java:800) ~[?:?]","at java.nio.file.Files.createDirectories(Files.java:786) ~[?:?]","at org.elasticsearch.env.NodeEnvironment.lambda$new$0(NodeEnvironment.java:274) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.env.NodeEnvironment$NodeLock.<init>(NodeEnvironment.java:211) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.env.NodeEnvironment.<init>(NodeEnvironment.java:271) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.node.Node.<init>(Node.java:277) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.node.Node.<init>(Node.java:257) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.bootstrap.Bootstrap$5.<init>(Bootstrap.java:221) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:221) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:349) ~[elasticsearch-7.6.2.jar:7.6.2]","at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:170) ~[elasticsearch-7.6.2.jar:7.6.2]","... 6 more"] }uncaught exception in thread [main]启动时会发现/usr/share/elasticsearch/data目录没有拜访权限,只须要批改/mydata/elasticsearch/data目录的权限,再重新启动即可chmod 777 /mydata/elasticsearch/data/装置中文分词器IKAnalyzer,并重新启动# 进入docker容器中docker exec -it elasticsearch /bin/bash#此命令须要在容器中运行elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip-> Installing https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip-> Downloading https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip[=================================================] 100%?? @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: plugin requires additional permissions @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* java.net.SocketPermission * connect,resolveSee http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.htmlfor descriptions of what these permissions allow and the associated risks.Continue with installation? [y/N]y-> Installed analysis-ik# 退出去后重启失效docker restart elasticsearch开启防火墙firewall-cmd --zone=public --add-port=9200/tcp --permanentfirewall-cmd --reload拜访会返回版本信息:http://39.103.203.41:9200/{ "name" : "4f5aaf7716c0", "cluster_name" : "elasticsearch", "cluster_uuid" : "PBB0ZytDStO-9SJjAarNyw", "version" : { "number" : "7.6.2", "build_flavor" : "default", "build_type" : "docker", "build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f", "build_date" : "2020-03-26T06:34:37.794943Z", "build_snapshot" : false, "lucene_version" : "8.4.0", "minimum_wire_compatibility_version" : "6.8.0", "minimum_index_compatibility_version" : "6.0.0-beta1" }, "tagline" : "You Know, for Search"}Logstash装置下载Logstash7.6.2的docker镜像docker pull logstash:7.6.2如下配置文件logstash.confinput { tcp { mode => "server" host => "0.0.0.0" port => 4560 codec => json_lines type => "debug" } tcp { mode => "server" host => "0.0.0.0" port => 4561 codec => json_lines type => "error" } tcp { mode => "server" host => "0.0.0.0" port => 4562 codec => json_lines type => "business" } tcp { mode => "server" host => "0.0.0.0" port => 4563 codec => json_lines type => "record" }}filter{ if [type] == "record" { mutate { remove_field => "port" remove_field => "host" remove_field => "@version" } json { source => "message" remove_field => ["message"] } }}output { elasticsearch { hosts => "39.103.203.41:9200" index => "mall-%{type}-%{+YYYY.MM.dd}" }} 创立/mydata/logstash目录,并将Logstash的配置文件logstash.conf拷贝到该目录mkdir /mydata/logstash应用如下命令启动Logstash服务docker run --name logstash -p 4560:4560 -p 4561:4561 -p 4562:4562 -p 4563:4563 \--link elasticsearch:es \-v /mydata/logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf \-d logstash:7.6.2在logstash中装置json_lines插件# 进入logstash容器docker exec -it logstash /bin/bash# 进入bin目录cd /bin/# 装置插件logstash-plugin install logstash-codec-json_linesOpenJDK 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.WARNING: An illegal reflective access operation has occurredWARNING: Illegal reflective access by com.headius.backport9.modules.Modules to method sun.nio.ch.NativeThread.signal(long)WARNING: Please consider reporting this to the maintainers of com.headius.backport9.modules.ModulesWARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operationsWARNING: All illegal access operations will be denied in a future releaseValidating logstash-codec-json_linesInstalling logstash-codec-json_linesInstallation successful# 退出容器exit# 重启logstash服务docker restart logstashKibana装置下载Kibana7.6.2的docker镜像7.6.2: Pulling from library/kibanaab5ef0e58194: Already exists c64d415fc4c4: Pull complete 40a228497f87: Pull complete 047cebeb3d2b: Pull complete a1e90407e522: Pull complete b665bda75e65: Pull complete 12bc27d9cfdc: Pull complete 2611a8427d9d: Pull complete 12efd486dee3: Pull complete d2dfc5062b56: Pull complete Digest: sha256:097e2b7f33f353a8fc19bbf2a6558431c63637113fdc625e6d34fc46f96c0130Status: Downloaded newer image for kibana:7.6.2docker.io/library/kibana:7.6.2应用如下命令启动Kibana服务docker run --name kibana -p 5601:5601 \--link elasticsearch:es \-e "elasticsearch.hosts=http://es:9200" \-d kibana:7.6.2拜访地址进行测试 http://39.103.203.41:5601/ ...

June 7, 2021 · 4 min · jiezi

关于springboot:springboot调用https接口

1.springboot我的项目调用https接口import org.springframework.http.client.SimpleClientHttpRequestFactory;import org.springframework.stereotype.Component;import org.springframework.web.client.RestTemplate;import javax.net.ssl.*;import java.io.IOException;import java.net.HttpURLConnection;import java.net.InetAddress;import java.net.Socket;import java.security.cert.X509Certificate;@Componentpublic class SSLRestTemplate extends RestTemplate { public SSLRestTemplate() { super(new HttpsClientRequestFactory()); }}class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory { @Override protected void prepareConnection(HttpURLConnection connection, String httpMethod) { try { if (!(connection instanceof HttpsURLConnection)) { throw new RuntimeException("An instance of HttpsURLConnection is expected"); } HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory())); httpsConnection.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }); super.prepareConnection(httpsConnection, httpMethod); } catch (Exception e) { e.printStackTrace(); } } /** * We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"}); * see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section) */ private static class MyCustomSSLSocketFactory extends SSLSocketFactory { private final SSLSocketFactory delegate; public MyCustomSSLSocketFactory(SSLSocketFactory delegate) { this.delegate = delegate; } @Override public String[] getDefaultCipherSuites() { return delegate.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return delegate.getSupportedCipherSuites(); } @Override public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException { final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final String host, final int port) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final InetAddress host, final int port) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, final int localPort) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); return overrideProtocol(underlyingSocket); } private Socket overrideProtocol(final Socket socket) { if (!(socket instanceof SSLSocket)) { throw new RuntimeException("An instance of SSLSocket is expected"); } ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1","TLSv1.1","TLSv1.2"}); return socket; } }}2.应用demoSSLRestTemplate sslRestTemplate = new SSLRestTemplate();ResponseEntity<JsonObject> exchange = sslRestTemplate.exchange(url, HttpMethod.POST, requestEntity, JsonObject.class);

June 6, 2021 · 2 min · jiezi

关于springboot:SpringBootApplication源码详细解析

本文章的spring-boot版本为2.5.0-SNAPSHOT 在学习springboot源码前我在网上看了很多@SpringBootApplication解析的文章,在学习了spring源码和局部springboot源码后发现那些文章大多都没讲残缺,甚至有些是错的。所以想写一个略微残缺点的解析文章。 前置常识注解的继承性(或者叫派生性,传递性),怎么叫无所谓。 @C@interface B{}//定义一个B注解,B注解中增加了C注解@Bclass A{}//A类应用了B注解,也相当于同时应用了C注解一个类增加了一个注解,那么同时也相当于增加了该注解内的注解,这个层级无论嵌套多少层都无效。 BeanDefinition接口 spring通过asm技术加载类文件,而后并不是间接创建对象,而是解析这个类的信息,如:所有须要spring解决的注解,Aware接口,FactoryBean接口等信息。这个接口的子类性能十分弱小,能够通过这个BeanDefinition设置一个类的父类,接口,要应用的结构器,须要在初始化过程中调用的办法和传入的参数等。能够说通过BeanDefinition能够齐全的创立一个新的类,这也是第三方组件的重要扩大形式。spring会依据BeanDefinition中蕴含的信息构建一个bean。@Import的作用,这个须要对spring的解析流程有些理解能力晓得具体作用,这里只讲个大略。 @Import(B.class) //应用形式,依据B类的类型抉择不同的解决形式class A{}spring在解析类是如果发现有@Import注解,那么解析注解中的类,个别有三种类型 一个一般类,应用通用逻辑去加载,跟@Compoent一样ImportSelector接口的子类 public interface ImportSelector { //传入被注解类的注解信息元数据,能够从importingClassMetadata中获取被注解类 //的所有注解信息,包含传递的注解 //返回蕴含了一个类的全类名的String[],spring会依据全类名去加载这些类成为bean String[] selectImports(AnnotationMetadata importingClassMetadata);}DeferredImportSelector //DeferredImportSelector继承了ImportSelector然而解决逻辑略有不同public interface DeferredImportSelector extends ImportSelector{ //先应用getImportGroup返回一个Group的实现类,这个Group相当于一个处理器,能够多个类应用同一个Group,调用Group的两个办法返回一个Entry //spring会依据Entry中蕴含的信息进行初始化 //如果getImportGroup返回null,那么还是调用ImportSelector的selectImports办法 @Nullable default Class<? extends Group> getImportGroup() { return null; } //一个外部类 interface Group { void process(AnnotationMetadata metadata, DeferredImportSelector selector); Iterable<Entry> selectImports(); //外部类的外部类 class Entry { private final AnnotationMetadata metadata; private final String importClassName; } }}这个DeferredImportSelector解决的具体逻辑有些简单,有趣味的能够看spring源码 class ConfigurationClassParser { public void parse(Set<BeanDefinitionHolder> configCandidates) { //... //DeferredImportSelector的解决 this.deferredImportSelectorHandler.process(); }}ImportBeanDefinitionRegistrar的子类 ...

June 5, 2021 · 3 min · jiezi

关于springboot:SpringBoot获取所有的url路径

自己在做权限管制时会须要捕捉到所有url,其中会因为须要申请类型,过滤了一些无申请类型的url,话不多说,上码。Configuration@Slf4jpublic class RolePolicyConfig { private final WebApplicationContext context; @Autowired public RolePolicyConfig(WebApplicationContext context) { this.context = context; } @PostConstruct public void methodInit() { // 获取 RequestMappingHandlerMapping mapping = context.getBean(RequestMappingHandlerMapping.class); // 获取url与类和办法的对应信息 Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods(); // 初始化map汇合 List<Map<String, String>> listMap = new ArrayList<>(); for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : map.entrySet()) { Map<String, String> initMap = new HashMap<>(); RequestMappingInfo info = entry.getKey(); // 申请类型 RequestMethodsRequestCondition methodsCondition = info.getMethodsCondition(); // 申请url PatternsRequestCondition pattern = info.getPatternsCondition(); // 如果类型不为空则获取 Set<RequestMethod> methods = methodsCondition.getMethods(); if (!ObjectUtils.isEmpty(pattern) && !CollectionUtils.isEmpty(methods)) { if (!CollectionUtils.isEmpty(pattern.getPatterns())) { log.info("办法名:{}", entry.getValue().getMethod().getName()); Set<String> patterns = pattern.getPatterns(); log.info("获取url:{}", patterns.toString()); for (String url : patterns) { initMap.put("url", url); initMap.put("name", url.replaceAll("/", "_").substring(1)); } for (RequestMethod requestMethod : methods) { initMap.put("type", requestMethod.toString()); } } listMap.add(initMap); } } log.info("获取所有url:{}", JSON.toJSONString(listMap)); }}

June 1, 2021 · 1 min · jiezi

关于springboot:SpringBoot2-集成测试组件七种测试手段对比

一、背景形容在版本开发中,时间段大抵的划分为:需要,开发,测试; 需要阶段:了解需要做好接口设计;开发阶段:实现性能开发和对接;测试上线:自测,提测,修复,上线;实际上开发阶段两个外围的工作,开发和流程自测,自测的基本目标是为本人提前解决可能呈现的问题;如果短少自测和提测两个关键步骤,那么问题就会被传递给更多的用户,产生更多的资源耗费; 自测是于开发而言,提测是对业余的测试人员而言,如果尽可能在自测阶段就发现问题,并解决问题,那么一个问题就不会影响到团队合作上的更多人员,如果一个简略的问题回升到团队合作层面,很可能会导致问题自身被放大。 工欲善其事必先利其器,开发如果要做好自测流程,学会应用工具提高效率是非常要害的,自测的关键在于发现问题和解决问题,所以抉择好用和高效的工具能够极大的升高自测的工夫耗费。 上面围绕几个本人开发过程中罕用的测试工具和伎俩,做简略的总结,不在于比照形式的好坏,存在即正当,在不同场景中对正当伎俩的抉择,疾速解决问题才是基本目标。 二、PostMan工具PostMan很罕用的接口测试工具,开发过程中疾速测试接口,功能强大并且简略不便,岂但能够单个接口测试,也能够对接口分块治理批量运行: 整体来说工具比拟好用,适应于开发阶段的接口疾速测试,或者在解决问题的过程中单个接口的测试,同时对测试参数有存储和记忆能力,这也是受欢迎的一大起因。 然而该工具不适应于简单的流程化测试,例如须要依据上次接口的响应报文做别离解决,或者下次申请须要填充某个接口响应的数据。 三、Swagger文档Swagger治理接口文档,是当下服务中很罕用的组件,通过对接口和对象的简略正文,疾速生成接口形容信息,并且能够对接口发送申请,帮助调试,该文档在前后端联调中极大的提高效率。 接口文档的治理自身是一件麻烦事,接口通常会依据业务一直的调整,如果独自保护一份接口文档,须要付出很多工夫老本,并且容易出问题,利用swagger就能够防止这个问题。 借助swagger注解标记对象 @TableName("jt_activity")@ApiModel(value="流动PO对象", description="流动信息表【jt_activity】")public class Activity { @ApiModelProperty(value = "主键ID") @TableId(type = IdType.AUTO) private Integer id; @ApiModelProperty(value = "流动主题") private String activityTitle; @ApiModelProperty(value = "分割号码") private String contactPhone; @ApiModelProperty(value = "1线上、2线下") private Integer isOnline; @ApiModelProperty(value = "举办地址") private String address; @ApiModelProperty(value = "主办单位") private String organizer; @ApiModelProperty(value = "创立工夫") private Date createTime;}借助swagger注解标记接口 @Api(tags = "活动主体接口")@RestControllerpublic class ActivityWeb { @Resource private ActivityService activityService ; @ApiOperation("新增流动") @PostMapping("/activity") public Integer save (@RequestBody Activity activity){ activityService.save(activity) ; return activity.getId() ; } @ApiOperation("主键查问") @GetMapping("/activity/{id}") public Activity getById (@PathVariable("id") Integer id){ return activityService.getById(id) ; } @ApiOperation("批改流动") @PutMapping("/activity") public Boolean updateById (@RequestBody Activity activity){ return activityService.updateById(activity) ; }} ...

May 31, 2021 · 3 min · jiezi

关于springboot:Mac-简单的-Spring-boot-项目Idea-2021-配合Docker和Maven

Mac 简略的 Spring boot 我的项目(Idea 2021 配合Docker和Maven)环境概述Mac Big Sur 11.4IntelliJ IDEA 2021.1.1 (Ultimate Edition)Docker的装置以及在Idea里的配置Docker的装置非常简单,只须要应用Homebrew装置即可(没有homebrew能够自行搜寻装置homebrew) brew install --cask --appdir=/Applications docker装置后须要点开顶部的图标,输出本机明码前方可通过命令行验证Docker是否装置胜利 docker --version如果装置胜利,即可进入Idea软件中对Docker进行配置,进入Preferences --> Editor --> Plugins,搜寻Docker进行装置 装置实现后进入Preferences --> Build, Execution, Deployment --> Docker,点击加号新建一个Docker,并尝试连贯,如连贯胜利上面会显示Connection successful Idea 创立新我的项目Idea 新创建我的项目,填入名字等信息 这里SDK版本为1.8, JAVA版本为JAVA 8是因为目前网上的教程大多以这两个版本开发,更高的版本我没有尝试,因为放心有兼容问题 第二步是选定依赖库,因为咱们前面会依据pom.xml对依赖库进行更新,所以这里能够临时不选,间接点FINISH即可 批改pom.xml并更新依赖库批改pom.xml文件,将所需的docker, spring-boot-web等依赖库增加进去,留神后面有些内容是依据我的项目的名字来决定的,如有须要能够间接复制<properties>标签及当前的内容对你的文件进行批改(留神spring-boot版本最好和你创立时设定版本统一) <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>docker-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>docker-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <docker.image.prefix>springboot</docker.image.prefix> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot</artifactId> <version>2.5.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!-- Docker maven plugin --> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>1.0.0</version> <configuration> <imageName>${docker.image.prefix}/${project.artifactId}</imageName> <dockerDirectory>src/main/docker</dockerDirectory> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> </configuration> </plugin> <!-- Docker maven plugin --> </plugins> </build></project>批改实现后能够看到左边多了个小图标,点击即可对依赖库进行下载安装 ...

May 30, 2021 · 1 min · jiezi

关于springboot:mica-250-246-发布Spring-native-实战

一、mica(云母)mica是一个微服务组件集,但不仅仅是组件,咱们关注的是微服务生态并继续演进,尽量做到开箱即用,简化应用和排坑。总共已有 40+ 组件,并且很多组件曾经买通。 二、版本阐明最新版本mica 版本boot 版本cloud 版本2.5.0mica 2.5.x2.5.x20202.4.6mica 2.4.x2.4.x20202.1.1-GAmica 2.0.x~2.1.x2.2.x ~ 2.3.xHoxton三、更新记录2.5.0 - 2021-05-23✨ mica-redis 微调,反对 Spring boot 到 2.5.0。⬆️ 降级 Spring boot 到 2.5.0。2.4.6 - 2021-05-23✨ mica-logging 实现 loki 反对 #36 #I3PX2F。✨ mica-ip2region、mica-captcha 增加对 spring-native 的反对 #38 #I3PX2N。✨ mica-jetcache 增加 metrics 反对 #37 #I3PX2K。✨ mica-caffeine 增加不反对自定义 Caffeine bean 提醒。✨ mica-core R 增加 throwOn 系列办法。✨ mica-redis 优化 ICacheKey 和 scan。✨ 代码对立优化,缩小局部阿里巴巴规约提醒。 mica-logging 修复 LoggingInitializer Spring boot 2.4.x 生效的问题。⬆️ 降级 druid 到 1.2.6。⬆️ 降级 Spring boot 到 2.4.6。四、重点阐明4.1 mica-logging 实现 loki 反对 ...

May 24, 2021 · 1 min · jiezi

关于springboot:库克苹果收取-30%-佣金合理Spring-Boot-250-正式发布-思否周刊

40s 新闻速递库克:苹果数百亿美元投入研发,收取 30% 佣金是应该的腾讯、快手拿下东京奥运会及北京冬奥会转播权SpaceX 打算在将来两年半里发射至多 6 个商业月球着陆器罗永浩质押合同案一审宣判,波及锤子数码超 2000 万债权国家网信办通报抖音等 105 款 App 守法违规收集应用个人信息状况5G 套餐累计用户数首次破 4 亿腾讯:截至 3 月底共有 89228 名员工,人均年薪 70+ 万华为打算 7 月底前在 200 家体验店卖车,指标明年销售 30 万台重磅 || Spring Boot 2.5.0 正式公布SpaceX 应用 Rust 为局部新我的项目构建原型Python 之父:明年在 Python 中实现至多 1 倍提速Rust/WinRT 更名 Rust for Windows,全面反对 Windows API微软 Math Solver 将以预览模式登陆微软 Edge 浏览器Linux 5.13 还原及修复明尼苏达大学的问题补丁行业资讯库克:苹果数百亿美元投入研发,收取 30% 佣金是应该的5 月 22 日音讯,美国当地工夫周五,苹果公司首席执行官蒂姆·库克首次在 Epic Games 诉苹果垄断案庭审中出庭作证。库克示意,Epic Games 去年绕过苹果在其热门视频游戏《堡垒之夜》上收取的 30% 利用内购买佣金的行动是“歹意的”,随后苹果间接下架了这款利用。此外,库克在承受苹果律师询问时指出,苹果在钻研、隐衷和平安方面的投资高达数百亿美元,这就是苹果对利用和利用内购买收取 30% 佣金的起因。 腾讯、快手拿下东京奥运会及北京冬奥会转播权5 月 21 日音讯,今日,快手官网发表与地方广播电视总台签订受权单干协定,平台已取得 2020 东京奥运会与北京 2022 年冬奥会视频点播及短视频权力,正式成为 2020 东京奥运会与北京 2022 年冬奥会持权转播机构;同时,腾讯发表与地方广播电视总台达成官网单干,将作为 2020 东京奥运会与北京 2022 年冬奥会持权转播商。由此,腾讯也成为了寰球奥运持权转播商中最大的互联网公司。 ...

May 23, 2021 · 3 min · jiezi

关于springboot:springboot-项目框架搭建

如果我须要使得一个办法采取异步执行如果我须要使得一个办法采取异步执行,并且执行的时候须要指定线程池,那么我须要做三步操作。1、配置线程池 @Configuration@EnableAsyncpublic class ExecutorConfig implements AsyncConfigurer { @Bean("asyncServiceExecutor") @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //配置外围线程数 executor.setCorePoolSize(10); //配置最大线程数 executor.setMaxPoolSize(20); //配置队列大小 executor.setQueueCapacity(99999); //配置线程池中的线程的名称前缀 executor.setThreadNamePrefix("async-service-"); // rejection-policy:当pool曾经达到max size的时候,如何解决新工作 // CALLER_RUNS:不在新线程中执行工作,而是由调用者所在的线程来执行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //执行初始化 executor.initialize(); return executor; }}2、启用异步办法注解 @EnableAsync3、在办法上开启注解,并配置线程池 @Async("asyncServiceExecutor")springboot系统启动时初始化数据 @Component@Slf4jpublic class ApplicationRunnerImpl implements ApplicationRunner { @Override public void run(ApplicationArguments args) { }}配置拦截器1、增加拦截器配置文件 @Configurationpublic class MyWebConfig implements WebMvcConfigurer { @Value("${api.contextPath}") private String apiContextPath; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { } public String getLocalFileServerDir() { return System.getProperty("user.dir") + "/data/"; } @Resource private SessionInterceptor sessionInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(sessionInterceptor) .addPathPatterns(apiContextPath + "/**") .excludePathPatterns(apiContextPath + "/user/test") .excludePathPatterns(apiContextPath + "/***") .excludePathPatterns(apiContextPath + "/log/**"); } @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowCredentials(true) .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH") .maxAge(3600 * 24) .allowedHeaders("*")// 容许头部设置 .allowCredentials(true); // 是否发送cookie }}

May 21, 2021 · 1 min · jiezi

关于springboot:springbootmysql实现登录次数限制

需要实现登录限度 实现后端老手,借助数据库实现了这么一个性能。具体实现思路如下: 1.思路1.1 锁定账户验证用户输错明码次数,记录谬误次数,当次数超过规定数字后,给该账户设置锁定状态,并设置锁定工夫(当天的24点)这样当用户每次登陆的时候先验证下是否锁定,再解决其余业务 1.2 解锁账户解锁的办法比较简单,当用户再次登录时,用以后工夫去和锁定状态下的锁定工夫进行比对,大于锁定工夫就进行解锁,并重置锁定工夫为null,锁定状态 2.革新表在原有的user表新增字段 lock_status: 账户锁定状态 默认0未锁定 1已锁定lock_time: 记录锁定截止工夫 默认nullerror_num: 记录明码验证谬误次数 默认0lock_num: 容许的谬误次数(默认5),这里写在数据库里是为了前期不便批改,而没有写在代码里3. UserDO@Datapublic class UserDO { private int id; private String username; @JSONField(serialize = false) private String password; private int lock_status; private Timestamp lock_time;}4.LoginMapper@Mapperpublic interface LoginMapper { UserDO login(Map params); void setLockNum(Map params); void setLockTime(Map params); UserDO getLockStatus(Map params); void resetLockStatus(Map params);}5.LoginServicepublic interface LoginService { Result login(Map params);}6.LoginServiceImplpublic class LoginServiceImpl implements LoginService { @Autowired LoginMapper loginMapper; @Override public Result login(Map params) { UserDO res = loginMapper.login(params); UserDO lockStatus = loginMapper.getLockStatus(params); if(lockStatus!=null && lockStatus.getLock_status()==1){ Date date = new Date(); if(lockStatus.getLock_time() == null){ System.out.println("--------⏰⏰⏰⏰⏰----------"); System.out.println("锁定工夫为空,设置锁定工夫"); System.out.println("--------⏰⏰⏰⏰⏰----------"); loginMapper.setLockTime(params); }else{ if(date.getTime() > lockStatus.getLock_time().getTime()){ System.out.println("------------------"); System.out.println("锁定工夫生效,清空锁定状态"); System.out.println("------------------"); loginMapper.resetLockStatus(params); UserDO freshmen = loginMapper.login(params); return allResult(params, freshmen); } else { System.out.println("------------------"); System.out.println("锁定状态中"); System.out.println("------------------"); } } String time = UtilFun.formatTime("yyyy年MM月dd日").format(date); return new Result(false, 40001, "失败", "该账户验证次数已达下限,临时解冻至" + time + "24点,解冻期完结明码将被重置为初始密码,请分割管理员进行批改"); }else return allResult(params, res); } private Result allResult(Map params, UserDO res) { if(res == null){ System.out.println("--------❌❌❌❌----------"); System.out.println("用户名或明码谬误"); System.out.println("--------❌❌❌❌----------"); loginMapper.setLockNum(params); return new Result(false, 40001, "失败", "账号或明码谬误,请从新输出,超过5次将会被长期解冻"); }else{ System.out.println("-------✅✅✅✅✅-----------"); System.out.println("用户名或明码"); System.out.println("-------✅✅✅✅✅-----------"); return new Result(true, 20000, "胜利", res); } }}7.sql <select id="login" resultType="com.zdxf.domain.UserDO"> select * from fenghuang_cmda.sys_user <where> username = #{username} and password = #{password} and lock_status = 0 </where> </select> <select id="getLockStatus" resultType="com.zdxf.domain.UserDO"> select * from fenghuang_cmda.sys_user <where> username = #{username} </where> </select> <update id="setLockNum"> update fenghuang_cmda.sys_user set lock_status = (case when error_num = lock_num + 1 then 1 else 0 end), error_num =(case when error_num &lt; lock_num then error_num + 1 else 5 end) <where> username = #{username} </where> </update> <update id="setLockTime"> update fenghuang_cmda.sys_user set lock_time = DATE_FORMAT(now(),'%Y-%m-%d 23:59:59')/*date_add(now(), interval + 5 minute)*/ <where> username = #{username} </where> </update> <update id="resetLockStatus"> update fenghuang_cmda.sys_user set lock_time = null,error_num=0,lock_status=0,password = 'af14a24b93d61df48ee08514cf92c4c5' <where> username = #{username} </where> </update>

May 21, 2021 · 2 min · jiezi

关于springboot:SpringBoot-FFmpeg实现一个简单的M3U8切片转码系统

应用赫赫有名的ffmpeg,把视频文件切片成m3u8,并且通过springboot,能够实现在线的点播。 想法客户端上传视频到服务器,服务器对视频进行切片后,返回m3u8,封面等拜访门路。能够在线的播放。服务器能够对视频做一些简略的解决,例如裁剪,封面的截取工夫。 视频转码文件夹的定义喜羊羊与灰太狼 // 文件夹名称就是视频题目 |-index.m3u8 // 主m3u8文件,外面能够配置多个码率的播放地址 |-poster.jpg // 截取的封面图片 |-ts // 切片目录 |-index.m3u8 // 切片播放索引 |-key // 播放须要解密的AES KEY实现须要先在本机装置FFmpeg,并且增加到PATH环境变量,如果不会先通过搜索引擎找找材料工程 pom<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.demo</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.5</version> <relativePath /> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> </dependencies> <build> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <executable>true</executable> </configuration> </plugin> </plugins> </build></project>配置文件server: port: 80app: # 存储转码视频的文件夹地址 video-folder: "C:\\Users\\Administrator\\Desktop\\tmp"spring: servlet: multipart: enabled: true # 不限度文件大小 max-file-size: -1 # 不限度申请体大小 max-request-size: -1 # 长期IO目录 location: "${java.io.tmpdir}" # 不提早解析 resolve-lazily: false # 超过1Mb,就IO到长期目录 file-size-threshold: 1MB web: resources: static-locations: - "classpath:/static/" - "file:${app.video-folder}" # 把视频文件夹目录,增加到动态资源目录列表TranscodeConfig,用于管制转码的一些参数package com.demo.ffmpeg;public class TranscodeConfig { private String poster; // 截取封面的工夫 HH:mm:ss.[SSS] private String tsSeconds; // ts分片大小,单位是秒 private String cutStart; // 视频裁剪,开始工夫 HH:mm:ss.[SSS] private String cutEnd; // 视频裁剪,完结工夫 HH:mm:ss.[SSS] public String getPoster() { return poster; } public void setPoster(String poster) { this.poster = poster; } public String getTsSeconds() { return tsSeconds; } public void setTsSeconds(String tsSeconds) { this.tsSeconds = tsSeconds; } public String getCutStart() { return cutStart; } public void setCutStart(String cutStart) { this.cutStart = cutStart; } public String getCutEnd() { return cutEnd; } public void setCutEnd(String cutEnd) { this.cutEnd = cutEnd; } @Override public String toString() { return "TranscodeConfig [poster=" + poster + ", tsSeconds=" + tsSeconds + ", cutStart=" + cutStart + ", cutEnd=" + cutEnd + "]"; }}MediaInfo,封装视频的一些根底信息package com.demo.ffmpeg;import java.util.List;import com.google.gson.annotations.SerializedName;public class MediaInfo { public static class Format { @SerializedName("bit_rate") private String bitRate; public String getBitRate() { return bitRate; } public void setBitRate(String bitRate) { this.bitRate = bitRate; } } public static class Stream { @SerializedName("index") private int index; @SerializedName("codec_name") private String codecName; @SerializedName("codec_long_name") private String codecLongame; @SerializedName("profile") private String profile; } // ---------------------------------- @SerializedName("streams") private List<Stream> streams; @SerializedName("format") private Format format; public List<Stream> getStreams() { return streams; } public void setStreams(List<Stream> streams) { this.streams = streams; } public Format getFormat() { return format; } public void setFormat(Format format) { this.format = format; }}FFmpegUtils,工具类封装FFmpeg的一些操作package com.demo.ffmpeg;import java.io.BufferedReader;import java.io.File;import java.io.IOException;import java.io.InputStreamReader;import java.nio.charset.StandardCharsets;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;import java.nio.file.StandardOpenOption;import java.security.NoSuchAlgorithmException;import java.util.ArrayList;import java.util.List;import javax.crypto.KeyGenerator;import org.apache.commons.codec.binary.Hex;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.util.StringUtils;import com.google.gson.Gson;public class FFmpegUtils { private static final Logger LOGGER = LoggerFactory.getLogger(FFmpegUtils.class); // 跨平台换行符 private static final String LINE_SEPARATOR = System.getProperty("line.separator"); /** * 生成随机16个字节的AESKEY * @return */ private static byte[] genAesKey () { try { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(128); return keyGenerator.generateKey().getEncoded(); } catch (NoSuchAlgorithmException e) { return null; } } /** * 在指定的目录下生成key_info, key文件,返回key_info文件 * @param folder * @throws IOException */ private static Path genKeyInfo(String folder) throws IOException { // AES 密钥 byte[] aesKey = genAesKey(); // AES 向量 String iv = Hex.encodeHexString(genAesKey()); // key 文件写入 Path keyFile = Paths.get(folder, "key"); Files.write(keyFile, aesKey, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); // key_info 文件写入 StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("key").append(LINE_SEPARATOR); // m3u8加载key文件网络门路 stringBuilder.append(keyFile.toString()).append(LINE_SEPARATOR); // FFmeg加载key_info文件门路 stringBuilder.append(iv); // ASE 向量 Path keyInfo = Paths.get(folder, "key_info"); Files.write(keyInfo, stringBuilder.toString().getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); return keyInfo; } /** * 指定的目录下生成 master index.m3u8 文件 * @param fileName master m3u8文件地址 * @param indexPath 拜访子index.m3u8的门路 * @param bandWidth 流码率 * @throws IOException */ private static void genIndex(String file, String indexPath, String bandWidth) throws IOException { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("#EXTM3U").append(LINE_SEPARATOR); stringBuilder.append("#EXT-X-STREAM-INF:BANDWIDTH=" + bandWidth).append(LINE_SEPARATOR); // 码率 stringBuilder.append(indexPath); Files.write(Paths.get(file), stringBuilder.toString().getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); } /** * 转码视频为m3u8 * @param source 源视频 * @param destFolder 指标文件夹 * @param config 配置信息 * @throws IOException * @throws InterruptedException */ public static void transcodeToM3u8(String source, String destFolder, TranscodeConfig config) throws IOException, InterruptedException { // 判断源视频是否存在 if (!Files.exists(Paths.get(source))) { throw new IllegalArgumentException("文件不存在:" + source); } // 创立工作目录 Path workDir = Paths.get(destFolder, "ts"); Files.createDirectories(workDir); // 在工作目录生成KeyInfo文件 Path keyInfo = genKeyInfo(workDir.toString()); // 构建命令 List<String> commands = new ArrayList<>(); commands.add("ffmpeg"); commands.add("-i") ;commands.add(source); // 源文件 commands.add("-c:v") ;commands.add("libx264"); // 视频编码为H264 commands.add("-c:a") ;commands.add("copy"); // 音频间接copy commands.add("-hls_key_info_file") ;commands.add(keyInfo.toString()); // 指定密钥文件门路 commands.add("-hls_time") ;commands.add(config.getTsSeconds()); // ts切片大小 commands.add("-hls_playlist_type") ;commands.add("vod"); // 点播模式 commands.add("-hls_segment_filename") ;commands.add("%06d.ts"); // ts切片文件名称 if (StringUtils.hasText(config.getCutStart())) { commands.add("-ss") ;commands.add(config.getCutStart()); // 开始工夫 } if (StringUtils.hasText(config.getCutEnd())) { commands.add("-to") ;commands.add(config.getCutEnd()); // 完结工夫 } commands.add("index.m3u8"); // 生成m3u8文件 // 构建过程 Process process = new ProcessBuilder() .command(commands) .directory(workDir.toFile()) .start() ; // 读取过程规范输入 new Thread(() -> { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { String line = null; while ((line = bufferedReader.readLine()) != null) { LOGGER.info(line); } } catch (IOException e) { } }).start(); // 读取过程异样输入 new Thread(() -> { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { String line = null; while ((line = bufferedReader.readLine()) != null) { LOGGER.info(line); } } catch (IOException e) { } }).start(); // 阻塞直到工作完结 if (process.waitFor() != 0) { throw new RuntimeException("视频切片异样"); } // 切出封面 if (!screenShots(source, String.join(File.separator, destFolder, "poster.jpg"), config.getPoster())) { throw new RuntimeException("封面截取异样"); } // 获取视频信息 MediaInfo mediaInfo = getMediaInfo(source); if (mediaInfo == null) { throw new RuntimeException("获取媒体信息异样"); } // 生成index.m3u8文件 genIndex(String.join(File.separator, destFolder, "index.m3u8"), "ts/index.m3u8", mediaInfo.getFormat().getBitRate()); // 删除keyInfo文件 Files.delete(keyInfo); } /** * 获取视频文件的媒体信息 * @param source * @return * @throws IOException * @throws InterruptedException */ public static MediaInfo getMediaInfo(String source) throws IOException, InterruptedException { List<String> commands = new ArrayList<>(); commands.add("ffprobe"); commands.add("-i") ;commands.add(source); commands.add("-show_format"); commands.add("-show_streams"); commands.add("-print_format") ;commands.add("json"); Process process = new ProcessBuilder(commands) .start(); MediaInfo mediaInfo = null; try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { mediaInfo = new Gson().fromJson(bufferedReader, MediaInfo.class); } catch (IOException e) { e.printStackTrace(); } if (process.waitFor() != 0) { return null; } return mediaInfo; } /** * 截取视频的指定工夫帧,生成图片文件 * @param source 源文件 * @param file 图片文件 * @param time 截图工夫 HH:mm:ss.[SSS] * @throws IOException * @throws InterruptedException */ public static boolean screenShots(String source, String file, String time) throws IOException, InterruptedException { List<String> commands = new ArrayList<>(); commands.add("ffmpeg"); commands.add("-i") ;commands.add(source); commands.add("-ss") ;commands.add(time); commands.add("-y"); commands.add("-q:v") ;commands.add("1"); commands.add("-frames:v") ;commands.add("1"); commands.add("-f"); ;commands.add("image2"); commands.add(file); Process process = new ProcessBuilder(commands) .start(); // 读取过程规范输入 new Thread(() -> { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { String line = null; while ((line = bufferedReader.readLine()) != null) { LOGGER.info(line); } } catch (IOException e) { } }).start(); // 读取过程异样输入 new Thread(() -> { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { String line = null; while ((line = bufferedReader.readLine()) != null) { LOGGER.error(line); } } catch (IOException e) { } }).start(); return process.waitFor() == 0; }}UploadController,执行转码操作package com.demo.web.controller;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;import java.time.LocalDate;import java.time.format.DateTimeFormatter;import java.util.HashMap;import java.util.Map;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Value;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestPart;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.multipart.MultipartFile;import com.demo.ffmpeg.FFmpegUtils;import com.demo.ffmpeg.TranscodeConfig;@RestController@RequestMapping("/upload")public class UploadController { private static final Logger LOGGER = LoggerFactory.getLogger(UploadController.class); @Value("${app.video-folder}") private String videoFolder; private Path tempDir = Paths.get(System.getProperty("java.io.tmpdir")); /** * 上传视频进行切片解决,返回拜访门路 * @param video * @param transcodeConfig * @return * @throws IOException */ @PostMapping public Object upload (@RequestPart(name = "file", required = true) MultipartFile video, @RequestPart(name = "config", required = true) TranscodeConfig transcodeConfig) throws IOException { LOGGER.info("文件信息:title={}, size={}", video.getOriginalFilename(), video.getSize()); LOGGER.info("转码配置:{}", transcodeConfig); // 原始文件名称,也就是视频的题目 String title = video.getOriginalFilename(); // io到临时文件 Path tempFile = tempDir.resolve(title); LOGGER.info("io到临时文件:{}", tempFile.toString()); try { video.transferTo(tempFile); // 删除后缀 title = title.substring(0, title.lastIndexOf(".")); // 依照日期生成子目录 String today = DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDate.now()); // 尝试创立视频目录 Path targetFolder = Files.createDirectories(Paths.get(videoFolder, today, title)); LOGGER.info("创立文件夹目录:{}", targetFolder); Files.createDirectories(targetFolder); // 执行转码操作 LOGGER.info("开始转码"); try { FFmpegUtils.transcodeToM3u8(tempFile.toString(), targetFolder.toString(), transcodeConfig); } catch (Exception e) { LOGGER.error("转码异样:{}", e.getMessage()); Map<String, Object> result = new HashMap<>(); result.put("success", false); result.put("message", e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result); } // 封装后果 Map<String, Object> videoInfo = new HashMap<>(); videoInfo.put("title", title); videoInfo.put("m3u8", String.join("/", "", today, title, "index.m3u8")); videoInfo.put("poster", String.join("/", "", today, title, "poster.jpg")); Map<String, Object> result = new HashMap<>(); result.put("success", true); result.put("data", videoInfo); return result; } finally { // 始终删除临时文件 Files.delete(tempFile); } }}index.html,客户端<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.jsdelivr.net/hls.js/latest/hls.min.js"></script> </head> <body> 抉择转码文件: <input name="file" type="file" accept="video/*" onchange="upload(event)"> <hr/> <video id="video" width="500" height="400" controls="controls"></video> </body> <script> const video = document.getElementById('video'); function upload (e){ let files = e.target.files if (!files) { return } // TODO 转码配置这里固定死了 var transCodeConfig = { poster: "00:00:00.001", // 截取第1毫秒作为封面 tsSeconds: 15, cutStart: "", cutEnd: "" } // 执行上传 let formData = new FormData(); formData.append("file", files[0]) formData.append("config", new Blob([JSON.stringify(transCodeConfig)], {type: "application/json; charset=utf-8"})) fetch('/upload', { method: 'POST', body: formData }) .then(resp => resp.json()) .then(message => { if (message.success){ // 设置封面 video.poster = message.data.poster; // 渲染到播放器 var hls = new Hls(); hls.loadSource(message.data.m3u8); hls.attachMedia(video); } else { alert("转码异样,详情查看控制台"); console.log(message.message); } }) .catch(err => { alert("转码异样,详情查看控制台"); throw err }) } </script></html>应用在配置文件中,配置到本地视频目录后启动关上页面 localhost点击【抉择文件】,抉择一个视频文件进行上传,期待执行结束(没有做加载动画)后端转码实现后,会主动把视频信息加载到播放器,此时能够手动点击播放按钮进行播放能够关上控制台,查看上传进度,以及播放时的网络加载信息 ...

May 17, 2021 · 7 min · jiezi

关于springboot:后台增加id资源权限控制

前言上周写了一个角色权限治理,就是比如说有学生角色,老师角色,避免学生角色对老师角色的相干性能进行操作,不如说对于学生作业评分,如果学生能够对本人作业评分就乱套了,所以须要退出权限管制接口只能老师操作。然而有一部分违规操作无法控制,比如说A学生提交了B学生的作业。提交作业接口尽管管制只能学生拜访,然而无法控制雷同角色的用户对本人的资源的操作。这里作业就是本人的资源,他人不应该能够随便写一份提交。这时候就须要用到id资源权限管制。 实现这里并不是加一个注解那么简略了。大抵思路就是操作id资源时会传入id, 只有在批改前验证id对应资源对应所属用户是否是以后登录用户即可。那上边例子来说就是提交作业时验证id对应作业对应所属用户是否为以后登录用户。如果id对应作业对应所属用户为A,以后登录用户为B,就禁止其操作。实现起来也非常简略。 public Work submit(Long id, Work work) { Work oldWork = this.getById(id); if (!oldWork.getStudent().getId().equals(this.studentService.getCurrentStudent().getId())) { throw new AccessDeniedException("无权更新其它学生的作业"); } ... return this.workRepository.save(oldWork);}然而这并不符合规范,好的代码应该是其余操作与业务逻辑相抽离,这就用到了spring弱小的Aop,面向切面编程。在下面的代码中,咱们提交作业中保留作业到数据库就是业务逻辑。而角色判断是权限判断,须要进行抽离。这里拿老我的项目的代码学习一下。首先加一个办法注解,注解哪个接口须要id资源权限管制。 /** * 拥有者权限认证. */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface OwnerSecured { Class<? extends OwnerAuthority> value();}再注解哪个id是咱们须要认证的id,注解哪个id使咱们拿来认证的id,有可能办法参数里传入很多个id。 /** * 拥有者权限参数. * */@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)public @interface OwnerKey {}而后写一个切面,在办法执行前进行权限认证。 /** * 资源权限校验. */@Aspect@Componentpublic class OwnerSecuredAspect { private Logger logger = LoggerFactory.getLogger(this.getClass()); private final ApplicationContext applicationContext; public OwnerSecuredAspect(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } /** * 切入点. * * @param ownerSecured 注解 */ @Pointcut("@annotation(club.yunzhi.api.workReview.annotation.OwnerSecured) " + "&& @annotation(ownerSecured)") public void annotationPointCut(OwnerSecured ownerSecured) { } /** * 在切点前执行,权限不通过报403. * * @param joinPoint 切点 * @param ownerSecured 拥有者权限注解 */ @Before("annotationPointCut(ownerSecured)") public void before(JoinPoint joinPoint, OwnerSecured ownerSecured) { // 依据切点的@OwnerKey注解获取咱们判断所属用户的id Object paramKey = this.getOwnerKeyValueFromMethodParam(joinPoint); try { // 依据咱们在@OwnerSecured注解里传入的值获取相应的认证器 OwnerAuthority ownerAuthority = applicationContext.getBean(ownerSecured.value()); // 认证 if (!ownerAuthority.checkAccess(paramKey)) { throw new AccessDeniedException("您无权对该资源进行操作"); } } catch (BeansException beansException) { logger.error("未获取到类型" + ownerSecured.value().toString() + "的bean,请增加"); beansException.printStackTrace(); } } /** * 获取在参数中应用@OwnerKey注解的值. * * @param joinPoint 切点 * @return 参数值 */ private Object getOwnerKeyValueFromMethodParam(JoinPoint joinPoint) { Object result = null; boolean found = false; Object[] methodArgs = joinPoint.getArgs(); int numArgs = methodArgs.length; MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Annotation[][] annotationMatrix = methodSignature.getMethod().getParameterAnnotations(); for (int i = 0; i < numArgs; i++) { Annotation[] annotations = annotationMatrix[i]; for (Annotation annotation : annotations) { if (annotation.annotationType().equals(OwnerKey.class)) { if (!found) { result = methodArgs[i]; found = true; } else { this.logger.warn("找到多个OwnerKey注解,将以首个OwnerKey注解为主,非首注解将被疏忽"); } } } } if (result != null) { return result; } else { throw new RuntimeException("未在办法中找到OwnerKey注解,无奈标识其关键字"); } }}最初咱们在接口中使用 ...

May 15, 2021 · 2 min · jiezi

关于springboot:SpringBoot整合76x版本ES入门学习

1.Elaticsearch概述1.1Elaticsearch是什么The Elastic Stack, 包含 Elasticsearch、Kibana、Beats 和 Logstash(也称为 ELK Stack)。可能安全可靠地获取任何起源、任何格局的数据,而后实时地对数据进行搜寻、剖析和可视化。Elaticsearch,简称为ES, ES是一个开源的高扩大的分布式全文搜索引擎,是整个Elastic Stack技术栈的外围。它能够近乎实时的存储、检索数据;自身扩展性很好,能够扩大到上百台服务器,解决PB级别的数据。 1.2Elaticsearch And SolrElasticsearch和Solr,这两款都是基于Lucene搭建的,能够独立部署启动的搜索引擎服务软件。因为内核雷同,所以两者除了服务器装置、部署、治理、集群以外,对于数据的操作 批改、增加、保留、查问等等都非常相似。 1.3Elaticsearch的利用案例GitHub、维基百科、SoundCloud、百度、新浪、阿里、Stack Overflow 2.相干软件的下载2.1Elaticsearch7.6.x版本下载在elasticsearch-7.6.1/config/elasticsearch.yml配置文件加上xpack.ml.enabled: false;则可进入到bin目录,./elasticsearch启动Elaticsearch。 新开一个黑窗口输出:curl localhost:9200,收到如下信息阐明启动胜利。 默认只容许本机拜访,如果想近程拜访能够批改config/elasticsearch.yml,去掉network.host的正文,将它的值改成0.0.0.0,而后重新启动Elasticsearch。 2.2Head插件关上谷歌浏览器搜寻谷歌商城,关上搜寻elasticsearch head即可看见如下: 点击增加至chrome,即可在扩大程序找到关上elasticsearch head。 2.3 Kibana7.6.1关上kibana.yml配置文件,将 i18n.locale: "en"-->i18n.locale: "zh-CN",能够将kibana汉化。进入bin目录,./kibana启动。 2.4ik分词器将下载好的ik文件放到elasticsearch的plugins文件夹上面,重启elasticsearch即可失效。 3.springboot-2.2.5整合ES-7.6.13.1依赖<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target><!--&lt;!&ndash; 自定义es版本依赖,保障和本地统一&ndash;&gt;--> <elasticsearch.version>7.6.1</elasticsearch.version> </properties><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>3.2配置ES连贯package com.sanzhu.config;import org.apache.http.HttpHost;import org.elasticsearch.client.RestClient;import org.elasticsearch.client.RestHighLevelClient;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class ElasticSearchConfig { @Bean public RestHighLevelClient restHighLevelClient(){ return new RestHighLevelClient( RestClient.builder( new HttpHost("127.0.0.1", 9200, "http") ) ); }}3.3索引的CRUD//1.测试索引的创立 @Test public void test01() throws IOException { //1.创立索引申请 CreateIndexRequest request = new CreateIndexRequest("sanzhu_index"); //2.执行创立申请 CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT); System.out.println("response.index() = " + response.index()); }//2.获取索引 @Test public void text02() throws IOException { //1.获取索引申请 GetIndexRequest request = new GetIndexRequest("sanzhu_index"); boolean exists = client.indices().exists(request,RequestOptions.DEFAULT); System.out.println("exists = " + exists); }//3.删除索引 @Test public void test03() throws IOException { DeleteIndexRequest request = new DeleteIndexRequest("sanzhu_index"); AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT); System.out.println("response.isAcknowledged() = " + response.isAcknowledged()); }3.4文档的CRUD//4.增加文档 @Test public void text04() throws IOException { User user = new User(); user.setName("陈辉"); user.setAge(18); IndexRequest request = new IndexRequest("sanzhu_index"); // 增加数据的规定 PUT /sanzhu_index/_doc/1 request.id("1"); request.timeout(TimeValue.timeValueSeconds(1)); request.timeout("1s"); //增加数据进索引库 request.source(JSON.toJSONString(user), XContentType.JSON); IndexResponse response = client.index(request, RequestOptions.DEFAULT); System.out.println("response.status() = " + response.status()); System.out.println("response.toString() = " + response.toString()); }//5.判断文档是否存在 @Test public void test05() throws IOException { GetRequest getRequest = new GetRequest("sanzhu_index", "1"); getRequest.fetchSourceContext(); boolean exists = client.exists(getRequest, RequestOptions.DEFAULT); System.out.println("exists = " + exists); }//6.获取文档信息 @Test public void test06() throws IOException { GetRequest getRequest = new GetRequest("sanzhu_index", "1"); GetResponse documentFields = client.get(getRequest, RequestOptions.DEFAULT); System.out.println("documentFields.getSourceAsString() = " + documentFields.getSourceAsString()); System.out.println("documentFields = " + documentFields); }//7.更新文档信息 @Test public void test07() throws IOException { UpdateRequest updateRequest = new UpdateRequest("sanzhu_index", "1"); updateRequest.timeout("1s"); User user = new User(); user.setName("张家辉"); user.setAge(1918); updateRequest.doc(JSON.toJSONString(user),XContentType.JSON); UpdateResponse update = client.update(updateRequest, RequestOptions.DEFAULT); System.out.println("update.status() = " + update.status()); System.out.println("update.toString() = " + update.toString()); }//8.删除文档申请 @Test public void test08() throws IOException { DeleteRequest deleteRequest = new DeleteRequest("sanzhu_index", "1"); deleteRequest.timeout(); DeleteResponse response = client.delete(deleteRequest, RequestOptions.DEFAULT); System.out.println("response.status() = " + response.status()); System.out.println("response.toString() = " + response.toString()); }//9.批量插入数据 @Test public void test09() throws IOException { BulkRequest bulkRequest = new BulkRequest(); bulkRequest.timeout("10s"); ArrayList<User> users = new ArrayList<>(); for (int i = 0; i < 10; i++) { User user = new User(); user.setName("张家辉"+i+"号"); user.setAge(i+20); users.add(user); } for (int i = 0; i < users.size(); i++) { bulkRequest.add( new IndexRequest("sanzhu_index").id("" + (i + 2)) .source(JSON.toJSONString(users.get(i)), XContentType.JSON) ); } BulkResponse responses = client.bulk(bulkRequest, RequestOptions.DEFAULT); System.out.println("responses.status() = " + responses.status()); System.out.println("responses.hasFailures() = " + responses.hasFailures()); }//单条件查问 @Test public void test14() throws IOException { SearchRequest searchRequest = new SearchRequest("sanzhu_index"); SearchSourceBuilder builder = new SearchSourceBuilder(); builder.timeout(TimeValue.timeValueSeconds(2)); //过滤字段 builder.fetchSource("name","age"); //分页 builder.from(0); builder.size(3); //排序 builder.sort("_id", SortOrder.DESC); //条件查问 TermQueryBuilder query = QueryBuilders.termQuery("age", "99"); builder.query(query); searchRequest.source(builder); SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); SearchHit[] hits = response.getHits().getHits(); for (SearchHit hit : hits) { System.out.println("hit = " + hit); System.out.println("hit = " + hit.getSourceAsMap()); } } // 多条件查问 @Test public void test15() throws IOException{ SearchRequest searchRequest = new SearchRequest("sanzhu_index"); SearchSourceBuilder builder = new SearchSourceBuilder(); builder.timeout(TimeValue.timeValueSeconds(2)); //多条件查询器 BoolQueryBuilder query = QueryBuilders.boolQuery(); //必须满足 query.must(QueryBuilders.matchQuery("name","peter")); //必须不满足 query.mustNot(QueryBuilders.matchQuery("age","999")); //或者 should查问和must查问一起用会有效// query.should(QueryBuilders.matchQuery("age","777"));// query.should(QueryBuilders.matchQuery("age","888")); builder.query(query); searchRequest.source(builder); SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); SearchHit[] hits = response.getHits().getHits(); for (SearchHit hit : hits) { System.out.println("hit = " + hit); } }//范畴查问 @Test public void test16() throws Exception{ SearchRequest searchRequest = new SearchRequest("sanzhu_index"); SearchSourceBuilder builder = new SearchSourceBuilder(); RangeQueryBuilder queryBuilder = QueryBuilders.rangeQuery("age").gte(200).lte(666); builder.query(queryBuilder); searchRequest.source(builder); SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); SearchHit[] hits = response.getHits().getHits(); for (SearchHit hit : hits) { System.out.println("hit = " + hit); } }//高亮查问@Test public void test13() throws IOException { SearchRequest searchRequest = new SearchRequest("sanzhu_index"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); TermQueryBuilder query = QueryBuilders.termQuery("name", "peter"); searchSourceBuilder.query(query); searchSourceBuilder.timeout(TimeValue.timeValueSeconds(1)); searchSourceBuilder.from(1); searchSourceBuilder.size(5); searchRequest.source(searchSourceBuilder); //高亮 HighlightBuilder highlightBuilder = new HighlightBuilder(); highlightBuilder.field("name"); highlightBuilder.preTags("<span style='color:red'>"); highlightBuilder.postTags("</span>"); highlightBuilder.requireFieldMatch(false);//敞开多个高亮,只第一个高亮 searchSourceBuilder.highlighter(highlightBuilder); SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); SearchHit[] hits = response.getHits().getHits(); System.out.println("hits.length = " + hits.length); for (SearchHit hit : hits) { //获取高亮字段 Map<String, HighlightField> map = hit.getHighlightFields(); HighlightField name = map.get("name"); Map<String, Object> map1 = hit.getSourceAsMap(); //用高亮的字段替换原来的字段 if (name!=null){ Text[] fragments = name.fragments();//获取高亮内容 String n_name = ""; for (Text fragment : fragments) { n_name+=fragment; } map1.put("name",n_name); } System.out.println("map1 = " + map1); }

May 14, 2021 · 3 min · jiezi

关于springboot:spring项目优雅停机

        最近,公司我的项目优化重构,为了我的项目能够无缝高低线,如丝般顺滑,思考到增加优雅停机性能。优雅停机蕴含线程池、音讯、数据库、服务下线。         jvm有钩子函数在收到kill命令后会执行回掉,能够应用这个实现优雅下线。简略介绍一下kill命令。-2相似于ctrl+c,kill -15服务失常下线会触发jvm钩子函数。kill -9零碎内核间接杀死,不会触发钩子函数。 优雅下线波及到bean的生命周期销毁。ContextClosedEvent和@PreDestroy和周期和DisposableBean参考链接https://blog.csdn.net/qq_4384... 线程池、mq、db等都在spring的bean销毁之前下线,留神下线程序。例子: private ThreadPoolExecutor executor; @Bean @Primary public ThreadPoolExecutor asyncServiceExecutor() { executor = new ThreadPoolExecutor(5, 20, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy()); return executor; } @PreDestroy public void destroyThreadPool() { if (!executor.isTerminated()){ executor.shutdown(); try { executor.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); executor.shutdownNow(); } } log.info("ThreadPoolExecutor destroyed !"); }dubbo下线dubbo默认开启了优雅停机。下线分为从注册核心下线,敞开协定2.7之后源码如下:ShutdownHookListener,继承 Spring ApplicationListener 接口,用以监听 Spring 相干事件。这里 ShutdownHookListener 仅仅监听 Spring 敞开事件,当 Spring 开始敞开,将会触发 ShutdownHookListener 外部逻辑 public class SpringExtensionFactory implements ExtensionFactory { private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class); private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>(); private static final ApplicationListener SHUTDOWN_HOOK_LISTENER = new ShutdownHookListener(); public static void addApplicationContext(ApplicationContext context) { CONTEXTS.add(context); if (context instanceof ConfigurableApplicationContext) { // 注册 ShutdownHook ((ConfigurableApplicationContext) context).registerShutdownHook(); // 勾销 AbstractConfig 注册的 ShutdownHook 事件 DubboShutdownHook.getDubboShutdownHook().unregister(); } BeanFactoryUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER); } // 继承 ApplicationListener,这个监听器将会监听容器敞开事件 private static class ShutdownHookListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextClosedEvent) { DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook(); shutdownHook.doDestroy(); } } }}通过配置dubbo.application.shutwait=30s能够设置dubbo等待时间springboot下线        相熟介绍springboot优雅停机,在Springboot2.3之前须要本人实现优雅停机。2.3后本人实现了优雅停机。 ...

May 12, 2021 · 2 min · jiezi

关于springboot:数据质量分析

数据品质剖析一、引言1.1 我的项目背景数据品质监测是大数据处理中最重要的一个环节,是数据服务、数据分析、数据挖掘等流动的必备反对条件。 1.2 我的项目概述提出了一个基于大数据平台的数据品质治理服务Qualitis,提供对立的流程来定义和检测数据集的品质并及时报告问题。 1.3 术语表术语 含意我的项目(project) 一系列规定的汇合,决定告警人和告警级别,是任务调度的单位之一。规定(rule) 数据源的数据品质模型的定义,决定是否告警,是任务调度的根底单位。工作(application) 数据品质检测工作,通过运行数据品质工作,能够查看数据品质校验后果。二、总体设计2.1 总体架构设计 2.2 灰度功能设计因为每个Qualitis后端服务是幂等的,要灰度只须要对单个后端服务进行隔离,让其无奈承受用户申请。 2.3 高可用及性能设计Qualitis各个服务之间是幂等的,能够通过同时起多个Qualitis服务,对Qualitis服务进行负载平衡进行实现。如下图所示: 负载平衡负载平衡这一策略,不仅实现了高可用,并且也实现了性能的晋升。 性能方面的设计,思考以下计划。但目前暂未实现。 1.查问缓存应用分布式缓存,将查问后果缓存起来,就不用查问的时候,每次都查询数据库,大大的缩小了数据库的压力,并且晋升了查问的速度。 2.4 多线程同步设计1.进程同步因为存在多个Qualitis实例,多个实例之间可能会存在同时刷新监控工作状态的状况,所以须要解决进程同步的问题。 Qualitis零碎采纳Zookeeper协调多过程,多个Qualitis实例会争抢在Zookeeper中建设长期节点,建设长期节点胜利的,会作为Monitor角色,由Monitor角色对监控工作,并刷新工作的状态。 2.线程限流当触发监控工作提交时,须要连贯hive meta store,判断保留未通过校验的数据的数据库是否存在。 当提交任务量上来是,可能会对hive meta store造成微小压力,所以须要对工作提交进行限流。 Qualitis零碎应用线程池的形式,对连贯hive meta store进行限流,如果从线程池中拿不到线程,工作会期待,直到拿到线程,才连贯hive meta store。 三、 模块设计3.1 总体模块设计图 3.2 用例图 四、 接口设计4.1 外部接口外部接口次要分为两类接口:1.管理员接口2.用户接口 管理员接口设计: /qualitis/api/v1/admin/*用户接口设计: /qualitis/api/v1/projector/*通过两种不同的接口定义形式,将用户的权限辨别开。 4.2 内部接口内部接口url定义:/qualitis/outer/api/v1/*此类接口调用须要在query中减少如下参数: 参数名 必选 类型 阐明app_id 是 string 零碎调配的受权利用APP_ID.timestamp 是 string 工夫戳。毫秒级的工夫戳,时效性:7天nonce 是 string 随机数,长度为5signature 是 string 加密签名。md5(md5(appId + nonce + timestamp) + appToken),其中md5生成32长度,小写其中app_id和appToken须要管理员授予内部零碎。 五、系统工程结构设计零碎的工程构造能够分为2层,Web层和Core层。 Web层次要包含Controller和Service,次要蕴含对外提供服务的服务层,Core层次要包含外围代码逻辑和存储层。 ...

May 9, 2021 · 1 min · jiezi

关于springboot:超详细教程SpringBoot整合MybatisPlus

摘要:本文为大家带来SpringBoot整合MybatisPlus的教程,实现SpringBoot我的项目中依赖数据模块进行数据操作,并进行简略测试。本文分享自华为云社区《SpringBoot整合MybatisPlus【超具体】》,原文作者:牛哄哄的柯南。 创立个SpringBoot我的项目选生所需的依赖:== 我把application的后缀改为.yml了,不便些。 ==pom.xml:== <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.keafmd</groupId> <artifactId>springboot-mybatisplus</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot-mybatisplus</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build></project>因为咱们配置了数据源,所以须要在application.yml中配置下数据源,不然会起不来,我顺便也改了下端口。 ...

May 8, 2021 · 2 min · jiezi

关于springboot:springbootautoconfiguration自动配置实战装载一个School-Bean

0过往的开发教训通知咱们,大部分的配置其实应用默认值即可,并不需要手动逐条配置,springboot的外围性能之一就是尽可能地帮忙咱们进行主动配置,同时在application.yml配置文件中提供了批改默认值的入口。 本文通过结构一个主动配置类来探索其原理。最终代码地址:https://github.com/RonaldBBB/... 1.对于@ConditionalOnMissingBean的纳闷之前始终纳闷,Bean被退出容器的程序是否会影响@ConditionalOnMissingBean的行为。 事实上首先,通过主动配置将Bean退出容器的行为,总是产生在利用自身的配置类的行为之后;其次依据@ConditionalOnMissingBean本身的正文: The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only. If a candidate bean may be created by another auto-configuration, make sure that the one using this condition runs after.能够晓得如果通过主动配置退出容器的Bean之间存在先后关系,必须严格地应用@AutoConfigureBefore @AutoConfigureAfter进行规定。 2需要:应用主动配置类向容器中装载一个School Bean,School蕴含List<Student>的依赖。使用者无奈通过application.yml笼罩默认配置。 剖析:和咱们日常在@Configuration类中应用@Bean装载Bean惟一的区别在于,须要在META-INF/spring.factories中增加自定义的配置类,以让AutoConfigurationImportSelector能够扫描到该主动配置类。 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.zyf.autoconfigure.SchoolAutoConfiguration具体代码:为了切合惯例架构,将须要主动配置的类和主动配置类放到了不同的jar包中.school用来寄存被主动装载的类. @Data@NoArgsConstructor@AllArgsConstructorpublic class School { private String name; private List<Student> students;}school-autoconfigure用来寄存主动配置类. ...

May 8, 2021 · 1 min · jiezi

关于springboot:手撸了一个starter同事直夸我666

Spring Boot starter原理Spring Boot 将常见的开发性能,分成了一个个的starter,这样咱们开发性能的时候只须要引入对应的starter,而不须要去引入一堆依赖了!starter能够了解为一个依赖组,其次要性能就是实现引入依赖和初始化配置。Spring 官网提供的starter 命名标准为 spring-boot-starter-xxx ,第三方提供的starter命名标准为 xxx-spring-boot-starter 。 这里咱们以 RocketMQ 的依赖 rocketmq-spring-boot-starter 来学习 starter的原理。 在我的项目中引入 rocketmq-spring-boot-starter 之后,实际上就引入了 rocketmq 的一些相干依赖。 在 rocketmq-spring-boot 中有一个主动拆卸的类RocketMQAutoConfiguration ,我截取了其中的一小段代码,一起来看看。 @Configuration@EnableConfigurationProperties(RocketMQProperties.class)@ConditionalOnClass({MQAdmin.class})@ConditionalOnProperty(prefix = "rocketmq", value = "name-server", matchIfMissing = true)@Import({MessageConverterConfiguration.class, ListenerContainerConfiguration.class, ExtProducerResetConfiguration.class, RocketMQTransactionConfiguration.class})@AutoConfigureAfter({MessageConverterConfiguration.class})@AutoConfigureBefore({RocketMQTransactionConfiguration.class})public class RocketMQAutoConfiguration { private static final Logger log = LoggerFactory.getLogger(RocketMQAutoConfiguration.class); public static final String ROCKETMQ_TEMPLATE_DEFAULT_GLOBAL_NAME = "rocketMQTemplate"; @Autowired private Environment environment; @Bean(destroyMethod = "destroy") @ConditionalOnBean(DefaultMQProducer.class) @ConditionalOnMissingBean(name = ROCKETMQ_TEMPLATE_DEFAULT_GLOBAL_NAME) public RocketMQTemplate rocketMQTemplate(DefaultMQProducer mqProducer, RocketMQMessageConverter rocketMQMessageConverter) { RocketMQTemplate rocketMQTemplate = new RocketMQTemplate(); rocketMQTemplate.setProducer(mqProducer); rocketMQTemplate.setMessageConverter(rocketMQMessageConverter.getMessageConverter()); return rocketMQTemplate; }}@Configuration 阐明这是一个配置类,类中被@Bean注解了的办法,就是spring的一个bean,例如rocketMQTemplate。@EnableConfigurationProperties,启用被@ConfigurationProperties的bean,这里引入了 RocketMQProperties 。RocketMQProperties 就是须要在yml文件中写入的属性。 ...

May 8, 2021 · 2 min · jiezi

关于springboot:在SpringBoot中缓存HTTP请求响应体实现请求响应日志的记录

缓存申请响应体的目标把一个HTTP的申请,响应信息残缺的纪录到日志。是一种常见无效的问题排查,BUG重现的伎俩。 然而流这种货色,有一个特点就是只能读取/写入一次,不能反复。下一次读写,就是一个空的流,为了实现流的重用,就很有必要,把读取和写入的数据缓存起来, 能够在某个中央,再一次的读取。 实现的思路HttpServletRequestWrapperHttpServletResponseWrapper下面2个类,相熟Servlet的都晓得,这俩就是Request和Response的装璜模式实现。 通过装璜者设计模式,咱们能够在Request读取申请body的时候,把读取到的数据复制一份缓存起来,记录日志时应用。同理,也能够把Response响应的数据,先缓存起来,用于记录日志,而后再响应给客户端。 Spring提供的实现ContentCachingRequestWrapper// 这里疏忽了 HttpServletRequest 的相干办法public class ContentCachingRequestWrapper extends HttpServletRequestWrapper { // 包装Servlet,不限度申请体的大小 public ContentCachingRequestWrapper(HttpServletRequest request) // 包装Servlet,限度申请体的大小 public ContentCachingRequestWrapper(HttpServletRequest request, int contentCacheLimit) // 获取到缓存的申请体 public byte[] getContentAsByteArray() // 申请体超过限度时会调用这个办法,默认空实现 protected void handleContentOverflow(int contentCacheLimit) }比拟好了解的一个类,倡议通过contentCacheLimit限度申请体大小。因为它默认把申请体缓存到内存中,如果客户端发动歹意申请,结构大体积的申请体可能会耗费洁净服务器的内存 ContentCachingResponseWrapper// 这里疏忽了 HttpServletResponse 的相干办法public class ContentCachingResponseWrapper { // 把缓存中的响应数据,刷出到客户端 void copyBodyToResponse() // 获取缓存数据 byte[] getContentAsByteArray() // 获取缓存数据 InputStream getContentInputStream() // 获取缓存数据的大小 int getContentSize()}很简略,通过ContentCachingResponseWrapper 的包装,任何往客户端的响应数据,都会被它缓存起来,反复的读取应用,最终响应给客户端 申请日志的实现Controller及其简略,把申请体,增加工夫戳后回写给客户端。 import java.util.HashMap;import java.util.Map;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/demo")public class DemoController { @RequestMapping(produces = { "application/json; charset=utf-8" }) public Object demo (@RequestBody(required = false) String body) { Map<String, Object> response = new HashMap<>(); response.put("reqeustBody", body); response.put("timesttamp", System.currentTimeMillis()); return response; }}AccessLogFilter通过AccessLogFilter输入申请体/响应体,耗时,等等信息到日志。还对以后申请体生成了一个全局惟一request-id,能够作为检索的条件。 ...

May 7, 2021 · 2 min · jiezi

关于springboot:如有神助阿里P7大牛把Spring-Boot讲解得如此透彻送你上岸

Hello,明天给各位童鞋们分享Spring Boot,连忙拿出小本子记下来吧! Spring Boot 整合 Druid概述Druid 是阿里巴巴开源平台上的一个我的项目,整个我的项目由数据库连接池、插件框架和 SQL 解析器组成。该我的项目次要是为了扩大 JDBC 的一些限度,能够让程序员实现一些非凡的需要,比方向密钥服务申请凭证、统计 SQL 信息、SQL 性能收集、SQL 注入查看、SQL 翻译等,程序员能够通过定制来实现本人须要的性能。 Druid 是目前最好的数据库连接池,在性能、性能、扩展性方面,都超过其余数据库连接池,包含 DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。Druid 曾经在阿里巴巴部署了超过 600 个利用,通过多年生产环境大规模部署的严苛考验。Druid 是阿里巴巴开发的号称为监控而生的数据库连接池! 引入依赖 在 pom.xml 文件中引入 druid-spring-boot-starter 依赖 <dependency>     <groupId>com.alibaba</groupId>     <artifactId>druid-spring-boot-starter</artifactId>     <version>1.1.10</version> </dependency> 引入数据库连贯依赖     <groupId>mysql</groupId>     <artifactId>mysql-connector-java</artifactId>     <scope>runtime</scope> 配置 application.yml在 application.yml中配置数据库连贯 spring:   datasource: url: jdbc:mysql://ip:port/dbname?useUnicode=true&characterEncoding=utf-8&useSSL=false     username: root     password: 123456     type: com.alibaba.druid.pool.DruidDataSource ...

May 6, 2021 · 5 min · jiezi

关于springboot:mica-245-发布完善-druidundertow-metrics

一、mica(云母)mica是一个微服务组件集,但不仅仅是组件,咱们关注的是微服务生态并继续演进,尽量做到开箱即用,简化应用和排坑。总共已有 40+ 组件,并且很多组件曾经买通。 二、版本阐明留神:2.4.5 开始去掉了 GA 后缀,mica-v2.0分支仅做 bug 修复,不再做性能更新。 最新版本mica 版本spring boot 版本spring cloud 版本2.4.5mica 2.4.x2.4.x20202.1.1-GAmica 2.0.x~2.1.x2.2.x ~ 2.3.xHoxton三、更新记录v2.4.5 - 2021-04-28✨ 增加 mica-jetcache(二级缓存)模块,方便使用。✨ 增加 mica-lite 模块,不便 Spring boot 我的项目应用。✨ mica-metrics 重构 UndertowMetrics,裸露更加有用的指标。✨ mica-metrics 欠缺 DruidMetrics,裸露更加有用的指标。✨ mica-redis 调整 bean 名称 redisTemplate 为 micaRedisTemplate 缩小抵触。✨ mica-captcha 中的 cache 改为每次读取, caffeine 会刷新,照成援用为 null。✨ mica-captcha 优化 bean 名称和增加 generateBase64Vo 办法。✨ mica-logging 缩小 reflections 日志,readme 增加阿里云、腾讯云日志服务接入链接。✨ mica-qrcode 增加 base64 image 办法。✨ mica-core 增加网关通用 code。✨ mica-core 增加 CollectionUtil computeIfAbsent 办法 防止 jdk8 下的 bugs JDK-8161372✨ mica-core Pkcs7Encoder 中默认的 BLOCK_SIZE 改为 16 github #35 兼容更多编程语言。 mica-caffeine 多 cache name 时报错。⬆️ 降级 spring boot 到 2.4.5⬆️ 降级 mica-weixin 到 2.1.0(优化对 mica-caffeine 的反对) ...

April 29, 2021 · 1 min · jiezi

关于springboot:非常棒的练手开源电商项目

 大家好,我是小编南风吹,每天举荐一个小工具/源码,装满你的收藏夹,让你轻松节俭开发效率,实现不加班不熬夜不掉头发! 明天小编举荐一套开源电商零碎,包含前台商城零碎及后盾管理系统,基于SpringBoot+MyBatis实现,采纳Docker容器化部署。 前台商城零碎蕴含首页门户、商品举荐、商品搜寻、商品展现、购物车、订单流程、会员中心、客户服务、帮忙核心等模块。 后盾管理系统蕴含商品治理、订单治理、会员治理、促销治理、经营治理、内容治理、统计报表、财务管理、权限治理、设置等模块。 开源协定 应用 Apache-2.0 开源许可协定 链接地址 公众号【Github导航站】回复关键词【mal】获取git地址 技术栈后端技术 前端技术 局部性能一览商品模块 商品治理商品分类管理商品类型治理品牌治理订单模块 订单治理订单设置退货申请解决退货起因设置营销模块 秒杀流动治理优惠价治理品牌举荐治理新品举荐治理人气举荐治理专题举荐治理首页广告治理数据库表概览目前有71张数据表,业务逻辑有肯定复杂度,平时做我的项目参考也够了 演示截图前端 后端 结尾 本期就分享到这里,我是小编南风吹,专一分享好玩乏味、离奇、实用的开源我的项目及开发者工具、学习资源!心愿能与大家独特学习交换,欢送关注我的公众号【Github导航站】。

April 26, 2021 · 1 min · jiezi

关于springboot:jasypt在springboot项目启动异常不兼容问题解决

背景在应用jasypt对spring boot的配置文件中的敏感信息进行加密解决时,应用stater间接启动时,遇到了一个异样 <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.3</version></dependency>遇到如下异样: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'enableEncryptablePropertySourcesPostProcessor' defined in class path resource [com/ulisesbocchio/jasyptspringboot/configuration/EnableEncryptablePropertiesConfiguration.class]: Unsatisfied dependency expressed through method 'enableEncryptablePropertySourcesPostProcessor' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'xxxDao' defined in xxxDao defined in @EnableJpaRepositories declared on Application: Unsatisfied dependency expressed through constructor parameter 1: Ambiguous argument values for parameter of type [javax.persistence.EntityManager] - did you specify the correct bean references as arguments? at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:797) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:538) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1176) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:172) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:707) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:533) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)...以上的信息是指enableEncryptablePropertySourcesPostProcessor创立时,因为创立其余类Bean失败而导致失败的。 ...

April 25, 2021 · 3 min · jiezi

关于springboot:详解项目后台Spring-Security流程

前言这周写了一下后盾登录,老师叫我参考一下教程后盾,正好通过这次机会学习一下spring Security。 Spring Security咱们看一下官网对于spring security的介绍 Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements这段文字的大抵意思是:Spring Security是一个弱小的、可高度定制化的身份验证和访问控制的框架,它基本上是爱护基于Spring利用的平安规范。Spring Security是一个专一于向Java应用程序提供身份验证和受权的框架。像所有的Spring我的项目一样,Spring Security的真正威力在于它能够很容易地被扩大以满足定制需要。 咱们开发一个后盾,一些资源想要供所有人拜访,一些资源则只想让登录的人拜访,这时候就须要用到咱们的spring security。spring security将身份验证抽离于业务代码之外。 ...

April 24, 2021 · 3 min · jiezi

关于springboot:升级完微服务架构-去除多余组件简化应用

<spring-boot.version>2.4.5</spring-boot.version> <spring-cloud.version>2020.0.2</spring-cloud.version> <spring-cloud-alibaba.version>2020.0.RC1</spring-cloud-alibaba.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <nacos.version>2.0.0</nacos.version> <spring-boot-admin.version>2.4.0</spring-boot-admin.version> <mybatis-plus.version>3.4.2</mybatis-plus.version> <dynamic-ds.version>3.2.0</dynamic-ds.version> <hutool.version>5.6.1</hutool.version> <swagger.fox.version>3.0.0</swagger.fox.version> GITEE:https://gitee.com/arch777

April 22, 2021 · 1 min · jiezi

关于springboot:Spring-Boot-接入支付宝实战来了

支付宝推出了新的转账接口alipay.fund.trans.uni.transfer(降级后安全性更高,性能更加弱小) ,老转账接口alipay.fund.trans.toaccount.transfer将不再保护,新老接口的一个区别就是新接口采纳的证书验签形式。 应用新接口要将sdk版本升级到最新版本,博主降级时最新版本是4.10.97。 接下来看集成步骤。 1.将支付宝开放平台里下载的3个证书放在resources上面在这里插入图片形容2.写支付宝领取的配置文件alipay.properties alipay.appId=你的利用idalipay.serverUrl=https://openapi.alipay.com/ga...alipay.privateKey=你的利用私钥alipay.format=jsonalipay.charset=UTF-8alipay.signType=RSA2alipay.appCertPath=/cert/appCertPublicKey_2021001164652941.crtalipay.alipayCertPath=/cert/alipayCertPublicKey_RSA2.crtalipay.alipayRootCertPath=/cert/alipayRootCert.crt3.引入pom依赖 <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.10.97.ALL</version></dependency>4.将配置信息注入AliPayBean import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.PropertySource;import org.springframework.stereotype.Component; @Component@PropertySource("classpath:/production/alipay.properties")@ConfigurationProperties(prefix = "alipay")@Datapublic class AliPayBean { private String appId;private String privateKey;private String publicKey;private String serverUrl;private String domain;private String format;private String charset;private String signType;private String appCertPath;private String alipayCertPath;private String alipayRootCertPath;}5.写配置类 import com.alipay.api.AlipayClient;import com.alipay.api.CertAlipayRequest;import com.alipay.api.DefaultAlipayClient;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.util.FileCopyUtils; import java.io.InputStream; @Configurationpublic class AliConfig { @Value("${custom.http.proxyHost}")private String proxyHost;@Value("${custom.http.proxyPort}")private int proxyPort;@Value("${spring.profiles.active}")private String activeEnv;@Autowiredprivate AliPayBean aliPayBean;@Bean(name = {"alipayClient"})public AlipayClient alipayClientService() throws Exception{ CertAlipayRequest certAlipayRequest = new CertAlipayRequest(); //设置网关地址 certAlipayRequest.setServerUrl(aliPayBean.getServerUrl()); //设置利用Id certAlipayRequest.setAppId(aliPayBean.getAppId()); //设置利用私钥 certAlipayRequest.setPrivateKey(aliPayBean.getPrivateKey()); //设置申请格局,固定值json certAlipayRequest.setFormat(aliPayBean.getFormat()); //设置字符集 certAlipayRequest.setCharset(aliPayBean.getCharset()); //设置签名类型 certAlipayRequest.setSignType(aliPayBean.getSignType()); //如果是生产环境或者预演环境,则应用代理模式 if ("prod".equals(activeEnv) || "stage".equals(activeEnv) || "test".equals(activeEnv)) { //设置利用公钥证书门路 certAlipayRequest.setCertContent(getCertContentByPath(aliPayBean.getAppCertPath())); //设置支付宝公钥证书门路 certAlipayRequest.setAlipayPublicCertContent(getCertContentByPath(aliPayBean.getAlipayCertPath())); //设置支付宝根证书门路 certAlipayRequest.setRootCertContent(getCertContentByPath(aliPayBean.getAlipayRootCertPath())); certAlipayRequest.setProxyHost(proxyHost); certAlipayRequest.setProxyPort(proxyPort); }else { //local String serverPath = this.getClass().getResource("/").getPath(); //设置利用公钥证书门路 certAlipayRequest.setCertPath(serverPath+aliPayBean.getAppCertPath()); //设置支付宝公钥证书门路 certAlipayRequest.setAlipayPublicCertPath(serverPath+aliPayBean.getAlipayCertPath()); //设置支付宝根证书门路 certAlipayRequest.setRootCertPath(serverPath+aliPayBean.getAlipayRootCertPath()); } return new DefaultAlipayClient(certAlipayRequest);}public String getCertContentByPath(String name){ InputStream inputStream = null; String content = null; try{ inputStream = this.getClass().getClassLoader().getResourceAsStream(name); content = new String(FileCopyUtils.copyToByteArray(inputStream)); }catch (Exception e){ e.printStackTrace(); } return content;}}6.写领取工具类 ...

April 15, 2021 · 3 min · jiezi

关于springboot:SpringSecurity入坑一

SpringSecurity入坑第一步:内存的权限验证与受权构建依赖 pom.xml<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.shaojie.authority</groupId><artifactId>authority</artifactId><version>1.0-SNAPSHOT</version><parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.0.RELEASE</version> <relativePath/></parent><properties> <java.version>1.8</java.version> <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-cloud.version>Hoxton.RC1</spring-cloud.version></properties><!--治理依赖--><dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.RC1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies></dependencyManagement><dependencies> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- Spring Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!--spring-data-jpa--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- druid 连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency></dependencies><build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins></build></project>构建权限验证package com.shaojie.authority.security; ...

April 14, 2021 · 2 min · jiezi

关于springboot:Spring-ContentNegotiation内容协商之使用篇一

背景随着业务零碎的成熟,如果你的我的项目正好是公司的中台策略之一,然而上游零碎的接管形式不对立,这一种状况在一些老的公司零碎架构总经常出现,如果上游零碎不不便兼容,那么就须要中台零碎对外提供各种不同格局返回报文 内容协商简略说就是服务提供方依据客户端所反对的格局来返回对应的报文,在 Spring 中,REST API 基本上都是以 json 格局进行返回,而如果须要一个接口即反对 json,又反对其余格局,开发和保护多套代码显然是不合理的,而 Spring 又恰好提供了该性能,那便是ContentNegotiation 在 Spring 中,决定一个数据是以 json、xml 还是 html 的形式返回有三种形式,别离如下: 1:favorPathExtension 后缀模式,例如:xxx.json,xxx.xml2:favorParameter format模式,例如:xxx?format=json,xxx?format=xml,3:通过申请的 Accept 来决定返回的值在这三种模式中,后面两种模式都是敞开,如果须要关上,能够通过以下形式来开启1:重写 WebMvcConfigurer(Spring5.X当前举荐的实现类) 的 configureContentNegotiation 来设置为 true 即可2:设置 spring.mvc.contentnegotiation.favor-path-extension=true 或者 pring.mvc.contentnegotiation.favor-parameter=true tips:如果是应用 Spring2.X以上的版本,不要开启 @EnableWebMvc 注解,否则会导致你的配置有效,如果须要开启该注解,则只能应用办法一重写 WebMvcConfigurer 了三种模式1:favorPathExtension 后缀模式server: port: 8081spring: mvc: contentnegotiation: favor-path-extension: true media-types: json: application/jsonfavor-path-extension 示意是否开启后缀匹配,media-types 示意后缀以何种形式进行解析,在这里须要留神一下肯定是须要有对应的 HttpMessageConvert 能力解析,否则是会提醒 406 Could not find acceptable representation 在 Spring 中曾经默认含有json解析的 HttpMessageConvert,所以是能够间接解析的,如果须要反对解析 xml,能够引入 xml 包<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId></dependency>当开启了后缀模式当前,返回的文本类型会依据你的入参做不同的解决,.json 会返回 json 格局的数据,.xml 会返回 xml 格局的数据,当然也能够自定义一个 HttpMessageConverter 来自定义的返回文本格式 ...

April 11, 2021 · 1 min · jiezi

关于enum:springboot枚举类型传递

在本周写我的项目时,须要将枚举类型作为参数进行传递。 测试首先先建设一个枚举类: public enum ScoreType { TOTAL_SCORE("总评问题"), MIDDLE_SCORE("期中问题"), FINAL_SCORE("期末问题"); String des; // 形容 ScoreType(String des) { this.des = des; } public String getDes() { return des; } }再建设一个枚举api接口: @RestController@RequestMapping("/Klass")public class KlassController { @GetMapping("testEnum") public String testEnum(@RequestParam ScoreType scoreType) { return "枚举序号:" + scoreType.ordinal() + ",枚举名:" + scoreType.name(); }}进行测试,应用枚举名发送数据: 应用枚举序号发送数据: 由此可见,在springboot默认申请参数映射中,枚举类型只能通过枚举名来进行参数映射,但有时候咱们须要用序号来做映射。 Converter顾明思议Converter就是转换的意思,咱们能够通过定义的Converter来确定参数到枚举类型之间的转换: public class BaseEnumConverter<T extends Enum> implements Converter<String, T> { private Map<String, T> enumMap = new HashMap<>(); public BaseEnumConverter(Class<T> enumType) { T[] enums = enumType.getEnumConstants(); for (T e : enums) { enumMap.put(String.valueOf(e.ordinal()), e); enumMap.put(e.name(), e); } } @Override public T convert(String source) { T t1 = enumMap.get(source.toLowerCase()); T t2 = enumMap.get(source.toUpperCase()); if (t1 == null && t2 == null) { throw new IllegalArgumentException("无奈匹配对应的枚举类型"); } return t1 == null ? t2 : t1; }}剖析代码,依据运行时具体枚举类的参数,获取所有枚举值,并将各个枚举值序列和枚举值名与枚举值之间做映射(保留在Map中),如上述枚举类型,将会生成以下Map: ...

April 10, 2021 · 2 min · jiezi

关于springboot:基于-magicapi-搭建自己的低代码平台

一、前言2021 开年“低代码”成了热门话题,各大云厂商都在加码。作为一般企业的咱们是否有也能够深度定制一套本人的“低代码”平台呢? 二、云厂商的低代码平台阿里推出了易搭,通过简略的拖拽、配置,即可实现业务利用的搭建。旨在为宽广中小企业提供一套低成本的企业应用搭建解决方案。利用无缝植入钉钉企业工作台,随时随地、高效协同。 腾讯则是推出了微搭,通过行业化模板、拖放式组件和可视化配置疾速构建多端利用(小程序、H5 利用、Web 利用等),买通了小程序、云函数。 三、搭建咱们本人的低代码平台?!回到前言中的问题,咱们是否能够基于开源我的项目来疾速搭建咱们本人的低代码平台呢?答案是必定的,目前曾经有很多十分不错的开源我的项目,apijson、dataway 还有前面我要重点介绍的 magic-api 都是十分不错的低代码开源我的项目。上面大家请跟着我一起来看看明天我要举荐的三个低代码开源我的项目:百度 amis、h5-Dooring 和 magic-api。 3.1百度 amis(前端)百度 amis 是一套前端低代码框架,通过 JSON 配置就能生成各种后盾页面,极大缩小开发成本,甚至能够不须要理解前端。 3.2 h5-Dooring(前端)h5-Dooring,让 H5 制作像搭积木一样简略, 轻松搭建 H5 页面, H5 网站, PC 端网站, 可视化设计。 H5 页面拖拽生成: 新建数字大屏: 数字大屏成果: 更多请查看官网http://h5.dooring.cn 3.3 magic-api(后端)magic-api 是一个基于 Java 的接口疾速开发框架,编写接口将通过 magic-api 提供的 UI 界面实现,主动映射为 HTTP 接口,无需定义 Controller、Service、Dao、Mapper、XML、VO 等 Java 对象即可实现常见的 HTTP API 接口开发。 在线开发调试 UI: 四、magic-api 搭建自 magic-api 在开源中国开源,笔者始终在关注此我的项目。magic-api 搭建比较简单,跟着官网仓库疾速开始即可。 4.1 退出依赖<!-- 以 spring-boot-starter 的形式援用 --><dependency> <groupId>org.ssssssss</groupId> <artifactId>magic-api-spring-boot-starter</artifactId> <version>1.0.1</version></dependency>4.2 增加配置server.port=9999#配置 web 页面入口magic-api.web=/magic/web#配置文件存储地位。当以 classpath 结尾时,为只读模式magic-api.resource.location=/data/magic-api4.3 成果 ...

April 8, 2021 · 2 min · jiezi

关于springboot:Spring-Boot-搭建-ELK这才是正确看日志的方式

在看大型网站的中间件技术,对于Elasticsearch有点趣味,所以将配置流程记录了一下。 为什么要用ELK ELK实际上是三个工具,Elastricsearch + LogStash + Kibana,通过ELK,用来收集日志还有进行日志剖析,最初通过可视化UI进行展现。一开始业务量比拟小的时候,通过简略的SLF4J+Logger在服务器打印日志,通过grep进行简略查问,然而随着业务量减少,数据量也会一直减少,所以应用ELK能够进行大数量的日志收集和剖析 简略画了一下架构图在环境配置中,次要介绍Mac和linux配置,windows零碎大致相同,当然,前提是大家都装置了JDK1.8及以上版本~ [root@VM_234_23_centos ~]# java -versionjava version "1.8.0_161"Java(TM) SE Runtime Environment (build 1.8.0_161-b12)Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode) 留神: 高版本的ELK同样须要高版本的JDK反对,本文配置的ELK版本是6.0+,所以须要的JDK版本不小于1.8 ElasticSearch Elasticsearch 是一个分布式的 RESTful 格调的搜寻和数据分析引擎,可能解决不断涌现出的各种用例。作为 Elastic Stack 的外围,它集中存储您的数据,帮忙您发现意料之中以及意料之外的状况。 Mac装置和运行 装置:brew install elasticsearch运行:elasticsearch linux: 从Elasticsearch官网地址下载(也能够下载完,通过ftp之类的工具传上去),gz文件的话通过tar进行解压缩,而后进入bin目录下运行软件 [root@VM_234_23_centos app]# curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.2.4.tar.gz[root@VM_234_23_centos app]# tar -zxvf elasticsearch-6.2.4.tar.gz[root@VM_234_23_centos app]# cd elasticsearch-6.2.4[root@VM_234_23_centos elasticsearch-6.2.4]# ./bin/elasticsearch 留神: 在Linux机器上,运行elasticsearch须要一个新的用户组,文章最初有Elastic在linux装置的踩坑记录 Logstash Logstash 是开源的服务器端数据处理管道,可能同时从多个起源采集数据,转换数据,而后将数据发送到您最喜爱的 “存储库” 中。(咱们的存储库当然是 Elasticsearch。)-官网卖萌 1.软件装置 Mac装置: brew install logstash linux装置: ...

April 2, 2021 · 4 min · jiezi

关于springboot:一次事务提交错误

在应用JPA审计性能后,我的项目保留问卷过程中呈现了如下谬误,依据错误信息可知,是提交jpa事务时产生谬误。 2021-03-20 14:52:04.570 ERROR 21225 --- [io-8002-exec-10] c.y.q.e.GlobalExceptionHandler : 程序运行异样: 主机 127.0.0.1 调用地址 http://localhost/admin/questionnaire 错误信息 Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction过后在保留问卷过程中存在 @CreatedByprivate User createUser;/** * 在保留之前设置发明工夫. */ @PrePersistpublic void initCreateTime() { this.createTime = new Timestamp(System.currentTimeMillis());}/** * 在保留之后设置token. */ @PostPersistpublic void initToken() {...}主动设置user,保留前设置发明工夫,保留后设置token操作。狐疑是设置user后提交了事务,导致设置token操作提交时报错。进而得出@CreatedBy注解与@PostPersist注解无奈一起应用的论断 解决尝试在save办法上增加事务注解。后果还是报错。尝试在则须要将 @PrePersist @PostPersist注解的办法挪动到实体监听器中,后果还是报错。将设置token办法放到m层中,监听器调用m层设置token办法,后果监听器无奈注入m层。切面实现token设置,未尝试。对m层save办法进行集成测试,排除其余的影响,发现能正确执行,排除了@CreatedBy注解与@PostPersist注解的抵触问题,问题出在别处。将日志等级变更为debug,查看报错起因。依据堆栈信息,发现在保留问卷过程中执行了获取以后登录用户的办法,该办法操作了数据库,导致提交了事务,从而使得@CreateBy注解在获取以后登录用户后,再次更新数据时无事务可用。 /** * 审计 获取以后登录用户实现. */ private class SpringSecurityAuditorAware implements AuditorAware<User> { @Autowired private UserService userService; @Override public Optional<User> getCurrentAuditor() { User user = this.userService.getCurrentLoginUser(); if (user == null) { throw new RuntimeException("未获取到以后登录用户"); } return Optional.of(user); }}启用审计性能在对数据库执行操作时总是会获取以后登录用户。,咱们之前的办法就是从数据库中查问。问题找到了,剩下的就是使得此办法不操作数据库,最简略的思路就是像前台一样设置缓存。在用户登录的时候设置user,当要获取以后登录用户时不再进行数据库操作,而是间接返回user。 ...

March 20, 2021 · 1 min · jiezi

关于springboot:04SpringBoot工程下如何实现对HikariCP连接池的整合

池化思维剖析池化思维是咱们我的项目开发过程中的一种十分重要的思维,如整数池,字符串池,对象池、连接池、线程池等都是池化思维的一种利用,都是通过复用对象,以缩小因创立和开释对象所带来的资源耗费,进而来晋升零碎性能。例如Integer对象的外部池利用,代码如下: package com.cy.java.pool;public class TestInteger01 { public static void main(String[] args) { Integer n1=100;//Integer.valueOf(100) 编译时优化 Integer n2=100; Integer n3=200; Integer n4=200;//池中没有则new Integer(200) System.out.println(n1==n2);//true System.out.println(n3==n4);//false } }数据库连接池简介背景剖析目开发过程中应用程序与数据库交互时,“取得连贯”或“开释连贯”是十分耗费系统资源的两个过程,频繁地进行数据库连贯的建设和敞开会极大影响零碎的性能,若多线程并发量很大,这样耗时的数据库连贯就可能让零碎变得卡顿。因为TCP连贯的创立开销非常低廉,并且数据库所能承载的TCP并发连接数也有限度,针对这种场景,数据库连接池应运而生。如下图所示: 思考:如果当初是让你去设计一个连接池,你会从什么角度进行设计?第一:物理存储构造(基于什么构造去存储数据)第二:基于什么算法从池中取连贯?第三:基于什么算法从池中移除连贯?第四:当池中没有连贯时,基于什么形式解决连贯申请?第五:池是能够共享,咱们须要思考池在拜访的时并发平安? 连接池原理剖析在零碎初始化的时候,在内存中开拓一片空间,将肯定数量的数据库连贯作为对象存储在对象池里,并对外提供数据库连贯的获取和偿还办法。用户拜访数据库时,并不是建设一个新的连贯,而是从数据库连接池中取出一个已有的闲暇连贯对象;应用结束偿还后的连贯也不会马上敞开,而是由数据库连接池对立治理回收,为下一次借用做好筹备。如果因为高并发申请导致数据库连接池中的连贯被借用结束,其余线程就会期待,直到有连贯被偿还。整个过程中,连贯并不会敞开,而是源源不断地循环应用,有借有还。数据库连接池还能够通过设置其参数来管制连接池中的初始连接数、连贯的上上限数,以及每个连贯的最大应用次数、最大闲暇工夫等,也能够通过其本身的管理机制来监督数据库连贯的数量、应用状况等。 Java中的连接池Java官网,为了在应用程序中更好的利用连接池技术,定义了一套数据源标准,例如javax.sql.DataSource接口,基于这个接口,很多团队或集体创立了不同的连接池对象。而后咱们的应用程序中通过耦合与DataSource接口,便能够不便的切换不同厂商的连接池。Java我的项目中通过连接池获取连贯的一个根本过程,如下图所示: 在上图中,用户通过DataSource对象的getConnection()办法,获取一个连贯。如果池中有连贯,则间接将连贯返回给用户。如果池中没有连贯,则会调用Dirver(驱动,由数据库厂商进行实现)对象的connect办法从数据库获取,拿到连贯当前,能够将连贯在池中放一份,而后将连贯返回给调用方。连贯需求方再次须要连贯时,能够从池中获取,用完当前再还给池对象。 数据库连接池在Java数据库相干中间件产品群中,应该算是底层最根底的一类产品,作为企业应用开发必不可少的组件,有数蠢才们为咱们奉献了一个又一个的优良产品,它们有的随时代倒退,功成身退,有的则还在一直迭代,老而弥坚,更有新生代产品,或性能无敌,或性能全面。目前市场上常见的连接池有DBCP、C3P0,DRUID,HikariCP等。SpringBoot工程下HikariCP整合测试数据初始化关上mysql控制台,而后按如下步骤执行goods.sql文件。第一步:登录mysql。 mysql –uroot –proot第二步:设置控制台编码方式。 set names utf8;第三步:执行goods.sql文件(切记不要关上文件复制到mysql客户端运行)。 source d:/goods.sql其中goods.sql文件内容如下: drop database if exists dbgoods;create database dbgoods default character set utf8;use dbgoods;create table tb_goods( id bigint primary key auto_increment, name varchar(100) not null, remark text, createdTime datetime not null)engine=InnoDB;insert into tb_goods values (null,'java','very good',now());insert into tb_goods values (null,'mysql','RDBMS',now());insert into tb_goods values (null,'Oracle','RDBMS',now());insert into tb_goods values (null,'java','very good',now());insert into tb_goods values (null,'mysql','RDBMS',now());insert into tb_goods values (null,'Oracle','RDBMS',now());insert into tb_goods values (null,'java','very good',now());创立我的项目Module并增加相干依赖第一步:基于IDEA创立我的项目Module,如图所示: ...

March 16, 2021 · 2 min · jiezi

关于springboot:03基于IDEA创建SpringBoot项目并进行入门分析

SpringBoot 我的项目创立创立Module基于IDEA创立我的项目Module,模块名为04-springboot-start,组id和包名为com.cy,如图所示:填写module信息,如图所示:抉择我的项目module版本,临时不须要本人手动增加任何依赖,如图所示:填写Module名称,实现module创立,如图所示 我的项目构造剖析我的项目Module创立好当前,其代码构造剖析,如图所示: SpringBoot 我的项目启动剖析启动入口SpringBoot 工程中由SpringBootApplication注解形容的类为启动入口类,例如: package com.cy;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class Application {//Application.class public static void main(String[] args) {//Main Thread SpringApplication.run(Application.class, args); }}启动过程概要剖析SpringBoot工程启动时其繁难初始化过程,如图所示: 在启动过程中底层做了哪些事件,大抵形容如下:1)基于配置加载类(通过ClassLoader将指定地位的类读到内存->底层通过线程调用IO从磁盘读取到内存)。2)对类进行剖析(创立字节码对象-Class类型,通过反射获取器配置信息)。3)对于指定配置(例如由spring特定注解形容)的对象存储其配置信息(借助BeanDefinition对象存储)。4)基于BeanDefinition对象中class的配置构建类的实例(Bean对象),并进行bean对象的治理(可能会存储到bean池)。 SpringBoot 疾速入门剖析业务形容在我的项目Module中定义一个类,类名为DefaultCache,而后将此类对象交给Spring创立并治理。最初通过单元测试对类的实例进行剖析。 API设计剖析基于业务形容,进行API及关系设计,如图所示: 代码编写及运行基于业务及API设计,进行代码编写,其过程如下: 第一步:定义DefaultCache类 package com.cy.pj.common.cache;import org.springframework.stereotype.Component;/** * @Component 注解形容的类,示意此类交给Spring框架治理。 */@Componentpublic class DefaultCache {}第二步:定义DefaultCacheTests单元测试类 package com.cy.pj.common.cache;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;@SpringBootTestpublic class DefaultCacheTests { /** * @Autowired 注解形容的属性由spring框架依照肯定规定为其注入值(赋值) * 赋值过程是怎么的? * 1)依赖查找?(请问查找规定是什么?) * 2)依赖注入?(须要借助什么技术?) */ @Autowired private DefaultCache defaultCache; @Test void testDefaultCache(){ System.out.println(defaultCache.toString()); //FAQ? defaultCache变量援用的对象是由谁创立的,存储 到了哪里?bean pool }}第三步:运行单元测试类进行利用剖析启动运行单元测试办法,检测其输入后果,基于后果剖析:1)SpringBoot我的项目中Bean对象的构建。2)SpringBoot我的项目中Bean对象的获取。 ...

March 16, 2021 · 1 min · jiezi

关于springboot:02IDEA工具之IDEA中Module的创建删除导入

创立我的项目Module并运行创立并运行java module在IDEA关上的我的项目中创立Java Module,如图所示: 在创立Java Module的界面,抉择Next,输出module名,如图所示: Java Module创立好当前的构造,如图所示: 在我的项目模块01-javase中创立包,例如: 在指定包中创立类,如图所示: 运行Java类,如图所示: 创立并运行Maven Module在我的项目中,创立maven module,如图所示: 接下来,输出module根本信息,如图所示: 关上pom.xml文件,增加Junit依赖,如图所示: 创立包、单元测试类和办法,进行单元测试,如图所示: 创立并运行Spring Initializr Module在我的项目中,创立Spring Initializr Module,如图所示: 接下来输出Spring Initializr Module信息,如图所示: 抉择springboot版本,依赖,而后进入一下,如图所示: 输出module信息,而后实现Module的创立,如图所示: Spring Initializr Module创立好当前,其构造如图所示: Module创立好当前,能够关上pom.xml文件,批改其springboot版本,如图所示: SpringBoot 版本批改好当前(可选),向pom.xml右键,抉择Add as Maven Project 选项,进行maven我的项目构建(此时才会springboot 依赖下载和编译),例如: Module 构建实现,关上启动类运行module,如图所示: Module胜利启动和运行后果,如图所示: IDEA中我的项目的其它操作从IDEA删除我的项目module?首先关上我的项目构造(Project Structure),找到Moudles菜单项,选中某个moudle,点击"-"符号,移除我的项目.而后在IDEA中,在具体的我的项目模块上,右键抉择delete执行我的项目module删除操作.从IDEA中导入我的项目Module?首先关上我的项目构造(Project Structure),找到Moudles菜单项,选中某个moudle,点击"+"符号.找到对应的我的项目,而后执行导入(import)操作.从IDEA中load/unload 我的项目modules?间接抉择我的项目module,而后右键执行 load/upload操作即可,这个动作相似eclipse中的close操作 总结(Summary)本章节中解说了IDEA工具下Java Module,Maven Module,Spring Boot Module 的创立,运行,删除,导入等操作,通过这些操作把握IDEA中对我的项目的一个基本操作。

March 16, 2021 · 1 min · jiezi

关于springboot:服务端响应数据的标准化封装

在Controller类的逻辑办法中进行失常的响应数据封装,例如: package com.cy.pj.module.controller;@RestControllerpublic class ArithmeticController { @RequestMapping("/doCompute/{n1}/{n2}") public JsonResult doCompute(@PathVariable Integer n1, @PathVariable Integer n2){ Integer result=n1/n2; JsonResult r=newJsonResult("计算结果:"+result); r.setData(result); return r; } } 在全局异样解决对象中进行异样响应数据的封装,例如: package com.cy.pj.common.web;@RestControllerAdvicepublic class GlobalExceptionHandler { private static final Logger log=LoggerFactory.getLogger(GlobalExceptionHandler.class); @ExceptionHandler(ArithmeticException.class) public JsonResult doHandleArithmeticException(ArithmeticException e){ e.printStackTrace(); log.info("exception {}",e.getMessage()); return new JsonResult(e);//封装异样后果 } }

March 13, 2021 · 1 min · jiezi

关于springboot:SpringBoot-健康检查分析

SpringBoot工程中提供了对象我的项目整体运行环境的监控,提供了对Bean对象以及Bean之间依赖关系的检查,提供了Web申请中url映射关系的剖析和查看,提供了连接池,线程池,jvm等参数的监控. SpringBoot 工程中健康检查落地实现第一步:增加健康检查依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId></dependency>第二步:关上监控选项(默认可查看的选项比拟少) management.endpoints.web.exposure.include=*第三步:关上浏览器,在浏览器地址栏输出如下地址,而后进行拜访 http://localhost/actuator如果没有问题的话,会在浏览器中出现各种监控选项,通过拜访监控选项获取具体相干信息.

March 13, 2021 · 1 min · jiezi

关于sping:Spring-Boot-工程中Bean对象的核心特性

背景信息对于计算机而言,"计算"是它要解决的一个最外围的问题?那如何让计算更加的"高效"和"低耗",这也是咱们程序员在编程过程中要思考的一个间接指标.在spring工程中将对象交给spring治理,其目标也是让Spring赋予这些对象更多的迷信个性,进而让对象在内存中更加高效和低耗的运行,对于这些迷信的个性又如何了解呢? Spring 中Bean的个性剖析?在Spring框架中为咱们Bean对象赋予了很多迷信个性,例如:1)提早加载(提早对象创立)-通过此个性较少资源占用问题(临时不必的一些大对象,可思考应用此个性)2)作用域(让对象存储在指定作用域中而后能够重复使用-进步拜访性能)3)生周期办法(对象在创立和销毁之前能够对对象进行一些初始化和资源销毁操作)基于这些个性,能够让咱们在sping工程中的对象能够更加高效的解决一些业务问题。 1.如何对池对象进行设计才可能更好的让池对象服务于咱们的业务?(低耗,高效)2.个别池对象的设计会利用到什么设计模式?(享元模式-重点设计在对象的重用上)3.Spring中提早加载指的是类临时不加载到内存吗?(不是,类加载到内存后临时不创立类的实例)4.Spring中的提早加载对象的实例何时创立?(应用时)5.Spring中的提早加载个性重点要解决什么问题?(资源耗费问题)6.Spring中对象作用域如何了解?(对象的一个利用领域问题)7.Singleton作用域的类,雷同名字的实例在内存中只有一份(会存储到spring的对象池中),能够重用.8.Singleton作用域的类如何让其反对提早加载个性?(应用@Lazy注解形容)9.Prototype作用域的类默认反对提早加载吗?(反对,默认就是应用时创立,无需应用@Lazy注解形容)10.Prototype作用域的类的实例是每次从spring框架申请时都会创立吗?会11.程序中的每个对象都有生命周期,但不肯定都要定义生命周期办法?是

March 13, 2021 · 1 min · jiezi

关于springboot:Eureka集群搭建3个节点

环境:windows单台电脑 步骤如下: 在eureka服务项目里新建3个properies文件 2.批改host文件,eureka服务器会过滤雷同ip的注册地址,所以要批改host文件 #进行主机名映射127.0.0.1 sdpeer1127.0.0.1 sdpeer2127.0.0.1 sdpeer33.java -jar形式启动三个节点 #启动sdpeer1的eureka服务器,端口:8260java -jar -Dspring.profiles.active=sdpeer1 service-discovery-0.0.1-SNAPSHOT.jar#启动sdpeer2的eureka服务器,端口:8262java -jar -Dspring.profiles.active=sdpeer2 service-discovery-0.0.1-SNAPSHOT.jar#启动sdpeer3的eureka服务器,端口:8263java -jar -Dspring.profiles.active=sdpeer3 service-discovery-0.0.1-SNAPSHOT.jar

March 12, 2021 · 1 min · jiezi

关于springboot:看看人家那后端-API-接口写得那叫一个优雅

公众号:MarkerHub,网站:https://markerhub.com作者:里奥 ii 起源:r6d.cn/tEvn 在挪动互联网,分布式、微服务流行的明天,当初我的项目绝大部分都采纳的微服务框架,前后端拆散形式,(题外话:前后端的工作职责越来越明确,当初的前端都称之为大前端,技术栈以及生态圈都曾经十分成熟;以前后端人员瞧不起前端人员,那当初后端人员要重新认识一下前端,前端曾经很成体系了)。 个别零碎的大抵整体架构图如下: ❝须要阐明的是,有些小伙伴会回复说,这个架构太简略了吧,太 low 了,什么网关啊,缓存啊,消息中间件啊,都没有。因为老顾这篇次要介绍的是 API 接口,所以咱们聚焦点,其余的模块小伙伴们自行去补充。 ❞ 接口交互前端和后端进行交互,前端依照约定申请 URL 门路,并传入相干参数,后端服务器接管申请,进行业务解决,返回数据给前端。 ❝针对 URL 门路的 restful 格调,以及传入参数的公共申请头的要求(如:app_version,api_version,device 等),老顾这里就不介绍了,小伙伴们能够自行去理解,也比较简单。 ❞ 着重介绍一下后端服务器如何实现把数据返回给前端? 返回格局后端返回给前端咱们个别用 JSON 体形式,定义如下: {   #返回状态码   code:integer,     #返回信息形容   message:string,   #返回值   data:object  }CODE 状态码code 返回状态码,个别小伙伴们是在开发的时候须要什么,就增加什么。 如接口要返回用户权限异样,咱们加一个状态码为 101 吧,下一次又要加一个数据参数异样,就加一个 102 的状态码。这样尽管可能照常满足业务,但状态码太凌乱了 咱们应该能够参考 HTTP 申请返回的状态码 :上面是常见的HTTP状态码:  200 - 申请胜利  301 - 资源(网页等)被永恒转移到其它URL  404 - 申请的资源(网页等)不存在  500 - 外部服务器谬误 咱们能够参考这样的设计,这样的益处就把谬误类型归类到某个区间内,如果区间不够,能够设计成 4 位数。 #1000~1999 区间示意参数谬误  #2000~2999 区间示意用户谬误  #3000~3999 区间示意接口异样   这样前端开发人员在失去返回值后,依据状态码就能够晓得,大略什么谬误,再依据 message 相干的信息形容,能够疾速定位。 Message这个字段绝对了解比较简单,就是产生谬误时,如何敌对的进行提醒。个别的设计是和 code 状态码一起设计,如 再在枚举中定义,状态码 状态码和信息就会一一对应,比拟好保护。 Data返回数据体,JSON 格局,依据不同的业务又不同的 JSON 体。 咱们要设计一个返回体类 Result 管制层 Controller咱们会在 controller 层解决业务申请,并返回给前端,以 order 订单为例 咱们看到在取得 order 对象之后,咱们是用的 Result 构造方法进行包装赋值,而后进行返回。小伙伴们有没有发现,构造方法这样的包装是不是很麻烦,咱们能够优化一下。 ...

March 10, 2021 · 1 min · jiezi

关于springboot:SpringBoot的核心特性

Spring boot是一个脚手架,构建于Spring框架(Framework)根底之上,基于疾速构建理念,提供了主动配置性能,可实现其开箱即用个性(创立完一个根本的我的项目当前,可零配置或者大量配置即可运行咱们的我的项目),其外围次要有如下几个方面: 起步依赖(Starter Dependency)-我的项目创立时底层帮你关联依赖。主动配置(Auto Configuration)。健康检查(Actator)-监控。其中,Spring Boot官网地址为https://spring.io/projects/spring-boot。

March 9, 2021 · 1 min · jiezi

关于springboot:开放平台用户服务

背景用户服务是对应用gateway的客户进行治理的服务,想应用gateway的用户通过门户网站进行注册,登陆之后申请API进行测试调用。 用户服务包含以下几个性能: 个人用户相干:集体用户注册,登录,登记机构用户相干: 治理端录入用户-发送激活验证码-用户去激活画面凭借手机号和机构号激活,激活的同时生成密钥对和appSecret。登录申请API查看api调用状况查看AppSecret,上传机构公钥,下载平台公钥内网用户相干:治理端录入并激活,没有密钥对,只有appSecret机构用户状态转移图:已录入2(发送激活短信,批改用户信息,销户)-待激活3(可发送激活验证码,批改用户信息,销户)-已激活1(批改用户信息,销户,批改明码,登陆)-已销户0。不同的状态对应机构和管理员可进行不同的操作。在信息录入的时候,须要进行的校验:明码强度;机构号格局;手机号格局;用户名格局;邮箱格局。 用户服务波及敏感性息,应防止用户明码被透露,因而对于安全性思考如下: 用户在注册、登录等须要传输明码的场合,明码均加密传输明码在数据库中加密保留设置明码时,查看明码强度,至多六位数字及大小写字母的组合用户1分钟内登录失败5次及以上就锁定用户,24小时内无奈再次登录,防止用户名透露后被暴力破解用户在登录时,减少图片验证码。图片验证码应用后即生效机构用户激活/用户找回明码时应用到手机验证码,手机验证码有效期为5分钟,每分钟最多发一次,输错5次即生效如果用户名或明码谬误,弹出“用户名或明码”谬误,而不是“用户名不存在”,“明码谬误”等提示信息,防止枚举用户名或机构号技术细节的实现: 明码加密传输明码在数据库中加密保留图片验证码手机验证码的有效期和发送次数

March 7, 2021 · 1 min · jiezi

关于springboot:用WebMvcTest测试MVC-Web-Contorller一

原文 https://reflectoring.io/sprin...翻译: 祝坤荣在这个测试Spring Boot系列的第二局部,咱们来看下web contoller。开始,咱们会摸索下web controller到底做了什么,而后咱们能够基于写单元测试来笼罩所有它的职责。 而后,咱们来看看如果在测试用笼罩这些职责。只有当所有这些职责都被笼罩到了,咱们才能够必定咱们的contoller的行为应该与线上环境一样了。 样例代码这篇文章提供在GitHub上的可运行代码。 测试Spring Boot系列这篇教程是一个系列的一部分: Spring Boot的单元测试应用@WebMvcTest测试Spring Boot的MVC Web Controller应用@DataJpaTest测试Spring Boot的JPA Queries应用@SpringBootTest进行集成测试如果你喜爱看视频学习,能够看看Philip的测试Spring Boot利用课程(如果你通过这个链接购买,我有分成)。 依赖咱们会应用JUnit Jupiter(JUnit 5)作为测试框架,Mockito来模仿,AssertJ来建设断言,Lombok来缩小冗余代码: dependencies { compile('org.springframework.boot:spring-boot-starter-web') compileOnly('org.projectlombok:lombok') testCompile('org.springframework.boot:spring-boot-starter-test') testCompile 'org.junit.jupiter:junit-jupiter-engine:5.2.0' testCompile('org.mockito:mockito-junit-jupiter:2.23.0')} AssertJ和Mockito会通过引入spring-boot-starter-test主动引入。 Web Controller的职责让咱们先看一个典型的REST controller: @RequiredArgsConstructorclass RegisterRestController { private final RegisterUseCase registerUseCase; @PostMapping("/forums/{forumId}/register") UserResource register( @PathVariable("forumId") Long forumId, @Valid @RequestBody UserResource userResource, @RequestParam("sendWelcomeMail") boolean sendWelcomeMail) { User user = new User( userResource.getName(), userResource.getEmail()); Long userId = registerUseCase.registerUser(user, sendWelcomeMail); return new UserResource( userId, user.getName(), user.getEmail()); }}Controller的办法通过@PostMapping的申明来定义须要监听的URL,HTTP办法和content类型。 ...

February 28, 2021 · 1 min · jiezi

关于springboot:微服务组件-mica-243GA-发布开源-micaloggingmicacaffeine-组件

一、mica(云母)mica 由如梦技术外部的 lutool(撸秃) 演变而来。lutool 诞生于 2017 年,受 jhipster 启发逐步形成一个微服务的外围集。 因 lutool 名称与性能不太合乎,故在2019年开源时将其改名为 mica(云母),寓意为云服务的基石。 二、版本阐明留神: mica-v2.0 分支仅做 bug 修复,不再做性能更新。 最新版本mica 版本spring boot 版本spring cloud 版本2.4.3-GAmica 2.4.x2.4.x20202.1.1-GAmica 2.0.x~2.1.x2.2.x ~ 2.3.xHoxton2.4.x 累积更新v2.4.3-GA - 2021-02-27 开源 mica-logging 组件。 mica-core 欠缺 JsonUtil 和 SystemUtil。 mica-core 申请开始工夫 key。 mica-xss 反对本义和清理2种模式。 mica-swagger api key 认证 token key-name 默认改为 Authorization。 增加观星图。 更换模块图。 降级 spring boot 到 2.4.3v2.4.2-GA - 2021-02-08 mica-caffeine、mica-redis 默认 @EnableCaching。 mica-bom 增加 mica-caffeine 模块。v2.4.1-GA - 2021-01-29 【重大】mica-xss ThreadLocal remove。 mica-xss 欠缺,新增字符串全局 trim、换行、本义配置。v2.4.0-GA - 2021-01-18 mica-xss XssUtil 放开 Whitelist 不便自定义。 mica-core 增加 ACTIVE_PROFILES_PROPERTY 常量。 挪动 SpringContextUtil 到 mica-core 中。 降级到 spring boot 2.4.2。 应用 spring cloud 2020.0.0 降级 mica auto 到 2.0.3。 降级到 mica-weixin 2.0.5。2.1.x 更新v2.1.1-GA - 2021-02-22【重大】mica-xss ThreadLocal remove。 降级到 mica-weixin 2.0.5。重点阐明: 应用了 mica-xss 的同学,请降级到 2.1.1-GA 或者 2.4.3-GA ...

February 28, 2021 · 1 min · jiezi

关于springboot:springboot240以上版本config-client配置bootstrapproperties无法启动

确保config server可能拜访1.拜访config server(config server配置为git),拜访链接http://localhost:7070/app/test/master 2.拜访如下图: 3.确认config server所有工作失常,config server并没有问题,排查config client。 查看config client1.查看配置application.properties ## Spring Cloud Eureka 客户端利用名称spring.application.name = spring-cloud-eureka-client## Spring Cloud Eureka 客户端服务端口server.port = 8080# 启用端点 envmanagement.endpoint.env.enabled=true# 裸露端点 env 配置多个,隔开management.endpoints.web.exposure.include=*management.endpoint.info.enabled=truemanagement.info.env.enabled=true#eureka.client.serviceUrl.defaultZone=http://localhost:9090/eurekalogging.level.org.springframework.cloud=tracebootstrap.properties eureka.client.serviceUrl.defaultZone=http://127.0.0.1:9090/eurekaspring.cloud.config.name=appspring.cloud.config.profile=testspring.cloud.config.label=masterspring.profiles.active=test#spring.config.import=optional:spring-cloud-config-server:http://localhost:7070/spring.cloud.config.fail-fast=true## 激活 Config 服务器发现spring.cloud.config.discovery.enabled=true## 配置 Config 服务器的利用名称(Service ID)spring.cloud.config.discovery.serviceId=spring-cloud-config-server查看配置发现并没有什么问题,确认配置没有问题,问题应该在其余中央。 2. 查看日志查看日志后发现,我配置的profile为test,然而并没有读取进去,依据日志来看,貌似并没有从bootstrap.properties读取配置,得出如下猜想: 1.配置文件应该在全副配置在application中。 2.springboot 并没有从bootstrap.properties启动,可能是短少什么依赖。 2.1 针对猜想1查看问题将所有配置整合在application中,再次启动。 ## Spring Cloud Eureka 客户端利用名称spring.application.name = spring-cloud-eureka-client## Spring Cloud Eureka 客户端服务端口server.port = 8080# 启用端点 envmanagement.endpoint.env.enabled=true# 裸露端点 env 配置多个,隔开management.endpoints.web.exposure.include=*management.endpoint.info.enabled=truemanagement.info.env.enabled=true#eureka.client.serviceUrl.defaultZone=http://localhost:9090/eurekalogging.level.org.springframework.cloud=trace#logging.level.org.springframework=traceeureka.client.serviceUrl.defaultZone=http://127.0.0.1:9090/eurekaspring.cloud.config.name=appspring.cloud.config.profile=testspring.cloud.config.label=masterspring.profiles.active=test#spring.config.import=optional:spring-cloud-config-server:http://localhost:7070/spring.cloud.config.fail-fast=true## 激活 Config 服务器发现spring.cloud.config.discovery.enabled=true## 配置 Config 服务器的利用名称(Service ID)spring.cloud.config.discovery.serviceId=spring-cloud-config-server启动之后日志如下,发现还是没有所有利用配置文件 ...

February 27, 2021 · 1 min · jiezi

关于springboot:SpringSecurity

SpringSecurity原理一款管制基于SpringAOP或者Servlet过滤器的平安框架在web利用开发中,平安无疑是非常重要的,抉择Spring Security来爱护web利用是一个十分好的抉择。Spring Security 是spring我的项目之中的一个平安模块,能够十分不便与spring我的项目无缝集成。特地是在spring boot我的项目中退出spring security更是非常简略。反对的认证形式能够通过 form 表单来认证能够通过 HttpBasic 来认证外围组件SecurityContext、SecurityContextHolder、Authentication、Userdetails 和 AuthenticationManager SecurityContext 平安上下文,用户通过Spring Security 的校验之后,验证信息存储在SecurityContext中SecurityContextHolder 其作用就是存储以后认证信息。 看名知义,是一个holder,用来hold住SecurityContext实例的。在典型的web应用程序中,用户登录一次,而后由其会话ID标识。服务器缓存持续时间会话的主体信息。在Spring Security中,在申请之间存储SecurityContext的责任落在SecurityContextPersistenceFilter上,默认状况下,该上下文将上下文存储为HTTP申请之间的HttpSession属性。它会为每个申请复原上下文SecurityContextHolder,并且最重要的是,在申请实现时革除SecurityContextHolder。SecurityContextHolder是一个类,他的性能办法都是动态的(static)。SecurityContextHolder能够设置指定JVM策略(SecurityContext的存储策略),这个策略有三种: MODE_THREADLOCAL:SecurityContext 存储在线程中。MODE_INHERITABLETHREADLOCAL:SecurityContext 存储在线程中,但子线程能够获取到父线程中的 SecurityContext。MODE_GLOBAL:SecurityContext 在所有线程中都雷同。SecurityContextHolder默认应用MODE_THREADLOCAL模式,即存储在以后线程中。在spring security利用中,咱们通常能看到相似如下的代码:  Copy SecurityContextHolder.getContext().setAuthentication(token); Authentication authentication 直译过去是“认证”的意思,在Spring Security 中Authentication用来示意以后用户是谁,一般来讲你能够了解为authentication就是一组用户名明码信息。UserDetails 看命知义,是用户信息的意思。其存储的就是用户信息,UserDetailsService 提到了UserDetails就必须得提到UserDetailsService, UserDetailsService也是一个接口,且只有一个办法loadUserByUsername,他能够用来获取UserDetails。通常在spring security利用中,咱们会自定义一个CustomUserDetailsService来实现UserDetailsService接口,并实现其public UserDetails loadUserByUsername(final String login);办法。咱们在实现loadUserByUsername办法的时候,就能够通过查询数据库(或者是缓存、或者是其余的存储模式)来获取用户信息,而后组装成一个UserDetails,(通常是一个org.springframework.security.core.userdetails.User,它继承自UserDetails) 并返回AuthenticationManager 用来解决身份认证的类,也称之为认证管理器。他的最罕用的实现类事 ProviderManager (认证管理中心)AuthenticationManager 是一个接口,它只有一个办法,接管参数为Authentication,其定义如下:  public interface AuthenticationManager { Authentication authenticate(Authentication authentication)  throws AuthenticationException; }AuthenticationManager 的作用就是校验Authentication,如果验证失败会抛出AuthenticationException异样。AuthenticationException是一个抽象类,因而代码逻辑并不能实例化一个AuthenticationException异样并抛出,实际上抛出的异样通常是其实现类,如DisabledException,LockedException,BadCredentialsException等。BadCredentialsException可能会比拟常见,即明码谬误的时候。ProviderManagerAuthenticationManager的实现类治理AuthenticationProvider列表,每一个AuthenticationProvider都是一个认证器不同的认证器解决不同的Authentication对象的认证。providerManager相当于代理了多个认证器三大configure()HttpSecurity 这个厉害了,做的大部分配置都是基于他来配置的HttpSecurityBuilder 看名字就是用来构建 HttpSecurity 的。属性 web.ignoring() 用来配置疏忽掉的 URL 地址,个别对于动态文件,咱们能够采纳此操作。  and 办法示意完结以后标签  permitAll 示意登录相干的页面/接口不要被拦挡。  antMatchers 过滤条件,范畴小的放在后面,范畴大的放在前面  .antMatchers("/login").permitAll()  .antMatchers("/user/**").hasRole("admin")  .antMatchers("/pubnews/**").hasRole("admin") SecurityBuilder ,即WebSecurity ...

February 25, 2021 · 2 min · jiezi

关于springboot:关于JSCH使用自义定连接池说明

1. JSCH应用办法jsch应用办法 2. JSCH工具类JSCH工具类 3. 创立连接池ConnectionPool.java @Slf4jpublic class ConnectionPool { private String strictHostKeyChecking; private Integer timeout; /** * ip地址 */ private String ip = ""; /** * 端口号 */ private Integer port = 22; /** * 用户名 */ private String username = ""; /** * 明码 */ private String password = ""; /** * 每次扩容减少几个连贯 */ private int incrementalConnections = 2; /** * 最大连接数 */ private int maxConnections = 10; /** * 最大闲暇连贯 */ private int maxIdle = 4; /** * 最小闲暇连贯 */ private int minIdel = 2; private Vector<PooledConnection> connections = null; @PostConstruct private void init() { createPool(); } /** * 构造方法 * * @param strictHostKeyChecking 连贯模式 * @param timeout 超时工夫 */ public ConnectionPool(String strictHostKeyChecking, Integer timeout) { this.strictHostKeyChecking = strictHostKeyChecking; this.timeout = timeout; } /** * 构造方法 * * @param strictHostKeyChecking 连贯模式 * @param timeout 超时工夫 * @param incrementalConnections 增量大小 */ public ConnectionPool(String strictHostKeyChecking, Integer timeout, int incrementalConnections) { this.strictHostKeyChecking = strictHostKeyChecking; this.timeout = timeout; this.incrementalConnections = incrementalConnections; } /** * 构造方法 * * @param strictHostKeyChecking 连贯模式 * @param timeout 超时工夫 * @param incrementalConnections 增量大小 * @param maxConnections 连接池最大连接数 */ public ConnectionPool(String strictHostKeyChecking, Integer timeout, int incrementalConnections, int maxConnections) { this.strictHostKeyChecking = strictHostKeyChecking; this.timeout = timeout; this.incrementalConnections = incrementalConnections; this.maxConnections = maxConnections; } /** * 创立连接池,判断连接池是否创立,如果连接池没有创立则创立连接池 */ public synchronized void createPool() { if (Objects.nonNull(connections)) { return; } connections = new Vector<>(); log.info("create shell connectionPool success!"); } /** * 创立指定数量的连贯放入连接池中 * * @param numConnections 创立数量 * @throws JSchException 建设近程连贯异样 */ private void createConnections(int numConnections) throws JSchException { for (int x = 0; x < numConnections; x++) { // 判断是否已达连接池最大连贯,如果达到最大连贯数据则不再创立连贯 if (this.maxConnections > 0 && this.connections.size() >= this.maxConnections) { break; } //在连接池中新增一个连贯 try { connections.addElement(new PooledConnection(newConnection(), ip)); } catch (JSchException e) { log.error("create shell connection failed {}", e.getMessage()); throw new JSchException(); } log.info("Session connected!"); } } /** * 新一个连贯session * * @return 创立的session * @throws JSchException 近程连贯异样 */ private Session newConnection() throws JSchException { // 创立一个session JSch jsch = new JSch(); Session session = jsch.getSession(username, ip, port); session.setPassword(password); Properties sshConfig = new Properties(); sshConfig.put("StrictHostKeyChecking", strictHostKeyChecking); session.setConfig(sshConfig); session.connect(timeout); session.setServerAliveInterval(1800); return session; } /** * 获取一个可用session * * @param ip ip地址 * @param port 端口号 * @param username 用户名 * @param password 明码 * @return 可用的session * @throws JSchException 近程连贯异样 */ public synchronized Session getConnection(String ip, Integer port, String username, String password) throws JSchException { this.ip = ip; this.port = port; this.username = username; this.password = password; // 连接池还没创立,则返回 null if (Objects.isNull(connections)) { return null; } // 取得一个可用的数据库连贯 Session session = getFreeConnection(); // 如果目前没有能够应用的连贯,即所有的连贯都在应用中,等一会重试 while (Objects.isNull(session)) { wait(250); session = getFreeConnection(); } return session; } /** * 获取一个可用session * * @return 返回可用session * @throws JSchException 近程连贯异样 */ private Session getFreeConnection() throws JSchException { Session session = findFreeConnection(); // 如果没有可用连贯,则创立连贯, if (Objects.isNull(session)) { createConnections(incrementalConnections); session = findFreeConnection(); if (Objects.isNull(session)) { return null; } } return session; } /** * 查找可用连贯 * * @return 返回可用连贯 */ private Session findFreeConnection() { Session session = null; PooledConnection conn; Enumeration<PooledConnection> enumerate = connections.elements(); // 遍历所有的对象,看是否有可用的连贯 while (enumerate.hasMoreElements()) { conn = enumerate.nextElement(); if (!ip.equals(conn.getTag())) { continue; } if (!conn.isBusy()) { session = conn.getSession(); conn.setBusy(true); if (!testConnection(session)) { try { session = newConnection(); } catch (JSchException e) { log.error("create shell connection failed {}", e.getMessage()); return null; } conn.setSession(session); } break; } } return session; } /** * 测试连贯是否可用 * * @param session 须要测试的session * @return 是否可用 */ private boolean testConnection(Session session) { boolean connected = session.isConnected(); if (!connected) { closeConnection(session); return false; } return true; } /** * 将session放回连接池中 * * @param session 须要放回连接池中的session */ public synchronized void returnConnection(Session session) { // 确保连接池存在,如果连贯没有创立(不存在),间接返回 if (Objects.isNull(connections)) { log.error("ConnectionPool does not exist"); return; } PooledConnection conn; Enumeration<PooledConnection> enumerate = connections.elements(); // 遍历连接池中的所有连贯,找到这个要返回的连贯对象,将状态设置为闲暇 while (enumerate.hasMoreElements()) { conn = enumerate.nextElement(); if (session.equals(conn.getSession())) { conn.setBusy(false); } } } /** * 刷新连接池 * * @throws JSchException 近程连贯异样 */ public synchronized void refreshConnections() throws JSchException { // 确保连接池己翻新存在 if (Objects.isNull(connections)) { log.error("ConnectionPool does not exist"); return; } PooledConnection conn; Enumeration<PooledConnection> enumerate = connections.elements(); while (enumerate.hasMoreElements()) { conn = enumerate.nextElement(); if (conn.isBusy()) { wait(5000); } closeConnection(conn.getSession()); conn.setSession(newConnection()); conn.setBusy(false); } } /** * 敞开连接池 */ public synchronized void closeConnectionPool() { // 确保连接池存在,如果不存在,返回 if (Objects.isNull(connections)) { log.info("Connection pool does not exist"); return; } PooledConnection conn; Enumeration<PooledConnection> enumerate = connections.elements(); while (enumerate.hasMoreElements()) { conn = enumerate.nextElement(); if (conn.isBusy()) { wait(5000); } closeConnection(conn.getSession()); connections.removeElement(conn); } connections = null; } /** * 敞开session会话 * * @param session 须要敞开的session */ private void closeConnection(Session session) { session.disconnect(); } /** * 线程暂停 * * @param mSeconds 暂停工夫 */ private void wait(int mSeconds) { try { Thread.sleep(mSeconds); } catch (InterruptedException e) { log.error("{} 线程暂停失败 -> {}", Thread.currentThread().getName(), e.getMessage()); } } /** * 对象连接状态 */ class PooledConnection { /** * 近程连贯 */ Session session; /** * 此连贯是否正在应用的标记,默认没有正在应用 */ boolean busy = false; /** * 连贯标记 */ String tag; /** * 构造函数,依据一个 Session 结构一个 PooledConnection 对象 * * @param session 连贯 * @param tag 连贯标记 */ public PooledConnection(Session session, String tag) { this.session = session; this.tag = tag; } public Session getSession() { return session; } public void setSession(Session session) { this.session = session; } public boolean isBusy() { return busy; } public void setBusy(boolean busy) { this.busy = busy; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } } public int getIncrementalConnections() { return this.incrementalConnections; } public void setIncrementalConnections(int incrementalConnections) { this.incrementalConnections = incrementalConnections; } public int getMaxConnections() { return this.maxConnections; } public void setMaxConnections(int maxConnections) { this.maxConnections = maxConnections; }}4. 革新shellUtilShellUtil.java ...

February 20, 2021 · 9 min · jiezi

关于springboot:二自动装配

springboot的主动拆卸大大加重了开发人员的工作量,约定大于配置的思维深刻javaer的心,那么springboot是如何实现主动拆卸的呢? 上面通过局部源码与图片开始解开其主动配置的神秘面纱。 首先,先从启动类注解开始能够看到@SpringBootApplication中还有一个@EnableAutoConfiguration注解,从字面意思上就能够看出它与主动拆卸有不浅的关系,再看看它的全貌。能够看到@EnableAutoConfiguration下面还有一个@Import注解,其作用是导入一个class,再看看这个AutoConfigurationImportSelector类。这个类有一个getAutoConfigurationEntry办法,作用是获取须要主动拆卸类名的set的条目。getAutoConfigurationEntry中还调用了一个办法getCandidateConfigurationsgetCandidateConfigurations中调用了SpringFactoriesLoader.loadFactoryNames(这个办法在上一篇 spi与SpringFactoriesLoader中有具体的解析),从META-INF/spring.factories中读取key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的所有的值返回对应的List。最初咱们来看看spring-boot-autoconfigure包下的META-INF/spring.factories文件。![上传中...]()能够看到springboot为我的项目筹备了十分多的配置类,这样开发人员就只需按需更改配置就行了,不须要再去手动实现配置。 最初附上简略的流程图

February 19, 2021 · 1 min · jiezi

关于springboot:SpringBoot工程下的健康检查应用

衰弱监控简述Spring Boot 中actuator模块提供了健康检查,审计、指标收集,HTTP跟踪等性能,能够帮忙咱们更好的治理和跟踪springboot我的项目。 衰弱监控配置实现在须要应用衰弱监控的我的项目或module中,增加如下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId></dependency>增加完依赖当前,reload我的项目或module。 衰弱监控启动剖析启动我的项目,在浏览器中输出如下地址:(SpringBoot默认关上的监控选项无限) http://localhost/actuator其拜访后果,如图所示: 还能够在actuator列出的选中中进行点击,例如拜访health http://localhost/actuator/health其出现后果,如图所示(其中up状态示意失常): 如果心愿查看更多actuator选项,能够在spring boot中配置文件 application.properties中增加如下语句: management.endpoints.web.exposure.include=*而后,重启服务器,基于拜访http://localhost/actuator地址...) 总结(Summary)本大节次要是对springboot工程中的衰弱监控性能做了一个繁难剖析和实现,本人能够基于业务的须要,进行监控的配置和剖析. springboot

February 13, 2021 · 1 min · jiezi

关于springboot:SpringBoot工程下的lombok应用

背景剖析在理论的java我的项目中咱们创立的所有pojo类简直都要为属性增加set/get/toString等相干办法,所有的日志记录相干类可能都要创立日志等对象,这些样板代码既没有技术含量,又影响着代码的好看,同时反复的编码过程会在无形中加大咱们的工作量。 此时Lombok应运而生。 lombok简介概述Lombok是一个第三的Java库,它会主动插入编辑器和构建工具中,Lombok提供了一组有用的正文,用来通知编译过程中的编译工具,在源代码编译成字节码的过程中,在字节码中增加一些量样板代码。 罕用注解剖析@Setter 用于为形容的类生成setter办法,不蕴含final润饰属性。@Getter 用于为形容的类生成getter办法。@ToString 用于为形容的类增加toString办法。@EqualsAndHashCode 用于为形容的类,生成hashCode和equals办法。@NoArgsConstructor 用于为形容的类生成无参的构造方法。@AllArgsConstructor 用于为形容的类生成蕴含类中所有字段的构造方法。@Data用于为形容的类生成setter/getter、equals、canEqual、hashCode、toString办法,如为final属性,则不会为该属性生成setter办法。@Slf4J 用于为形容的类增加一个日志属性对象。lombok装置idea中的装置配置第一步:关上idea的设置窗口,找到plugins菜单,搜寻lombok进行装置,如图所示: 第二步:启动注解解决,如图所示: 第三步:重启idea(可选,有的idea版本须要)。 sts中的装置配置本人百度尝试。 lombok在maven我的项目中利用第一步:增加lombok依赖。 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>annotationProcessor</scope></dependency>第二步:在类上利用lombok注解。 @Data @NoArgsConstructor @AllArgsConstructor public class Goods { private Long id; private String name; private String remark; private Date createdTime;}第三步:编写单元测试类检测Lombok注解利用 @Slf4j@SpringBootTestpublic class GoodsTests{ @Test void testGoods(){ Goods g=new Goods(); g.setId(100L); g.setName("Lombok"); log.info("id的值为{}",g.getId()); log.info("name的值为{}",g.getName()); }}总结(Summary)本大节次要对lombok做了一个剖析,装置和配置,并结合实际我的项目解说了lombok的利用场景及具体利用过程。 springboot

February 13, 2021 · 1 min · jiezi

关于springboot:给你的SpringBoot项目定制一个牛年专属banner吧

新春快乐,牛年大吉! 新的一年是牛年,在SpringBoot我的项目里自定义了一个牛年相干的banner,看起来可真不错。 下面是本人制作的一个banner,相干的ASCII字符在文末。 SpringBoot我的项目自定义banner非常简单,通过在classpath下增加一个banner.txt或设置banner.location来指定相应的文件能够扭转启动过程中打印的banner。 如果想以编程的形式产生一个banner,能够应用SpringBootApplication.setBanner(…)办法。应用org.springframework.boot.Banner接口,实现你本人的printBanner()办法。好了,接下来咱们来看看怎么一步步制作牛年的专属banner。 在resource目录下创立 banner.txt 应用图片转ASCII博主从网上找了一张图片 应用图片转ASCII的网站有很多,博主尝试了不少,感觉比拟好用的有上面几个: ✔ https://www.twitchquotes.com/... ✔ https://codebeautify.org/imag... ✔ https://www.fontke.com/tool/i... 博主应用是: https://www.twitchquotes.com/... 上传图片,就能够看到生成的ASCII,将生成的ASCII粘贴到 banner.txt中,启动发现banner曾经变: 批改色彩,红色显著不合乎咱们过年的氛围,所以将色彩改成红色。Spring Boot 为提供了三个枚举类来设定这些款式,他们别离是: ⭐ AnsiColor:用来设定字符的前景色; ⭐ AnsiBackground:用来设定字符的背景色。 ⭐ AnsiStyle:用来管制加粗、斜体、下划线等等。 咱们通过{AnsiColor.BRIGHT_RED} 将字符设置为红色 文字局部就没什么好说的,自行添加。就是前面的文本没对齐,博主搞了半天也没有搞定,大过年的,不想搞了???? 增加SpringBoot版本信息,通过${spring-boot.version} 能够获取SpringBoot版本号${AnsiColor.BRIGHT_YELLOW}Spring Boot 版本:${spring-boot.version}这是博主的自定义banner,左边的文字始终对不齐,有解决办法的请在评论区评论,谢谢! ${AnsiColor.BRIGHT_RED} 升职加薪钱 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀多 ⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 身事 ⠸⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢙⢂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 强少 ⢸⣱⢄⣀⣀⣀⣀⣀ ⢀⣀⣠⢀⡀⣤⠤⣚⣼⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 体干 ⠀⠒⠿⣊⣇⣧⣾⣟⣝⣝⣿⣿⣜⡵⠵⢼⣊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 健的 ⠀⠀⠀⠐⠺⠿⣿⠻⢟⣿⢍⢍⢻⣿⣿⣧⡞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 进爽 ⠀⠀⠀⠀⠀⠀⣿⢠⣿⣿ ⣿⣴⣿⣿⣟⢷⣦⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 步 ⠀⠀⠀⠀⠀⢸⢿⡿⢿⣿⣿⣿ ⢫⣿⠏⢤⣿⠩⣝⣷⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⣠⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 快 ⠀⠀⠀⠀⠀⣏⡃⣘⣃⣿⠿⢛⣡⣿⠿⢶⣿⣿⣷⣬⣍⣼⡟⢻⣟⢟⠿⣿⠿⢛⣿⠿⣿⠿⢳⣶⣿⡿⠛⠛⠿⣷⣄⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠈⠉⠛⠛⠻⡿⡻⡉⠡⡀⣿⣿⣿⣿⣿⣿⣿⣷⣾⣬⣥⣴⣿⣗⣿⠡⣷⡗⣼⡷⠛⡻⣷⡀⠀⠀⠈⠻⣦⣀⠀⡔⠒⠲⡇ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⡷⣷⡷⡿⣿⣿⣿⣿⣿⣿⣿⡍⠛⢛⢍⣽⠏⣼⠻⣜⣻⢟⣵⣷⠿⣿⢜⣧⠀⠀⠀⠀⠀⠉⠉⠑⠒⠊⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣮⣠⣠⣴⠿⣿⣿⣿⣿⣿⣿⣿⣾⣼⣾⡿⢧⣽⣎⠿⠻⣻⡵⢻⣿⠏⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣸⡟⢹⣗⣽⠿⣿⣿⣿⣿⣿⣿⡿⣆⣌⠐⣰⣿⣄⣛⣿⣻⠛⢍⣼⣋⣵⠣⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⡤⡲⣴⢄⣯⣬⣿⣷⣄⣹⠅⣱⣿⣿⣿⣿⣿⣿⠌⢽⣿⡟⠛⡋⣹⡝⣷⣉⣻⣏⣉⣲⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⢨⡪⡭⢸⣿⠃⠀⠉⠙⠛⠳⢼⣿⣿⣿⣿⢻⢊⣢⣽⣋⣿⣾⠾⣟⣯⣷⣿⡻⢿⣿⣿⣿⣿⣿⣦⡀⢠⣀⣤⠀⠀⠀⡀⠀⠀ ⠀⠛⣠⣃⣬⣿⣁⠀⣿⣗⣤⠀⡀⠀⠀⣜⢿⣿⠏⠉⠋⠉⠉⠉⠉⠀⠀⠀⠙⠿⣿⣿⣧⠀⠈⠙⠛⠿⣿⣯⣼⠀⡃⣣⣧⣤⣾⣇⠀⠀ ⢀⣀⣴⡿⣺⡏⢻⣆⡈⠓⠻⠃⡐⠀⠀⣷⣿⠋⠀⠀⠀⠀⠀⣼⣵⠇⡀⠀⠀⠀⣸⣿⡭⠃⠀⠀⠀⠀⠈⠻⣿⡄⠀⠿⣯⣖⡽⠃⠀⠀ ⢨⣵⣭⣽⡗⡟⢿⣶⣿⡄⠀⠀⠀⠀⢠⣿⡇⠀⠀⠀⠙⡒⣥⣄⡘⠍⠀⢀⣠⣼⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⢹⣷⡀⠠⢵⠸⣤⣀⠀⠀ ⠀⠉⢈⣋⣦⣴⡄⢉⣰⣲⡂⠀⠀⢰⣭⡽⠂⠀⠀⠀⠈⠀⠐⠻⢽⠿⡳⠶⡾⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣯⣵⠣⣟⣆⡃⠧⣾⡅⠀ ⠀⠀⠙⠻⠿⠛⠉⠑⠋⠉⠀⠀⠪⣀⣉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠚⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⣱⣀⡁⠀⠀⠃${AnsiColor.BRIGHT_YELLOW}Spring Boot 版本:${spring-boot.version}<big>参考:</big> ...

February 11, 2021 · 1 min · jiezi

关于springboot:Nacos作为注册中心和配置中心

装置Nacos见官网装置启动后Nacos拜访地址是 127.0.0.1:8848/nacos/index.html登录名和明码默认都是 nacos 应用Nacos作为注册核心版本对于本人去理解,我这里用的boot 2.3.2.RELEASE版本,spring cloud alibaba 2.2.5.RELEASE版本 <!--这里是我引入的依赖--><!--注册核心的依赖,服务的注册与发现--><dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>2.2.5.RELEASE</version></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>接下来在入口类的下面加上注解@EnableDiscoveryClient,实现服务的注册与发现。 /** * 开启Spring Cloud的服务注册与发现 */@EnableDiscoveryClient@SpringBootApplication@RestControllerpublic class NacosServerApplication { public static void main(String[] args) { SpringApplication.run(NacosServerApplication.class, args); } @GetMapping("/hello") public String hello(@RequestParam String name) { return "Hello " + name; }}配置文件application.yml中配置注册核心地址和服务注册到注册核心的名称 server: port: 7001# 注册到注册核心服务的名称spring: application: name: nacos-server # nacos 注册核心地址 cloud: nacos: discovery: server-addr: 127.0.0.1:8848```启动后去Nacos注册核心查看,能够看到配置的服务注册到了注册核心![图1](https://yunqing-img.oss-cn-beijing.aliyuncs.com/hexo/article/202102/image-20210206132200241.png)## 介绍三种服务的生产形式这里不介绍RPC调用,别离介绍 spring 提供的 RestTemplate、WebClient 还有 Netflix 封装的 Feign一共三种形式。### RestTemplate 形式首先新建一个新的服务用来调用`nacos-server`服务,新建的服务命名为`nacos-client-common`,配置关系和`nacos-server`一样@RestController@EnableDiscoveryClient@SpringBootApplicationpublic class NacosClientCommonApplication { // 须要@Autowired注入 @Autowired private RestTemplate restTemplate; /** ...

February 8, 2021 · 2 min · jiezi

关于springboot:一java-Spi-与-SpringFactoriesLoader

spring的SpringFactoriesLoader是spring框架外部工具类,在 Spring boot 利用启动的过程中,这个类的工作很重要, 启动逻辑应用该类从classpath上所有jar包中找出各自的 META-INF/spring.factories 属性文件,并剖析出其中定义的工厂类。这些工厂类进而被启动逻辑应用,利用于进一步初始化工作。其应用的形式和java的spi根本相似,咱们能够先学习java的spi而且进一步学习SpringFactoriesLoader。一、java spi1、什么是SpiSPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩大的接口,它能够用来启用框架扩大和替换组件。 SPI的作用就是为这些被扩大的API寻找服务实现。 2、Spi的利用场景SPI (Service Provider Interface)是调用方来制订接口标准,提供给内部来实现,调用方在调用时则抉择本人须要的内部实现。  从应用人员上来说,SPI 被框架扩大人员应用。 3、Spi的简略demo(1)、定义一个接口 public interface Upload { void upload();}(2)、接口实现类 package cn..antispam.soa.service;public class BduUploadImpl implements Upload { @Override public void upload(){ System.out.println("上传到百度云"); }}public class AliUploadImpl implements Upload { @Override public void upload() { System.out.println("上传到阿里oss"); }}(3)、测试类 public class spidemo { public static void main(String[] args) { ServiceLoader<Upload> uploads = ServiceLoader.load(Upload.class);for (Upload upload :uploads){ upload.upload();} }}(4)输入 上传到阿里oss上传到百度云Process finished with exit code 04、Spi源码剖析public final class ServiceLoader<S> implements Iterable<S> { //扫描目录前缀 private static final String PREFIX = "META-INF/services/"; // 被加载的类或接口 private final Class<S> service; // 用于定位、加载和实例化实现方实现的类的类加载器 private final ClassLoader loader; // 上下文对象 private final AccessControlContext acc; // 依照实例化的程序缓存曾经实例化的类 private LinkedHashMap<String, S> providers = new LinkedHashMap<>(); // 懒加载迭代器 private java.util.ServiceLoader.LazyIterator lookupIterator; // 公有外部类,提供对所有的service的类的加载与实例化 private class LazyIterator implements Iterator<S> { Class<S> service; ClassLoader loader; Enumeration<URL> configs = null; String nextName = null; public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader){ return new ServiceLoader<>(service, loader); } public static <S> ServiceLoader<S> load(Class<S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); } private ServiceLoader(Class<S> svc, ClassLoader cl) { //.... reload(); } public void reload() { // 清空providers providers.clear(); lookupIterator = new LazyIterator(service, loader); } private LazyIterator(Class<S> service, ClassLoader loader) { this.service = service; this.loader = loader; } //... private boolean hasNextService() { if (configs == null) { try { //获取目录下所有的类 //"META-INF/services/"+service的全限定类名 String fullName = PREFIX + service.getName(); if (loader == null) //加载 META-INF/services/"+service里的所有全限定类名 configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { //... } //.... } } private S nextService() { String cn = nextName; nextName = null; Class<?> c = null; try { //反射加载类 c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { } try { //实例化 S p = service.cast(c.newInstance()); //放进缓存 providers.put(cn, p); return p; } catch (Throwable x) { //.. } //.. } }}下面的代码只贴出了局部要害的实现,上面贴出比拟直观的spi加载的次要流程供参考: ...

February 4, 2021 · 2 min · jiezi

关于springboot:SpringBoot-背景分析解决问题以及关键特性

SpringBoot 简介: SpringBoot 是一个基于spring框架的脚手架,在这个脚手架中曾经做好了spring工程中须要的一些根底工作;例如:一些根底的配置,依赖,监控等性能,其次要作用就是为了简化spring对资源整合的过程,实现疾速的业务开发。总结:SpringBoot次要用于spring工程项目;就是要基于疾速结构理念,基于约定大于配置的形式,实现技术的开箱即用,以进步开发效率。 SpringBoot 外围特色: 1)起步依赖(我的项目创立时曾经整合好了一些根底的依赖)。2)主动配置(将我的项目中的很多共性配置曾经做好)。3)衰弱检测(为我的项目中Bean对象的运行提供相干监控操作)。4)内嵌服务(在我的项目中能够间接嵌入一个tomcat服务,间接通过这个服务运行我的项目)。

February 3, 2021 · 1 min · jiezi

关于springboot:SpringBoot实战派读书笔记

《SpringBoot实战派》读书笔记前言:这本书是集体抽奖送的,然而看完的感觉就是情绪简单。看了几天之后算是对于SpringBoot做了一个回顾,然而脑子里没有留下印象太深的货色。这篇读书笔记集体原本不太想要写的。然而秉持着看书必须写读书笔记的习惯,还是评估一下这本书。 举荐水平:豆瓣7.1的打分。 不值得珍藏的书,也不值得购买,80元你能够买好几本更有品质的编程书。甚至买几本闻名的小说也能够比这个好。 对于相熟的人,能够拿来查漏补缺,也能够拿来做半个工具书。 如果你是理解了springboot同时想要深刻SpringBoot的,这本书也是没有什么价值。没有学过SSM间接上手SpringBoot看一下。这本书还是能够看一下的。 本书评估:长处 适宜初学者适宜对于spring boot从未接触的人内容总结比拟齐全,涵盖spring boot的利用毛病 代码占了很多篇幅内容比拟根底和入门很厚,然而没有养分,不倡议买思维导图:简略画了一下一些内容,加上本人做的一些笔记 https://share.mubu.com/doc/60... 目录截图:https://gitee.com/lazyTimes/i... https://gitee.com/lazyTimes/i... https://gitee.com/lazyTimes/i... 感悟:作为小白学习来说,这本书算是不错而且合格的书,然而技术的更迭切实太快了,这些书根本参考大于理论的应用价值。 这次的文章心愿能够给想买书的人一点揭示把,这本书对于想要深刻SpringBoot的人没有啥价值。 总结:SpringBoot的货色还是倡议多看看官网文档,或者说所有的货色学习都倡议看官网文档,毕竟设计出框架的人尽管不见得能够讲得很好,然而讲得货色相对都会对的。 越来越感觉近几年写的好书越来越少,不得不跑去看以前的一些新书,这本书还是差点意思。 技术书还是倡议多做做笔记,而后思考能够学到什么再去看,这样成果会比拟好。最近跑去转转上买了不少书,开始缓缓看咯。

January 30, 2021 · 1 min · jiezi

关于springboot:如何解决mybatisplus调用update方法时自动填充字段不生效问题

前言应用过mybatis-plus的敌人可能会晓得,通过实现元对象处理器接口com.baomidou.mybatisplus.core.handlers.MetaObjectHandler能够实现字段填充性能。但如果在更新实体,应用boolean update(Wrapper<T> updateWrapper)这个办法进行更新时,则主动填充会生效。明天就来聊聊这个话题,本文例子应用的mybatis-plus版本为3.1.2版本 为何应用boolean update(Wrapper<T> updateWrapper),主动填充会生效?从mybatis-plus 3.1.2版本跟踪源码,能够得悉,主动填充的调用代码实现逻辑是由上面的外围代码块实现 /** * 自定义元对象填充控制器 * * @param metaObjectHandler 元数据填充处理器 * @param tableInfo 数据库表反射信息 * @param ms MappedStatement * @param parameterObject 插入数据库对象 * @return Object */ protected static Object populateKeys(MetaObjectHandler metaObjectHandler, TableInfo tableInfo, MappedStatement ms, Object parameterObject, boolean isInsert) { if (null == tableInfo) { /* 不解决 */ return parameterObject; } /* 自定义元对象填充控制器 */ MetaObject metaObject = ms.getConfiguration().newMetaObject(parameterObject); // 填充主键 if (isInsert && !StringUtils.isEmpty(tableInfo.getKeyProperty()) && null != tableInfo.getIdType() && tableInfo.getIdType().getKey() >= 3) { Object idValue = metaObject.getValue(tableInfo.getKeyProperty()); /* 自定义 ID */ if (StringUtils.checkValNull(idValue)) { if (tableInfo.getIdType() == IdType.ID_WORKER) { metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getId()); } else if (tableInfo.getIdType() == IdType.ID_WORKER_STR) { metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getIdStr()); } else if (tableInfo.getIdType() == IdType.UUID) { metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.get32UUID()); } } } if (metaObjectHandler != null) { if (isInsert && metaObjectHandler.openInsertFill()) { // 插入填充 metaObjectHandler.insertFill(metaObject); } else if (!isInsert) { // 更新填充 metaObjectHandler.updateFill(metaObject); } } return metaObject.getOriginalObject(); }从源码剖析咱们能够得悉当tableInfo为null时,是不走主动填充逻辑。而tableInfo又是什么从中央进行取值,持续跟踪源码,咱们得悉tableInfo能够由底下代码获取 ...

January 30, 2021 · 3 min · jiezi

关于springboot:SpringMVC-Json自定义序列化和反序列化

需要背景需要一:SpringMVC构建的微服务零碎,数据库对日期的存储是Long类型的工夫戳,前端之前是默认应用Long类型工夫,当初前端框架改变,要求后端响应数据时,Long类型的工夫主动变成规范工夫格局(yyyy-MM-dd HH:mm:ss)。 波及到这个转换的范畴挺大,所有的实体表都有创立工夫createTime和批改工夫updateTime,目前的次要诉求也是针对这两个字段,并且在实体详情数据和列表数据都存在,须要一个对立的办法,对这两个字段进行解决。 需要二:前端申请上传的JSON报文,String类型的内容,可能会呈现前后有空格的景象,如果前端框架未对此问题进行解决,后端收到的JSON申请反序列化为对象时,就会呈现String类型的值,前后有空格,现须要一个对立的解决办法,对接管的String类型属性执行trim办法。 解决方案SpringMVC默认的JSON框架为jackson,也能够应用fastjson。 jackson框架自定义序列化如果我的项目应用jackson框架做json序列化,举荐的计划是应用@JsonSerialize注解,示例代码如下: @JsonSerialize(using = CustomDateSerializer.class) private Long createTime;@JsonSerialize(using = CustomDateSerializer.class) private Long updateTime;CustomDateSerializer类的实现示例如下: public class CustomDateSerializer extends JsonSerializer<Long> { @Override public void serialize(Long aLong, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = new Date(aLong); jsonGenerator.writeString(sdf.format(date)); }}这种计划的益处如下: 自定义的实现类能够复用精准到须要转换解决的字段,不受限于createTime和updateTime,更贴近于需要毛病就是须要转换的字段都须要应用注解,工作量有点大 当然有其余的对立解决计划,这里不赘述。 自定义反序列化在jackson框架上实现自定义序列化,也是十分不便的,继承SimpleModule类即可: @Componentpublic class StringTrimModule extends SimpleModule { public StringTrimModule() { addDeserializer(String.class, new StdScalarDeserializer<String>(String.class) { @Override public String deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException { String value = jsonParser.getValueAsString(); if (StringUtils.isEmpty(value)) { return value; } return value.trim(); } }); }}fastjson框架如果工程里呈现这个依赖: ...

January 28, 2021 · 2 min · jiezi

关于springboot:上车了一文尽览Scheduling-Framework-应用实践

Kubernetes 是目前最受欢迎的⾃动化容器治理平台,它提供了灵便的申明式容器编排、主动部署、资源调度等性能。Kube-Scheduler 作为 Kubernetes 的外围组件之一,次要负责整个集群资源的调度性能,依据特定的调度算法和策略,将 Pod 调度到最优的工作节点下面去,从而更加正当、充沛地利用集群资源。 然而随着 Kubernetes 部署的工作类型越来越多,原生 Kube-Scheduler 曾经不能应答多样的调度需要:比方机器学习、深度学习训练任务对于协同调度性能的需要;高性能计算作业,基因计算工作流对于一些动静资源 GPU、网络、存储卷的动静资源绑定需要等。因而自定义 Kubernetes 调度器的需要愈发迫切,本文探讨了扩大 Kubernetes 调度程序的各种办法,而后应用目前最佳的扩大形式 Scheduling Framework,演示如何扩大 Scheduler。 01 自定义调度器的形式 02 Scheduling Framework 解析 如下图所示,调度框架提供了丰盛的扩大点,在这幅图中,Filter 相当于之前 Predicate 预选模块, Score 相当于 Priority 优选模块,每一个扩大点模块都提供了接口,咱们能够实现扩大点定义的接口来实现本人的调度逻辑,并将实现的插件注册到扩大点。 Scheduling Framework 在执行调度流程时,当运行到扩大点时,会调用咱们注册的插件,通过执行自定义插件的策略,满足调度需要。此外,一个插件能够在多个扩大点注册,用以执行更简单或有状态的工作。 Scheduling Framework 每次调度一个 Pod ,都分为调度周期和绑定周期两局部来执行,调度周期为 Pod 抉择一个节点,绑定周期则调用 Apiserver,用选好的 Node,更新 Pod 的 spec.nodeName 字段。调度周期和绑定周期统称为 “Scheduling Context” (调度上下文)。调度周期是串行运行的,同一时间只能有一个 Pod 被调度,是线程平安的;而绑定周期因为须要拜访 Apiserver 的接口,耗时较长,为了进步调度的效率,须要异步执行,即同一时间,可执行多个 bind 操作,是非线程平安的。 如果 Pod 被确定为不可调度或远程桌面存在外部谬误,那么调度周期或绑定周期将被停止。Pod 将返回队列并期待下一次重试。如果一个绑定周期被终止,它将触发 Reserve 插件中的 UnReserve 办法。 Scheduling Cycle 的扩大点 QueueSort ...

January 27, 2021 · 2 min · jiezi

关于springboot:notnull-notempty-notblank区别

@notnull @notempty @notblank区别@NotNull://CharSequence, Collection, Map 和 Array 对象不能是 null, 但能够是空集(size = 0)。@NotEmpty://CharSequence, Collection, Map 和 Array 对象不能是 null 并且相干对象的 size 大于 0。@NotBlank://String 不是 null 且去除两端空白字符后的长度(trimmed length)大于 0。

January 27, 2021 · 1 min · jiezi

关于springboot:springboot-RestTemplate不能注入的原因

Spring Boot<=1.3 无需定义,Spring Boot主动为您定义了一个。 Spring Boot >= 1.4 Spring Boot不再主动定义一个RestTemplate,而是定义了一个RestTemplateBuilder容许您更好地管制所RestTemplate创立的对象。 定义RestTemplate Bean即可实现注入: @Configurationpublic class AppConfig { @Bean public RestTemplate restTemplate(RestTemplateBuilder builder){ return builder.build(); }}

January 27, 2021 · 1 min · jiezi

关于springboot:一步步使用SpringBoot结合Vue实现登录和用户管理功能

前后端拆散开发是当今开发的支流。本篇文章从零开始,一步步应用SpringBoot联合Vue来实现日常开发中最常见的登录性能,以及登录之后对用户的治理性能。通过这个例子,能够疾速入门SpringBoot+Vue前后端拆散的开发。 前言1、前后端拆散简介在这里首先简略阐明一下什么是前后端拆散和单页式利用:前后端拆散 的核心思想是前端页面通过 ajax 调用后端的 restuful api 进行数据交互,而 单页面利用(single page web application,SPA),就是只有一个页面,并在用户与应用程序交互时动静更新该页面的 Web 应用程序。 2、示例所用技术简介简略阐明以下本示例中所用到的技术,如图所示: 后端 SpringBoot:SpringBoot是以后最风行的Java后端框架。能够简略地看成简化了的、依照约定开发的SSM(H), 大大晋升了开发速度。官网地址:https://spring.io/projects/sp... MybatisPlus: MyBatis-Plus(简称 MP)是一个 MyBatis的加强工具,在 MyBatis 的根底上只做加强不做扭转,为简化开发、提高效率而生。官网地址:https://mybatis.plus/ 前端: Vue :Vue 是一套用于构建用户界面的渐进式框架。只管Vue3曾经公布,然而至多一段时间内支流利用还是vue2.x,所以示例里还是采纳Vue2.x版本。官网地址:https://cn.vuejs.org/ ElementUI: ElementUI 是目前国内最风行的Vue UI框架。组件丰盛,款式泛滥,也比拟合乎公众审美。尽管一度传出进行保护更新的风闻,然而随着Vue3的公布,官网也Beta了适配Vue3的ElementPlus。官网地址:https://element.eleme.cn/#/zh-CN 数据库: MySQL:MySQL是一个风行的开源关系型数据库。官网地址:https://www.mysql.com/ 下面曾经简略介绍了本实例用到的技术,在开始本实例之前,最好能对以上技术具备肯定水平的把握。 一、环境筹备1、前端1.1、装置Node.js前端我的项目应用 veu-cli脚手架,vue-cli须要通过npm装置,是而 npm 是集成在 Node.js 中的,所以第一步咱们须要装置 Node.js,拜访官网 https://nodejs.org/en/,首页即可下载。 下载实现后运行安装包,一路下一步就行。而后在 cmd 中输出 node -v,查看是否装置胜利。 如图,呈现了版本号(依据下载时候的版本确定),阐明曾经装置胜利了。同时,npm 包也曾经装置胜利,能够输出 npm -v 查看版本号 1.2、配置NPM源NPM原始的源是在国外的服务器上,下载货色比较慢。 能够通过两种形式来晋升下载速度。 下载时指定源 //本次从淘宝仓库源下载npm --registry=https://registry.npm.taobao.org install配置源为淘宝仓库 //设置淘宝源npm config set registry https://registry.npm.taobao.org也能够装置 cnpm ,然而应用中可能会遇到一些问题。 ...

January 26, 2021 · 18 min · jiezi

关于springboot:SpringBoot项目实践过程中遇到Bug集

SpringBoot 常见问题剖析 HikariCP 常见问题剖析 MyBatis常见问题剖析 Spring 常见问题剖析 thymeleaf 常见问题剖析 Ajax 常见问题剖析 Summary本大节次要心愿进步同学们对问题的思考,剖析以及解决问题的能力,并对问题进行总结,积淀,造成教训和习惯。

January 25, 2021 · 1 min · jiezi

关于springboot:1如何理解springboot

一.Spring Boot 背景剖析JAVAEE利用体系中沉重的配置、低下的开发效率、高难度的三方集成,简单的部署流程等等始终被开发人员所诟病。即便是应用Spring这样的轻量级的资源整合框架,在实现其绝对比拟多的资源整合时,仍旧须要大量的手动依赖治理,简单的XML配置(还常常没有提醒)。还有就是当初的软件生态利用也曾经造成肯定的规模,零碎架构正在从单体架构,分布式架构,逾越到微服务架构。随着整个架构体系的变动,企业对技术的要求也在变动,当初的企业更重视技术的开箱即用,更重视技术在生态圈中的深度交融,更重视轻量级的运维。由此由此spring boot诞生。 二.Spring Boot 要解决什么问题Spring Boot是由Pivotal团队提供的全新的Java软件开发框架(很多人当初把它了解为一个脚手架),其设计目标是用来简化Spring我的项目的初始搭建以及开发过程。该框架应用了特定的注解形式来进行配置,从而使开发人员不再须要大量的xml配置。不再须要大量的手动依赖治理。Spring Boot基于疾速构建理念,通过约定大于配置,开箱即用的形式,心愿可能在蓬勃发展的疾速利用开发畛域成为其领导者。 三.Spring Boot 有哪些外围的要害个性起步依赖(Starter Dependency)。主动配置(Auto Configuration)。健康检查(Actator)-监控。嵌入式服务(Tomcat,Jetty)。四.总结(Summary)总之,Spring Boot 框架就是要基于疾速构建理念,基于约定大于配置形式,实现技术的开箱即用,以进步开发效率。

January 21, 2021 · 1 min · jiezi

关于springboot:Reactive-Spring实战-WebFlux使用教程

WebFlux是Spring 5提供的响应式Web利用框架。它是齐全非阻塞的,能够在Netty,Undertow和Servlet 3.1+等非阻塞服务器上运行。本文次要介绍WebFlux的应用。 FluxWeb vs noFluxWebWebFlux是齐全非阻塞的。在FluxWeb前,咱们能够应用DeferredResult和AsyncRestTemplate等形式实现非阻塞的Web通信。咱们先来比拟一下这两者。 留神:对于同步阻塞与异步非阻塞的性能差别,本文不再论述。阻塞即节约。咱们通过异步实现非阻塞。只有存在阻塞时,异步能力进步性能。如果不存在阻塞,应用异步反而可能因为线程调度等开销导致性能降落。 上面例子模仿一种业务场景。订单服务提供接口查找订单信息,同时,该接口实现还须要调用仓库服务查问仓库信息,商品服务查问商品信息,并过滤,取前5个商品数据。 OrderService提供如下办法 public void getOrderByRest(DeferredResult<Order> rs, long orderId) { // [1] Order order = mockOrder(orderId); // [2] ListenableFuture<ResponseEntity<User>> userLister = asyncRestTemplate.getForEntity("http://user-service/user/mock/" + 1, User.class); ListenableFuture<ResponseEntity<List<Goods>>> goodsLister = asyncRestTemplate.exchange("http://goods-service/goods/mock/list?ids=" + StringUtils.join(order.getGoodsIds(), ","), HttpMethod.GET, null, new ParameterizedTypeReference<List<Goods>>(){}); // [3] CompletableFuture<ResponseEntity<User>> userFuture = userLister.completable().exceptionally(err -> { logger.warn("get user err", err); return new ResponseEntity(new User(), HttpStatus.OK); }); CompletableFuture<ResponseEntity<List<Goods>>> goodsFuture = goodsLister.completable().exceptionally(err -> { logger.warn("get goods err", err); return new ResponseEntity(new ArrayList<>(), HttpStatus.OK); }); // [4] warehouseFuture.thenCombineAsync(goodsFuture, (warehouseRes, goodsRes)-> { order.setWarehouse(warehouseRes.getBody()); List<Goods> goods = goodsRes.getBody().stream() .filter(g -> g.getPrice() > 10).limit(5) .collect(Collectors.toList()); order.setGoods(goods); return order; }).whenCompleteAsync((o, err)-> { // [5] if(err != null) { logger.warn("err happen:", err); } rs.setResult(o); });}加载订单数据,这里mack了一个数据。通过asyncRestTemplate获取仓库,产品信息,失去ListenableFuture。设置ListenableFuture异样解决,防止因为某个申请报错导致接口失败。合并仓库,产品申请后果,组装订单数据通过DeferredResult设置接口返回数据。能够看到,代码较繁琐,通过DeferredResult返回数据的形式也与咱们同步接口通过办法返回值返回数据的形式天壤之别。 ...

January 17, 2021 · 4 min · jiezi

关于springboot:SpringBoot-Import-Export-Excel

前言之前始终心心念的文件解决(导出/导入)终于在本周遇到了,一步一步尝试过去发现真的挺乏味的,在此记录Excel文件的导入与导出。 筹备首先在我的项目的pom.xml文件引入依赖: <!-- Microsoft文档的Java API --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency>ImportC层: @PostMapping("upload") public void upload(@RequestParam("file") MultipartFile multipartFile) throws IOException, InvalidFormatException { this.excelService.upload(multipartFile); }M层: @Override //上传文件 public void upload(MultipartFile multipartFile) throws IOException, InvalidFormatException { //文件大小 System.out.println(multipartFile.getSize()); //文件名 System.out.println(multipartFile.getOriginalFilename()); //读取文件信息 this.ReadFromFile(multipartFile); } @Override public void ReadFromFile(MultipartFile multipartFile) throws IOException, InvalidFormatException { //通过将整个文件输出流缓冲到内存中,结构一个XSSFWorkbook对象 Workbook workbook = new XSSFWorkbook(multipartFile.getInputStream()); //从第1行开始读取数据 Sheet sheet = workbook.getSheetAt(0); //迭代器 Iterator<Row> rows = sheet.iterator(); while (rows.hasNext()) { Row currentRow = rows.next(); Iterator<Cell> cellsInRow = currentRow.iterator(); //循环,输入数据 while (cellsInRow.hasNext()) { Cell currentCell = cellsInRow.next(); System.out.println(currentCell); } workbook.close(); } }因为在开发时是前后台拆散开发,因而在测试时应用了Postman进行了该办法的测试: ...

January 16, 2021 · 2 min · jiezi

关于springboot:XXLJOB定时任务默认使用

Hello,大家好,我是一个在互联网收废铁的程序员,曾经很久没有更新过文章了,明天呢,就来学习一下新的知识点吧。明天呢?咱们来讲讲什么好呢? 好吧,明天咱们就简略的来学习一下XXL-JOB吧 废话咱们就不必多说了吧,咱们间接进入咱们的正题 首先别慌,咱们先来看看什么是XXL-JOB 咱们在应用任务调度的时候呢,咱们都晓得还有很多的框架,比方Quartz,Elastic-Job,TBSchedule等等 不过咱们要通过本人的技术选型来决定咱们想要选定哪一种 每个都有本人的优缺点,可能和这个不能满足你的需要,那么咱们就要考虑一下另外一个能不能满足呢? 废话有点多啦 简介XXL-JOB是一个轻量级分布式任务调度平台,其外围设计指标是开发迅速、学习简略、轻量级、易扩大。现已凋谢源代码并接入多家公司线上产品线,开箱即用。 个性1、简略:反对通过Web页面对工作进行CRUD操作,操作简略,一分钟上手;2、动静:反对动静批改工作状态、启动/进行工作,以及终止运行中工作,即时失效;3、调度核心HA(核心式):调度采纳核心式设计,“调度核心”自研调度组件并反对集群部署,可保障调度核心HA;4、执行器HA(分布式):工作分布式执行,工作"执行器"反对集群部署,可保障工作执行HA;5、注册核心: 执行器会周期性主动注册工作, 调度核心将会主动发现注册的工作并触发执行。同时,也反对手动录入执行器地址;6、弹性扩容缩容:一旦有新执行器机器上线或者下线,下次调度时将会重新分配工作;7、路由策略:执行器集群部署时提供丰盛的路由策略,包含:第一个、最初一个、轮询、随机、一致性HASH、最不常常应用、最近最久未应用、故障转移、繁忙转移等;8、故障转移:工作路由策略抉择"故障转移"状况下,如果执行器集群中某一台机器故障,将会主动Failover切换到一台失常的执行器发送调度申请。9、阻塞解决策略:调度过于密集执行器来不及解决时的解决策略,策略包含:单机串行(默认)、抛弃后续调度、笼罩之前调度;10、工作超时管制:反对自定义工作超时工夫,工作运行超时将会被动中断工作;11、工作失败重试:反对自定义工作失败重试次数,当工作失败时将会依照预设的失败重试次数被动进行重试;其中分片工作反对分片粒度的失败重试;12、工作失败告警;默认提供邮件形式失败告警,同时预留扩大接口,可不便的扩大短信、钉钉等告警形式;13、分片播送工作:执行器集群部署时,工作路由策略抉择"分片播送"状况下,一次任务调度将会播送触发集群中所有执行器执行一次工作,可依据分片参数开发分片工作;14、动静分片:分片播送工作以执行器为维度进行分片,反对动静扩容执行器集群从而动静减少分片数量,协同进行业务解决;在进行大数据量业务操作时可显著晋升工作解决能力和速度。15、事件触发:除了"Cron形式"和"工作依赖形式"触发工作执行之外,反对基于事件的触发工作形式。调度核心提供触发工作单次执行的API服务,可依据业务事件灵便触发。16、工作进度监控:反对实时监控工作进度;17、Rolling实时日志:反对在线查看调度后果,并且反对以Rolling形式实时查看执行器输入的残缺的执行日志;18、GLUE:提供Web IDE,反对在线开发工作逻辑代码,动静公布,实时编译失效,省略部署上线的过程。反对30个版本的历史版本回溯。19、脚本工作:反对以GLUE模式开发和运行脚本工作,包含Shell、Python、NodeJS、PHP、PowerShell等类型脚本;20、命令行工作:原生提供通用命令行工作Handler(Bean工作,"CommandJobHandler");业务方只须要提供命令行即可;21、工作依赖:反对配置子工作依赖,当父工作执行完结且执行胜利后将会被动触发一次子工作的执行, 多个子工作用逗号分隔;22、一致性:“调度核心”通过DB锁保障集群散布式调度的一致性, 一次任务调度只会触发一次执行;23、自定义工作参数:反对在线配置调度工作入参,即时失效;24、调度线程池:调度零碎多线程触发调度运行,确保调度准确执行,不被梗塞;25、数据加密:调度核心和执行器之间的通信进行数据加密,晋升调度信息安全性;26、邮件报警:工作失败时反对邮件报警,反对配置多邮件地址群发报警邮件;27、推送maven地方仓库: 将会把最新稳定版推送到maven地方仓库, 不便用户接入和应用;28、运行报表:反对实时查看运行数据,如工作数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度胜利分布图等;29、全异步:任务调度流程全异步化设计实现,如异步调度、异步运行、异步回调等,无效对密集调度进行流量削峰,实践上反对任意时长工作的运行;30、跨语言:调度核心与执行器提供语言无关的 RESTful API 服务,第三方任意语言可据此对接调度核心或者实现执行器。除此之外,还提供了 “多任务模式”和“httpJobHandler”等其余跨语言计划;31、国际化:调度核心反对国际化设置,提供中文、英文两种可选语言,默认为中文;32、容器化:提供官网docker镜像,并实时更新推送dockerhub,进一步实现产品开箱即用;33、线程池隔离:调度线程池进行隔离拆分,慢工作主动降级进入"Slow"线程池,防止耗尽调度线程,进步零碎稳定性;34、用户治理:反对在线管理系统用户,存在管理员、普通用户两种角色;35、权限管制:执行器维度进行权限管制,管理员领有全量权限,普通用户须要调配执行器权限后才容许相干操作;以上呢间接能够在官网查看,嘿嘿嘿嘿嘿 好了,我就不多比比了,间接开始咱们的正题 首先呢,咱们要怎么开始咱们的第一个工作呢 咱们先去GitHub上拉取最新的代码 XXL_JOB 1.疾速入门将代码以maven的形式导入到咱们的IDE中,这里我置信大多数人都是用的IDEA吧,如果你的IDE不是这个,那么你就太掉队啦,赶快口头起来吧 这外面就显示咱们须要的模块 xxl-job-admin: 调度核心xxl-job-core:  公共依赖xxl-job-executor-samples:执行器实例 次要的呢还是咱们的调度核心和公共依赖这个两个模块 xxl-job-executor-sample-spring:Spring版本,通过Spring容器治理执行器,比拟通用xxl-job-executor-sample-frameless:无框架版本xxl-job-executor-sample-springboot:Springboot版本,通过Springboot治理执行器,举荐这种形式 1.1初始化数据库在咱们解压后的文件夹中咱们能够看见有一个sql文件在/xxl-job/doc/db/tables_xxl_job.sql 1.2部署调度核心调度核心我的项目:xxl-job-admin作用:对立治理任务调度平台上调度工作,负责触发调度执行,并且提供工作治理平台,可视化操作 1.2.1 调度核心地位首先找到咱们的application.properties(我置信这个就不必多说了吧,置信学过SpringBoot的都晓得它在哪儿) 批改一下配置文件里的信息 xxl-job, datasourcespring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghaispring.datasource.username=rootspring.datasource.password=rootspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 次要就是这个数据库的配置,其余先不做批改 1.2.2 启动咱们的我的项目而后拜访咱们的urlHttp://localhost:9999(端口)/xxl-job-admin登录账户默认是admin 123456就会呈现这个首页信息 到这里咱们的调度核心就配置启动好了,是不是很简略呢 2.实现执行器工作首先咱们能够应用我的项目外面的模板,咱们也能够本人实现 这里呢咱们就独自进去做一个 那么这个xxl-job-admin始终运行着 那么咱们就须要去手动创立一个SpringBoot我的项目 2.1 引入次要的Pom文件<dependency> <groupId>com.xuxueli</groupId> <artifactId>xxl-job-core</artifactId> <version>2.2.0</version> </dependency> 2.2 在咱们的配置文件中增加如下信息(我这里应用的是yml文件)xxl: job: accessToken: admin: addresses: http://localhost:9999/xxl-job-admin executor: address: appname: xxl-job-marketing ip: logpath: /data/applogs/xxl-job/jobhandler logretentiondays: 30 port: 9004 ...

January 14, 2021 · 1 min · jiezi

关于springboot:了解这些你就可以在Spring启动时为所欲为了

八仙过海,各显神通Spring 是一个管制反转依赖治理的容器,作为 Java Web 的开发人员,根本没有不相熟 Spring 技术栈的,只管在依赖注入畛域,Java Web 畛域不乏其余优良的框架,如 google 开源的依赖治理框架 guice,如 Jersey web 框架等。但 Spring 曾经是 Java Web 畛域应用最多,利用最宽泛的 Java 框架。 此文将专一解说如何在 Spring 容器启动时实现咱们本人想要实现的逻辑。咱们时常会遇到在 Spring 启动的时候必须实现一些初始化的操作,如创立定时工作,创立连接池等。 如果没有 Spring 容器,不依赖于 Spring 的实现,回归 Java 类实现自身,咱们能够在动态代码块,在类构造函数中实现相应的逻辑,Java 类的初始化程序顺次是动态变量 > 动态代码块 > 全局变量 > 初始化代码块 > 结构器。 比方,Log4j 的初始化,就是在 LogManager 的动态代码块中实现的: static { Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG)); repositorySelector = new DefaultRepositorySelector(h); String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,null); if(override == null || "false".equalsIgnoreCase(override)) { String configurationOptionStr = OptionConverter.getSystemProperty(DEFAULT_CONFIGURATION_KEY, null); String configuratorClassName = OptionConverter.getSystemProperty(CONFIGURATOR_CLASS_KEY, null); URL url = null; if(configurationOptionStr == null) { url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE); if(url == null) { url = Loader.getResource(DEFAULT_CONFIGURATION_FILE); } } else { try { url = new URL(configurationOptionStr); } catch (MalformedURLException ex) { url = Loader.getResource(configurationOptionStr); } } if(url != null) { LogLog.debug("Using URL ["+url+"] for automatic log4j configuration."); try { OptionConverter.selectAndConfigure(url, configuratorClassName,LogManager.getLoggerRepository()); } catch (NoClassDefFoundError e) { LogLog.warn("Error during default initialization", e); } } else { LogLog.debug("Could not find resource: ["+configurationOptionStr+"]."); } } else { LogLog.debug("Default initialization of overridden by " + DEFAULT_INIT_OVERRIDE_KEY + "property."); }}比方在构造函数中实现相应的逻辑: ...

January 13, 2021 · 3 min · jiezi

关于springboot:个人学习系列-二维码图片生成

钻研一下二维码的生成吧!1. 新建spring boot我的项目1.1 pom.xml<!-- 生成二维码依赖 --><dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.4.1</version></dependency>1.2 新建生成二维码工具类/** * 生成二维码工具类 * * @author zhouzhaodong */public class QRCodeGenerator { /** * 生成二维码图片 * @param text * @param width * @param height * @param filePath * @throws WriterException * @throws IOException */ public static void generateQRCodeImage(String text, int width, int height, String filePath) throws WriterException, IOException { QRCodeWriter qrCodeWriter = new QRCodeWriter(); BitMatrix bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height); Path path = FileSystems.getDefault().getPath(filePath); MatrixToImageWriter.writeToPath(bitMatrix, "PNG", path); } /** * 生成二维码流 * @param text * @param width * @param height * @return */ public static String writeToStream(String text, int width, int height) { String message = ""; QRCodeWriter qrCodeWriter = new QRCodeWriter(); BitMatrix bitMatrix; try { bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); MatrixToImageWriter.writeToStream(bitMatrix, "PNG", outputStream); Base64.Encoder encoder = Base64.getEncoder(); message = encoder.encodeToString(outputStream.toByteArray()); } catch (Exception e) { e.printStackTrace(); } return message; }}1.3 新建controller/** * 生成二维码 * @author zhouzhaodong */@RestControllerpublic class QRCodeController { /** * 生成二维码图片并将地址回传给前端 * @param orderNo * @return */ @RequestMapping("/qrcode/image") public String qrcodeImage(String orderNo) { String failPath = "src/main/resources/png/" + orderNo + ".png"; try { QRCodeGenerator.generateQRCodeImage(orderNo, 350, 350, failPath); } catch (Exception e) { e.printStackTrace(); } return failPath; } /** * 生成二维码Base64回传给前端 * @param orderNo * @return */ @RequestMapping("/qrcode/base64") public String qrcodeBase64(String orderNo) { String message = ""; try { message = QRCodeGenerator.writeToStream(orderNo, 350, 350); } catch (Exception e) { e.printStackTrace(); } return message; }}1.4 测试这里我应用IDEA自带的HTTP申请客户端进行测试。 ...

January 11, 2021 · 2 min · jiezi

关于springboot:个人学习系列-二维码图片生成

钻研一下二维码的生成吧!1. 新建spring boot我的项目1.1 pom.xml<!-- 生成二维码依赖 --><dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.4.1</version></dependency>1.2 新建生成二维码工具类/** * 生成二维码工具类 * * @author zhouzhaodong */public class QRCodeGenerator { /** * 生成二维码图片 * @param text * @param width * @param height * @param filePath * @throws WriterException * @throws IOException */ public static void generateQRCodeImage(String text, int width, int height, String filePath) throws WriterException, IOException { QRCodeWriter qrCodeWriter = new QRCodeWriter(); BitMatrix bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height); Path path = FileSystems.getDefault().getPath(filePath); MatrixToImageWriter.writeToPath(bitMatrix, "PNG", path); } /** * 生成二维码流 * @param text * @param width * @param height * @return */ public static String writeToStream(String text, int width, int height) { String message = ""; QRCodeWriter qrCodeWriter = new QRCodeWriter(); BitMatrix bitMatrix; try { bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); MatrixToImageWriter.writeToStream(bitMatrix, "PNG", outputStream); Base64.Encoder encoder = Base64.getEncoder(); message = encoder.encodeToString(outputStream.toByteArray()); } catch (Exception e) { e.printStackTrace(); } return message; }}1.3 新建controller/** * 生成二维码 * @author zhouzhaodong */@RestControllerpublic class QRCodeController { /** * 生成二维码图片并将地址回传给前端 * @param orderNo * @return */ @RequestMapping("/qrcode/image") public String qrcodeImage(String orderNo) { String failPath = "src/main/resources/png/" + orderNo + ".png"; try { QRCodeGenerator.generateQRCodeImage(orderNo, 350, 350, failPath); } catch (Exception e) { e.printStackTrace(); } return failPath; } /** * 生成二维码Base64回传给前端 * @param orderNo * @return */ @RequestMapping("/qrcode/base64") public String qrcodeBase64(String orderNo) { String message = ""; try { message = QRCodeGenerator.writeToStream(orderNo, 350, 350); } catch (Exception e) { e.printStackTrace(); } return message; }}1.4 测试这里我应用IDEA自带的HTTP申请客户端进行测试。 ...

January 11, 2021 · 2 min · jiezi

关于springboot:SpringBoot集成Kafka

SpringBoot集成Kafka本篇次要解说SpringBoot 如何集成Kafka ,并且简略的 编写了一个Demo 来测试 发送和生产性能 前言抉择的版本如下: springboot : 2.3.4.RELEASE spring-kafka : 2.5.6.RELEASE kafka : 2.5.1 zookeeper : 3.4.14 本Demo 应用的是 SpringBoot 比拟高的版本 SpringBoot 2.3.4.RELEASE 它会引入 spring-kafka 2.5.6 RELEASE ,对应了版本关系中的Spring Boot 2.3 users should use 2.5.x (Boot dependency management will use the correct version). spring和 kafka 的版本 关系 https://spring.io/projects/sp... 1.搭建Kafka 和 Zookeeper 环境搭建kafka 和 zookeeper 环境 并且启动 它们 2.创立Demo 我的项目引入spring-kafka2.1 pom 文件<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId></dependency><dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId></dependency>2.2 配置application.ymlspring: kafka: bootstrap-servers: 192.168.25.6:9092 #bootstrap-servers:连贯kafka的地址,多个地址用逗号分隔 consumer: group-id: myGroup enable-auto-commit: true auto-commit-interval: 100ms properties: session.timeout.ms: 15000 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.apache.kafka.common.serialization.StringDeserializer auto-offset-reset: earliest producer: retries: 0 #若设置大于0的值,客户端会将发送失败的记录从新发送 batch-size: 16384 #当将多个记录被发送到同一个分区时, Producer 将尝试将记录组合到更少的申请中。这有助于晋升客户端和服务器端的性能。这个配置管制一个批次的默认大小(以字节为单位)。16384是缺省的配置 buffer-memory: 33554432 #Producer 用来缓冲期待被发送到服务器的记录的总字节数,33554432是缺省配置 key-serializer: org.apache.kafka.common.serialization.StringSerializer #关键字的序列化类 value-serializer: org.apache.kafka.common.serialization.StringSerializer #值的序列化类2.3 定义音讯体Message/** * @author johnny * @create 2020-09-23 上午9:21 **/@Datapublic class Message { private Long id; private String msg; private Date sendTime;}2.4 定义KafkaSender次要利用 KafkaTemplate 来发送音讯 ,将音讯封装成Message 并且进行 转化成Json串 发送到Kafka中@Component@Slf4jpublic class KafkaSender { private final KafkaTemplate<String, String> kafkaTemplate; //结构器形式注入 kafkaTemplate public KafkaSender(KafkaTemplate<String, String> kafkaTemplate) { this.kafkaTemplate = kafkaTemplate; } private Gson gson = new GsonBuilder().create(); public void send(String msg) { Message message = new Message(); message.setId(System.currentTimeMillis()); message.setMsg(msg); message.setSendTime(new Date()); log.info("【++++++++++++++++++ message :{}】", gson.toJson(message)); //对 topic = hello2 的发送音讯 kafkaTemplate.send("hello2",gson.toJson(message)); }}2.5 定义KafkaConsumer在监听的办法上通过注解配置一个监听器即可,另外就是指定须要监听的topickafka的音讯再接收端会被封装成ConsumerRecord对象返回,它外部的value属性就是理论的音讯。 ...

January 6, 2021 · 2 min · jiezi

关于springboot:springboot项目接入sap与部署到docker遇到的问题实录

前言本文例子来源于某个业务团队,本文次要记录在帮助他们部署接入sap遇到的问题,且只记录解决问题的答案,不阐明原理。 问题实录1、问题一:Illegal JCo archive "sapjco-1.0.jar". It is not allowed to rename or repackage the original archive "sapjco3.jar"解决方案一:手动我的项目中将sapjco-1.0.jar重名为sapjco3.jar不过咱们没采纳这种计划 解决方案二:指定maven的scope为system,形如下 <dependency> <groupId>com.sap</groupId> <artifactId>sapjco</artifactId> <version>${sap.version}</version> <scope>system</scope> <systemPath>${project.basedir}/lib/sapjco3.jar</systemPath> </dependency>而后指定resources,形如下 <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> <compilerArguments> <extdirs>${project.basedir}/lib</extdirs> </compilerArguments> </configuration> </plugin> </plugins> <resources> <resource> <directory>${project.basedir}/lib</directory> <targetPath>BOOT-INF/lib/</targetPath> <includes> <include>**/*.jar</include> </includes> </resource> <resource> <directory>${project.basedir}/lib</directory> <targetPath>BOOT-INF</targetPath> <includes> <include>**/*.so</include> <include>**/*.dll</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <!--<targetPath>BOOT-INF/classes/</targetPath>--> <includes> <include>**/*.yml</include> <include>**/*.xml</include> </includes> </resource> </resources> </build>这个的作用是将sapjco3.jar打进BOOT-INF/lib/ ...

January 5, 2021 · 1 min · jiezi

关于springboot:SpringBoot系列3AOP实战1权限日志

摘要实现简略的权限管制实现简略的日志权限管制pom<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId></dependency>annotation@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface PermissionAnnotation{}aspect@Aspect@Component@Order(1)public class PermissionFirstAdvice { @Pointcut("@annotation(xx.xx.xx.PermissionAnnotation)") private void permissionCheck() { } @Around("permissionCheck()") public Object permissionCheckFirst(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println(System.currentTimeMillis()); //获取申请参数 Object[] objects = joinPoint.getArgs(); String userName = (String) objects[0]; if (!userName.equals("admin")) { return "失败"; } return joinPoint.proceed(); }}controller@RestController@RequestMapping(value = "/permission")public class TestController { @RequestMapping(value = "/check", method = RequestMethod.POST) @PermissionsAnnotation() public String getGroupList(@RequestParam String userName) { return "Hello "+userName; }}日志pom <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.5.RELEASE</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.5.6</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.70</version> </dependency> </dependencies>aspect@Aspect@Componentpublic class OperLogAspect { //操作切入点 @Pointcut("@annotation(com.bothsavage.annotation.OperLog)") public void operLogPoinCut() {} //失常返回告诉 @AfterReturning(value = "operLogPoinCut()", returning = "keys") public void saveOperLog(JoinPoint joinPoint, Object keys) { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); OperationLog operlog = new OperationLog(); try { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); String className = joinPoint.getTarget().getClass().getName(); String methodName = method.getName(); OperLog opLog = method.getAnnotation(OperLog.class); methodName = className + "." + methodName; Map<String, String> rtnMap = converMap(request.getParameterMap()); String params = JSON.toJSONString(rtnMap); operlog.setOperId(IdUtil.randomUUID()); operlog.setOperModul(opLog.operModul()); operlog.setOperType(opLog.operType()); operlog.setOperDesc(opLog.operDesc()); operlog.setOperMethod(methodName); // 申请办法 operlog.setOperRequParam(params); // 申请参数 operlog.setOperRespParam(JSON.toJSONString(keys)); // 返回后果 operlog.setOperUri(request.getRequestURI()); // 申请URI operlog.setOperCreateTime(new Date()); // 创立工夫 //打印日志 System.out.println(JSON.toJSONString(operlog)); } catch (Exception e) { e.printStackTrace(); } } //转换request 申请参数 public Map<String, String> converMap(Map<String, String[]> paramMap) { Map<String, String> rtnMap = new HashMap<String, String>(); for (String key : paramMap.keySet()) { rtnMap.put(key, paramMap.get(key)[0]); } return rtnMap; } //转换异样信息为字符串 public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) { StringBuffer strbuff = new StringBuffer(); for (StackTraceElement stet : elements) { strbuff.append(stet + "\n"); } String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString(); return message; }}实体类package com.bothsavage.entity;import lombok.Data;import java.util.Date;@Datapublic class OperationLog { private String operId; private String operModul; private String operType; private String operDesc; private String OperMethod; private String OperRequParam; private String OperRespParam; private String OperUserId; private String OperUserName; private String OperIp; private String OperUri; private Date OperCreateTime; private String OperVer;}annotation@Target(ElementType.METHOD) //注解搁置的指标地位,METHOD是可注解在办法级别上@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行@Documentedpublic @interface OperLog { String operModul() default ""; // 操作模块 String operType() default ""; // 操作类型 String operDesc() default ""; // 操作阐明}controller@RestControllerpublic class TestController { @GetMapping("/test/{testName}") @OperLog(operModul = "测试模块",operType = "test",operDesc = "这个是用来测试的") public String test(@PathVariable String testName){ return "hello"+testName; }}参考[1].把本人牛逼到了,在SpringBoot用AOP切面实现一个权限校验... ...

January 5, 2021 · 2 min · jiezi

关于springboot:springboot集成jsp搭建文化旅游地图

点赞再看,养成习惯 开发环境jdk 8intellij ideamaven 3.6所用技术springbootjsp我的项目介绍北京游览地图,快捷导航! 我的项目适用人群正在做毕设的学生,或者须要我的项目实战练习的Java学习者 程序拜访 http://localhost:8090/index筹备工作pom.xml退出jsp模板引擎反对:<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope></dependency>springboot配置jspspring.mvc.view.prefix=/spring.mvc.view.suffix=.jsp运行成果 技术实现1.整体布局 <frameset rows="10%,90%"> <frame src="search"> <frameset cols="10%,90%" id="frame1"> <frame src="menu"> <frameset rows="60%,40%" id="frame2"> <frame src="https://www.amap.com/search" id="mainHtml" name="showframe"> <frameset cols="50%,50%" id="frame3"> <frame src="https://www.baidu.com" id="mfwFrame" name="mfwFrame"> <frame src="https://weixin.sogou.com/weixin" id="sgwxFrame" name="sgwxFrame"> </frameset> </frameset> </frameset></frameset>左侧菜单代码实现<div class="container" style="padding-left: 80px;padding-top: 50px"> <div class="row"> <div class="span6"> <ul class="nav nav-list"> <li class="nav-header"><span class="glyphicon glyphicon glyphicon-map-marker"></span>&nbsp;总览</li> <li class="active "><a href="https://www.amap.com/search?query=%E5%8C%97%E4%BA%AC%E5%B8%82&city=110101&geoobj=106.165942%7C29.343435%7C107.154712%7C29.801484&zoom=11" target="showframe">默认地图</a></li> <li><a href="https://www.amap.com/search?query=%E5%8C%97%E4%BA%AC%E6%99%AF%E7%82%B9&city=110000&geoobj=116.022898%7C39.709792%7C117.011668%7C40.113754&zoom=11" target="showframe">热门景点</a></li> <li class="nav-header"><span class="glyphicon glyphicon-globe"></span>&nbsp;攻略</li> <li><a href="https://you.ctrip.com/place/beijing1.html#ctm_ref=www_hp_bs_lst" target="_blank">携程</a></li> <li><a href="https://dujia.qunar.com/pdqk/list_%E8%B5%A3%E5%B7%9E_%E5%8C%97%E4%BA%AC%E6%99%AF%E7%82%B9_all?ti=3&tf=pc_big_search&tm=l01_all_search_origin" target="_blank">去哪儿</a></li> <li><a href="https://www.xiaohongshu.com/explore" target="_blank">小红书</a></li> <li class="nav-header"><span class="glyphicon glyphicon-user"></span> &nbsp;友情链接</li> <li><a href="http://whlyj.beijing.gov.cn/" target="_blank">北京文化局</a></li> </ul> </div> </div></div>我的项目总结frame 目前曾经不太敌对,可用其余技术实现页面宰割其余有更好的简洁操作,心愿留言交换原地址认准程序帮:https://segmentfault.com/a/11...

January 4, 2021 · 1 min · jiezi

关于springboot:springspringBoot常用注解总结

0.前言能够毫不夸大地说,这篇文章介绍的 Spring/SpringBoot 罕用注解根本曾经涵盖你工作中遇到的大部分罕用的场景。对于每一个注解我都说了具体用法,把握搞懂,应用 SpringBoot 来开发我的项目根本没啥大问题了! 为什么要写这篇文章? 话不多说,间接上干货! 1. @SpringBootApplication这里先独自拎出@SpringBootApplication 注解说一下,尽管咱们个别不会被动去应用它。 这个注解是 Spring Boot 我的项目的基石,创立 SpringBoot 我的项目之后会默认在主类加上。 @SpringBootApplicationpublic class SpringSecurityJwtGuideApplication { public static void main(java.lang.String[] args) { SpringApplication.run(SpringSecurityJwtGuideApplication.class, args); }} 咱们能够把 @SpringBootApplication看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的汇合。 package org.springframework.boot.autoconfigure;@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication { ......}package org.springframework.boot;@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic @interface SpringBootConfiguration {} 依据 SpringBoot 官网,这三个注解的作用别离是: @EnableAutoConfiguration:启用 SpringBoot 的主动配置机制@ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类。@Configuration:容许在 Spring 上下文中注册额定的 bean 或导入其余配置类2. Spring Bean 相干2.1. @Autowired主动导入对象到类中,被注入进的类同样要被 Spring 容器治理比方:Service 类注入到 Controller 类中。 ...

January 4, 2021 · 7 min · jiezi

关于springboot:Springboot中如何优雅进行字段校验

差不多大半年没写文章了,终于将手头上的事忙完了,能够对外输入了。前段时间提交代码审核,共事提了一个代码标准缺点:参数校验应该放在controller层。到底应该如何做参数校验呢Controller层 VS Service层去网上查阅了一些材料,个别举荐与业务无关的放在Controller层中进行校验,而与业务无关的放在Service层中进行校验。那么如何将参数校验写的优雅好看呢,如果都是if - else,就感觉代码写的很low,还好有轮子能够应用 罕用校验工具类应用Hibernate Validate引入依赖 <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>4.3.1.Final</version> </dependency>罕用注解阐明|注解 | 阐明 ||:-| :-| | @Length(min=,max=) | 查看所属的字段的长度是否在min和max之间,只能用于字符串 || @Range(min=,max=,message=) | 被正文的元素必须在适合的范畴内 || @Max | 该字段的值只能小于或等于该值 || @Min | 该字段的值只能大于或等于该值 || @NotNull | 不能为null || @NotBlank | 不能为空,查看时会将空格疏忽 || @NotEmpty | 不能为空,这里的空是指空字符串 || @Pattern(regex=,flag=) | 被正文的元素必须合乎指定的正则表达式 | 应用姿态须要搭配在Controller中搭配@Validated或@Valid注解一起应用,@Validated和@Valid注解区别不是很大,个别状况下任选一个即可,区别如下:|注解| @Validated | @Valid ||:-|:-| :-| |所属的包| 属于org.springframework.validation.annotation包下的,是spring提供的 | 属于javax.validation包下,是jdk给提供的 ||是否反对分组和排序| 是 | 否|尽管@Validated比@Valid更加弱小,在@Valid之上提供了分组性能和验证排序功能,不过在理论我的项目中始终没有用到过 Hibernate-validate框架中的注解是须要加在实体中一起应用的 定义一个实体public class DataSetSaveVO { //惟一标识符为空 @NotBlank(message = "user uuid is empty") //用户名称只能是字母和数字 @Pattern(regexp = "^[a-z0-9]+$", message = "user names can only be alphabetic and numeric") @Length(max = 48, message = "user uuid length over 48 byte") private String userUuid; //数据集名称只能是字母和数字 @Pattern(regexp = "^[A-Za-z0-9]+$", message = "data set names can only be letters and Numbers") //文件名称过长 @Length(max = 48, message = "file name too long") //文件名称为空 @NotBlank(message = "file name is empty") private String name; //数据集形容最多为256字节 @Length(max = 256, message = "data set description length over 256 byte") //数据集形容为空 @NotBlank(message = "data set description is null") private String description;}阐明:message字段为不合乎校验规定时抛出的异样信息 ...

January 4, 2021 · 2 min · jiezi

关于springboot:Springboot中如何优雅进行字段校验

差不多大半年没写文章了,终于将手头上的事忙完了,能够对外输入了。前段时间提交代码审核,共事提了一个代码标准缺点:参数校验应该放在controller层。到底应该如何做参数校验呢Controller层 VS Service层去网上查阅了一些材料,个别举荐与业务无关的放在Controller层中进行校验,而与业务无关的放在Service层中进行校验。那么如何将参数校验写的优雅好看呢,如果都是if - else,就感觉代码写的很low,还好有轮子能够应用 罕用校验工具类应用Hibernate Validate引入依赖 <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>4.3.1.Final</version> </dependency>罕用注解阐明|注解 | 阐明 ||:-| :-| | @Length(min=,max=) | 查看所属的字段的长度是否在min和max之间,只能用于字符串 || @Range(min=,max=,message=) | 被正文的元素必须在适合的范畴内 || @Max | 该字段的值只能小于或等于该值 || @Min | 该字段的值只能大于或等于该值 || @NotNull | 不能为null || @NotBlank | 不能为空,查看时会将空格疏忽 || @NotEmpty | 不能为空,这里的空是指空字符串 || @Pattern(regex=,flag=) | 被正文的元素必须合乎指定的正则表达式 | 应用姿态须要搭配在Controller中搭配@Validated或@Valid注解一起应用,@Validated和@Valid注解区别不是很大,个别状况下任选一个即可,区别如下:|注解| @Validated | @Valid ||:-|:-| :-| |所属的包| 属于org.springframework.validation.annotation包下的,是spring提供的 | 属于javax.validation包下,是jdk给提供的 ||是否反对分组和排序| 是 | 否|尽管@Validated比@Valid更加弱小,在@Valid之上提供了分组性能和验证排序功能,不过在理论我的项目中始终没有用到过 Hibernate-validate框架中的注解是须要加在实体中一起应用的 定义一个实体public class DataSetSaveVO { //惟一标识符为空 @NotBlank(message = "user uuid is empty") //用户名称只能是字母和数字 @Pattern(regexp = "^[a-z0-9]+$", message = "user names can only be alphabetic and numeric") @Length(max = 48, message = "user uuid length over 48 byte") private String userUuid; //数据集名称只能是字母和数字 @Pattern(regexp = "^[A-Za-z0-9]+$", message = "data set names can only be letters and Numbers") //文件名称过长 @Length(max = 48, message = "file name too long") //文件名称为空 @NotBlank(message = "file name is empty") private String name; //数据集形容最多为256字节 @Length(max = 256, message = "data set description length over 256 byte") //数据集形容为空 @NotBlank(message = "data set description is null") private String description;}阐明:message字段为不合乎校验规定时抛出的异样信息 ...

January 4, 2021 · 2 min · jiezi

关于springboot:SpringBoot事务对TestRestTemplate的影响

前言在本周进行后盾用户登录单元测试的过程中,因为与之前的构造产生了较大的变动,因而呈现了很多的问题,在泛滥问题中,因为事务呈现的问题堪称是涉及到常识盲区,甚至老师说了问题出在事务上,本人也还是处于很懵的状态,因而特意理解了一下事务对该测试的影响,力求下次再呈现问题时,能晓得问题的基本。 问题在本周对后盾登录办法进行单元测试的过程中,呈现了断言谬误: ERROR: 测试方法: @BeforeEachpublic void addCurrentLoginUser() { this.username = "188" + String.valueOf(CommonService.getRandomNumberLongs(10000000, 99999999)); this.password = RandomString.make(40); this.user = new User(); this.user.setUsername(this.username); this.user.setPassword(this.password); this.userRepository.save(this.user); } @Test void getCurrentLoginUser() { logger.debug("初始化根底数据"); HttpHeaders headers; HttpEntity<Void> entity; ResponseEntity<Void> response; logger.debug("1: 测试用户名明码正确"); headers = new HttpHeaders(); entity = new HttpEntity<>(null, headers); response = this.restTemplate .withBasicAuth(this.username, this.password) .exchange(CONFIG_LOGIN, HttpMethod.GET, entity, Void.class); logger.debug("断言: 状态码为200"); assertThat(response.getStatusCode().value()).isEqualTo(HttpStatus.OK.value());}执行办法: @Transactional @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { logger.debug("依据用户名查问用户"); logger.debug(username); User user = this.userRepository.findByUsername(username).orElseThrow(() -> new ObjectNotFoundException("user实体未找到")); if (user == null) { logger.error("用户名不存在"); throw new UsernameNotFoundException("用户名不存在"); } logger.debug("结构用户"); return new org.springframework.security.core.userdetails.User(username, user.getPassword(), authorities); }测试原理: ...

January 4, 2021 · 3 min · jiezi

关于springboot:springboot整合jsp完成公交车站路线图

点赞再看,养成习惯 开发环境:jdk 8intellij ideatomcat 8mysql 5.7maven 3.6所用技术:springbootjsp数据动态初始化我的项目介绍应用springboot整合jsp,在后端写入公交路线名称和具体站点,前端页面可条件查问具体的内容,如公交路线,公交名称,车俩信息等。 运行成果前台用户端: 路线抉择 路线详情 数据筹备:BusData.txt 筹备工作:pom.xml退出jsp模板引擎反对:<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope></dependency>springboot配置jspspring.mvc.view.prefix=/spring.mvc.view.suffix=.jsp重要代码:bus数据初始化@PostConstructprivate void initBusData(){ try{ File file = new File(BusMap.getClass().getResource("/").getPath()); FileReader fileReader = new FileReader(file.getPath()+"/static/BusData.txt","GBK"); //初始化BusData.txt 数据 List<String> readLines = fileReader.readLines(); for(String str:readLines){ if(!"".equals(str)){ String[] data=str.split("#"); String way=data[0]; //几路线 String location=data[1];/ /地名 String[] locations=location.split(","); List<Bus> list=new ArrayList<>(); for(int i=0;i<locations.length;i++){ int busnum=0; if(i%4==0){ //随机busnum busnum=1; }if(i%5==0){ busnum=2; } Bus bus=new Bus(locations[i],busnum); list.add(bus); } WayList.add(way); //增加路线 BusMap.put(way,list); //增加车站 } } }catch (Exception e){ e.printStackTrace(); }}路线查问@RequestMapping("/way")public String search(HttpServletRequest request,String way) { try { if(null==way||"".equalsIgnoreCase(way)){ request.setAttribute("list", BusMap.WayList); //没有搜寻默认显示所有路线 return "way"; }else{ List<String> wayList=new ArrayList<>(); //含糊查问路线 for(String str:BusMap.WayList){ if(str.indexOf(way)>-1){ wayList.add(str); } } if(wayList.size()>0){ request.setAttribute("list", wayList); //含糊搜寻进去的路线列表 return "way"; }else{ return "noView"; //没有所选路线 } } } catch (Exception e) { e.printStackTrace(); } return "way";}公交车路线站展现@RequestMapping("/view")public String view(HttpServletRequest request,String way) { try { List<Bus> list= BusMap.getBusMap(way); if(list.size()>0){ request.setAttribute("list",list ); //获取总路线 request.setAttribute("firstBus", list.get(0).getLocation()); //第一站 request.setAttribute("lastBus", list.get(list.size()-1).getLocation()); //最初一站 int size = list.size(); size =(size-1)*99; request.setAttribute("size",size); return "view"; } } catch (Exception e) { e.printStackTrace(); } return "noView";//没有对应公交车站}//前端页面数据渲染<div class="pageContent" style="background: #eeeeee;"> <div class="pageFormContent" layoutH="55"> <div class="timeText">${firstBus}<----->${lastBus} <span>( 首/末班车工夫:<span style="color: red">6:00 / 23:00</span>)</span> </div> <div class="timezone" style="margin-top: 20px"> <c:forEach var="list" items="${list}" varStatus="s"> <div class="time" <c:if test="${s.index!=0}"> style="top: ${s.index*100+25}px;" a="1" </c:if> ><a onclick="javascript:alert(1);">${s.index+1}</a> <h2>${list.location}</h2> <c:if test="${list.busNum>0}"> <span class="timezone3"></span> <div> <p><span style="padding-left: 30px;">${list.busNum}辆公交</span></p> </div> </c:if> </div> </c:forEach> </div> </div> <div class="formBar"></div></div>我的项目总结我的项目寄存门路最好不要带中文门路,否则可能存在动态busData资源初始化失败页面工夫车站路线所采纳时间轴形式展现,长度动静计算,局部浏览器显示可能有点错位其余后续迭代性能后续开发,敬请关注

January 2, 2021 · 1 min · jiezi

关于springboot:SpringBoot集成WebSocket实现后台向前端推送信息

更多文章欢送关注公众号:Java版web我的项目,外面还蕴含了从根底到进阶的各种材料前言在一次我的项目开发中,应用到了Netty网络应用框架,以及MQTT进行音讯数据的收发,这其中须要后盾来将获取到的音讯被动推送给前端,于是就应用到了MQTT,特此记录一下。 一、什么是websocket?WebSocket协定是基于TCP的一种新的网络协议。它实现了客户端与服务器全双工通信,学过计算机网络都晓得,既然是全双工,就阐明了服务器能够被动发送信息给客户端。这与咱们的推送技术或者是多人在线聊天的性能不约而同。 为什么不应用HTTP 协定呢?这是因为HTTP是单工通信,通信只能由客户端发动,客户端申请一下,服务器解决一下,这就太麻烦了。于是websocket应运而生。 上面咱们就间接开始应用Springboot开始整合。以下案例都在我本人的电脑上测试胜利,你能够依据本人的性能进行批改即可。我的我的项目构造如下: 二、应用步骤1.增加依赖Maven依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> 12342.启用Springboot对WebSocket的反对启用WebSocket的反对也是很简略,几句代码搞定: import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;/** * @ Auther: 马超伟 * @ Date: 2020/06/16/14:35 * @ Description: 开启WebSocket反对 */@Configurationpublic class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); }}1234567891011121314153.外围配置:WebSocketServer因为WebSocket是相似客户端服务端的模式(采纳ws协定),那么这里的WebSocketServer其实就相当于一个ws协定的Controller @ ServerEndpoint 注解是一个类档次的注解,它的性能次要是将目前的类定义成一个websocket服务器端, 注解的值将被用于监听用户连贯的终端拜访URL地址,客户端能够通过这个URL来连贯到WebSocket服务器端新建一个ConcurrentHashMap webSocketMap 用于接管以后userId的WebSocket,不便传递之间对userId进行推送音讯。上面是具体业务代码: package cc.mrbird.febs.external.webScoket;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;import org.springframework.stereotype.Service;import javax.websocket.*;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;import java.io.IOException;import java.time.LocalDateTime;import java.util.List;import java.util.concurrent.CopyOnWriteArraySet;/** * Created with IntelliJ IDEA. * @ Auther: 马超伟 * @ Date: 2020/06/16/14:35 * @ Description: * @ ServerEndpoint 注解是一个类档次的注解,它的性能次要是将目前的类定义成一个websocket服务器端, * 注解的值将被用于监听用户连贯的终端拜访URL地址,客户端能够通过这个URL来连贯到WebSocket服务器端 */@Component@Slf4j@Service@ServerEndpoint("/api/websocket/{sid}")public class WebSocketServer { //动态变量,用来记录以后在线连接数。应该把它设计成线程平安的。 private static int onlineCount = 0; //concurrent包的线程平安Set,用来寄存每个客户端对应的MyWebSocket对象。 private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>(); //与某个客户端的连贯会话,须要通过它来给客户端发送数据 private Session session; //接管sid private String sid = ""; /** * 连贯建设胜利调用的办法 */ @OnOpen public void onOpen(Session session, @PathParam("sid") String sid) { this.session = session; webSocketSet.add(this); //退出set中 this.sid = sid; addOnlineCount(); //在线数加1 try { sendMessage("conn_success"); log.info("有新窗口开始监听:" + sid + ",以后在线人数为:" + getOnlineCount()); } catch (IOException e) { log.error("websocket IO Exception"); } } /** * 连贯敞开调用的办法 */ @OnClose public void onClose() { webSocketSet.remove(this); //从set中删除 subOnlineCount(); //在线数减1 //断开连接状况下,更新主板占用状况为开释 log.info("开释的sid为:"+sid); //这里写你 开释的时候,要解决的业务 log.info("有一连贯敞开!以后在线人数为" + getOnlineCount()); } /** * 收到客户端音讯后调用的办法 * @ Param message 客户端发送过去的音讯 */ @OnMessage public void onMessage(String message, Session session) { log.info("收到来自窗口" + sid + "的信息:" + message); //群发音讯 for (WebSocketServer item : webSocketSet) { try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } /** * @ Param session * @ Param error */ @OnError public void onError(Session session, Throwable error) { log.error("产生谬误"); error.printStackTrace(); } /** * 实现服务器被动推送 */ public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * 群发自定义音讯 */ public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException { log.info("推送音讯到窗口" + sid + ",推送内容:" + message); for (WebSocketServer item : webSocketSet) { try { //这里能够设定只推送给这个sid的,为null则全副推送 if (sid == null) {// item.sendMessage(message); } else if (item.sid.equals(sid)) { item.sendMessage(message); } } catch (IOException e) { continue; } } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketServer.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketServer.onlineCount--; } public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() { return webSocketSet; }}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401414.测试Controllerimport org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.servlet.ModelAndView;import java.io.IOException;import java.util.HashMap;import java.util.Map;/** * Created with IntelliJ IDEA. * * @ Auther: 马超伟 * @ Date: 2020/06/16/14:38 * @ Description: */@Controller("web_Scoket_system")@RequestMapping("/api/socket")public class SystemController { //页面申请 @GetMapping("/index/{userId}") public ModelAndView socket(@PathVariable String userId) { ModelAndView mav = new ModelAndView("/socket1"); mav.addObject("userId", userId); return mav; } //推送数据接口 @ResponseBody @RequestMapping("/socket/push/{cid}") public Map pushToWeb(@PathVariable String cid, String message) { Map<String,Object> result = new HashMap<>(); try { WebSocketServer.sendInfo(message, cid); result.put("code", cid); result.put("msg", message); } catch (IOException e) { e.printStackTrace(); } return result; }}123456789101112131415161718192021222324252627282930313233343536373839404142434445465.测试页面index.html<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>Java后端WebSocket的Tomcat实现</title> <script type="text/javascript" src="js/jquery.min.js"></script> </head> <body> <div id="main" style="width: 1200px;height:800px;"></div> Welcome<br/><input id="text" type="text" /> <button onclick="send()">发送音讯</button> <hr/> <button onclick="closeWebSocket()">敞开WebSocket连贯</button> <hr/> <div id="message"></div> </body> <script type="text/javascript"> var websocket = null; //判断以后浏览器是否反对WebSocket if('WebSocket' in window) { //改成你的地址 websocket = new WebSocket("ws://192.168.100.196:8082/api/websocket/100"); } else { alert('以后浏览器 Not support websocket') } //连贯产生谬误的回调办法 websocket.onerror = function() { setMessageInnerHTML("WebSocket连贯产生谬误"); }; //连贯胜利建设的回调办法 websocket.onopen = function() { setMessageInnerHTML("WebSocket连贯胜利"); } var U01data, Uidata, Usdata //接管到音讯的回调办法 websocket.onmessage = function(event) { console.log(event); setMessageInnerHTML(event); setechart() } //连贯敞开的回调办法 websocket.onclose = function() { setMessageInnerHTML("WebSocket连贯敞开"); } //监听窗口敞开事件,当窗口敞开时,被动去敞开websocket连贯,避免连贯还没断开就敞开窗口,server端会抛异样。 window.onbeforeunload = function() { closeWebSocket(); } //将音讯显示在网页上 function setMessageInnerHTML(innerHTML) { document.getElementById('message').innerHTML += innerHTML + '<br/>'; } //敞开WebSocket连贯 function closeWebSocket() { websocket.close(); } //发送音讯 function send() { var message = document.getElementById('text').value; websocket.send('{"msg":"' + message + '"}'); setMessageInnerHTML(message + "&#13;"); } </script></html>12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273746.后果展现后盾:如果有连贯申请 ...

January 2, 2021 · 3 min · jiezi

关于springboot:如何解决springboot参数传中文乱码

前言本文案例来自业务部门的一个业务场景。他们的业务场景是他们部门研发了一个微服务上下文透传组件,其透传原理也挺简略的,就是通过springboot拦截器把申请参数塞进threadlocal,而后上游通过threadlocal取到值,服务之间进行feign调用时,再把threadlocal的参数塞到header头外面。这个组件始终用得好好的,忽然有一天因为传的参数值是中文,导致乱码。他们通过尝试上面的各种计划,都无奈解决。最初就让咱们部门排查解决。 业务部门的实现思路他们一开始的思路方向是参数编码不统一导致中文乱码。于是他们就朝这个方向致力着,于是就有了如下计划 计划一:String value = new String("我是中文乱码".getBytes("ISO-8859-1"),"UTF-8");这个是罕用解决字符串中文乱码的办法之一 计划二:编写字符编码过滤器@WebFilter(urlPatterns = "/*",filterName = "CharacterEncodingFilter")public class CharacterEncodingFilter implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); filterChain.doFilter(request , response); } @Override public void destroy() { }}而后启动类上加上@ServletComponentScan。@WebFilter是servlet3.0才有的注解。当然这个过滤器你还能够这么写 public class CharacterEncodingFilter implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); filterChain.doFilter(request , response); } @Override public void destroy() { }}写个bean配置类,如下 ...

December 31, 2020 · 1 min · jiezi

关于springboot:SpringBoot-的多数据源配置

最近在我的项目开发中,须要为一个应用 MySQL 数据库的 SpringBoot 我的项目,新增加一个 PLSQL 数据库数据源,那么就须要进行 SpringBoot 的多数据源开发。代码很简略,上面是实现的过程。 <!-- more --> 环境筹备试验环境: JDK 1.8SpringBoot 2.4.1Maven 3.6.3MySQL 5.7因为我本地只有 MySQL 数据库,为了不便演示,我会在启动一个本地 MySQL,在 MySQL 创立两个数据库,每个库中均有一个表,以此进行演示。 数据筹备本地 MySQL 端口默认不做改变,端口号 3306。 创立数据库 demo1,demo2。在 demo1 数据库中创立表 book。 -- create tablecreate table Book( id int auto_increment primary key, author varchar(64) not null comment '作者信息', name varchar(64) not null comment '书籍名称', price decimal not null comment '价格', createTime datetime null comment '上架工夫', description varchar(128) null comment '书籍形容');-- insert dataINSERT INTO demo1.Book (id, author, name, price, createTime, description) VALUES (1, '金庸', '笑傲江湖', 13, '2020-12-19 15:26:51', '武侠小说');INSERT INTO demo1.Book (id, author, name, price, createTime, description) VALUES (2, '罗贯中', '三国演义', 14, '2020-12-19 15:28:36', '历史小说');在 demo2 数据库中创立表 user。 ...

December 30, 2020 · 3 min · jiezi

关于springboot:Solon-特性简集相较于-Springboot-有什么区别

Solon 是一个相似Springboot的微型开发框架,也是一个不基于Servlet的开发框架。我的项目从2018年启动以来,参考过大量前人作品;历时两年,3500屡次的commit;内核放弃0.1m的身材,超高的Web跑分,良好的应用体验。 Solon 强调:克服 + 简洁 + 凋谢的准则;力求:更小、更快、更自在的体验。 所谓更小:内核0.1m,最小Web开发单位0.2m(相比Springboot我的项目包,小到能够乎略不计了)。 具用户反映,某些我的项目切换到Solon后,能够缩减到原来10%的包大小。 所谓更快:本机helloworld测试,启动最快可达0.09s,Qps可达12万之多。可参考:《helloworld_wrk_test》。 所谓更自在:代码操控自在:// 除了注入模式之外,还能够按需手动////手动获取配置String userName = Solon.cfg().get("user.name");Properties dbcfg = Solon.cfg().getProp("db");//手动获取容器里的BeanUserService userService = Aop.get(UserService.class);//手动监听http post申请Solon.global().post("/user/update", x-> userService.updateById(x.paramMap())); 框架抉择自在:能够用solon-web这样的疾速开发集成包。也能够按我的项目须要抉择不同的插件组装,比方:为非Solon我的项目增加solon.boot.jlhttp,0.2m即可让我的项目实现http+rpc开发;还能够用MVC开发Socket利用。 个性简集:1、与Springboot的罕用注解比拟Solon 1.2.12 Springboot 2.3.3 阐明 @Inject * @Autowired 注入Bean(by type) @Inject("name") @Qualifier+@Autowired 注入Bean(by name) @Inject("${name}") @Value("${name}") 注入配置 @Component @Component 托管组件 @Singleton @Scope(“singleton”) 单例(Solon 默认是单例) @Singleton(false) @Scope(“prototype”) 非单例 @Init * @PostConstruct 结构实现并注入后的初始化 @Configuration @Configuration 配置类 @Bean @Bean 配置组件 @Mapping @RequestMapping,@GetMapping... 映射 @Param @RequestParam ...

December 28, 2020 · 3 min · jiezi

关于springboot:Springboot216集成-activiti7-出现登录验证

一、问题Spring2.1.5集成activiti7.1.24时拜访要输出用户名和明码。 @Autowired private ProcessRuntime processRuntime; /** * 启动工作 */ @Test void startProcess(){ /** * 流程变量 * 给<userTask id="销假申请" name="销假申请" activiti:assignee="#{student}"></userTask> * 的student赋值 */ HashMap<String, Object> variables = new HashMap<>(); // String username = SecurityUtils.getNickName(); String username = "小王"; variables.put("staff", username); ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder .start() .withProcessDefinitionKey("baoxiao") .withName("报销测试") //.withBusinessKey(id) // .withVariable("deptLeader", join) .withVariables(variables) .build()); System.out.println(processInstance.getId()); }在单元测试中测试Activiti框架,呈现如下的异样: org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:379) at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:223) at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:65) ... at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)其余的形式大家如果试过胜利就能够了,如果不行,可试下这个:勾销登录验证如下: ...

December 27, 2020 · 2 min · jiezi

关于springboot:SpringBoot整合mybatis常见问题

Spring中常见问题1.NoSuchBeanDefinitionException 2.'..Service' that could not be found service找不到 3.port 80 was already in use 端口号被占用 4.TemplateInputException 模板解析异样或找不到模板1.查看模板所在的目录是否与配置的前缀目录雷同2.查看返回的模板是否存在,返回值类型是否统一3.查看配置前缀时是否以"/"斜杠结尾4.管制层的url与客户端的ur是否统一 5. 404异样 拜访资源不存在 6. 500异样 500异样要查看控制台 Mybatis中常见问题1.springboot中增加maven依赖 2.BadSqlGrammarException 谬误的sql语句 3.BindingException 绑定异样1.查看映射文件的门路配置与理论存储地位是否统一2.查看dao接口的类名是否与映射文件的namespace值雷同(不能有空格)3.查看dao接口中的办法名是否在映射文件中有对应的id 4.IllegalArgumentException起因:同样说我sql映射是否呈现了重复性的定义(例如:别离以注解形式和xml配置文件形式进行定义,也就是说在同一个namespace下呈现了反复的元素id) 5.SAXParseException xml解析问题

December 24, 2020 · 1 min · jiezi

关于springboot:常用项目设计

1 罕用我的项目设计框架1.1 用户在拜访服务器时因为并发量比拟大,通常应用多台服务器来解决高拜访的问题。而域名、端口等对立且惟一,用户不可能在输出网址时一直的批改,因而用对立的代理服务器作为惟一的入口,再进行调配。 1.2 数据库端同样为缩小访问量,削减了缓存服务器。以及主从数据库,外面数据完全相同。一旦主机呈现的故障,会依据特定算法主动切换到从机上。 2 项目表设计2.1 表与表之间的关系2.1.1 一对一一个商品对应一个商品形容,一个商品形容对应一个商品。通常将一个表中的主键充当另一个表中的外键。(pk--主键,fk--外键) 2.1.2 一对多一个商品分类中对应多个商品,但一个商品却只属于一个商品分类。(当以某个对象为主体时,则以此对象为参考点。在以商品为参考点时,此时曾经选中了这个商品,即有且只有一个,只对应一个商品分类。) 2.1.3 多对多一个角色对应多个权限,一个权限对应多个角色。(多对多并不是指多个对象对多个对象,而是咱们在选取这个对象时,这个对象曾经被确定下来了,有且只有一个。这个‘多’示意其余对象与它的关系。) 2.2 导入数据库导入指令; source 门路/xxx.sql('/'和'\' 都一样,在导入时能够先找到sql文件,先输出指令 source+空格 而后将文件间接拉入,再回车即可,也可通过其余工具导入。) 2.3 IDEA配置环境2.3.1 JDK配置2.3.1.1 输出变量名,并找到对应的JDK装置根目录 3.3.1.2 在path上新建该变量 3.3.1.3 检测JDK装置是否胜利输出指令:java -version 2.3.2 IDEA罕用配置2.3.2.1 装置Lombok插件 2.3.2.2 设置习惯的快捷键 2.3.2.3 批改鼠标缩放 2.3.2.4 智能提醒 2.3.2.5 构建我的项目 2.3.2.6 配置maven 2.3.2.7 配置主动保留 2.3.2.8 批改主动提醒 2.3.2.9 设置编码 2.3.3 设置背景色彩 3 SpringBoot高级利用3.1 创立SpringBoot我的项目 3.2 对于Maven阐明Maven是一站式的项目管理工具,能够创立我的项目、治理我的项目、我的项目打包、依赖、公布等。 3.2.1 对于Maven坐标的阐明Maven中有很多其余机构的开源jar包文件,通过坐标模式进行对立的治理。 <dependency> <groupId>org.springframework.boot 公司域名倒写</groupId> <artifactId>spring-boot-starter-web 项目名称</artifactId> <version>XX 版本号</version> </dependency>jar包地位:C:\The_four_step\maven\rep\org\springframework\boot\spring-boot-starter-web\2.4.1 ...

December 24, 2020 · 1 min · jiezi

关于springboot:Spring-boot-接入swagger以及使用

Springboot 集成Swagger 2(springfox) 1、集成导入依赖Springfox Swagger2: <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 --><dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>3.0.0</version></dependency>Springfox Swagger UI: <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui --><dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>3.0.0</version></dependency>配置swagger@Configuration@EnableSwagger2public class SwaggerConfig{}创立一个以上一个类配置swagger即可。 默认成果拜访界面:IP:port/swagger-ui.html 2、配置在以上配置类中增加办法: //配置swagger的Docket的Bean实例@Beanpublic Docket docket(){ return new Docket(DocumentationType.SWAGGER_2).apiInfo(this.apiInfo());}//自定义swagger信息private ApiInfo apiInfo(){ Contact contact = new Contact("name","url","email"); return new ApiInfo("title","description","version","termsOfServiceUrl",contact,"license",licenseUrl,new ArrayList());}3、配置扫描接口我的项目中并不需要swagger将整个我的项目的所有类裸露进来,通过以上配置的Bean实例,设置swagger关注并显示的接口,以及是否开启应用: return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .enable(false)//false时,浏览器不能拜访,默认为true .select() //RequestHandlerSelect,配置要扫描接口的形式 //basePackage("包门路"):指定要扫描的包 //any():全副扫描 //none():不扫描 //withClassAnnotation():扫描类上有指定的类注解的类,参数是一个类注解,如"RestContrller.class" //withMethodAnnotation():扫描办法上有指定的注解的办法,参数如"GetMapping.class" .api(RequestHandlerSelectors.basePackage("com.noel.swagger.controller")) //paths():过滤某些门路,参数为正则匹配参数 .paths(PathSelectors.ant("/omit/**")) .build();只心愿在开发环境中应用swagger,然而生产环境不应用: @Beanpublic Docket docket(Environment environment){ //dev: application-dev.yml or application-anotherEnv.yml 或者相似的properties配置文件 //如果容许时加载了指定的环境,则返回true Profiles profiles = Profiles.of("dev","anotherEnv"); Boolean isSwaggerOn = environment.acceptsProfiles(profiles); return new Docket(DocumentationType.SWAGGER_2) .apiInfo(this.apiInfo()) .enable(isSwaggerOn);}配置API文档分组: ...

December 24, 2020 · 1 min · jiezi

关于springboot:SpringBoot项目中的问题

1.SocketTimeoutException:创立我的项目时连贯超时 2.NoUniqueBeanDefinitionException非惟一的bean对象定义 3.No tests found with test runner 'JUnit5'单元测试失败留神:增加@Test注解的条件 1.必须是公共的,非动态的2.办法无返回值(void润饰)3.办法必须是无参的4.类名不能间接用Test 4.Unable to find a @SpringBootConfiguration单元测试异样 5.1.NoSuchBeanException 没有bean的定义 或者 No beans of '类名'type fount Bean类型找不到 5.2.NoSuchBeanException 如果对应的bean不是本人写的,可能是因为依赖没有下载下来 6.JDBC连接池配置异样 7.NPE异样 空指针异样解决办法: 1.查看是否呈现了对象调办法,对象可能为空2.查看调用办法的对象是否附了值(本人赋值或spring框架赋值)3.如果是spring框架赋值,要查看属性是否有@Autowired形容,属性所在的类是否交给了spring治理

December 22, 2020 · 1 min · jiezi

关于springboot:个人学习系列-Spring-Boot使用RedisGeo实现位置查找功能

最近应用团油的时候总是感觉他的那个依照间隔排序的性能很好,所以就试着钻研一下。1. 新建spring boot我的项目1.1 pom.xml增加redis依赖和lombok依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> <scope>provided</scope></dependency>1.2 application.yml# Redis数据库索引(默认为0)spring: redis: database: 0 # Redis服务器地址 host: 127.0.0.1 # Redis服务器连贯端口 port: 6379 # Redis服务器连贯明码(默认为空) password: # 连接池最大连接数(应用负值示意没有限度) jedis: pool: max-active: 20 # 连接池最大阻塞等待时间(应用负值示意没有限度) max-wait: -1 # 连接池中的最大闲暇连贯 max-idle: 10 # 连接池中的最小闲暇连贯 min-idle: 0 # 连贯超时工夫(毫秒) timeout: 10001.3 新建实体类/** * 油站实体类 * @author zhouzhaodong */@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic class ServiceStation implements Serializable { /** 油站 */ private String serviceStationName; /** 经度 */ private Double longitude; /** 纬度 */ private Double latitude;}1.4 新建service/** * 服务接口定义 * @author zhouzhaodong */public interface RedisGeoService { /** * 把油站信息保留到 Redis 中 * @param serviceStations {@link ServiceStation} * @return 胜利保留的个数 * */ Long saveServiceStationToRedis(Collection<ServiceStation> serviceStations); /** * 获取给定油站的坐标 * @param serviceStations 给定油站 key * @return {@link Point}s * */ List<Point> getServiceStationPos(String[] serviceStations); /** * 获取两个油站之间的间隔 * @param serviceStation1 第一个油站 * @param serviceStation2 第二个油站 * @param metric {@link Metric} 单位信息, 能够是 null * @return {@link Distance} * */ Distance getTwoServiceStationDistance(String serviceStation1, String serviceStation2, Metric metric); /** * 依据给定地理位置坐标获取指定范畴内的地理位置汇合 * @param within {@link Circle} 中心点和间隔 * @param args {@link RedisGeoCommands.GeoRadiusCommandArgs} 限度返回的个数和排序形式, 能够是 null * @return {@link RedisGeoCommands.GeoLocation} * */ GeoResults<RedisGeoCommands.GeoLocation<String>> getPointRadius( Circle within, RedisGeoCommands.GeoRadiusCommandArgs args); /** * 依据给定地理位置获取指定范畴内的地理位置汇合 * @param member 油站名称 * @param distance 间隔范畴 * @param args {@link RedisGeoCommands.GeoRadiusCommandArgs} 限度返回的个数和排序形式, 能够是 null * @return */ GeoResults<RedisGeoCommands.GeoLocation<String>> getMemberRadius( String member, Distance distance, RedisGeoCommands.GeoRadiusCommandArgs args); /** * 获取某个地理位置的 geohash 值 * @param serviceStations 给定油站 key * @return city geohashs * */ List<String> getServiceStationGeoHash(String[] serviceStations);}1.5 新建service实现类/** * 服务接口实现 * @author zhouzhaodong */@Service@Slf4jpublic class RedisGeoServiceImpl implements RedisGeoService { /** * redis的key */ private final String GEO_KEY = "ah-cities"; @Autowired private StringRedisTemplate redisTemplate; @Override public Long saveServiceStationToRedis(Collection<ServiceStation> serviceStation) { log.info("start to save station info: {}.", serviceStation); GeoOperations<String, String> ops = redisTemplate.opsForGeo(); Set<RedisGeoCommands.GeoLocation<String>> locations = new HashSet<>(); // 将坐标转为坐标点 serviceStation.forEach(ci -> locations.add(new RedisGeoCommands.GeoLocation<>( ci.getServiceStationName(), new Point(ci.getLongitude(), ci.getLatitude()) ))); log.info("done to save station info."); return ops.add(GEO_KEY, locations); } @Override public List<Point> getServiceStationPos(String[] serviceStations) { GeoOperations<String, String> ops = redisTemplate.opsForGeo(); // 依据油站名称获取油站的坐标 return ops.position(GEO_KEY, serviceStations); } @Override public Distance getTwoServiceStationDistance(String serviceStations1, String serviceStations2, Metric metric) { GeoOperations<String, String> ops = redisTemplate.opsForGeo(); return metric == null ? ops.distance(GEO_KEY, serviceStations1, serviceStations2) : ops.distance(GEO_KEY, serviceStations1, serviceStations2, metric); } @Override public GeoResults<RedisGeoCommands.GeoLocation<String>> getPointRadius(Circle within, RedisGeoCommands.GeoRadiusCommandArgs args) { GeoOperations<String, String> ops = redisTemplate.opsForGeo(); return args == null ? ops.radius(GEO_KEY, within) : ops.radius(GEO_KEY, within, args); } @Override public GeoResults<RedisGeoCommands.GeoLocation<String>> getMemberRadius(String member, Distance distance, RedisGeoCommands.GeoRadiusCommandArgs args) { GeoOperations<String, String> ops = redisTemplate.opsForGeo(); return args == null ? ops.radius(GEO_KEY, member, distance) : ops.radius(GEO_KEY, member, distance, args); } @Override public List<String> getServiceStationGeoHash(String[] serviceStations) { GeoOperations<String, String> ops = redisTemplate.opsForGeo(); return ops.hash(GEO_KEY, serviceStations); }}1.6 测试代码@SpringBootTestclass RedisGeoApplicationTests { @Autowired private RedisGeoService geoService; /** * 测试 把油站信息保留到 Redis 中 * */ @Test public void testSaveServiceStationToRedis() { List<ServiceStation> serviceStations = new ArrayList<>(); serviceStations.add(new ServiceStation("金盾", 117.17, 31.52)); serviceStations.add(new ServiceStation("中石油", 117.02, 30.31)); serviceStations.add(new ServiceStation("中石化", 116.47, 33.57)); serviceStations.add(new ServiceStation("山东石化", 116.58, 33.38)); serviceStations.add(new ServiceStation("青岛石化", 115.48, 32.54)); serviceStations.add(new ServiceStation("壳牌", 117.21, 32.56)); serviceStations.add(new ServiceStation("中国化工", 118.18, 29.43)); System.out.println(geoService.saveServiceStationToRedis(serviceStations)); } /** * 测试 获取给定油站的坐标 * 如果传递的 city 在 Redis 中没有记录, 会返回什么呢 ? 例如, 这里传递的 xxx * */ @Test public void testGetServiceStationPos() { System.out.println(geoService.getServiceStationPos( Arrays.asList("中石油", "中石化", "xxx").toArray(new String[3]) )); } /** * 测试 获取两个油站之间的间隔 * */ @Test public void testGetTwoServiceStationDistance() { System.out.println(geoService.getTwoServiceStationDistance("壳牌", "金盾", null).getValue()); System.out.println(geoService.getTwoServiceStationDistance("壳牌", "金盾", Metrics.KILOMETERS).getValue()); } /** * 测试 依据给定地理位置坐标获取指定范畴内的地理位置汇合 * */ @Test public void testGetPointRadius() { Point center = new Point(117.17, 31.52); Distance radius = new Distance(140, Metrics.KILOMETERS); Circle within = new Circle(center, radius); System.out.println(geoService.getPointRadius(within, null)); // 获取前两个油站地位, 同时返回间隔中心点的间隔 RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(2).sortAscending(); System.out.println(geoService.getPointRadius(within, args)); } /** * 测试 依据给定地理位置获取指定范畴内的地理位置汇合 * */ @Test public void testGetMemberRadius() { Distance radius = new Distance(200, Metrics.KILOMETERS); System.out.println(geoService.getMemberRadius("金盾", radius, null)); // order by 间隔 limit 2, 同时返回间隔中心点的间隔 RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(2).sortAscending(); System.out.println(geoService.getMemberRadius("金盾", radius, args)); } /** * 测试 获取某个地理位置的 geohash 值 * */ @Test public void testGetServiceStationGeoHash() { System.out.println(geoService.getServiceStationGeoHash( Arrays.asList("中石化", "中石油", "xxx").toArray(new String[3]) )); }}结束! ...

December 22, 2020 · 3 min · jiezi

关于springboot:SpringBoot2-整合OAuth2组件模拟第三方授权访问

本文源码:GitHub·点这里 || GitEE·点这里 一、模式形容 受权服务 验证第三方服务的身份,验证邮箱用户的身份,记录和治理认证Token,为资源服务器提供Token校验。场景:第三方网站借助用户的邮箱登录,并拜访邮箱账户的根底信息,头像、名称等。 资源服务 第三方服务通过邮箱账户登录后须要获取的一些信息,即了解为资源,存储邮箱账户的数据资源。 第三方服务 即借助邮箱用户的账户,疾速登录第三个服务,免去繁冗的注册流程,有助于疾速积攒新用户。 交互流程 第三方服务给用户凋谢疾速邮箱登录性能,疏导用户调到邮箱认证服务,通过认证后返回身份令牌到第三方服务,第三方服务携带令牌拜访邮箱的资源服务,获取一些根本的邮箱用户信息。 二、我的项目配置管理1、案例构造 外围依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>2.1.3.RELEASE</version></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId></dependency>这里有两个外围组件依赖:OAuth2组件和Security组件。 模块划分 auth-server:受权服务resource-server:资源服务器third-server:第三个服务2、配置形容【受权服务】 OAuth2配置 这里的配置管理的是第三方的受权流程和发放给第三方的身份证明ClientID和明码,理论的场景就是第三方借助邮箱账号登录,首先就是向邮箱管理方提供资料,获取拜访邮箱服务的身份证明,而后能力对接凋谢服务,这种模式在第三方对接业务中很常见。 /** * 模仿第三方受权配置 */@EnableAuthorizationServer@Configurationpublic class AuthConfig extends AuthorizationServerConfigurerAdapter { @Resource ClientDetailsService clientDetailsService; /** * 资源服务器校验Token */ @Override public void configure(AuthorizationServerSecurityConfigurer security) { security.checkTokenAccess("permitAll()").allowFormAuthenticationForClients(); } /** * 第三方客户端申请配置,和资源服务拜访的配置,不设置默认都能够拜访,提供默认回调地址 */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("third01") .secret(new BCryptPasswordEncoder().encode("third01")) .resourceIds("resource-01") .authorizedGrantTypes("authorization_code","refresh_token") .scopes("all") .redirectUris("http://localhost:8082/notify.html"); } /** * 配置拜访端点 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.authorizationCodeServices(authorizationCodeServices()).tokenServices(tokenServices()); } /** * 内存治理 */ @Bean AuthorizationCodeServices authorizationCodeServices() { return new InMemoryAuthorizationCodeServices(); } /** * Token治理规定 */ @Bean AuthorizationServerTokenServices tokenServices() { DefaultTokenServices services = new DefaultTokenServices(); services.setClientDetailsService(clientDetailsService); services.setSupportRefreshToken(true); services.setTokenStore(tokenStore()); services.setAccessTokenValiditySeconds(3600); services.setRefreshTokenValiditySeconds(3600*7); return services; } @Bean TokenStore tokenStore() { return new InMemoryTokenStore(); }}通常须要数据库存储第三方信息,能够到第OAuth2开源我的项目中,获取表构造放到本地数据库中,而后这里换成数据源加载模式即可,简略的流程治理都在源码里写了SQL语句,数据源引入即可。 ...

December 22, 2020 · 2 min · jiezi

关于springboot:03IDE工具之IDEA中Git的基本应用

基于Git的我的项目操作装置Git工具Git是版本控制系统,能够借助Git实现团队代码版本控制及治理,从官网https://www.git-scm.com/downl...,如图所示:Git下载实现当前,傻瓜式(始终下一步)装置即可,不要更改装置目录(如果已装置过则毋庸装置)。 Git全局配置关上Git客户端工具,配置用户和明码,用于辨认提交代码的用户。 $ git config --global user.name "your-name"$ git config --global user.email "your-email@youremail.com" 查看配置信息 $ git config --listuser.email=xxxxxx@xxxxxx.comuser.name=xxxxxxIdea中查看Git配置。找到Git配置选项,进行Git测试,如图所示: IDEA我的项目中创立本地库创立我的项目本地库,如图所示: 个别本地库会创立在你我的项目的根目录,如图所示: 本地库创立好当前会在我的项目的根目录增加一个.git目录(可能是暗藏目录。 IDEA我的项目中本地库配置对.git目录中的exclude文件进行配置,对指定资源进行过滤(例如哪些资源不提交、上传,能够此文件做全局配置),内容如下: HELP.mdtarget/out/### IntelliJ IDEA ###.idea*.iws*.iml*.ipr.gitignore### maven ###mvnw*.cmd.mvn/我的项目Add,Commit操作将我的项目、Module更新增加到暂存区,提交(Commit)本地库,例如: 也能够,基于工具栏的按钮进行相干操作,如图所示: Idea中装置Gitee插件关上Setting中的Plugins选项,而后进行gitee插件搜寻和装置,如图所示: Gitee 装置胜利当前,查看是否在Version Control中有Gitee选项,如图所示: 点击Version Control的Gitee选项,进入Gitee配置,如图所示: 在Gitee配置界面,选则增加账户(Add Account),进入账户配置界面,如图所示: 在Gitee账户配置界面,进行连贯Gitee平台的账户配置(要当时注册好Gitee平台账户),而后点击Login进行登陆,登陆胜利当前会出现如下界面,如图所示: 我的项目Push操作剖析及实现将我的项目、Module推送的Gitee近程代码托管平台,如图所示: 指定Gitee仓库的仓库名(库不存在,推送时主动创立),如图所示: 登陆Gitee,查看近程仓库内容,如图所示: 从Gitee地址克隆(Clone)我的项目关上IDEA中可克隆(clone)选项,如图所示: 指定克隆地址和克隆目录,如果要克隆到本地地位曾经有一个同名的我的项目,则能够批改新的我的项目名,如图所示: 克隆实现当前,抉择关上我的项目的窗口,例如: 我的项目关上当前,配置JDK,MAVEN,主动编译,编码等,并将maven我的项目增加到maven区,如图所示: 也能够在我的项目的pom.xml文件上右键抉择add as maven project,将我的项目增加到maven区。如果是一般的java我的项目(非maven我的项目),此时还须要将src转换为sources root格局(抉择src目录,右键抉择mark directory as sources root). 总结(Summary)在本大节中重点解说了IDEA中Git的一个基本操作,通过Git在IDEA中实现了我的项目的Commit,Push,Update等操作.

December 21, 2020 · 1 min · jiezi

关于springboot:produces在requestMapping中的使用方式和作用

produces可能不算一个注解,因为什么呢,它是注解@requestMapping注解外面的属性项, 它的作用是指定返回值类型,岂但能够设置返回值类型还能够设定返回值的字符编码; 还有一个属性与其对应,就是consumes: 指定申请的提交内容类型(Content-Type),例如application/json, text/html; 他们的应用办法如下: 一、produces的例子 produces第一种应用,返回json数据,下边的代码能够省略produces属性,因为咱们曾经应用了注解@responseBody就是返回值是json数据: @Controller @RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, produces="application/json") @ResponseBody public Pet getPet(@PathVariable String petId, Model model) { // implementation omitted } produces第二种应用,返回json数据的字符编码为utf-8.: @Controller @RequestMapping(value = "/pets/{petId}", produces="MediaType.APPLICATION_JSON_VALUE"+";charset=utf-8") @ResponseBody public Pet getPet(@PathVariable String petId, Model model) { // implementation omitted } 二、consumes的例子( 办法仅解决request Content-Type为“application/json”类型的申请。) @Controller @RequestMapping(value = "/pets", method = RequestMethod.POST, consumes="application/json") public void addPet(@RequestBody Pet pet, Model model) { // implementation omitted }

December 19, 2020 · 1 min · jiezi

关于springboot:京淘day20商品订单的实现

1.订单确认页面跳转1.1 需要剖析页面跳转: order-cart.jsp页面,须要展示商品数据信息,同时有收件人地址信息. 1.2 编辑OrderController实现订单确认页面跳转 package com.jt.controller;import com.alibaba.dubbo.config.annotation.Reference;import com.jt.pojo.Cart;import com.jt.service.DubboCartService;import com.jt.util.UserThreadLocal;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;import java.util.List;@Controller@RequestMapping("/order")public class OrderController { @Reference(check = false) private DubboCartService cartService; /** * 1.实现订单确认页面跳转 * url地址: http://www.jt.com/order/create.html * 参数阐明: 获取userId * 返回值后果: 订单确认页面 order-cart.jsp * 页面取值形式: ${carts} */ @RequestMapping("/create") public String orderCart(Model model){ long userId = UserThreadLocal.getUser().getId(); List<Cart> cartList = cartService.findCartList(userId); model.addAttribute("carts", cartList); return "order-cart"; }} 2. 创立订单我的项目2.1 订单模块表设计 2.2 导入POJO对象 2.3 创立订单我的项目1).创立我的项目2).增加继承/依赖/插件 ...

December 17, 2020 · 2 min · jiezi

关于springboot:京淘day19实现商品数据展现

1. 构建JT-MANAGE为服务生产者1.1 定义接口的实现 1.2 编辑YML配置文件 1.3编辑服务消费者 2. 实现商品动静获取2.1 业务需要1.当用户点击商品按钮时,跳转到商品的展示页面 item.jsp2.依据itemId号 查问item表/itemDesc表3.将数据在页面中进行展示 2.2 编辑ItemController@Controllerpublic class ItemController { //启动时是否校验有服务提供者 @Reference(check=false) private DubboItemService itemService; /** * 依据商品ID查问商品信息 * 1.URL地址:http://www.jt.com/items/562379.html * 2.参数: 562379 restFul格调 * 3.返回值: String * 4.页面取值操作 * ${item.title } 获取商品信息 同步取值 * ${itemDesc.itemDesc } 获取商品详情信息 */ @RequestMapping("/items/{itemId}") public String findItemById(@PathVariable Long itemId, Model model){ //1.获取商品相干信息 Item item = itemService.findItemById(itemId); ItemDesc itemDesc = itemService.findItemDescById(itemId); //2.将数据传递到页面中 model.addAttribute("item",item); model.addAttribute("itemDesc",itemDesc); //跳转到商品展示页面 return "item"; }}2.3 编辑ItemService@Service(timeout = 3000)public class DubboItemServiceImpl implements DubboItemService { @Autowired private ItemMapper itemMapper; @Autowired private ItemDescMapper itemDescMapper; @Override public Item findItemById(Long itemId) { return itemMapper.selectById(itemId); } @Override public ItemDesc findItemDescById(Long itemId) { return itemDescMapper.selectById(itemId); }}2.4 商品展示成果测试 ...

December 16, 2020 · 3 min · jiezi

关于springboot:京淘day18重构京淘项目单点登录

1.重构京淘我的项目1.1 导入jar包 <!--引入dubbo配置 如果下载失败 则去本地仓库中删除从新下载--> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency>1.2 定义Dubbo接口 2. 定义服务生产者2.1 编辑UserController 2.2 编辑YML配置文件server: port: 8093 servlet: context-path: /spring: datasource: #引入druid数据源 #type: com.alibaba.druid.pool.DruidDataSource #driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true #链接的是数据库代理 #url: jdbc:mysql://192.168.126.129:8066/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true username: root password: root mvc: view: prefix: /WEB-INF/views/ suffix: .jsp#mybatis-plush配置mybatis-plus: type-aliases-package: com.jt.pojo mapper-locations: classpath:/mybatis/mappers/*.xml configuration: map-underscore-to-camel-case: truelogging: level: com.jt.mapper: debug#对于Dubbo配置dubbo: scan: basePackages: com.jt #指定dubbo的包门路 application: #利用名称 name: provider-user #一个接口对应一个服务名称 如果是多个实现类则利用名称统一 registry: address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183 protocol: #指定协定 name: dubbo #应用dubbo协定(tcp-ip) web-controller间接调用sso-Service port: 20880 #每一个服务都有本人特定的端口 不能反复.` 3.定义服务消费者3.1 编辑UserController ...

December 16, 2020 · 2 min · jiezi

关于springboot:Spring-Boot-日志各种使用姿势是时候捋清楚了

@[toc]之前录过一个视频和大家分享 Spring Boot 日志问题,然而总感觉差点意思,因而松哥打算再通过一篇文章来和大家捋一捋 Java 中的日志问题,顺便咱们把 Spring Boot 中的日志问题也说分明。 1. Java 日志概览说到 Java 日志,很多初学者可能都比拟懵,因为这里波及到太多货色了:Apache Commons Logging、Slf4j、Log4j、Log4j2、Logback、Java Util Logging 等等,这些框架各自有什么作用?他们之间有什么区别? 1.1 总体概览上面这张图很好的展现了 Java 中的日志体系: 能够看到,Java 中的日志框架次要分为两大类:日志门面和日志实现。 日志门面 日志门面定义了一组日志的接口标准,它并不提供底层具体的实现逻辑。Apache Commons Logging 和 Slf4j 就属于这一类。 日志实现 日志实现则是日志具体的实现,包含日志级别管制、日志打印格局、日志输入模式(输入到数据库、输入到文件、输入到控制台等)。Log4j、Log4j2、Logback 以及 Java Util Logging 则属于这一类。 将日志门面和日志实现拆散其实是一种典型的门面模式,这种形式能够让具体业务在不同的日志实现框架之间自在切换,而不须要改变任何代码,开发者只须要把握日志门面的 API 即可。 日志门面是不能独自应用的,它必须和一种具体的日志实现框架相结合应用。 那么日志框架是否能够独自应用呢? 技术上来说当然没问题,然而咱们个别不会这样做,因为这样做可维护性很差,而且前期扩大不易。例如 A 开发了一个工具包应用 Log4j 打印日志,B 援用了这个工具包,然而 B 喜爱应用 Logback 打印日志,此时就会呈现一个业务应用两个甚至多个日志框架,开发者也须要保护多个日志的配置文件。因而咱们都是用日志门面打印日志。 1.2 日志级别应用日志级别的益处在于,调整级别,就能够屏蔽掉很多调试相干的日志输入。不同的日志实现定义的日志级别不太一样,不过也都大同小异。 Java Util Logging Java Util Logging 定义了 7 个日志级别,从重大到一般顺次是: ...

December 16, 2020 · 3 min · jiezi

关于springboot:京淘day17Dubbo框架

一、 Dubbo框架1. Dubbo框架介绍Apache Dubbo |db| 提供了六大外围能力:面向接口代理的高性能RPC调用,智能容错和负载平衡,服务主动注册和发现,高度可扩大能力,运行期流量调度,可视化的服务治理与运维。 2. Dubbo框架劣势问题阐明:问题1: 当服务生产者有一个宕机,问程序是否失常运行???阐明: 因为dubbo框架的机制,仍然能够保障失常运行…问题2: 如果整合zk集群宕机,问程序是否运行失常??阐明: 如果只是主机宕机,则zk有高可用的成果,程序不受影响.阐明2: 如果整合zk集群宕机,则用户仍然能够失常拜访,然而当初的程序处于危险状态.阐明3: 如果在上述的状况下,再次宕机一台生产者 程序仍然能够失常运行. 因为保护了本地的服务列表信息. 3. Dubbo 负载平衡机制3.1 负载平衡品种阐明: dubbo框架中负载平衡机制是客户端负载平衡. 该配置须要在客户端(消费者)中配置即可. 3.1.1 负载平衡-随机策略1).类名称2).配置负载平衡机制 3.1.2 负载平衡-轮询机制1).设置轮询策略1).轮询类名 3.1.3 负载平衡-一致性hash1).Controller配置2).具体类名 3.1.4 负载平衡-“起码拜访”1).编辑Controller2).具体类名 3.2 数据在zk中存储构造阐明 4. Dubbo入门案例4.1 导入jar包 <!--引入dubbo配置 如果下载失败 则去本地仓库中删除从新下载--> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency>2.2 导入我的项目1).批改版本号2).批改子项目名称3).复制我的项目4).增加为maven我的项目 5. 对于入门案例阐明5.1 定义中立接口1).我的项目构造2).定义接口 5.2 编辑服务生产者5.2.1 编辑接口实现阐明:留神注解导入的是dubbo的注解 5.2.2 编辑YML配置文件server: port: 9000 #定义tomcat端口 服务启动时占用的端口spring: datasource: #引入druid数据源 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true username: root password: root#对于Dubbo配置 dubbo: scan: basePackages: com.jt #指定dubbo的包门路 application: #利用名称 name: provider-user #一个接口对应一个服务名称 如果是多个实现类则利用名称统一 registry: address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183 protocol: #指定协定 name: dubbo #应用dubbo协定(tcp-ip) web-controller间接调用sso-Service port: 20880 #每一个服务都有本人特定的端口 不能反复. mybatis-plus: type-aliases-package: com.jt.dubbo.pojo #配置别名包门路 mapper-locations: classpath:/mybatis/mappers/*.xml #增加mapper映射文件 configuration: map-underscore-to-camel-case: true #开启驼峰映射规定5.2.3 生产者启动测试 ...

December 15, 2020 · 1 min · jiezi

关于springboot:HandlerMethodArgumentResolver方法解析器可以用于SQL注入拦截

HandlerMethodArgumentResolver1、策略接口:用于在给定申请的上下文中将办法参数解析为参数值,它是HandlerMethod办法的解析器,将HttpServletRequest解析为HandlerMethod办法的参数。 public interface HandlerMethodArgumentResolver {/** * 性能形容: 判断是否反对 MethodParameter * 〈〉 * @Param: * @Return: * @Author: Steven * @Date: 2020/12/15 11:47 */boolean supportsParameter(MethodParameter parameter);/** * 性能形容: 用于数据绑定解析申请参数 * 〈〉 * @Param: * @Return: * @Author: Steven * @Date: 2020/12/15 11:47 */Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;}2、 public class SqlFilterArgumentResolver implements HandlerMethodArgumentResolver {/** * 性能形容: 判断controller申请参数中是否蕴含 CurrentUser * 〈〉 * @Param: * @Return: * @Author: Steven * @Date: 2020/12/15 11:47 */@Overridepublic boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().equals(CurrentUser.class);}/** * 性能形容: 获取获取申请体重的报文、解析 * 〈〉 * @Param: * @Return: * @Author: Steven * @Date: 2020/12/15 11:47 */@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { Optional.ofNullable(descs).ifPresent(s -> orderItemList.addAll( Arrays.stream(s).filter(sqlInjectPredicate()).map(OrderItem::desc).collect(Collectors.toList()))); } /** * 性能形容: 解决查问关键字 * 〈〉 * @Param: * @Return: * @Author: Steven * @Date: 2020/12/15 11:47 */ private Predicate<String> sqlInjectPredicate() { return sql -> { for (String keyword : KEYWORDS) { if (StrUtil.containsIgnoreCase(sql, keyword)){ return false; } } return true; }}}

December 15, 2020 · 1 min · jiezi

关于springboot:SpringBoot魔法堂应用热部署实践与原理浅析

前言后端开发的同学想必每天都在反复经验着批改代码、执行代码编译,期待……重启Tomcat服务,期待……最初测试发现还是有bug,而后上述流程再来一遍(我听不见):(能不能像前端开发的同学那样,批改代码保留文件后主动编译、从新加载利用呢?Spring Boot给了咱们一个大大的Yes!本文咱们就一起来摸索Spring Boot的热部署性能晋升开发效率吧! 长话短说热部署作为开发阶段的个性,由spring-boot-devtools模块提供,用于在批改类、配置文件和页面等动态资源后,主动编译Spring Boot利用和加载利用和页面动态资源,从而进步开发流程自动化水平晋升开发效率。那么第一步当然是在pom.xml中增加配置: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <option>true</option></dependency><build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> <!-- 默认值为false,必须设置为true能力启用热部署性能(具体起因请见下文) --> </configuration> </plugin> </plugins></build>动态资源热部署对于HTML页面、图片、CSS款式文件这些显然不须要编译的动态资源,Spring Boot Devtools模块通过内置的livereload服务端和浏览器的LiveReload插件独特实现热部署。 服务端配置spring: devtools: livereload: enabled: true # 启用LiveReload服务端 port: 35729 # LiveReload服务端口默认仅触发LiveReload事件的默认门路如下: /META-INF/maven,/META-INF/resources,/resources,/static,/public和/templates。 浏览器配置无论时FireFox还是Chrome都有相应的LiveReload插件,按步骤装置就能够了。 Java类资源热部署Spring Boot Devtools模块是通过监听Java类资源变动触发利用热部署,请留神这里监听的是Java类资源而不是Java源代码文件,那么什么是Java类资源**呢?其实就是.class文件。这样从保留Java源代码文件到Spring Boot Devtools监听到Java类资源变动之间,就有一道不可逾越的鸿沟了。咱们必须通过额定伎俩填平: 手动形式:批改Java源代码文件后,执行mvn compile主动形式:配置IDEA监听Java源代码文件变动,触发从新编译2.1. 右键点击SpringBootApplication入口类文件,并点击Create XXXX.main(),创立Application类型的Configuration;2.2. 勾选File/Settings/Compiler/Build Project automatically;2.3. 按ctrl+shift+alt+/,而后抉择Registry并勾选Compiler autoMake allow when app running;2.4. 通过IDEA左上角绿色的运行按钮启动Spring Boot利用,而后批改Java源代码文件后IDEA会主动从新编译我的项目,从而触发Spring Boot Devtools热部署。 更多配置配置项spring: devtools: restart: enabled: true # 启用热部署 exclude: main/static/** # 除默认门路外,增加文件变动不触发热部署的门路列表,多个门路之间通过逗号分隔。 additional: assets/** # 增加文件变动会触发热部署的门路列表,多个门路之间通过逗号分隔。 additional-exclude: assets/public/** # 设置additional属性指定的门路下某些门路的文件变动,不触发热部署,多个门路之间通过逗号分隔。默认不触发热部署的门路有:/META-INF/maven,/META-INF/resources,/resources,/static,/public和/templates。 ...

December 15, 2020 · 1 min · jiezi

关于springboot:京淘day16微服务

1.微服务思维1. SOA思维面向服务的架构(SOA)是一个组件模型(编程办法),它将应用程序的不同性能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和协定分割起来。接口是采纳中立的形式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的零碎中的服务能够以一种对立和通用的形式进行交互。 2. RPC(规定)RPC是近程过程调用(Remote Procedure Call)的缩写模式。SAP零碎RPC调用的原理其实很简略,有一些相似于三层构架的C/S零碎,第三方的客户程序通过接口调用SAP外部的规范或自定义函数,取得函数返回的数据进行解决后显示或打印。 本地过程调用: 如果须要实现业务逻辑,则间接去调用本地的办法即可.近程过程调用: 我想实现一个业务的调用,然而该性能我没有方法间接调用,须要通过第三方帮忙我实现业务规定.具体用法: RPC不关注具体的实现规定,用户也不须要理解具体的协定.谁调用谁实现. 3. Http协定标准 2. 微服务调用思维2.1 传统形式调用问题阐明 2.2 微服务思维-注册核心微服务: 架构设计采纳分布式思维,当服务器产生故障时,能够实现自动化的故障迁徙.无需人为干涉.注册核心实现原理:1.当服务器启动时,会将服务器的状态(服务名称/IP/端口) 一起写入注册核心2.注册核心接管到服务器信息时,会动静的保护服务列表数据.3/4.当客户端(消费者)启动时,首先会链接注册核心,获取所有的服务列表数据.并且将服务列表数据保留到本地.5.当消费者执行业务调用时,如果有多个服务的生产者时,采纳负载平衡的思维筛选其中的一个服务进行拜访(RPC).6.当服务器产生宕机时,因为注册核心有心跳检测机制,所有会动静的保护服务列表数据.会全网播送告诉所有的客户端(消费者)更新服务列表数据. 在更新服务列表时,数据的同步会陷入阻塞的状态. 2.3 对于微服务思维负载平衡阐明2.3.1 集中式负载平衡阐明: 所有的申请都必须由某个服务器进行对立的治理.案例: Nginx是集中式的负载平衡,然而nginx次要的作用是做反向代理 2.3.2 客户端负载平衡阐明: 申请发送之前,每个客户端都十分分明的晓得,本人应该拜访哪台服务器.在服务器外部.由客户端间接拜访后端服务器,将负载的压力进行了分担. 3. 注册核心-zookeeper装置阐明: 具体的装置形式参数课前材料文档. 3.1. zk启动命令 3.2 对于集群相干概念3.2.1 几台服务器能够搭建集群公式: 存活节点 > N/2算数计算:1个节点 1-1 > 1/2 假的 不能搭建集群2个节点 2-1 > 2/2 假的 不能搭建集群3个节点 3-1 > 3/2 真的 能够搭建集群论断:集群最小单位3台. 3.2.2 集群个别都是奇数台为什么?3个节点 3-1 > 3/2 真的 能够搭建集群 容许宕机1台4个节点 4-1 > 4/2 真的 能够搭建集群 容许宕机1台阐明: 因为搭建偶数台和搭建奇数台的容灾成果雷同的,所以个别都是奇数个. ...

December 15, 2020 · 1 min · jiezi