开发笔记 相关博客-Java技术分享站

    开发笔记 kai-fa-bi-ji jvm123.com 是一个java技术分享站,内容涉及java、jvm、程序开发,单元测试框架Spock教程,测试视频教程,spring框架教程等, 以及服务器搭建、linux、docker等相关技术。

Flutter_webview_plugin 与 Http协议

<!-- wp:paragraph --> <p>引入flutter_webview_plugin 以来如下:</p> <!-- /wp:paragraph --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">dependencies: flutter_webview_plugin: ^0.3.5</pre> <!-- /wp:preformatted --> <!-- wp:paragraph --> <p>则直接使用 WebviewScaffold ,如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>import 'package:flutter/services.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_app/widget/Utils.dart'; import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; class Browser extends StatelessWidget { final String title; final String url; final String shareUrl; final MethodChannel channel = const MethodChannel('flutter_share_me'); Browser({Key key, @required this.title, @required this.url, this.shareUrl}):super(key: key); @override Widget build(BuildContext context) { return WebviewScaffold( appBar: AppBar( title: Text(title), actions: &lt;Widget>[ IconButton( icon: Icon(Icons.share), onPressed: () { if(shareUrl == null || shareUrl == "") { shareImage(url); } else { shareImage(shareUrl); } }, ) ], ), url: url, withZoom: false, withLocalStorage: true, withJavascript: true, //本地缓存 hidden: false, //默认状态隐藏 initialChild: Container( color: Colors.white, child: Center( child: Text('Loading...'), ), ), //设置初始化界面 ); } shareImage(String url) async { dynamic result; try { result = await channel.invokeMethod('system', {"msg": url}); Utils.showToast("分享成功!"); } catch (e) { Utils.showToast("分享失败!"); } } }</code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2>flutter webview 打开 http 网页</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>默认情况下,android只支持webview打开https协议的网页,如果需要打开http网页,需要配置 android:usesCleartextTraffic="true",如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> &lt;application android:name="io.flutter.app.FlutterApplication" android:label="jvm123博客" android:icon="@mipmap/logo" android:usesCleartextTraffic="true"> &lt;activity ......</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p></p> <!-- /wp:paragraph -->

分类:开发笔记flutter
标签:httpswebviewflutterandroid

2020-11-24 16:08:58.0
Flutter_webview_plugin 与 Http协议  flutterwebviewplugin

日志self4j、log4j和logback

<!-- wp:paragraph --> <p>应用中使用日志框架 SLF4J 中的 API,不直接使用日志系统(Log4j、Logback)中的 API ,有利于维护和各个类的日志处理方式统一。示例 如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Wombat { final static Logger logger = LoggerFactory.getLogger(Wombat.class); Integer t; Integer oldT; public void setTemperature(Integer temperature) { oldT = t; t = temperature; logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT); if(temperature.intValue() > 50) { logger.info("Temperature has risen above 50 degrees."); } } }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>输出日志信息不要进行字符串拼接,应该使用占位符,因为java8中 String 字符串的拼接会使用 StringBuilder 的 append()方法,有一定的性能损耗。使用占位符 仅是替换动作,可以有效提升性能。如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>异常信息应该包括:案发现场信息和异常堆栈信息,切勿忘记打印异常堆栈信息。 如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>logger.error("提示信息+各类参数或者对象+" + e.getMessage(), e);</code></pre> <!-- /wp:code -->

分类:开发笔记日志
标签:logback日志日志框架self4jjava日志框架log4j

2020-03-12 03:44:53.0
日志self4j、log4j和logback  self4j-log4j-logback

Springboot中logback日志配置示例

<!-- wp:paragraph --> <p>在springboot项目中,如果需要写日志,需要将日志配置文件放在resource目录下即可生效。Springboot默认使用logback日志框架,所以,无需加入任何依赖,只需要一个日志配置文件logback.xml文件,即可按照配置文件中的配置生成日志。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>logback.xml配置文件的示例讲解如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>&lt;configuration scan="true" scanPeriod="60 seconds" debug="false"> &lt;property name="LOG_HOME" value="./log"/> &lt;property name="LOG_NAME" value="xxx"/> &lt;!--输出到控制台--> &lt;appender name="console" class="ch.qos.logback.core.ConsoleAppender"> &lt;encoder> &lt;pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{50} %L : %msg%n&lt;/pattern> &lt;charset>UTF-8&lt;/charset> &lt;/encoder> &lt;/appender> &lt;!--输出到文件--> &lt;appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender"> &lt;file>${LOG_HOME}/${LOG_NAME}.log&lt;/file> &lt;rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> &lt;fileNamePattern>${LOG_HOME}/history/${LOG_NAME}.%d{yyyy-MM-dd}.log&lt;/fileNamePattern> &lt;maxHistory>90&lt;/maxHistory> &lt;/rollingPolicy> &lt;encoder> &lt;pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{50} : %msg%n&lt;/pattern> &lt;charset>UTF-8&lt;/charset> &lt;/encoder> &lt;/appender> &lt;!-- ERROR --> &lt;appender name="errorAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> &lt;!-- 文件路径 --> &lt;file>${LOG_HOME}/${LOG_NAME}-error.log&lt;/file> &lt;rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> &lt;fileNamePattern>${LOG_HOME}/error/${LOG_NAME}.%d{yyyy-MM-dd}.log&lt;/fileNamePattern> &lt;maxHistory>90&lt;/maxHistory> &lt;/rollingPolicy> &lt;encoder> &lt;pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{50} : %msg%n&lt;/pattern> &lt;/encoder> &lt;filter class="ch.qos.logback.classic.filter.LevelFilter"> &lt;level>ERROR&lt;/level> &lt;onMatch>ACCEPT&lt;/onMatch> &lt;onMismatch>DENY&lt;/onMismatch> &lt;/filter> &lt;/appender> &lt;!--根节点--> &lt;root level="INFO"> &lt;appender-ref ref="console"/> &lt;appender-ref ref="file"/> &lt;appender-ref ref="errorAppender"/> &lt;/root> &lt;/configuration></code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>property标签问定义的属性变量,可以在这个文件中使用${}引用。 </p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>root节点下配置需要生效的appender即可,在这个节点下引用的appender会生效,没引用的appender不会生效。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>appender节点可以有多个,配置多个appender,供root节点引用。对于不同的日志 appender ,其节点配置不同,一般有以下节点:</p> <!-- /wp:paragraph --> <!-- wp:list {"ordered":true} --> <ol><li>file 表示生成日志文件的位置</li><li>rollingPolicy 日志存储策略</li><li>encoder 日志的显示格式</li><li>filter 日志过滤器</li></ol> <!-- /wp:list --> <!-- wp:paragraph --> <p></p> <!-- /wp:paragraph -->

分类:javaspringbootspring日志开发笔记
标签:springboot日志框架logbackjava日志框架日志配置日志

2019-10-29 22:49:17.0
Springboot中logback日志配置示例  springboot-zhong

java接口实现回调(观察者模式基础)

<!-- wp:paragraph --> <p>回调是一种双向的调用模式,例如:我们正在写一个类A的a方法,其中调用了B的一个特定的b方法,b在方法执行过程中又要调用一个c方法,这就是回调,c方法被称作回调方法或回调函数。b方法是已有的,而c方法是需要我们自己实现的,也就是原有的方法(b)在执行过程中调用了我们自己实现的代码(c)。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>所以可以这样理解:“回调函数也是一个函数或过程,不过它是一个由调用方自己实现,供被调用方使用的特殊函数。”</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>在js等语言中,c方法(函数)可以被当作参数传给方法b,供它使用。但是在java中,利用接口,就可以实现回调。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>java中,按照如下步骤就可以实现回调:</p> <!-- /wp:paragraph --> <!-- wp:list {"ordered":true} --> <ol><li>定义一个接口方法c;</li><li>在实现被调用方法b的时候,调用接口定义的方法;</li><li>在实现调用方法a的时候,调用b方法并把实现了c方法的对象传给b即可。</li></ol> <!-- /wp:list --> <!-- wp:paragraph --> <p>示例实现如下:</p> <!-- /wp:paragraph --> <!-- wp:list --> <ul><li>定义一个接口CallBackInterface,其中方法 onFinish 未实现;</li></ul> <!-- /wp:list --> <!-- wp:code --> <pre class="wp-block-code"><code>package com.yawn.callback; public interface CallBackInterface { void onFinish(); } </code></pre> <!-- /wp:code --> <!-- wp:list --> <ul><li>在实现被调用方法go的时候,调用接口CallBackInterface 定义的方法 onFinish ;</li></ul> <!-- /wp:list --> <!-- wp:code --> <pre class="wp-block-code"><code>package com.yawn.callback; /** * B的内部实现也很简单,只需要一个接口类型的成员变量(这个成员变量的具体实现由我们初始化B时指定), * 在执行方法完成时调用这个成员变量(接口对象)的onFinish方法即可 * * @author yawn * */ public class B { CallBackInterface f ; public B(CallBackInterface f){ this.f = f; } void go(){ System.out.println("Go go go!"); if (f != null){ f.onFinish(); } } } </code></pre> <!-- /wp:code --> <!-- wp:list --> <ul><li> 在实现方法a的时候,调用b方法并把实现了onFinish方法的对象传给B进行实例化即可。这样在调用b方法过程中,第二步b中调用的接口的方法就会成为其实现类Callback的方法。 </li></ul> <!-- /wp:list --> <!-- wp:code --> <pre class="wp-block-code"><code>package com.yawn.callback; /** * 回调要达到的效果就是在执行完b的go方法之后,再自动调用我们自己实现的onFinish方法 * A可以是任何一个实现了Finish接口的类,用于存放我们实现的onFinish方法 * * @author yawn * */ public class A{ public static void main(String[] args) { B b = new B(new CallBack()); // 初始化B并指定B所要回调的对象(方法) b.go(); } } class CallBack implements CallBackInterface { @Override public void onFinish() { System.out.println("finish"); } }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>这样就可以在调用B的go方法之后又调用我们实现的onFinish方法了。</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>运行结果 Go go go! finish</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>其实在初始化B时不一定要实现一个类A,也可以直接实现接口中的方法:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>package com.yawn.callback; /** * 回调要达到的效果就是在执行完b的go方法之后,再自动调用我们自己实现的onClick方法 * A可以是任何一个实现了Finish接口的类,用于存放我们实现的onFinish方法 * * @author yawn * */ public class A { public static void main(String[] args) { B b = new B(new CallBackInterface() { @Override public void onFinish() { System.out.println("finish"); } }); b.go(); } }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>Java中回调的实现也可以这样理解:</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>在编写B类中被调用方法b的时候,先调用接口的方法占位;在后面实例化B并调用b方法的时候,传入接口的实现即可。这样b方法在执行时调用的接口方法实际上是实现类的方法。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>在java中使用接口实现回调,是观察者模式的基础,也是java8中 lambda表达式的基础。在java8中,jdk已经定义了常用的几类接口,我们在实例化b的时候,直接传入接口的实现类即可。如果实现类使用lambda表达式,则书写形式与js中传入函数作为参数很类似。</p> <!-- /wp:paragraph -->

分类:javajs/jquery开发笔记
标签:java回调函数λ表达式观察者模式回调方法回调lambda表达式

2019-10-22 23:03:06.0
java接口实现回调(观察者模式基础)  java-jie-kou-shi

Groovy环境的安装和配置

<!-- wp:heading --> <h2>groovy环境下载配置</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>下载: www.groovy-lang.org/download.html </p> <!-- /wp:paragraph --> <!-- wp:image {"id":1453} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/10/groovy.jpg" alt="" class="wp-image-1453"/></figure> <!-- /wp:image --> <!-- wp:paragraph --> <p>下载解压后,将其bin目录配置在系统的环境变量即可,然后cmd命令行分别输入groovy和groovyc,就可以看到命令的用法说明:</p> <!-- /wp:paragraph --> <!-- wp:image {"id":1454} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/10/groovy2.jpg" alt="" class="wp-image-1454"/></figure> <!-- /wp:image --> <!-- wp:heading --> <h2>编写编译运行第一个groovy脚本</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>新建一个文件hello.groovy,文件中写如下代码:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>println 'Hello World!'</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>保存后,在命令行使用如下命令编译:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>groovyc hello.groovy</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>编译后生成文件hello.class,再使用如下命令运行:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>groovy hello</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>结果如下:</p> <!-- /wp:paragraph --> <!-- wp:image {"id":1455} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/10/groovy4.jpg" alt="" class="wp-image-1455"/></figure> <!-- /wp:image --> <!-- wp:paragraph --> <p>至此,说明groovy环境安装配置妥当了。</p> <!-- /wp:paragraph --> <!-- wp:spacer --> <div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div> <!-- /wp:spacer -->

分类:JVMgroovy开发笔记
标签:jvmgroovy脚本groovygroovy环境配置groovy语法

2019-10-14 20:19:23.0
Groovy环境的安装和配置  groovy-huan-jing

Springboot项目中文乱码解决(源码案例)

<!-- wp:heading --> <h2>Springboot项目中中文乱码案例</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>使用idea搭建了一个springboot项目,版本号为1.5.22.ELEASE,只选择了spring-boot-starter-web依赖,自己编写controller之后,返回中文却乱码了,controller源码如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>@RestController class TestController { @GetMapping("test") String test() { "测试乱码111111abc" } }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>测试后返回结果如下:</p> <!-- /wp:paragraph --> <!-- wp:image {"id":1372} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/10/springboot乱码.jpg" alt="" class="wp-image-1372"/><figcaption>springboot项目中文乱码</figcaption></figure> <!-- /wp:image --> <!-- wp:paragraph --> <p>原因很明显,可以从上图中看出来:http响应的编码格式不对,设置为utf-8即可。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>在网上找到的方式,大都是设置以下两个配置,然而并没有用:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>spring: http: encoding: charset: utf-8 force: true</code></pre> <!-- /wp:code --> <!-- wp:image {"id":1373} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/10/springboot乱码2.jpg" alt="" class="wp-image-1373"/></figure> <!-- /wp:image --> <!-- wp:heading --> <h2>乱码原因分析解决</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>http响应头中的编码为 charset=ISO-8859-1,我们需要改变为UTF-8,所以我们可以通过在controller中设置响应编码的方式解决,controller源码可改为如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>@RestController @RequestMapping(produces = "application/json;charset=UTF-8") class TestController { @GetMapping("test") String test() { "测试乱码111111abc" } }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>最终解决了乱码问题,请求响应如下图:</p> <!-- /wp:paragraph --> <!-- wp:image {"id":1374} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/10/springboot中文乱码.jpg" alt="" class="wp-image-1374"/></figure> <!-- /wp:image --> <!-- wp:paragraph --> <p>返回的charset也变成了UTF-8。不过这只是解决了这一个请求的乱码问题。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>Springboot项目乱码统一解决办法</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>此外,如果已经有较多的controller,这样设置很不方便。还可以通过添加一个StringHttpMessageConverter的方式,统一设置,源码如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>package com.jvm123.api.config /** * * @author yawn &lt; http://jvm123.com > * */ @Configuration @EnableWebMvc class MvcConfig extends WebMvcConfigurerAdapter { @Override void configureMessageConverters(List&lt;HttpMessageConverter&lt;?>> converters) { converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8)) super.configureMessageConverters(converters) } } </code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>设置后的请求结果如下:</p> <!-- /wp:paragraph --> <!-- wp:image {"id":1375} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/10/springboot中文乱码4.jpg" alt="" class="wp-image-1375"/></figure> <!-- /wp:image --> <!-- wp:paragraph --> <p>这样就统一解决了springboot项目中的中文乱码问题。</p> <!-- /wp:paragraph -->

分类:javajava源码springcloudspring开发笔记日常报错groovyspringboot
标签:groovy中文乱码springboot案例源码springboothttp请求编码springspringboot案例

2019-10-06 14:51:26.0
Springboot项目中文乱码解决(源码案例)  springboot-xiang-mu

Tomcat报错Invalid character found in method name. HTTP method names must be tokens 的处理

<!-- wp:paragraph --> <p>项目在启动后,无缘无故报错,大意就是说tomcat接收到了不合法的字符,在解析http请求的方法时出错了,完整报错如下:</p> <!-- /wp:paragraph --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">o.apache.coyote.http11.Http11Processor : Error parsing HTTP request header Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level. java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:430) ~[tomcat-embed-core-8.5.43.jar:8.5.43] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684) ~[tomcat-embed-core-8.5.43.jar:8.5.43] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.43.jar:8.5.43] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:808) [tomcat-embed-core-8.5.43.jar:8.5.43] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498) [tomcat-embed-core-8.5.43.jar:8.5.43] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.43.jar:8.5.43] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_102] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_102] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.43.jar:8.5.43] at java.lang.Thread.run(Thread.java:745) [na:1.8.0_102]</pre> <!-- /wp:preformatted --> <!-- wp:heading --> <h2>处理方法:</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p><strong>换端口</strong>,目前已知普通用户使用8888端口会报错,可能与常用的内网,或者某些常用软件通信有关。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>原因分析:</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>既然是tomcat收到不合法的字符,所以我写了一个简单的 nio socket 服务器,想看看究竟收到了什么,nio socket 服务器代码如下,如果使用 ServerSocket 则接收不到,所以这个报错应该是出现在支持nio的tomcat服务器上。</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.util.Iterator; import java.util.Set; public class NioServer { public static void main(String[] args) throws IOException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(8888)); serverSocketChannel.configureBlocking(false); Selector selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { // 注册完之后Selector就可以通过select方法来等等请求, // select方法有一个long类型的参数,代表最长等待时间, // 如果在这段时间里接收到了相应操作的请求则返回可以处理 // 的请求的数量,否则在超时后返回0 // // eg: 如果没接收到请求,10000毫秒后回返回0 // int num = selector.select(10000L); int num = selector.select(); // 没有超时时间 // System.out.println("selector获取到" + num + "个channel"); if (num == 0) continue; Set&lt;SelectionKey> selectionKeys = selector.selectedKeys(); Iterator&lt;SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey selectionKey = iterator.next(); iterator.remove(); boolean connectable = selectionKey.isConnectable(); boolean valid = selectionKey.isValid(); boolean acceptable = selectionKey.isAcceptable(); boolean readable = selectionKey.isReadable(); boolean writable = selectionKey.isWritable(); // System.out.println(connectable ? "connectable" : ""); // System.out.println(valid ? "valid" : ""); // System.out.println(acceptable ? "acceptable" : ""); // System.out.println(readable ? "readable" : ""); // System.out.println(writable ? "writable" : ""); if (acceptable) { System.out.println("连接成功!"); SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); ByteBuffer byteBuffer = Charset.forName("UTF-8").encode("你已经成功连接服务器!"); socketChannel.write(byteBuffer); socketChannel.register(selector, SelectionKey.OP_READ); } if (readable) { SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); socketChannel.configureBlocking(false); try { ByteBuffer buffer = ByteBuffer.allocate(1024); // 写入buffer socketChannel.read(buffer); // 从buffer读取并转换为char buffer.flip(); System.out.println(Charset.defaultCharset().decode(buffer)); } catch (Exception e) { } socketChannel.register(selector, SelectionKey.OP_WRITE); } if (writable) { SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); socketChannel.configureBlocking(false); try { socketChannel.write(Charset.defaultCharset().encode("server已经收到你的信息。")); System.out.println("server write"); } catch (Exception e) { } socketChannel.register(selector, SelectionKey.OP_READ); } } } } }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>启动之后,每隔几秒就会收到如下字符:</p> <!-- /wp:paragraph --> <!-- wp:image {"id":1353} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/10/tomcat-error.jpg" alt="" class="wp-image-1353"/></figure> <!-- /wp:image --> <!-- wp:paragraph --> <p>这很明显不是http请求的格式,tomcat解析当然会出错。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>然后我在浏览器请求了localhost:8888/test123,则会打印处http请求的内容,如下:</p> <!-- /wp:paragraph --> <!-- wp:image {"id":1354} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/10/tomcat-error2-1024x441.jpg" alt="" class="wp-image-1354"/></figure> <!-- /wp:image --> <!-- wp:heading --> <h2>验证:</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>之后,我将 nio server 的端口修改为8000,重启后,等待一段时间,并未收到任何信息。由此说明,在网络中,有设备在向我电脑的8888端口发送信息,而且这些信息比较短,则很可能是检测心跳的信息。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>而我把项目的端口换为8000之后,也不会出现这个错误了。</p> <!-- /wp:paragraph -->

分类:服务器搭建日常报错java开发笔记
标签:tomcat原理http请求tomcatnio socketnio

2019-10-04 17:30:12.0
Tomcat报错Invalid character found in method name. HTTP method names must be tokens 的处理  invalid-character-found

响应式编程3数据处理(RxJava2.x示例)

<!-- wp:paragraph --> <p>除了观察者模式原理,响应式编程的另一个重要内容就是数据的处理。RxJava提供了类似java8中stream流式的数据处理方式,可以对被观察对象进行处理。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>RxJava数据处理实例</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code> @Test public void test6() { Observable.just(-1, -2, -2, -3, -4, 5, 5, 6, 7, 8) // 事件发生 // .observeOn(Schedulers.newThread()) // 指定事件发生的线程 // .subscribeOn(Schedulers.io()) // 指定事件回调的线程 .distinct() // 去掉重复的:[-1, -2, -3, -4, 5, 6, 7, 8] .filter(i -> i % 2 != 0) // 去掉偶数:[-1, -3, 5, 7] .map(Math::abs) // 取绝对值:[1, 3, 5, 7] .buffer(2, 2)// 分成多个buffer组: // count为每个buffer中元素的个数, // skip为下一个分组相对偏移的元素个数 // 结果为:[1, 3], [5, 7] // 可以调整为 buffer(2, 1) 试试效果 .subscribe( System.out::println, // 事件成功的处理 (onNext) e -> System.out.println(e.getMessage()) // 事件失败的处理 (onError) ); }</code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2>数据处理详解</h2> <!-- /wp:heading --> <!-- wp:list --> <ul><li>distinct 去掉重复的</li><li>filter 过滤器,接收一个Predicate类型的实例,用来判断是否过滤</li><li>map 映射器,接受一个Function,作为映射的映射关系(转换关系)</li><li>buffer 分组,接收两个整型参数count和skip,count表示每组的元素个数、skip表示每次分组起始元素的偏移量。</li></ul> <!-- /wp:list --> <!-- wp:paragraph --> <p>最终subscribe方法出现的数据就是经过以上几步处理过的数据。</p> <!-- /wp:paragraph -->

分类:java开发笔记响应式编程
标签:java响应式Reactive Javajava技术分享RxJava响应式编程rxjs

2019-09-30 16:29:13.0
响应式编程3数据处理(RxJava2.x示例)  xiang-ying-shi-bian-2

响应式编程2线程调度(RxJava2.x示例)

<!-- wp:paragraph --> <p>在响应式编程中,最基本是一个观察者模式的框架,一般我们自定义有以下三部分:</p> <!-- /wp:paragraph --> <!-- wp:list {"ordered":true} --> <ol><li>自定义事件的发生</li><li>自定义处理(下一篇文章讲到)</li><li>自定义监听(观察者)</li></ol> <!-- /wp:list --> <!-- wp:paragraph --> <p>其中在整个过程中,我们可以指定事件发生和事件监听的代码执行的线程,具体使用 observeOn 和 subscribeOn 方法指定。如下实例:</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>线程调度实例:</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code> Observable.create( ... ) // 事件发生 .observeOn(Schedulers.newThread()) // 指定事件发生的线程 .subscribeOn(Schedulers.io()) // 指定事件回调的线程 .subscribe( ... );</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>observeOn 指定了事件发生的线程;subscribeOn指定了事件监听(回调)执行的线程。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>在使用Rxjava进行网络请求时,一般请求的代码可以作为事件发生代码,请求完成后的响应回调就可以作为监听代码。结合以上代码,就是使用新的线程请求,请求完成后在io线程响应结果。例如,在Android开发时,对Activity的操作,只能在主线程进行,所以这里线程调度的设置就非常有必要。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>线程调度的用法</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>observeOn 和 subscribeOn 方法都接收一个Scheduler。</p> <!-- /wp:paragraph -->

分类:java开发笔记响应式编程
标签:java响应式Observer观察者Reactive Javajava技术分享RxJava线程调度响应式编程rxjs多线程

2019-09-30 16:02:07.0
响应式编程2线程调度(RxJava2.x示例)  xiang-ying-shi-bian

vsftpd配置禁用匿名用户并设置登陆用户

<!-- wp:paragraph --> <p>vsftpd是一个linux的ftp服务器,启动这个服务后,默认开启了匿名用户登陆,可以按照以下方式禁用匿名用户并设置登陆用户。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>vsftpd禁用匿名用户</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>修改vasftpd服务的配置文件/etc/vsftpd.conf(或/etc/vsftpd/vsftpd.conf)</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>anonymous_enable=NO</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>重启vsftpd服务即可生效:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>service vsftpd rstart </code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2>vsftpd设置登陆用户</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>设置系统登陆用户可分为以下几步:</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":3} --> <h3>添加系统用户</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>useradd -s /sbin/nologin ftpadmin </code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>添加系统用户ftpadmin,<code>-s /sbin/nologin</code>表示此用户不允许系统登陆,只能用作类似于vsftpd的服务登陆。</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":3} --> <h3>设置用户密码</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>passwd ftpadmin</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>输入以上命令后回车,就会提示修改此用户的密码,输入两次密码即可。期间会提示密码太简单不安全等,忽略就行了。最终后出现密码成功更新。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>ftp访问</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>可以在windows文件系统或者浏览器访问 ftp://192.168.xx.xx,验证时否可以登陆。如果关闭了匿名用户登陆功能,访问时就会弹出用户名和密码的输入框,输入后就可以访问了。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>但是,此时访问的是此系统用户的主目录,即 /home/ftpadmin 目录,由于之前项目的ftp文件夹并不在这里,所以可使用以下方法设置访问。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>设置ftp用户的访问目录</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>原项目名称为cba,其ftp文件夹为 /var/ftp/cba,现在需要让 ftpadmin 用户也能访问这个目录。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p><strong>可以在 /home/ftpadmin 下新建一个文件夹 cba,将 /var/ftp/cba 挂载到这个 cba文件夹就可以访问了。</strong></p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>mkdir cba mount --bind /var/ftp/cba cba</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>之前我也尝试过使用软链接的方式,但是ftp访问到的只是一个类似快捷方式图标的文件,并不能跳转到目标。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>另外看过一些技术文章介绍的 ,大都是配置 local_root ,但是我这里,对于系统用户,配置 local_root=/var/ftp后,并不会生效,还不知道是什么原因,所以目前就先使用mount挂载的方式。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p></p> <!-- /wp:paragraph -->

分类:服务器搭建linux开发笔记
标签:服务器文件上传ftp匿名登陆linuxftpcentosvsftpd

2019-09-29 15:53:10.0
vsftpd配置禁用匿名用户并设置登陆用户  vsftpd-pei-zhi-jin

excel-converter-starter导出excel文件时,数据列的顺序问题

<!-- wp:heading --> <h2>问题描述:</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>在使用 excel-converter-starter 时,注入ExExporter或者ExcelExporter导出数据时,有时导出的excel中,数据列的顺序不是按照配置文件中的顺序。例如有以下配置:</p> <!-- /wp:paragraph --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">excel: converter: props-map: default: sheet-index: 1 name-row-index: 0 comment-row-index: 1 first-data-row-index: 2 last-data-row-index: -1 data-row-num: -1 start-column-index: 0 <strong> name-header-map: id: ID name: 名称 address: 地址 date: 日期</strong> show-name-row: true</pre> <!-- /wp:preformatted --> <!-- wp:paragraph --> <p>数据列分别为id,name,address,date,但是最终导出excel中的列的顺序乱了。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>分析:</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>由于 <strong>name-header-map</strong> 是LinkedHashMap类型的,以保证最终导出是有序的,所以在推测应该是在读取配置文件时就已经乱序了。 </p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>再测试几次发现:使用application.properties配置文件就会使 nameHeaderMap 乱序,所以,应该是 spring boot 读取 .properties 和 .yml 配置文件的方式不同,导致读取进来的 LinkedHashMap 的顺序错乱。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>解决方法:</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>1 采用 application.yml配置文件即可</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>2 如果项目原本就采用application.properties配置文件,可以新建一个application-excel.yml 文件,再在 application.properties配置文件 中导入即可,导入 application-excel.yml 配置文件只需要加入以下配置:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>spring.profiles.include=excel</code></pre> <!-- /wp:code -->

分类:javaexcelspringcloudspring开发笔记日常报错源码项目分享springboot
标签:java导出SpringCloudexcelspringapplication.propertiesspringbootexcel converter日常报错

2019-09-27 23:17:39.0
excel-converter-starter导出excel文件时,数据列的顺序问题  excel-converter-2

Springboot Actuator 配置和使用说明

<!-- wp:heading --> <h2>Springboot Actuator依赖</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>springboot Actuator只需要加入依赖即可使用:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>&lt;dependency> &lt;groupId>org.springframework.boot&lt;/groupId> &lt;artifactId>spring-boot-starter-actuator&lt;/artifactId> &lt;/dependency></code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2> Springboot Actuator配置</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>application.properties 中可以加入以下配置:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code># false,表示不敏感,可以随意访问,否则就是做了一些保护,不能随意访问。 endpoints.mappings.sensitive=false # 敏感方法默认是需要用户拥有ACTUATOR角色,因此,也可以设置关闭安全限制 management.security.enabled=false # /shutdown这个需要post方式,通过请求来关闭应用。 # 这个操作比较敏感,要想真正生效,需要以下配置: endpoints.shutdown.enabled=true # 自定义系统的info信息 info.name=spring boot Application info.author.name=yawn info.author.email=com.***n@qq.com info.date=2017-10-11 16:12 </code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2>Springboot Actuator访问端点</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>现在在启动应用后,就可以对应用进行监控了,访问以下url端点</p> <!-- /wp:paragraph --> <!-- wp:table --> <table class="wp-block-table"><tbody><tr><th>ID</th><th>描述</th><th>Sensitive</th></tr><tr><td>autoconfig</td><td>显示一个auto-configuration的报告,该报告展示所有auto-configuration候选者及它们被应用或未被应用的原因</td><td>true</td></tr><tr><td>beans</td><td>显示一个应用中所有Spring Beans的完整列表</td><td>true</td></tr><tr><td>configprops</td><td>显示一个所有@ConfigurationProperties的整理列表</td><td>true</td></tr><tr><td>dump</td><td>执行一个线程转储</td><td>true</td></tr><tr><td>env</td><td>暴露来自Spring ConfigurableEnvironment的属性</td><td>true</td></tr><tr><td>health</td><td>展示应用的健康信息(当使用一个未认证连接访问时显示一个简单的’status’,使用认证连接访问则显示全部信息详情)</td><td>false</td></tr><tr><td>info</td><td>显示任意的应用信息</td><td>false</td></tr><tr><td>metrics</td><td>展示当前应用的’指标’信息</td><td>true</td></tr><tr><td>mappings</td><td>显示一个所有@RequestMapping路径的整理列表</td><td>true</td></tr><tr><td>shutdown</td><td>允许应用以优雅的方式关闭(默认情况下不启用)</td><td>true</td></tr><tr><td>trace</td><td>显示trace信息(默认为最新的一些HTTP请求)</td><td>true</td></tr></tbody></table> <!-- /wp:table --> <!-- wp:heading {"level":3} --> <h3>http://localhost:8082/info</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>{ "name": "spring boot Application", "date": "2017-10-11 16:12", "author": { "name": "yawn", "email": "com.***n@qq.com" } }</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>http://localhost:8082/mappings</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>{ "/index": { "bean": "viewControllerHandlerMapping" }, "/403": { "bean": "viewControllerHandlerMapping" }, "/jsp": { "bean": "viewControllerHandlerMapping" }, "/static/**": { "bean": "resourceHandlerMapping" }, "/templates/jsp/**": { "bean": "resourceHandlerMapping" }, "/**": { "bean": "defaultServletHandlerMapping" }, "{[/test]}": { "bean": "requestMappingHandlerMapping", "method": "public java.lang.String com.example.demo.controller.TestController.test(org.springframework.ui.Model)" }, "{[/test2]}": { "bean": "requestMappingHandlerMapping", "method": "public java.lang.String com.example.demo.controller.TestController.test2()" }, "{[/test3]}": { "bean": "requestMappingHandlerMapping", "method": "public java.lang.String com.example.demo.controller.TestController.test3(org.springframework.ui.Model)" }, "{[/test4]}": { "bean": "requestMappingHandlerMapping", "method": "public java.lang.Object com.example.demo.controller.TestController.test4(com.example.demo.entity.TestEntity,com.example.demo.entity.Person)" }, "{[/test5]}": { "bean": "requestMappingHandlerMapping", "method": "public java.lang.Object com.example.demo.controller.TestController.test5()" }, "{[/login],methods=[POST]}": { "bean": "requestMappingHandlerMapping", "method": "public java.lang.String com.example.demo.controller.TestController.test5(java.lang.String,java.lang.String,javax.servlet.http.HttpSession)" }, "{[/test6]}": { "bean": "requestMappingHandlerMapping", "method": "public java.lang.Object com.example.demo.controller.TestController.test6()" }, "{[/error]}": { "bean": "requestMappingHandlerMapping", "method": "public org.springframework.http.ResponseEntity&lt;java.util.Map&lt;java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)" }, "{[/error],produces=[text/html]}": { "bean": "requestMappingHandlerMapping", "method": "public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)" }, "{[/loggers/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint.get(java.lang.String)" }, "{[/loggers/{name:.*}],methods=[POST],consumes=[application/vnd.spring-boot.actuator.v1+json || application/json],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint.set(java.lang.String,java.util.Map&lt;java.lang.String, java.lang.String>)" }, "{[/loggers || /loggers.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()" }, "{[/beans || /beans.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()" }, "{[/configprops || /configprops.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()" }, "{[/shutdown || /shutdown.json],methods=[POST],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint.invoke()" }, "{[/metrics/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String)" }, "{[/metrics || /metrics.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()" }, "{[/env/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String)" }, "{[/env || /env.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()" }, "{[/heapdump || /heapdump.json],methods=[GET],produces=[application/octet-stream]}": { "bean": "endpointHandlerMapping", "method": "public void org.springframework.boot.actuate.endpoint.mvc.HeapdumpMvcEndpoint.invoke(boolean,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.io.IOException,javax.servlet.ServletException" }, "{[/dump || /dump.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()" }, "{[/trace || /trace.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()" }, "{[/autoconfig || /autoconfig.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()" }, "{[/health || /health.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(javax.servlet.http.HttpServletRequest,java.security.Principal)" }, "{[/auditevents || /auditevents.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public org.springframework.http.ResponseEntity&lt;?> org.springframework.boot.actuate.endpoint.mvc.AuditEventsMvcEndpoint.findByPrincipalAndAfterAndType(java.lang.String,java.util.Date,java.lang.String)" }, "{[/info || /info.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()" }, "{[/mappings || /mappings.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}": { "bean": "endpointHandlerMapping", "method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()" } }</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>http://localhost:8082/configprops </h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p>configprops (查看我们配置了的以及默认配置的属性,也可以看出我们还可以配置哪些属性)</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>{ "endpoints-org.springframework.boot.actuate.endpoint.EndpointProperties": { "prefix": "endpoints", "properties": { "enabled": true, "sensitive": null } }, "management.info-org.springframework.boot.actuate.autoconfigure.InfoContributorProperties": { "prefix": "management.info", "properties": { "git": { "mode": "SIMPLE" } } }, "metricsEndpoint": { "prefix": "endpoints.metrics", "properties": { "id": "metrics", "sensitive": true, "enabled": true } }, "spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties": { "prefix": "spring.jackson", "properties": { "serialization": {}, "propertyNamingStrategy": null, "defaultPropertyInclusion": null, "parser": {}, "dateFormat": null, "deserialization": {}, "generator": {}, "timeZone": null, "mapper": {}, "locale": null, "jodaDateTimeFormat": null } }, "heapdumpMvcEndpoint": { "prefix": "endpoints.heapdump", "properties": { "path": "/heapdump", "sensitive": true, "enabled": true } }, "endpoints.cors-org.springframework.boot.actuate.autoconfigure.EndpointCorsProperties": { "prefix": "endpoints.cors", "properties": { "allowedOrigins": [], "maxAge": 1800, "exposedHeaders": [], "allowedHeaders": [], "allowedMethods": [], "allowCredentials": null } }, "environmentMvcEndpoint": { "prefix": "endpoints.env", "properties": { "path": "/env" } }, "environmentEndpoint": { "prefix": "endpoints.env", "properties": { "id": "env", "sensitive": true, "enabled": true } }, "spring.http.multipart-org.springframework.boot.autoconfigure.web.MultipartProperties": { "prefix": "spring.http.multipart", "properties": { "maxRequestSize": "10MB", "fileSizeThreshold": "0", "location": null, "maxFileSize": "1MB", "enabled": true, "resolveLazily": false } }, "spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties": { "prefix": "spring.info", "properties": { "build": { "location": {} }, "git": { "location": {} } } }, "auditEventsEndpoint": { "prefix": "endpoints.auditevents", "properties": { "enabled": true } }, "traceEndpoint": { "prefix": "endpoints.trace", "properties": { "id": "trace", "sensitive": true, "enabled": true } }, "metricsMvcEndpoint": { "prefix": "endpoints.metrics", "properties": { "path": "/metrics" } }, "infoEndpoint": { "prefix": "endpoints.info", "properties": { "id": "info", "sensitive": false, "enabled": true } }, "auditEventMvcEndpoint": { "prefix": "endpoints.auditevents", "properties": { "path": "/auditevents", "sensitive": true, "enabled": true } }, "management.trace-org.springframework.boot.actuate.trace.TraceProperties": { "prefix": "management.trace", "properties": { "include": [ "RESPONSE_HEADERS", "COOKIES", "REQUEST_HEADERS", "ERRORS", "TIME_TAKEN" ] } }, "spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties": { "prefix": "spring.resources", "properties": { "cachePeriod": null, "addMappings": true, "chain": { "cache": true, "htmlApplicationCache": false, "gzipped": false, "strategy": { "fixed": { "enabled": false, "paths": [ "/**" ], "version": null }, "content": { "enabled": false, "paths": [ "/**" ] } } }, "staticLocations": [ "/", "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" ] } }, "management.health.status-org.springframework.boot.actuate.autoconfigure.HealthIndicatorProperties": { "prefix": "management.health.status", "properties": { "order": null } }, "healthMvcEndpoint": { "prefix": "endpoints.health", "properties": { "path": "/health" } }, "serverProperties": { "prefix": "server", "properties": { "contextParameters": {}, "address": null, "maxHttpPostSize": 0, "undertow": { "maxHttpPostSize": 0, "bufferSize": null, "buffersPerRegion": null, "ioThreads": null, "workerThreads": null, "directBuffers": null, "accesslog": { "enabled": null, "pattern": "common", "prefix": "access_log.", "suffix": "log", "dir": "D:\\ideaProject\\demo3\\logs", "rotate": true } }, "tomcat": { "accesslog": { "enabled": false, "pattern": "common", "directory": "logs", "prefix": "access_log", "suffix": ".log", "rotate": true, "renameOnRotate": false, "fileDateFormat": ".yyyy-MM-dd", "requestAttributesEnabled": false, "buffered": true }, "internalProxies": "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|192\\.168\\.\\d{1,3}\\.\\d{1,3}|169\\.254\\.\\d{1,3}\\.\\d{1,3}|127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}", "protocolHeader": null, "protocolHeaderHttpsValue": "https", "portHeader": "X-Forwarded-Port", "remoteIpHeader": null, "basedir": null, "backgroundProcessorDelay": 30, "maxThreads": 0, "minSpareThreads": 0, "maxHttpPostSize": 0, "redirectContextRoot": null, "uriEncoding": null, "maxConnections": 0, "acceptCount": 0, "additionalTldSkipPatterns": [] }, "displayName": "application", "session": { "timeout": null, "trackingModes": null, "persistent": false, "storeDir": null, "cookie": { "name": null, "domain": null, "path": null, "comment": null, "httpOnly": null, "secure": null, "maxAge": null } }, "contextPath": null, "error": { "path": "/error", "includeStacktrace": "NEVER" }, "ssl": null, "serverHeader": null, "useForwardHeaders": null, "port": 8082, "maxHttpHeaderSize": 0, "servletPath": "/", "jspServlet": null, "jetty": { "maxHttpPostSize": 0, "acceptors": null, "selectors": null }, "connectionTimeout": null } }, "spring.metrics.export-org.springframework.boot.actuate.metrics.export.MetricExportProperties": { "prefix": "spring.metrics.export", "properties": { "excludes": null, "statsd": { "host": null, "port": 8125, "prefix": null }, "includes": null, "triggers": {}, "enabled": true, "redis": { "prefix": "spring.metrics.application.134b1d0d4b1f3ea2a2fc4a26eb94e90b", "key": "******" }, "aggregate": { "prefix": "application.134b1d0d4b1f3ea2a2fc4a26eb94e90b", "keyPattern": "k.d" } } }, "configurationPropertiesReportEndpoint": { "prefix": "endpoints.configprops", "properties": { "id": "configprops", "sensitive": true, "enabled": true } }, "healthEndpoint": { "prefix": "endpoints.health", "properties": { "timeToLive": 1000, "id": "health", "sensitive": false, "enabled": true } }, "loggersMvcEndpoint": { "prefix": "endpoints.loggers", "properties": { "path": "/loggers" } }, "loggersEndpoint": { "prefix": "endpoints.loggers", "properties": { "id": "loggers", "sensitive": true, "enabled": true } }, "endpoints.metrics.filter-org.springframework.boot.actuate.autoconfigure.MetricFilterProperties": { "prefix": "endpoints.metrics.filter", "properties": { "counterSubmissions": [ "MERGED" ], "gaugeSubmissions": [ "MERGED" ] } }, "spring.thymeleaf-org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties": { "prefix": "spring.thymeleaf", "properties": { "excludedViewNames": null, "cache": false, "checkTemplate": true, "prefix": "classpath:/templates/", "viewNames": null, "suffix": ".html", "encoding": "UTF-8", "enabled": true, "mode": "HTML5", "checkTemplateLocation": true, "templateResolverOrder": null, "contentType": { "parameters": {} } } }, "dumpEndpoint": { "prefix": "endpoints.dump", "properties": { "id": "dump", "sensitive": true, "enabled": true } }, "autoConfigurationReportEndpoint": { "prefix": "endpoints.autoconfig", "properties": { "id": "autoconfig", "sensitive": true, "enabled": true } }, "endpoints.jmx-org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportProperties": { "prefix": "endpoints.jmx", "properties": { "uniqueNames": false, "staticNames": {}, "enabled": true, "domain": "" } }, "spring.http.encoding-org.springframework.boot.autoconfigure.web.HttpEncodingProperties": { "prefix": "spring.http.encoding", "properties": { "charset": "UTF-8", "force": false, "mapping": null, "forceRequest": false, "forceResponse": false } }, "shutdownEndpoint": { "prefix": "endpoints.shutdown", "properties": { "id": "shutdown", "sensitive": true, "enabled": true } }, "beansEndpoint": { "prefix": "endpoints.beans", "properties": { "id": "beans", "sensitive": true, "enabled": true } }, "managementServerProperties": { "prefix": "management", "properties": { "security": { "enabled": false, "roles": [ "ACTUATOR" ], "sessions": "STATELESS" }, "address": null, "port": null, "addApplicationContextHeader": true, "contextPath": "", "ssl": null } }, "requestMappingEndpoint": { "prefix": "endpoints.mappings", "properties": { "id": "mappings", "sensitive": false, "enabled": true } }, "endpoints.health-org.springframework.boot.actuate.autoconfigure.HealthMvcEndpointProperties": { "prefix": "endpoints.health", "properties": { "mapping": {} } }, "shutdownMvcEndpoint": { "prefix": "endpoints.shutdown", "properties": { "path": "/shutdown" } }, "spring.mvc-org.springframework.boot.autoconfigure.web.WebMvcProperties": { "prefix": "spring.mvc", "properties": { "dateFormat": null, "servlet": { "loadOnStartup": -1 }, "staticPathPattern": "/**", "dispatchOptionsRequest": true, "dispatchTraceRequest": false, "locale": null, "ignoreDefaultModelOnRedirect": true, "logResolvedException": false, "async": { "requestTimeout": null }, "messageCodesResolverFormat": null, "mediaTypes": {}, "view": { "prefix": null, "suffix": null }, "localeResolver": "ACCEPT_HEADER", "throwExceptionIfNoHandlerFound": false } }, "diskSpaceHealthIndicatorProperties": { "prefix": "management.health.diskspace", "properties": { "path": "D:\\ideaProject\\demo3\\.", "threshold": 10485760 } } }</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>http://localhost:8082/trace</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>[ { "timestamp": 1507710930293, "info": { "method": "GET", "path": "/env", "headers": { "request": { "host": "localhost:8082", "connection": "keep-alive", "user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36", "content-type": "application/json", "accept": "*/*", "accept-encoding": "gzip, deflate, br", "accept-language": "zh-CN,zh;q=0.8", "cookie": "optimizelyEndUserId=oeu1506501248024r0.08055419825898946; _ga=GA1.1.670033975.1498201816; _gid=GA1.1.333206455.1507599805; JSESSIONID=228C035661689BDEEAFA98C77794E3DC; defaultDoorMap=%7B%22buildingId%22%3A%22root%22%2C%22id%22%3A%2210006%22%7D; showTopTab=%5B%7B%22show%22%3Atrue%2C%22uiSerfName%22%3A%22alarmSite%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Atrue%2C%22uiSerfName%22%3A%22doorControl.monitor%22%2C%22tabName%22%3A%22%E5%AE%9E%E6%97%B6%E7%9B%91%E6%8E%A7%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%5D" }, "response": { "X-Application-Context": "application:8082", "Content-Type": "application/vnd.spring-boot.actuator.v1+json;charset=UTF-8", "Transfer-Encoding": "chunked", "Date": "Wed, 11 Oct 2017 08:35:30 GMT", "status": "200" } }, "timeTaken": "6" } }, { "timestamp": 1507710923743, "info": { "method": "POST", "path": "/env", "headers": { "request": { "host": "localhost:8082", "connection": "keep-alive", "content-length": "2", "origin": "null", "user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36", "content-type": "application/json", "accept": "*/*", "accept-encoding": "gzip, deflate, br", "accept-language": "zh-CN,zh;q=0.8", "cookie": "optimizelyEndUserId=oeu1506501248024r0.08055419825898946; _ga=GA1.1.670033975.1498201816; _gid=GA1.1.333206455.1507599805; JSESSIONID=228C035661689BDEEAFA98C77794E3DC; defaultDoorMap=%7B%22buildingId%22%3A%22root%22%2C%22id%22%3A%2210006%22%7D; showTopTab=%5B%7B%22show%22%3Atrue%2C%22uiSerfName%22%3A%22alarmSite%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Atrue%2C%22uiSerfName%22%3A%22doorControl.monitor%22%2C%22tabName%22%3A%22%E5%AE%9E%E6%97%B6%E7%9B%91%E6%8E%A7%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%2C%7B%22show%22%3Afalse%2C%22uiSerfName%22%3A%22%22%2C%22tabName%22%3A%22%22%7D%5D" }, "response": { "X-Application-Context": "application:8082", "Allow": "GET", "status": "405" } }, "timeTaken": "4" } } ]</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>http://localhost:8082/env</h3> <!-- /wp:heading --> <!-- wp:heading {"level":3} --> <h3>http://localhost:8082/beans</h3> <!-- /wp:heading -->

分类:javaspringbootspringspringcloud开发笔记
标签:springbootActuatorSpringCloudActuator端点Springboot Actuator

2019-09-27 15:37:42.0
Springboot Actuator 配置和使用说明  springboot-actuator

最详细的VUEX教程

<h1>最详细的Vuex教程</h1> <h2>什么是Vuex?</h2> vuex是一个专门为vue.js设计的集中式状态管理架构。状态?我把它理解为在data中的属性需要共享给其他vue组件使用的部分,就叫做状态。简单的说就是data中需要共用的属性。 引入Vuex(前提是已经用Vue脚手架工具构建好项目) 1、利用npm包管理工具,进行安装 vuex。在控制命令行中输入下边的命令就可以了。 <pre><code>npm install vuex --save </code></pre> 要注意的是这里一定要加上 –save,因为你这个包我们在生产环境中是要使用的。 2、新建一个store文件夹(这个不是必须的),并在文件夹下新建store.js文件,文件中引入我们的vue和vuex。 <pre><code>import Vue from 'vue'; import Vuex from 'vuex'; </code></pre> 3、使用我们vuex,引入之后用Vue.use进行引用。 <pre><code>Vue.use(Vuex); </code></pre> 通过这三步的操作,vuex就算引用成功了,接下来我们就可以尽情的玩耍了。 4、在main.js 中引入新建的vuex文件 <pre><code>import storeConfig from './vuex/store' </code></pre> 5、再然后 , 在实例化 Vue对象时加入 store 对象 : <pre><code>new Vue({ el: '#app', router, store,//使用store template: '&lt;App/&gt;', components: { App } }) </code></pre> <h2>初出茅庐 来个Demo</h2> 1、现在我们store.js文件里增加一个常量对象。store.js文件就是我们在引入vuex时的那个文件。 <pre><code>const state = { count:1 } </code></pre> 2、用export default 封装代码,让外部可以引用。 <pre><code>export default new Vuex.Store({ state }); </code></pre> 3、新建一个vue的模板,位置在components文件夹下,名字叫count.vue。在模板中我们引入我们刚建的store.js文件,并在模板中用{{$store.state.count}}输出count 的值。 <pre><code>&lt;template&gt; &lt;div&gt; &lt;h2&gt;{{msg}}&lt;/h2&gt; &lt;hr/&gt; &lt;h3&gt;{{$store.state.count}}&lt;/h3&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; import store from '@/vuex/store' export default{ data(){ return{ msg:'Hello Vuex', } }, store } &lt;/script&gt; </code></pre> 4、在store.js文件中加入两个改变state的方法。 <pre><code>const mutations={ add(state){ state.count+=1; }, reduce(state){ state.count-=1; } } </code></pre> 这里的mutations是固定的写法,意思是改变的,所以你先不用着急,只知道我们要改变state的数值的方法,必须写在mutations里就可以了。 5、在count.vue模板中加入两个按钮,并调用mutations中的方法。 <pre><code>&lt;div&gt; &lt;button @click="$store.commit('add')"&gt;+&lt;/button&gt; &lt;button @click="$store.commit('reduce')"&gt;-&lt;/button&gt; &lt;/div&gt; </code></pre> 这样进行预览就可以实现对vuex中的count进行加减了。 <h2>state访问状态对象</h2> const state ,这个就是我们说的访问状态对象,它就是我们SPA(单页应用程序)中的共享值。 学习状态对象赋值给内部对象,也就是把stroe.js中的值,赋值给我们模板里data中的值。有三种赋值方式 <h3>一、 通过computed的计算属性直接赋值</h3> computed属性可以在输出前,对data中的值进行改变,我们就利用这种特性把store.js中的state值赋值给我们模板中的data值。 <pre><code>computed:{ count(){ return this.$store.state.count; } } </code></pre> 这里需要注意的是return this.$store.state.count这一句,一定要写this,要不你会找不到$store的。这种写法很好理解,但是写起来是比较麻烦的,那我们来看看第二种写法。 <h3>二、通过mapState的对象来赋值</h3> 我们首先要用import引入mapState。 <pre><code>import {mapState} from 'vuex'; </code></pre> 然后还在computed计算属性里写如下代码: <pre><code>computed:mapState({ count:state=&gt;state.count //理解为传入state对象,修改state.count属性 }) </code></pre> 这里我们使用ES6的箭头函数来给count赋值。 <h3>三、通过mapState的数组来赋值</h3> <pre><code>computed:mapState(["count"]) </code></pre> 这个算是最简单的写法了,在实际项目开发当中也经常这样使用。 Mutations修改状态($store.commit( )) Vuex提供了commit方法来修改状态,我们粘贴出Demo示例代码内容,简单回顾一下,我们在button上的修改方法。 <pre><code>&lt;button @click="$store.commit('add')"&gt;+&lt;/button&gt; &lt;button @click="$store.commit('reduce')"&gt;-&lt;/button&gt; </code></pre> store.js文件: <pre><code>const mutations={ add(state){ state.count+=1; }, reduce(state){ state.count-=1; } } </code></pre> 传值:这只是一个最简单的修改状态的操作,在实际项目中我们常常需要在修改状态时传值。比如上边的例子,是我们每次只加1,而现在我们要通过所传的值进行相加。其实我们只需要在Mutations里再加上一个参数,并在commit的时候传递就就可以了。我们来看具体代码: 现在store.js文件里给add方法加上一个参数n。 <pre><code>const mutations={ add(state,n){ state.count+=n; }, reduce(state){ state.count-=1; } } </code></pre> 在Count.vue里修改按钮的commit( )方法传递的参数,我们传递10,意思就是每次加10. <pre><code>&lt;p&gt; &lt;button @click="$store.commit('add',10)"&gt;+&lt;/button&gt; &lt;button @click="$store.commit('reduce')"&gt;-&lt;/button&gt; &lt;/p&gt; </code></pre> <h2>模板获取Mutations方法</h2> 实际开发中我们也不喜欢看到$store.commit( )这样的方法出现,我们希望跟调用模板里的方法一样调用。 例如:@click=”reduce” 就和没引用vuex插件一样。要达到这种写法,只需要简单的两部就可以了: 1、在模板count.vue里用import 引入我们的mapMutations: <pre><code>import { mapState,mapMutations } from 'vuex'; </code></pre> 2、在模板的&lt;script&gt;标签里添加methods属性,并加入mapMutations <pre><code> methods:mapMutations([ 'add','reduce' ]), </code></pre> 通过上边两部,我们已经可以在模板中直接使用我们的reduce或者add方法了,就像下面这样。 <pre><code>&lt;button @click="reduce"&gt;-&lt;/button&gt; </code></pre> <h2>getters计算过滤操作</h2> getters从表面是获得的意思,可以把他看作在获取数据之前进行的一种再编辑,相当于对数据的一个过滤和加工。你可以把它看作store.js的计算属性。 <h3>getters基本用法:</h3> 比如我们现在要对store.js文件中的count进行一个计算属性的操作,就是在它输出前,给它加上100.我们首先要在store.js里用const声明我们的getters属性。 <pre><code>const getters = { count:function(state){ return state.count +=100; } } </code></pre> 写好了gettters之后,我们还需要在Vuex.Store()里引入,由于之前我们已经引入了state和mutations,所以引入里有三个引入属性。代码如下, <pre><code>export default new Vuex.Store({ state,mutations,getters }) </code></pre> 在store.js里的配置算是完成了,我们需要到模板页对computed进行配置。在vue 的构造器里边只能有一个computed属性,如果你写多个,只有最后一个computed属性可用,所以要对上节课写的computed属性进行一个改造。改造时我们使用ES6中的展开运算符”…”。 <pre><code>computed:{ ...mapState(["count"]), count(){ return this.$store.getters.count; } }, </code></pre> 需要注意的是,你写了这个配置后,在每次count 的值发生变化的时候,都会进行加100的操作。 <h3>用mapGetters简化模板写法</h3> 我们都知道state和mutations都有map的引用方法把我们模板中的编码进行简化,我们的getters也是有的,我们来看一下代码。 首先用import引入我们的mapGetters <pre><code>import { mapState,mapMutations,mapGetters } from 'vuex'; </code></pre> 在computed属性中加入mapGetters <pre><code>...mapGetters(["count"]) </code></pre> <h2> actions异步修改状态</h2> actions和之前讲的Mutations功能基本一样,不同点是,actions是异步的改变state状态,而Mutations是同步改变状态。至于什么是异步什么是同步这里我就不做太多解释了,如果你不懂自己去百度查一下吧。 <h3>在store.js中声明actions</h3> actions是可以调用Mutations里的方法的,我们还是继续在上节课的代码基础上进行学习,在actions里调用add和reduce两个方法。 <pre><code>const actions ={ addAction(context){ context.commit('add',10) }, reduceAction({commit}){ commit('reduce') } } </code></pre> 在actions里写了两个方法addAction和reduceAction,在方法体里,我们都用commit调用了Mutations里边的方法。细心的小伙伴会发现这两个方法传递的参数也不一样。 ontext:上下文对象,这里你可以理解称store本身。 {commit}:直接把commit对象传递过来,可以让方法体逻辑和代码更清晰明了。 <h3>模板中的使用</h3> 我们需要在count.vue模板中编写代码,让actions生效。我们先复制两个以前有的按钮,并改成我们的actions里的方法名,分别是:addAction和reduceAction。 <pre><code>&lt;p&gt; &lt;button @click="addAction"&gt;+&lt;/button&gt; &lt;button @click="reduceAction"&gt;-&lt;/button&gt; &lt;/p&gt; </code></pre> 改造一下我们的methods方法,首先还是用扩展运算符把mapMutations和mapActions加入。 <pre><code>methods:{ ...mapMutations([ 'add','reduce' ]), ...mapActions(['addAction','reduceAction']) }, </code></pre> 你还要记得用import把我们的mapActions引入才可以使用。 <h3>增加异步检验</h3> 我们现在看的效果和我们用Mutations作的一模一样,肯定有的小伙伴会好奇,那actions有什么用,我们为了演示actions的异步功能,我们增加一个计时器(setTimeOut)延迟执行。在addAction里使用setTimeOut进行延迟执行。 <pre><code>setTimeOut(()=&gt;{context.commit(reduce)},3000); console.log('我比reduce提前执行'); </code></pre> 我们可以看到在控制台先打印出了‘我比reduce提前执行’这句话。 <h2>module模块组</h2> 随着项目的复杂性增加,我们共享的状态越来越多,这时候我们就需要把我们状态的各种操作进行一个分组,分组后再进行按组编写。那今天我们就学习一下module:状态管理器的模块组操作。 <h3>声明模块组:</h3> 在vuex/store.js中声明模块组,我们还是用我们的const常量的方法声明模块组。代码如下: <pre><code>const moduleA={ state,mutations,getters,actions } </code></pre> 声明好后,我们需要修改原来 Vuex.Stroe里的值: <pre><code>export default new Vuex.Store({ modules:{a:moduleA} }) </code></pre> <h3>在模板中使用</h3> 现在我们要在模板中使用count状态,要用插值的形式写入。 <pre><code>&lt;h3&gt;{{$store.state.a.count}}&lt;/h3&gt; </code></pre> 如果想用简单的方法引入,还是要在我们的计算属性中rutrun我们的状态。写法如下: <pre><code>computed:{ count(){ return this.$store.state.a.count; } },</code></pre> 本文转载自:https://www.jianshu.com/p/2269e4db9de9

分类:前端笔记转载vue开发笔记
标签:vuevue教程vue-clivuex前端前端技术

2019-09-25 16:55:36.0
最详细的VUEX教程  vue-vuex

Java把中文汉字转换成拼音

<!-- wp:heading --> <h2>依赖pinyin4j(pinyin for java)</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code> &lt;dependency> &lt;groupId>com.belerweb&lt;/groupId> &lt;artifactId>pinyin4j&lt;/artifactId> &lt;version>2.5.1&lt;/version> &lt;/dependency></code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2>使用示例</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>package com.yawn.pinyin; import net.sourceforge.pinyin4j.PinyinHelper; import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType; import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; import static net.sourceforge.pinyin4j.format.HanyuPinyinCaseType.LOWERCASE; import static net.sourceforge.pinyin4j.format.HanyuPinyinCaseType.UPPERCASE; import static net.sourceforge.pinyin4j.format.HanyuPinyinToneType.WITHOUT_TONE; import static net.sourceforge.pinyin4j.format.HanyuPinyinToneType.WITH_TONE_NUMBER; import static net.sourceforge.pinyin4j.format.HanyuPinyinToneType.WITH_TONE_MARK; import static net.sourceforge.pinyin4j.format.HanyuPinyinVCharType.WITH_U_AND_COLON; import static net.sourceforge.pinyin4j.format.HanyuPinyinVCharType.WITH_V; import static net.sourceforge.pinyin4j.format.HanyuPinyinVCharType.WITH_U_UNICODE; /** * @author yawn * 2019/9/19 16:46 */ public class Test { public static void main(String[] args) throws BadHanyuPinyinOutputFormatCombination { String str = "今天天气 真好!good! 率"; HanyuPinyinOutputFormat fmt = new HanyuPinyinOutputFormat(); String pinyin1 = PinyinHelper.toHanYuPinyinString(str, fmt, "", true); System.out.println(pinyin1); // jin1tian1tian1qi4 zhen1hao3!good! lu:4 String pinyin2 = PinyinHelper.toHanYuPinyinString(str, fmt, "", false); System.out.println(pinyin2); // jin1tian1tian1qi4zhen1hao3lu:4 String pinyin3 = PinyinHelper.toHanYuPinyinString(str, fmt, "-", true); System.out.println(pinyin3); // jin1-tian1-tian1-qi4- zhen1-hao3-!good! lu:4 HanyuPinyinOutputFormat fmt2 = new HanyuPinyinOutputFormat(); fmt2.setCaseType(UPPERCASE); fmt2.setToneType(WITHOUT_TONE); fmt2.setVCharType(WITH_V); String pinyin4 = PinyinHelper.toHanYuPinyinString(str, fmt2, " ", false); System.out.println(pinyin4); // JIN TIAN TIAN QI ZHEN HAO LV } }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>输出结果:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> jin1tian1tian1qi4 zhen1hao3!good! lu:4 jin1tian1tian1qi4zhen1hao3lu:4 jin1-tian1-tian1-qi4- zhen1-hao3-!good! lu:4 JIN TIAN TIAN QI ZHEN HAO LV</code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2>PinyinHelper.toHanYuPinyinString方法说明</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>参数说明</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p> str – 需要转换成拼音的中文汉字字符串<br> outputFormat – 转换的格式设置<br> separate – 转换后每个汉字拼音之间的分隔符<br> retain – 是否保留非中文汉字的字符</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>HanyuPinyinOutputFormat 拼音格式设置</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>HanyuPinyinOutputFormat 类可以设置拼音的格式,其中有三个属性:</p> <!-- /wp:paragraph --> <!-- wp:list {"ordered":true} --> <ol><li>vCharType :对拼音 ü 的转换方式</li><li>caseType :大小写的转换方式</li><li>toneType :声调的转换方式</li></ol> <!-- /wp:list --> <!-- wp:paragraph --> <p>默认值如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> public void restoreDefault() { vCharType = HanyuPinyinVCharType.WITH_U_AND_COLON; caseType = HanyuPinyinCaseType.LOWERCASE; toneType = HanyuPinyinToneType.WITH_TONE_NUMBER; }</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>vCharType :对拼音 ü 的转换方式</h3> <!-- /wp:heading --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">HanyuPinyinVCharType.<em>WITH_U_AND_COLON</em>; // 使用 u: 代替 HanyuPinyinVCharType.<em>WITH_V</em>; // 使用 v 代替 HanyuPinyinVCharType.<em>WITH_U_UNICODE</em>; // 使用 ü (字符不支持时会报错)</pre> <!-- /wp:preformatted --> <!-- wp:heading {"level":3} --> <h3>caseType :大小写的转换方式</h3> <!-- /wp:heading --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">HanyuPinyinCaseType.<em>LOWERCASE</em>; // 转换后问小写拼音 HanyuPinyinCaseType.<em>UPPERCASE</em>; // 转换后为大写拼音</pre> <!-- /wp:preformatted --> <!-- wp:heading {"level":3} --> <h3>toneType :声调的转换方式</h3> <!-- /wp:heading --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">HanyuPinyinToneType.<em>WITHOUT_TONE</em>; // 忽略拼音的声调 HanyuPinyinToneType.<em>WITH_TONE_NUMBER</em>; // 使用1234表示声调(轻声将被忽略) HanyuPinyinToneType.<em>WITH_TONE_MARK</em>; // 使用声调字符 ā á ǎ à&nbsp;(字符不支持时会报错)</pre> <!-- /wp:preformatted -->

分类:java开发笔记
标签:java中文转换拼音拼音Pinyin

2019-09-20 11:08:20.0
Java把中文汉字转换成拼音  zhong-wen-han-zi

SpringCloud简明教程配置汇总笔记

<!-- wp:paragraph --> <p>本文涵盖 spring cloud 学习示例程序,eureka,feign,rebbion,hystrix,zuul,config,bus使用示例(使用svn管理配置) 。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>Eureka注册中心</h2> <!-- /wp:heading --> <!-- wp:heading {"level":3} --> <h3>Eureka服务端配置</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p> @EnableEurekaServer </p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>eureka: instance: hostname: localhost client: register-with-eureka: false #是否将eureka自身作为应用注册到eureka注册中心 fetch-registry: false #为true时,可以启动,但报异常:Cannot execute request on any known server serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>Eureka客户端配置</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p> @EnableDiscoveryClient </p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>spring: application: name: service-a eureka: client: serviceUrl: defaultZone: http://localhost:8010/eureka/ #eureka服务注册地址</code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2>Ribbon 客户端的负载均衡</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p> 在注入<code>RestTemplate</code>调用服务时,加注解<code>@LoadBalanced</code>即可实现客户端的负载均衡。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>Feign 作为http客户端,调用服务</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p> @EnableFeignClients </p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>@FeignClient(value = "service-provider") public interface IProviderClient { @RequestMapping(method = RequestMethod.GET, value = "/car/{id}") String getCar(@PathVariable("id") int id); }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>建立一个接口,使用<code>@FeignClient(value = "service-provider")</code>注解标明要调用的服务名称为<code>service-provider</code>;在方法上使用<code>@RequestMapping</code>标明要调用的接口地址。之后在代码中调用这个方法,就会使用Feign作为http客户端去调用对应的接口。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>Hystrix 熔断器保护被调用的服务方法</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p> @EnableCircuitBreaker </p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>feign: hystrix: enabled: true hystrix: command: # IProviderClient#getCar(): # 这是commandKey default: execution: isolation: thread: timeoutInMilliseconds: 2000 # 熔断条件1:请求超时时间 circuitBreaker: requestVolumeThreshold: 10 # 熔断条件2:线程池的大小(每一个hystrix的command都分配一个线程池执行,即某时刻只允许最多同时10个客户端的请求)</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>如果调用失败,在feign的接口客户端添加fallback方法,即可设置默认的返回结果。如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>@FeignClient(value = "service-provider", fallback = IProviderClientImpl.class) public interface IProviderClient { @RequestMapping(method = RequestMethod.GET, value = "/car/{id}") String getCar(@PathVariable("id") int id); }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p><code>IProviderClientImpl</code>中getCar方法的返回值即为默认的返回值。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2> Zuul 网关分发</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p> @EnableZuulProxy </p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>zuul: ignored-services: microservice-provider-user # 需要忽视的服务(配置后将不会被路由) routes: first: path: /first/** # 若路由名称配置为 first 则可以省略这句 (访问http://localhost/first/ca/23) url: http://localhost:8080 # 简单路由 second: path: /second/** url: forward:/second # 转发路由 third: path: /third/** service-id: service-invoker</code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2>Config 配置中心 </h2> <!-- /wp:heading --> <!-- wp:heading {"level":3} --> <h3>Config服务端</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p> @EnableConfigServer </p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>server: port: 8888 spring: application: name: config-server profiles: active: subversion cloud: config: server: svn: uri: https://192.168.50.33/svn/test username: yawn password: yawn default-label: trunk eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ management: security: enabled: false</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>Config 客户端</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>spring: application: name: config-client cloud: config: discovery: enabled: true # 根据服务id去查找配置服务器 service-id: config-server # 替代配置uri fail-fast: true # uri: http://localhost:8888 profile: dev name: config-client label: trunk # /trunk/config-client-dev.yml 也可以不指定,因为服务端指定default-label rabbitmq: host: localhost port: 5672 username: guest password: guest eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ management: security: enabled: false</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>http请求读取配置的匹配规则:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>/{application}/{profile}[/{label}] /{application}-{profile}.yml /{label}/{application}-{profile}.yml /{application}-{profile}.properties /{label}/{application}-{profile}.properties</code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2>SpringCloud 单点登陆</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>基于 auth2.0 协议的认证</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":3} --> <h3>认证服务器</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p>@EnableAuthorizationServer</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>server: port: 9999 context-path: /uaa security: # sessions: if_required ignored: /css/**,/js/**,/favicon.ico,/webjars/** user: name: yawn password: yawn oauth2: client: client-id: yawnClient client-secret: 123456 scope: openid # 表示权限范围,可选项,用户授权页面时进行选择 authorized-grant-types: authorization_code #,refresh_token,password,client_credentials # 有四种授权方式</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>资源服务器(用来向客户端提供用户的信息)</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p>@EnableResourceServer</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>@Configuration @EnableResourceServer @RestController public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @GetMapping("user") public Map user(Principal principal) { Map user = new HashMap(4); user.put("name", principal.getName()); user.put("description", principal.toString()); return user; } @Override public void configure(HttpSecurity http) throws Exception { // Resource Server 的配置, 客户端获取用户信息 http.antMatcher("/user").authorizeRequests().anyRequest() .authenticated(); } }</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>需要认证的客户端</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p> @EnableOAuth2Sso </p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>server: port: 8080 security: oauth2: client: client-id: yawnClient client-secret: 123456 access-token-uri: http://localhost:9999/uaa/oauth/token user-authorization-uri: http://localhost:9999/uaa/oauth/authorize resource: user-info-uri: http://localhost:9999/uaa/user</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>以上是springcloud各个组件的基本配置和使用方法,记录以备查询。</p> <!-- /wp:paragraph -->

分类:javaspringbootspringspringcloud开发笔记
标签:springbootzuulspringEurekaSpringCloudHystrix

2019-09-19 15:01:49.0
SpringCloud简明教程配置汇总笔记  springcloud-jian

SpringCloud环境下Springboot 上传文件名中文乱码

<!-- wp:heading --> <h2>文件名乱码问题</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>SpringCloud环境下Springboot 上传文件时, 中文文件名会出现乱码,文件名变成问号。但是,如果不通过网关zuul,上传文件就不会乱码。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>如下:</p> <!-- /wp:paragraph --> <!-- wp:image {"id":1080} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/09/upload-luanma-1024x182.jpg" alt="" class="wp-image-1080"/></figure> <!-- /wp:image --> <!-- wp:heading --> <h2>使用zuul过滤器解决</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>解决这个问题,zuul文档中推荐使用zuul的文件处理过滤器。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>而对于我们开发者来说,只需要在调用上传的接口时,url前面额外添加一个/zuul就可以应用这些过滤器了。如下代码:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> var uploaderOptions = { queueLimit: 1, autoUpload: true, url: '/zuul/cqwf-store/import/upload', accepts: ['pdf', 'zip'], wrongFormatInfo: '文件格式不正确!' }; self.uploader = createUploader(uploaderOptions);</code></pre> <!-- /wp:code -->

分类:JVM开发笔记日常报错javaspringbootspringspringcloud
标签:springbootSpringCloudzuul过滤器spring中文乱码日常报错文件上传zuul

2019-09-18 11:30:28.0
SpringCloud环境下Springboot 上传文件名中文乱码  springcloud-huan

Springboot 上传文件系统找不到指定的路径

<!-- wp:heading --> <h2>上传报错信息</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>Springboot 上传文件报错,在使用 MultipartFile 的 transferTo 方法时报错,代码如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> public String tmpMdbFile(MultipartFile file) { File dest = new File(TMP_DIR + System.currentTimeMillis() + "_" + file.getOriginalFilename()); try { file.transferTo(dest); } catch (IOException e) { e.printStackTrace(); } return dest.getAbsolutePath(); }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>报错信息如下:</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>java.io.IOException: java.io.FileNotFoundException: C:\Users\yawn\AppData\Local\Temp\tomcat.5222418755956874474.8095\work\Tomcat\localhost\ROOT\tmp\xxx (No such file or directory) 或</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>java.io.IOException: java.io.FileNotFoundException: C:\Users\yawn\AppData\Local\Temp\tomcat.5222418755956874474.8095\work\Tomcat\localhost\ROOT\tmp\xxx (系统找不到指定的路径。) </p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>解决方法</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>原因是我们使用了相对路径,MultipartFile 在使用transferTo的路径就会出现问题。解决办法如下:</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>1 解决这个问题可以使用绝对路径。即代码中的常量 TMP_DIR 设置为绝对路经。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>2 或者不使用transferTo方法,而从multipartFile的输入流读取文件内容。代码如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> public String tmpMdbFile(InputStream is, String fileName) { File dest = new File(TMP_DIR + System.currentTimeMillis() + "_" + fileName); saveFile(is, dest); return dest.getAbsolutePath(); } private void saveFile(InputStream is, File dest) { try(FileOutputStream fos = new FileOutputStream(dest)) { int len; byte[] buffer = new byte[1024]; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } }</code></pre> <!-- /wp:code -->

分类:服务器搭建开发笔记javaspringbootspring日常报错
标签:springbootMultipartFilespring日常报错文件上传

2019-09-18 11:05:34.0
Springboot 上传文件系统找不到指定的路径  springboot-shang

SpringBoot整合PostgreSQL结合Jpa的使用

<!-- wp:paragraph --> <p>SpringBoot整合PostgreSQL,并结合Jpa使用。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>PostgreSQL 是一个开源的功能强大的关系型数据库,类似于MySQL,但在某些方面比MySQL更强大。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p><strong>Springboot整合PostgreSQL可通过以下步骤:</strong></p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>1. SpringBoot项目加入Jpa和PostgreSQL依赖</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code> &lt;dependency> &lt;groupId>org.springframework.boot&lt;/groupId> &lt;artifactId>spring-boot-starter-data-jpa&lt;/artifactId> &lt;/dependency> &lt;dependency> &lt;groupId>org.springframework.boot&lt;/groupId> &lt;artifactId>spring-boot-starter-web&lt;/artifactId> &lt;/dependency> &lt;dependency> &lt;groupId>org.postgresql&lt;/groupId> &lt;artifactId>postgresql&lt;/artifactId> &lt;scope>runtime&lt;/scope> &lt;/dependency></code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>本例使用的SpringBoot版本为 1.5.22.RELEASE 。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>2. 配置postgresql数据源和jpa属性</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>spring: datasource: url: jdbc:postgresql://localhost:5432/postgres username: postgres password: root driverClassName: org.postgresql.Driver jpa: show-sql: true hibernate: ddl-auto: update properties: hibernate: dialect: org.hibernate.dialect.PostgreSQL9Dialect temp: use_jdbc_metadata_defaults: false</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>application.yml 配置如上图所示。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>3. 新建实体类,加上@Entity注解</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>import javax.persistence.* @Entity class Student { @Id @GeneratedValue(strategy = GenerationType.AUTO) long id String name int age Date birthDay }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>当在实体类加上@Entity注解后,启动项目后便会自动创建出对应的数据表。创建的策略与配置项 spring.jpa.hibernate.ddl-auto 的值有关。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>@Id 注解为指定数据表的主键。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>@GeneratedValue 指定了主键的生成策略。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>关于Jpa实体上的注解,可参考 《Jpa中,实体类相关的注解说明》。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>4. 继承Jpa的xxxRepository接口操作数据库</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>/** * * @author yawn &lt; http://jvm123.com > * 2019/9/16 16:10 */ interface StudentRepo extends JpaRepository&lt;Student, Long> { Student save(Student person) List&lt;Student> findAll() }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>接口可继承 CrudRepository、PageAndSortingRepository、JpaRepository、 Repository ,继承不同的接口就具有不同的功能。具体可参考:《<a rel="noreferrer noopener" aria-label="Springboot整合jpa的基本使用方法大全(在新窗口打开)" href="http://jvm123.com/2019/09/springboot-jpa.html" target="_blank">Springboot整合jpa的基本使用方法大全</a>》。 </p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p></p> <!-- /wp:paragraph -->

分类:JVM数据库Jpajavagroovyspring开发笔记
标签:数据库jpqlspringbootPostgreSQLjpa

2019-09-17 23:30:03.0
SpringBoot整合PostgreSQL结合Jpa的使用  springboot-postgresql

Jpa即hibernate的主键生成策略

<!-- wp:paragraph --> <p>在jpa中,定义主键如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>@Entity public class Person { @Id // 声明一个策略通用生成器 @GenericGenerator(name = "inc", strategy ="increment") // 用generator属性指定要使用的策略生成器。 @GeneratedValue(generator = "inc") private long id; private String name;</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>使用一个@Id注解就可以定义一个主键,但主键生成的策略定义却有很多方式:</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>Jpa的@GeneratedValue直接指定主键策略</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>使用 Jpa的@GeneratedValue直接指定主键策略 ,如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>@GeneratedValue(strategy = GenerationType.AUTO)</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>其中strategy是一个枚举类GenerationType,有如下四种值:</p> <!-- /wp:paragraph --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">AUTO 持久化层程序会自动选择一个合适的主键生成策略,策略与具体的数据库有关 IDENTITY 主键由数据库生成, 采用数据库自增长, Oracle不支持这种方式 SEQUENCE 通过数据库的序列产生主键, MYSQL&nbsp;&nbsp;不支持 TABLE 提供特定的数据库产生主键, 该方式更有利于数据库的移植</pre> <!-- /wp:preformatted --> <!-- wp:paragraph --> <p>eg:AUTO 类型生成的主键,在mysql中需要程序指定;但在PostgreSQL中,会自动创建一个序列,从序列中获取主键。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>Jpa的@GenericGenerator使用主键生成策略</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>通过以下两步可以指定:</p> <!-- /wp:paragraph --> <!-- wp:list {"ordered":true} --> <ol><li>@GenericGenerator声明一个主键生成策略</li><li>@GeneratedValue 使用这个策略</li></ol> <!-- /wp:list --> <!-- wp:paragraph --> <p>示例如下:</p> <!-- /wp:paragraph --> <!-- wp:preformatted --> <pre class="wp-block-preformatted"> // 声明一个策略通用生成器 @GenericGenerator(name = "inc", strategy ="increment") // 用generator属性指定要使用的策略生成器。 @GeneratedValue(generator = "inc") private long id;</pre> <!-- /wp:preformatted --> <!-- wp:paragraph --> <p>其中jpa默认的主键生成策略以及其对应的类如下:</p> <!-- /wp:paragraph --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">"uuid2", UUIDGenerator.class "guid", GUIDGenerator.class "uuid", UUIDHexGenerator.class "uuid.hex", UUIDHexGenerator.class "assigned", Assigned.class "identity", IdentityGenerator.class "select", SelectGenerator.class "sequence", SequenceStyleGenerator.class "seqhilo", SequenceHiLoGenerator.class "increment", IncrementGenerator.class "foreign", ForeignGenerator.class "sequence-identity", SequenceIdentityGenerator.class "enhanced-sequence", SequenceStyleGenerator.class "enhanced-table", TableGenerator.class</pre> <!-- /wp:preformatted -->

分类:JVM数据库java开发笔记springJpa
标签:数据库hqlPostgreSQLspringboot主键生成策略jpahibernate

2019-09-15 19:46:07.0
Jpa即hibernate的主键生成策略  jpa-ji-hibernate-de

Jpa Repository 支持的自定义查询方法名称关键词

<!-- wp:paragraph --> <p>上篇文章介绍了 “<a rel="noreferrer noopener" aria-label="使用jpa的 Repository 自定义声明式查询方法(在新窗口打开)" href="http://jvm123.com/2019/09/springboot-jpa.html" target="_blank">使用jpa的 Repository 自定义声明式查询方法</a>” (declare query method)</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>使用jpa的 Repository 自定义声明式查询方法</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>凡是继承了Repository的或继承其子接口的,都可以使用自定义的声明式查询方法,如下示例:</p> <!-- /wp:paragraph --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">public interface PersonQueryRepo extends Repository { // declare query method // 声明式查询方法 // 1. count 计数 long <strong>countByName</strong>(String name); // 2. get/find/stream/query/read 查询 Person <strong>readFirstByAge</strong>(int age); // 3. delete/remove 删除 int <strong>deleteById</strong>(long id); }</pre> <!-- /wp:preformatted --> <!-- wp:heading --> <h2>声明式查询方法可以分3大类:</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p><strong>1 计数类 :以count开头,返回数值类型</strong></p> <!-- /wp:paragraph --> <!-- wp:image {"id":1032} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/09/jpa-count.jpg" alt="springboot整合jpa的基本使用方法大全插图" class="wp-image-1032"/></figure> <!-- /wp:image --> <!-- wp:paragraph --> <p><strong>2 查询类:以get/find/stream/query/read开头,返回Person类或其集合</strong></p> <!-- /wp:paragraph --> <!-- wp:image {"id":1033} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/09/jpa-find.jpg" alt="springboot整合jpa的基本使用方法大全插图(1)" class="wp-image-1033"/></figure> <!-- /wp:image --> <!-- wp:paragraph --> <p><strong>3 删除类: 以delete/remove开始,返回int类型</strong></p> <!-- /wp:paragraph --> <!-- wp:image {"id":1034} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/09/jpa-delete.jpg" alt="springboot整合jpa的基本使用方法大全插图(2)" class="wp-image-1034"/></figure> <!-- /wp:image --> <!-- wp:paragraph --> <p>在以上方法之后还有可以加入下面的关键词:</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>jpa声明式查询方法名称关键词</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p> 其中声明式查询方法的方法名称支持的关键字 如下:</p> <!-- /wp:paragraph --> <!-- wp:table --> <table class="wp-block-table"><thead><tr><th>Keyword</th><th>Sample</th><th>JPQL snippet</th></tr></thead><tbody><tr><td><code>And</code></td><td><code>findByLastnameAndFirstname</code></td><td><code>… where x.lastname = ?1 and x.firstname = ?2</code></td></tr><tr><td><code>Or</code></td><td><code>findByLastnameOrFirstname</code></td><td><code>… where x.lastname = ?1 or x.firstname = ?2</code></td></tr><tr><td><code>Is,</code><br><code>Equals</code></td><td><code>findByFirstname</code>,<br><code>findByFirstnameIs</code>,<br><code>findByFirstnameEquals</code></td><td><code>… where x.firstname = ?1</code></td></tr><tr><td><code>Between</code></td><td><code>findByStartDateBetween</code></td><td><code>… where x.startDate between ?1 and ?2</code></td></tr><tr><td><code>LessThan</code></td><td><code>findByAgeLessThan</code></td><td><code>… where x.age &lt; ?1</code></td></tr><tr><td><code>LessThanEqual</code></td><td><code>findByAgeLessThanEqual</code></td><td><code>… where x.age &lt;= ?1</code></td></tr><tr><td><code>GreaterThan</code></td><td><code>findByAgeGreaterThan</code></td><td><code>… where x.age &gt; ?1</code></td></tr><tr><td><code>GreaterThanEqual</code></td><td><code>findByAgeGreaterThanEqual</code></td><td><code>… where x.age &gt;= ?1</code></td></tr><tr><td><code>After</code></td><td><code>findByStartDateAfter</code></td><td><code>… where x.startDate &gt; ?1</code></td></tr><tr><td><code>Before</code></td><td><code>findByStartDateBefore</code></td><td><code>… where x.startDate &lt; ?1</code></td></tr><tr><td><code>IsNull</code></td><td><code>findByAgeIsNull</code></td><td><code>… where x.age is null</code></td></tr><tr><td><code>IsNotNull,</code><br><code>NotNull</code></td><td><code>findByAge(Is)NotNull</code></td><td><code>… where x.age not null</code></td></tr><tr><td><code>Like</code></td><td><code>findByFirstnameLike</code></td><td><code>… where x.firstname like ?1</code></td></tr><tr><td><code>NotLike</code></td><td><code>findByFirstnameNotLike</code></td><td><code>… where x.firstname not like ?1</code></td></tr><tr><td><code>StartingWith</code></td><td><code>findByFirstnameStartingWith</code></td><td><code>… where x.firstname like ?1</code>&nbsp;(parameter bound with appended&nbsp;<code>%</code>)</td></tr><tr><td><code>EndingWith</code></td><td><code>findByFirstnameEndingWith</code></td><td><code>… where x.firstname like ?1</code>&nbsp;(parameter bound with prepended&nbsp;<code>%</code>)</td></tr><tr><td><code>Containing</code></td><td><code>findByFirstnameContaining</code></td><td><code>… where x.firstname like ?1</code>&nbsp;(parameter bound wrapped in&nbsp;<code>%</code>)</td></tr><tr><td><code>OrderBy</code></td><td><code>findByAgeOrderByLastnameDesc</code></td><td><code>… where x.age = ?1 order by x.lastname desc</code></td></tr><tr><td><code>Not</code></td><td><code>findByLastnameNot</code></td><td><code>… where x.lastname &lt;&gt; ?1</code></td></tr><tr><td><code>In</code></td><td><code>findByAgeIn(Collection&lt;Age&gt; ages)</code></td><td><code>… where x.age in ?1</code></td></tr><tr><td><code>NotIn</code></td><td><code>findByAgeNotIn(Collection&lt;Age&gt; ages)</code></td><td><code>… where x.age not in ?1</code></td></tr><tr><td><code>True</code></td><td><code>findByActiveTrue()</code></td><td><code>… where x.active = true</code></td></tr><tr><td><code>False</code></td><td><code>findByActiveFalse()</code></td><td><code>… where x.active = false</code></td></tr><tr><td><code>IgnoreCase</code></td><td><code>findByFirstnameIgnoreCase</code></td><td><code>… where UPPER(x.firstame) = UPPER(?1)</code></td></tr></tbody></table> <!-- /wp:table --> <!-- wp:paragraph --> <p></p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>因此,jpa中使用以上三类操作,加上上表中的关键词作为约束条件,就可以极限大部分的查询和日常操作。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>此外,对于jpa中更新的update操作,推荐使用@Query注解,详见<a href="http://jvm123.com/2019/09/springboot-jpa.html" target="_blank" rel="noreferrer noopener" aria-label="(在新窗口打开)">http://jvm123.com/2019/09/springboot-jpa.html</a> 。</p> <!-- /wp:paragraph -->

分类:java开发笔记springJpa数据库
标签:数据库spring datajpqlspringboot声明式查询jpahql

2019-09-15 19:25:40.0
Jpa Repository 支持的自定义查询方法名称关键词  jpa-repository

Springboot整合jpa的基本使用方法大全

<!-- wp:paragraph --> <p>《springboot整合jpa的基本使用方法大全》本文 以mysql为例 ,主要讲解springboot整合jpa,以及jpa对数据操作的各种不同方法。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>springboot项目中jpa的依赖</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code> &lt;dependency> &lt;groupId>org.springframework.boot&lt;/groupId> &lt;artifactId>spring-boot-starter-data-jpa&lt;/artifactId> &lt;/dependency> &lt;dependency> &lt;groupId>mysql&lt;/groupId> &lt;artifactId>mysql-connector-java&lt;/artifactId> &lt;scope>runtime&lt;/scope> &lt;/dependency></code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>如果使用的不是mysql,则可以加入其他的数据库驱动依赖,例如postgres。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>springboot项目中jpa的配置</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>spring: datasource: url: jdbc:mysql://localhost:3306/test?useSSL=false&amp;serverTimezone=UTC driver-class-name: com.mysql.jdbc.Driver username: root password: root jpa: show-sql: true hibernate: ddl-auto: update properties: hibernate: dialect: org.hibernate.dialect.MySQL5Dialect</code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2>定义jpa的实体Person</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>@Entity public class Person { @Id /** * 生成器的13种策略: * @see org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory */ // 声明一个策略通用生成器,name为system-uuid,策略strategy为uuid。 @GenericGenerator(name = "increment", strategy ="increment") // 用generator属性指定要使用的策略生成器。 @GeneratedValue(generator = "increment") private long id; private String name; private int age; private Date birthDay;</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>启动项目后,会自动在数据库中创建一个名为person的数据表,和实体person对应。其中关联表的定义和创建详见 A。 </p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>使用jpa的 CrudRepository 基本查询</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>/** * @author yawn * 2019/9/15 15:18 */ public interface PersonCrudRepo extends CrudRepository&lt;Person, Long> { }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>只需要继承CrudRepository接口即可, PersonCrudRepo即可注入其他service使用,这里提供了基本的crud方法,使用示例如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> @Autowired PersonCrudRepo personCrudRepo; Person createAPerson() { Person person = new Person(); person.setAge(18); person.setBirthDay(new Date()); person.setName("yawn"); return person; } @Test public void test1() { Person p = createAPerson(); // test crud Person p1 = personCrudRepo.save(p); System.out.println(p1); Person p3 = personCrudRepo.findOne(1L); System.out.println(p3); Iterable&lt;Person> all = personCrudRepo.findAll(); System.out.println(all); // personCrudRepo.delete(2L); // personCrudRepo.deleteAll(); }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>delete方法如果删除失败,请加上@Transactional注解。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>使用jpa的 PagingAndSortingRepository 分页查询和排序</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>/** * @author yawn * 2019/9/15 15:18 */ public interface PersonPageSortRepo extends PagingAndSortingRepository&lt;Person, Long> { }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>调用方法如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>@Test public void test2() { // all Iterable&lt;Person> all = personPageSortRepo.findAll(); System.out.println(all); // 1. page (page从0开始计数) Pageable pa1 = new PageRequest(1, 2); Page&lt;Person> page1 = personPageSortRepo.findAll(pa1); page1.forEach(System.out::println); // 3. page and sort Sort sort = new Sort(Sort.Direction.DESC, "id", "name", "age"); Pageable pa2 = new PageRequest(1, 2, sort); Page&lt;Person> page2 = personPageSortRepo.findAll(pa2); page2.forEach(System.out::println); // 2. sort Iterable&lt;Person> people = personPageSortRepo.findAll(sort); System.out.println(people); // 4. 多个sort Sort desc = new Sort(Sort.Direction.DESC, "id"); Sort asc = new Sort(Sort.Direction.ASC, "name"); Sort sort1 = desc.and(asc); // and 后的是次要排序属性 Iterable&lt;Person> people2 = personPageSortRepo.findAll(sort1); System.out.println(people2); }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>1. PageRequest是 Pageable 的一个实现类,其参数page和size分别表示从第page页开始,查询size条记录。执行sql语句时,相当于加上了 <strong>limit page, size</strong> </p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>2. Sort是一个数据库排序的辅助类,其属性direction表示升序或降序,值为 Sort.Direction.<em>DESC</em> 和 Sort.Direction.<em>ASC</em>;properties表示需要排序的字段。 执行sql语句时,相当于加上了 <strong>order by id desc, name desc, age desc</strong></p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p> 3. 在 PageRequest 类中传入一个sort对象,就可以实现分页和排序两种功能。 执行sql语句时,相当于加上了 <strong>order by id desc, name desc, age desc</strong> <strong>limit page, size</strong> </p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>4. 如果查询时,不同的字段使用不同的升序或降序,则需要使用Sort类的and()方法。如上代码中,两个不同的sort,使用and方法后,生成新的Sort对象结合了不同的排序方式。执行的sql语句,相当于加上了 <strong>order by id desc, name asc</strong></p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>使用jpa的 Repository 自定义声明式查询方法</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>凡是继承了Repository的或继承其子接口的,都可以使用自定义的声明式查询方法,如下示例:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>/** * @author yawn * 2019/9/15 15:18 */ public interface PersonQueryRepo extends Repository&lt;Person, Long> { // declare query method // 声明式查询方法 // 1. count 计数 long countByName(String name); // 2. get/find/stream/query/read 查询 Person readFirstByAge(int age); // 3. delete/remove 删除 @Transactional int deleteById(long id); }</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3> 声明式查询方法可以分3大类:</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p><strong>1 计数类 :以count开头,返回数值类型</strong></p> <!-- /wp:paragraph --> <!-- wp:image {"id":1032,"align":"center"} --> <div class="wp-block-image"><figure class="aligncenter"><img src="http://jvm123.com/wp-content/uploads/2019/09/jpa-count.jpg" alt="" class="wp-image-1032"/></figure></div> <!-- /wp:image --> <!-- wp:paragraph --> <p><strong>2 查询类:以get/find/stream/query/read开头,返回Person类或其集合</strong></p> <!-- /wp:paragraph --> <!-- wp:image {"id":1033} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/09/jpa-find.jpg" alt="" class="wp-image-1033"/></figure> <!-- /wp:image --> <!-- wp:paragraph --> <p><strong>3 删除类: 以delete/remove开始,返回int类型</strong></p> <!-- /wp:paragraph --> <!-- wp:image {"id":1034} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/09/jpa-delete.jpg" alt="" class="wp-image-1034"/></figure> <!-- /wp:image --> <!-- wp:paragraph --> <p>其中方法名称支持的关键字和可以参考:<a href="http://jvm123.com/2019/09/jpa-repository.html">jpa Repository 支持的自定义查询方法名称关键词</a>。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>@Query使用jpa的jpql或者hql(实现update更新的一种方法)</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>示例如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>/** * @author yawn * 2019/9/15 17:02 */ public interface PersonHqlDao extends JpaRepository&lt;Person, Long> { // 使用hql 或者 jpql 查询 @Query("from Person where name = ?1 order by id desc") List&lt;Person> listByName(String name); // 前几种方法中均未介绍update操作,要完成update操作,可使用以下方法 // 更新时需要加上 @Transactional 和 @Modifying @Transactional @Modifying // QueryExecutionRequestException: Not supported for DML operations @Query("update Person set name=?2 where id=?1") int updateNameById(long id, String name); }</code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2>@Query使用jpa的sql</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>@Query注解使用sql时,需要加上nativeQuery = true,示例如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>public interface UserRepository extends JpaRepository&lt;User, Long> { @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1", countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1", nativeQuery = true) Page&lt;User> findByLastname(String lastname, Pageable pageable); }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>总体来说,推荐使用hql,不推荐sql。曾经有一次数据库字段名称改变后,我查找修改了许多sql语句,项目上线后还是报错了,日志中显示出来的,正是sql语句中有一处漏掉还未修改过来。如果使用hql的话,就不存在这个问题,只需要在实体的@Column注解处修改一次即可。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>以上所有,就是jpa中常用到的数据库交互方法。</p> <!-- /wp:paragraph -->

分类:JVM数据库java开发笔记springJpa
标签:数据库spring data排序jpqlspringbootPagingAndSorting声明式查询jpa分页查询hql

2019-09-15 19:18:40.0
Springboot整合jpa的基本使用方法大全  springboot-jpa

MYSQL 的 case when ... then ... else ... end 语句

<!-- wp:heading --> <h2><strong>格式</strong>: </h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>case when 条件1 then 值1 when 条件2 then 值2 else 值3 end</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p><strong>说明</strong>: 当“条件”成立时,语句等同于值1,否则值3</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>Mysql的case when语句举例1 </h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p> 当user表的loked字段为1时设置为0,当其为0时设置为1 </p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> update user set locked=(case when locked=1 then 0 else 1 end) where id=1;</code></pre> <!-- /wp:code --> <!-- wp:heading --> <h2>Mysql的case when语句举例2</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>查询用户是否锁定的状态</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>select username, case when LOCKED=1 then '已锁定' when LOCKED=0 or LOCKED is null then '未锁定' else '未知' end from user limit 10</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>结果如下:</p> <!-- /wp:paragraph --> <!-- wp:image {"id":1015} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/09/case-when.jpg" alt="" class="wp-image-1015"/></figure> <!-- /wp:image -->

分类:JVM开发笔记服务器搭建数据库
标签:数据库case语句mysqlcase whensql语句sql

2019-09-11 15:49:06.0
MYSQL 的 case when ... then ... else ... end 语句  mysql-de-case-when

Spock测试桩mock和stub的区别及使用场景

<!-- wp:image {"id":1005,"align":"center"} --> <div class="wp-block-image"><figure class="aligncenter"><img src="http://jvm123.com/wp-content/uploads/2019/09/mock-stub.jpg" alt="" class="wp-image-1005"/></figure></div> <!-- /wp:image --> <!-- wp:paragraph --> <p>如图,有如上的方法调用关系(模块依赖关系):A调用B和E方法,B调用C和D方法。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>在使用spock进行单元测试时,有如下情景,分别可使用stub和mock。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>使用stub测试桩</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>如果我们需要测试A方法,但是E方法目前还没办法调用,或者还没开发完成。这种场景下,就可以使用stub测试桩。stub测试桩可以给E方法模拟一个或多个假的返回值,我们测试时只需要调用stub对象的E方法即可,调用后的返回值是我们在生成stub对象时指定的。如下:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> def "Stub 测试桩"() { given: "构造测试桩" CalculateInterface calculateService = Stub(CalculateInterface) calculateService.plusPlus(_) >> 1 when: int x = calculateService.plusPlus(12) int y = calculateService.plusPlus(3) then: x == 1 y == 1 }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>上面代码中,<code>calculateService.plusPlus(_) &gt;&gt; 1</code> 给一个并未实现的plusPlus()方法指定了返回值为1,测试代码就可以直接调用这个方法了。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>其中这个语句的常用格式有:</p> <!-- /wp:paragraph --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">subscriber.receive(_) &gt;&gt; "ok" | | | | | | | 生成返回值 | | 参数 | 方法 对象</pre> <!-- /wp:preformatted --> <!-- wp:heading {"level":3} --> <h3>生成返回值:</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>// 不同参数生成不同的返回值 subscriber.receive("message1") >> "ok" subscriber.receive("message2") >> "fail" // 生成多个返回值 subscriber.receive(_) >>> ["ok", "error", "error", "ok"] </code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>通过计算生成返回值</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p>这种方式,生成返回值的格式时一个闭包</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>// 1.使用方法参数计算 subscriber.receive(_) >> { args -> args[0].size() > 3 ? "ok" : "fail" } // 2. 使用其他参数 subscriber.receive(_) >> { String message -> message.size() > 3 ? "ok" : "fail" }</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>如果想调用方法抛出异常</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>subscriber.receive(_) >> { throw new InternalError("ouch") }</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>链式生成返回值</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>subscriber.receive(_) >>> ["ok", "fail", "ok"] >> { throw new InternalError() } >> "ok"</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>上面代码中,方法被调用的前三次分别返回 "ok", "fail", "ok",第四次会抛出异常,第五次及以后调用,会返回“ok”。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>以上是spock中stub测试桩的使用场景,总结为一句就是: <strong>stub测试桩给被调用者( 方法/模块)制造假的返回值,以便不影响调用者的测试。</strong></p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>使用mock测试桩</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>mock测试桩就是模拟一个测试的结果。如下图,A类调用类B和C类的某个方法:</p> <!-- /wp:paragraph --> <!-- wp:image {"id":1009,"align":"center"} --> <div class="wp-block-image"><figure class="aligncenter"><img src="http://jvm123.com/wp-content/uploads/2019/09/mock-stub2-1.jpg" alt="" class="wp-image-1009"/></figure></div> <!-- /wp:image --> <!-- wp:paragraph --> <p>如果要测试A的方法,但是我们没办法调用B来检测结果,就可以使用mock测试桩,生成一个B的mock对象。检验结果时,可以使用B的mock对象替代B。这个结果一般是B和C方法的调用或者状态的改变。</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> def subscriber = Mock(Subscriber) // 1. 创建一个mock对象 def "should send messages subscriber"() { when: publisher.send("hello") // 2. publisher 发送一个“hello” then: 1 * subscriber.receive("hello") // 3. subscriber 接收到一个“hello” 1 * subscriber.messageCount == 1 }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>对于mock对象的断言请参考: <a href="http://jvm123.com/2019/09/spock-ce-shi-zhuang-2.html">http://jvm123.com/2019/09/spock-ce-shi-zhuang-2.html</a> </p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p> 以上是spock中mock测试桩的使用场景,总结为一句就是: <strong>mock测试桩给被测试方法模拟一个预期的效果</strong>。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>mock和stub测试桩的对比</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p><strong>mock测试桩用于检测结果</strong>。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p><strong>stub测试桩用于提供测试的条件。</strong></p> <!-- /wp:paragraph -->

分类:JVM测试javaspock框架groovy开发笔记
标签:spockspock测试框架spock教程spock课程测试桩spock测试stubspock测试桩单元测试mock

2019-09-06 15:28:21.0
Spock测试桩mock和stub的区别及使用场景  spock-ce-shi-zhuang-3

spock测试桩mock的使用

<!-- wp:paragraph --> <p>上一篇详细介绍了<a href="http://jvm123.com/2019/08/spock.html" target="_blank" rel="noreferrer noopener" aria-label="(在新窗口打开)">spock框架的基本使用</a>。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>mock测试桩 使用场景</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>mock测试桩的使用场景是:<strong>调用一个方法后,测试需要达到的效果</strong>。例如以下代码:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> def subscriber = Mock(Subscriber) // 1. 创建一个mock对象 def "should send messages subscriber"() { when: publisher.send("hello") // 2. publisher 发送一个“hello” then: 1 * subscriber.receive("hello") // 3. subscriber 接收到一个“hello” }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>上面测试代码测试的是publisher的send方法,如果send一个“hello”,我们期望subscriber收到一个“hello”。如果收不到、或收到其他字符串、或收到多次都不能通过测试。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>而这个subscriber是我们使用mock工厂创建的一个测试桩假对象,我们根本不需要知道recieve方法的实现,就可以完成这个测试。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>以上就是mock测试桩的一个主要使用场景。下面介绍一个使用实例。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>mock测试桩 使用实例</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>class MockSpec extends Specification { Publisher publisher = new Publisher() def subscriber = Mock(Subscriber) def subscriber2 = Mock(Subscriber) def setup() { publisher.subscribers &lt;&lt; subscriber // &lt;&lt; 相当于 list.add() publisher.subscribers &lt;&lt; subscriber2 } def "should send messages to all subscribers"() { when: publisher.send("hello") then: 1 * subscriber.receive("hello") 1 * subscriber2.receive("hello") } } class Publisher { List&lt;Subscriber> subscribers = [] int messageCount = 0 void send(String message){ // *. 分别调用list每个元素的receive方法 subscribers*.receive(message) messageCount++ } } interface Subscriber { void receive(String message) }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>以上代码测试的目的是:当调用 <code>publisher.send("hello")</code> 后,我们期望两个subscriber都会接收到一次“hello”。如果出现其他结果,测试就算失败。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>如果我们将上述测试代码修改:当发送“hello”后,我们期望接收到“hello1”,如下</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> def "should send messages to all subscribers"() { when: publisher.send("hello") then: 1 * subscriber.receive("hello1") 1 * subscriber2.receive("hello1") }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>这个测试很明显不会通过,将会出现以下结果:</p> <!-- /wp:paragraph --> <!-- wp:image {"id":1001} --> <figure class="wp-block-image"><img src="http://jvm123.com/wp-content/uploads/2019/09/mock.jpg" alt="" class="wp-image-1001"/></figure> <!-- /wp:image --> <!-- wp:paragraph --> <p>失败的原因是只调用了0次recieve("hello1")方法,即subscriber没有接收到“hello1”。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>mock then后面结果的断言</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>对于mock then后面结果的断言,有以下的格式:</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":3} --> <h3><strong>1 调用频率</strong>约束</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>1 * subscriber.receive("hello") // exactly one call 0 * subscriber.receive("hello") // zero calls (1..3) * subscriber.receive("hello") // between one and three calls (inclusive) (1.._) * subscriber.receive("hello") // at least one call (_..3) * subscriber.receive("hello") // at most three calls _ * subscriber.receive("hello") // any number of calls, including zero // (rarely needed; see 'Strict Mocking')</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>2 调用对象约束</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>1 * subscriber.receive("hello") // a call to 'subscriber' 1 * _.receive("hello") // a call to any mock object</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>3 调用方法约束</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>1 * subscriber.receive("hello") // a method named 'receive' 1 * subscriber./r.*e/("hello") // a method whose name matches the given regular expression // (here: method name starts with 'r' and ends in 'e')</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p><strong>其中getter setter方法的调用可以用如下格式:</strong></p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>When expecting a call to a getter method, Groovy property syntax&nbsp;<em>can</em>&nbsp;be used instead of method syntax:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>1 * subscriber.status // same as: 1 * subscriber.getStatus()</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>When expecting a call to a setter method, only method syntax can be used:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>1 * subscriber.setStatus("ok") // NOT: 1 * subscriber.status = "ok"</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>4 调用参数约束</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>1 * subscriber.receive("hello") // an argument that is equal to the String "hello" 1 * subscriber.receive(!"hello") // an argument that is unequal to the String "hello" 1 * subscriber.receive() // the empty argument list (would never match in our example) 1 * subscriber.receive(_) // any single argument (including null) 1 * subscriber.receive(*_) // any argument list (including the empty argument list) 1 * subscriber.receive(!null) // any non-null argument 1 * subscriber.receive(_ as String) // any non-null argument that is-a String 1 * subscriber.receive(endsWith("lo")) // any non-null argument that is-a String 1 * subscriber.receive({ it.size() > 3 &amp;&amp; it.contains('a') }) // an argument that satisfies the given predicate, meaning that // code argument constraints need to return true of false // depending on whether they match or not // (here: message length is greater than 3 and contains the character a)</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p><strong>其中多参数和可变参数的方法可用以下方式:</strong></p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>Argument constraints work as expected for methods with multiple arguments:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>1 * process.invoke("ls", "-a", _, !null, { ["abcdefghiklmnopqrstuwx1"].contains(it) })</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>When dealing with vararg methods, vararg syntax can also be used in the corresponding interactions:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>interface VarArgSubscriber { void receive(String... messages) } ... subscriber.receive("hello", "goodbye")</code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>5 任意方法调用</h3> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>1 * subscriber._(*_) // any method on subscriber, with any argument list 1 * subscriber._ // shortcut for and preferred over the above 1 * _._ // any method call on any mock object 1 * _ // shortcut for and preferred over the above</code></pre> <!-- /wp:code --> <!-- wp:quote --> <blockquote class="wp-block-quote"><p> Although <code>(<em>..</em>) * <em>.</em>(*_) >> _</code> is a valid interaction declaration, it is neither good style nor particularly useful. </p></blockquote> <!-- /wp:quote --> <!-- wp:paragraph --> <p></p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>下一篇文章介绍<a href="http://jvm123.com/2019/08/spock-springboot.html" target="_blank" rel="noreferrer noopener" aria-label="(在新窗口打开)">在spring环境中使用spock</a>。</p> <!-- /wp:paragraph -->

分类:JVMgroovy开发笔记java测试springspock框架
标签:groovyspock测试框架groovy语法spock课程spockspock测试stubspock测试桩spock教程mock

2019-09-06 11:32:50.0
spock测试桩mock的使用  spock-ce-shi-zhuang-2

apache ant内置任务

<!-- wp:paragraph --> <p>apache ant内置任务一般在build.xml中以xml标签的形式使用,例如:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>&lt;target name="build" description="打包" depends="compile"> &lt;delete dir="${output}" /> &lt;mkdir dir="${output}" /> &lt;jar destfile="${output}/app.jar" basedir="${classes}" includes="**/*.class"> &lt;manifest> &lt;attribute name="Main-Class" value="Program"/> &lt;attribute name="Class-Path" value="${classes}"/> &lt;attribute name="classpath" value="${classes}"/> &lt;/manifest> &lt;/jar> &lt;/target></code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>以上代码中,在target标签下,使用了内置任务:delete、mkdir、jar 三个内置任务。以下对常用的 ant内置任务令进行介绍:</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2><strong>mkdir</strong> - (ant内置任务)</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>用于创建一个目录,它有一个属性dir用来指定所创建的目录名,其属性如下:</p> <!-- /wp:paragraph --> <!-- wp:quote --> <blockquote class="wp-block-quote"><p>(1) dir:要创建的目录。可以是绝对路径,还是可以是相对于project的basedir的相对路径。</p><p>其代码如下:&lt;mkdir dir=”${class.root}”/&gt;</p></blockquote> <!-- /wp:quote --> <!-- wp:heading --> <h2><strong>jar</strong> - (ant内置任务) </h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>用来生成一个JAR文件,其属性如下:</p> <!-- /wp:paragraph --> <!-- wp:quote --> <blockquote class="wp-block-quote"><p>(1) destfile表示JAR文件名。</p><p>(2) basedir表示被归档的文件名。</p><p>(3) includes表示被归档的文件模式。</p><p>(4) excludes表示被排除的文件模式。</p></blockquote> <!-- /wp:quote --> <!-- wp:heading --> <h2><strong>javac</strong> - (ant内置任务) </h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>用于编译一个或一组java文件,其属性如下 : </p> <!-- /wp:paragraph --> <!-- wp:quote --> <blockquote class="wp-block-quote"><p>(1).srcdir表示源程序的目录。</p><p>(2).destdir表示class文件的输出目录。</p><p>(3).include表示被编译的文件的模式。</p><p>(4).excludes表示被排除的文件的模式。</p><p>(5).classpath表示所使用的类路径。</p><p>(6).debug表示包含的调试信息。</p><p>(7).optimize表示是否使用优化。</p><p>(8).verbose 表示提供详细的输出信息。</p><p>(9).fileonerror表示当碰到错误就自动停止。</p></blockquote> <!-- /wp:quote --> <!-- wp:heading --> <h2><strong>java</strong> - (ant内置任务) </h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>用来执行编译生成的.class文件,其属性如下 : </p> <!-- /wp:paragraph --> <!-- wp:quote --> <blockquote class="wp-block-quote"><p>(1).classname 表示将执行的类名。</p><p>(2).jar表示包含该类的JAR文件名。</p><p>(3).classpath所表示用到的类路径。</p><p>(4).fork表示在一个新的虚拟机中运行该类。</p><p>(5).failonerror表示当出现错误时自动停止。</p><p>(6).output 表示输出文件。</p><p>(7).append表示追加或者覆盖默认文件。</p></blockquote> <!-- /wp:quote --> <!-- wp:heading --> <h2><strong>delete</strong> - (ant内置任务) </h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>用于删除一个文件或一组文件,其属性如下 : </p> <!-- /wp:paragraph --> <!-- wp:quote --> <blockquote class="wp-block-quote"><p>(1).file表示要删除的文件。</p><p>(2).dir表示要删除的目录。</p><p>(3).includeEmptyDirs 表示指定是否要删除空目录,默认值是删除。</p><p>(4).failonerror 表示指定当碰到错误是否停止,默认值是自动停止。</p><p>(5).verbose表示指定是否列出所删除的文件,默认值为不列出。</p></blockquote> <!-- /wp:quote --> <!-- wp:heading --> <h2><strong>copy</strong> - (ant内置任务) </h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>用于文件或文件集的拷贝,其属性如下:</p> <!-- /wp:paragraph --> <!-- wp:quote --> <blockquote class="wp-block-quote"><p>(1).file 表示源文件。</p><p>(2).tofile 表示目标文件。</p><p>(3).todir 表示目标目录。</p><p>(4).overwrite 表示指定是否覆盖目标文件,默认值是不覆盖。</p><p>(5).includeEmptyDirs 表示指定是否拷贝空目录,默认值为拷贝。</p><p>(6).failonerror 表示指定如目标没有发现是否自动停止,默认值是停止。</p><p>(7).verbose 表示制定是否显示详细信息,默认值不显示。</p></blockquote> <!-- /wp:quote --> <!-- wp:heading --> <h2><strong>exec</strong> - (ant内置任务) </h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>执行文件,示例如下:</p> <!-- /wp:paragraph --> <!-- wp:quote --> <blockquote class="wp-block-quote"><p>&lt;exec executable="${base.dir}/email.bat" >&lt;/exec> </p></blockquote> <!-- /wp:quote -->

分类:JVM项目构建java开发笔记
标签:javaApache Antant项目依赖管理项目构建Ant内置任务

2019-09-04 16:44:44.0
apache ant内置任务  apache-ant-nei-zhi

windows端口占用、查看端口、结束进程

<!-- wp:paragraph --> <p>在日常开发过程中,我们常常遇到端口占用的问题,如果使用的是windows开发,则可以按照以下方法解决:</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>windows查看某个端口是否被占用</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>windows 查看某个端口是否被占用,可使用以下命令:</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>netstat -ano | findstr 8080</code></pre> <!-- /wp:code --> <!-- wp:image {"id":957,"align":"center"} --> <div class="wp-block-image"><figure class="aligncenter"><img src="http://jvm123.com/wp-content/uploads/2019/09/win-port.jpg" alt="" class="wp-image-957"/></figure></div> <!-- /wp:image --> <!-- wp:paragraph --> <p>如上图可以看到PID为17964的进程占用了监听者8080端口,所以只需要结束这个进程即可。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>windows结束进程</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>windows结束进程有两种方法:</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p><strong>1 使用命令行:</strong></p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>taskkill /pid 17964 /f</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>强制结束17964进程,如下:</p> <!-- /wp:paragraph --> <!-- wp:image {"id":959,"align":"center"} --> <div class="wp-block-image"><figure class="aligncenter"><img src="http://jvm123.com/wp-content/uploads/2019/09/win-port3.jpg" alt="" class="wp-image-959"/></figure></div> <!-- /wp:image --> <!-- wp:paragraph --> <p><strong>2 使用任务管理器</strong></p> <!-- /wp:paragraph --> <!-- wp:image {"id":958,"align":"center"} --> <div class="wp-block-image"><figure class="aligncenter"><img src="http://jvm123.com/wp-content/uploads/2019/09/win-port2.jpg" alt="" class="wp-image-958"/></figure></div> <!-- /wp:image --> <!-- wp:paragraph --> <p>在任务管理器,打开“ 详细信息 ”,点击“PID”就可以按照进程号排序,然后很容易找到17964进程,右键结束运行即可。</p> <!-- /wp:paragraph -->

分类:未分类服务器搭建开发笔记
标签:端口查看端口netstatwindows结束进程端口占用pid

2019-09-02 16:33:56.0
windows端口占用、查看端口、结束进程  windows-duan-kou

Spock测试桩stub的使用

<!-- wp:paragraph --> <p>上一篇详细介绍了<a href="/2019/08/spock.html" target="_blank" rel="noreferrer noopener" aria-label="(在新窗口打开)">spock框架的基本使用</a>。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2> stub测试桩 使用场景</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>when: int a = 12; int b = xxxService.squre(a); int c = xxxService.plus(a, b); then: c == 100</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p> 假如我们需要测试以上代码,但被告知squre方法目前还没开发完成,或者正在修改中,现在使用无法得到正确的结果。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>这时就可以用测试桩:<strong>给squre()方法造一个或多个假的返回值</strong>,让我们能够正常测试后面的plus()方法。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>测试桩<em>Stub与Mock</em>的具体解释在后面代码注释中做了说明。</p> <!-- /wp:paragraph --> <!-- wp:heading --> <h2>stub测试桩 使用实例</h2> <!-- /wp:heading --> <!-- wp:code --> <pre class="wp-block-code"><code>package com.yawn.spock import com.yawn.spock.service.CalculateInterface import com.yawn.spock.service.CalculateService import spock.lang.Specification /** * &lt;pre> * * Stub与Mock * (1)相同点 * Stub和Mock对象都是用来模拟外部依赖,使我们能控制。 * 如果被测程序、系统或对象,我们称之为A。在测试A的过程中, * A需要与程序、系统或对象B进行交互,那么Stub/Mock就是用来模拟B的行为来与A进行交互。 * (2)不同点 *   Stub,也即“桩”,很早就有这个说法了,主要出现在集成测试的过程中, * 从上往下的集成时,作为下方程序的替代。作用如其名,就是在需要时, * 能够发现它存在,即可。就好像点名,“到”即可。 *   Mock,主要是指某个程序的傀儡,也即一个虚假的程序, * 可以按照测试者的意愿做出响应,返回被测对象需要得到的信息。 * 也即是要风得风、要雨得雨、要返回什么值就返回什么值。 * *   总体来说,stub完全是模拟一个外部依赖,用来提供测试时所需要的测试数据。 * 而mock对象用来判断测试是否能通过,也就是用来验证测试中依赖对象间的交互能否达到预期。 * &lt;/pre> * @author yawn * 2019/6/10 14:52 */ class StubSpec extends Specification { def "Stub 测试桩"() { given: "构造测试桩" CalculateInterface calculateService = Stub(CalculateInterface) calculateService.plusPlus(_) >> 1 when: int x = calculateService.plusPlus(12) int y = calculateService.plusPlus(3) then: x == 1 y == 1 } def "Stub 测试桩得到不同的多个值"() { given: "构造测试桩" CalculateService calculateService = Stub(CalculateService) calculateService.plusPlus(_) >>> [1, 2, 3] when: int x = calculateService.plusPlus(12) int y = calculateService.plusPlus(3) int z = calculateService.plusPlus(2) then: x == 1 y == 2 z == 3 } }</code></pre> <!-- /wp:code --> <!-- wp:paragraph --> <p>使用Stub与Mock方法就可以得到对应的傀儡实例,我们可以对这个 傀儡 实例定义其方法的返回值。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>calculateService.plusPlus(_) &gt;&gt; 1 // 定义了一个返回值</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>calculateService.plusPlus(_) &gt;&gt;&gt; [1, 2, 3] // 定义了多个返回值,每次执行,按照顺序取一个作为返回值。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>下一篇文章介绍<a href="/2019/08/spock-springboot.html" target="_blank" rel="noreferrer noopener" aria-label="(在新窗口打开)">在spring环境中使用spock</a>。</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p></p> <!-- /wp:paragraph -->

分类:JVMgroovy开发笔记java测试springspock框架
标签:groovyspockspock测试框架mock测试junitspock教程spock课程测试框架spock测试桩stub

2019-08-17 01:25:59.0
Spock测试桩stub的使用  spock-ce-shi-zhuang

Mysql 的 bit 类型与二进制的应用

<!-- wp:heading --> <h2>bit类型</h2> <!-- /wp:heading --> <!-- wp:quote --> <blockquote class="wp-block-quote"><p>首先,bit 类型不是对应true和false,也不是只有一位,而是0和1组成的串,即存储二进制的序列</p></blockquote> <!-- /wp:quote --> <!-- wp:heading --> <h2>示例</h2> <!-- /wp:heading --> <!-- wp:heading {"level":3} --> <h3>1 新建数据表test </h3> <!-- /wp:heading --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">CREATE TABLE `test` ( `id` int(20) NOT NULL, `codes` bit(8) NOT NULL, `remark` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; // 其中 codes 字段为8位的bit类型</pre> <!-- /wp:preformatted --> <!-- wp:heading {"level":3} --> <h3>2 插入数据 </h3> <!-- /wp:heading --> <!-- wp:preformatted --> <pre class="wp-block-preformatted">delete from test; insert into test(id, codes, remark) values (1, 1, 'number 1'), (2, 2, 'number 2'), (3, '1', 'string 1'), (4, 'a', 'string a'), (5, true, 'boolean true'), (6, false, 'boolean false'); // 给 codes 字段查入不同类型的值:数字、字符、布尔</pre> <!-- /wp:preformatted --> <!-- wp:heading {"level":3} --> <h3>3 存储结果如下</h3> <!-- /wp:heading --> <!-- wp:image {"id":282,"align":"center"} --> <div class="wp-block-image"><figure class="aligncenter"><img src="http://www.jvm123.com/wp-content/uploads/2019/07/mysql-bit.jpg" alt="" class="wp-image-282"/></figure></div> <!-- /wp:image --> <!-- wp:heading --> <h2>结论</h2> <!-- /wp:heading --> <!-- wp:paragraph --> <p>通过上述示例,我们可以得到以下结论:</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>在bit类型的字段中,插入数据时:</p> <!-- /wp:paragraph --> <!-- wp:list --> <ul><li>数字被转换成二进制;</li><li>字符将被转换成二进制的ascii码;</li><li>boolean 类型被转换成0或1 。</li></ul> <!-- /wp:list --> <!-- wp:heading --> <h2>应用</h2> <!-- /wp:heading --> <!-- wp:heading {"level":3} --> <h3>1 用来表示多选字段</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p>某题目有以下多选项:苹果(1),梨(2),西瓜(4),香蕉(8),可以选中其中任意几项,并使用一个字段保存。我们可以使用一个bit类型的字段将多选项存在数据库中,如下:</p> <!-- /wp:paragraph --> <!-- wp:list {"ordered":true} --> <ol><li>分别使用数字表示选项代码:苹果(1),梨(2),西瓜(4),香蕉(8);</li><li>转换成二进制为:苹果(0001),梨(0010),西瓜(0100),香蕉(1000);</li><li>将选中的数字相加(或进行位或运算),在数据库中存成bit字段即可;</li><li>读取结果时,将保存的字段与选项的代码进行位与运算即可;</li><li>其实就是规定了长度位4的bit类型,每一位表示一个选项,若选中位1,未选中为0,表示非常直观。</li></ol> <!-- /wp:list --> <!-- wp:heading {"level":3} --> <h3>2 Linux中的权限表示</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p>在Linux中,权限的表示也使用了这种方式:</p> <!-- /wp:paragraph --> <!-- wp:paragraph --> <p>linux的权限rwx表示可读、可写、可执行,三种权限有8种组合方式,熟悉二进制的人常常使用0~7表示权限的组合:</p> <!-- /wp:paragraph --> <!-- wp:table --> <table class="wp-block-table"><tbody><tr><td>标志</td><td>名称</td><td>十进制</td><td>二进制</td></tr><tr><td>r</td><td> 可读 </td><td>4</td><td>100</td></tr><tr><td>w</td><td> 可写 </td><td>2</td><td>010</td></tr><tr><td>x</td><td> 可执行 </td><td>1</td><td>001</td></tr></tbody></table> <!-- /wp:table --> <!-- wp:paragraph --> <p>例如: <code>chmod 777 start.sh</code> 表示给所有用户对文件 start.sh 添加了rwx的权限。</p> <!-- /wp:paragraph -->

分类:服务器搭建数据库开发笔记
标签:数据库二进制服务器多选mysqlbit类型

2019-07-30 13:52:52.0
Mysql 的 bit 类型与二进制的应用  mysql-bit