Kotlin 单例模式详解

2019-07-08

单例模式很熟悉了,在Java中有各种创建姿势,算是比较麻烦的,这里就不再赘述了。

Object

那么如何在kotlin中实现单例模式呢?请看代码

object JsonObjectMapper {
}

仅需简单的把class关键字替换为object就完成了!

// Kotlin 调用
JsonObjectMapper

// Java调用
JsonObjectMapper.INSTANCE

究竟Kotlin封装了什么细节?让我们一探究竟吧:

阅读全文...

Quartz失火指令、补偿执行机制配置

2019-06-28

处理规则

调度(scheduleJob)或恢复调度(resumeTrigger,resumeJob)后不同的misfire对应的处理规则如下:

CronTrigger

  • withMisfireHandlingInstructionDoNothing
    • 不触发立即执行
    • 等待下次Cron触发频率到达时刻开始按照Cron频率依次执行
  • withMisfireHandlingInstructionIgnoreMisfires
    • 以错过的第一个频率时间立刻开始执行
    • 重做错过的所有频率周期后
    • 当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行
  • withMisfireHandlingInstructionFireAndProceed
    • 以当前时间为触发频率立刻触发一次执行
    • 然后按照Cron频率依次执行
阅读全文...

解决HttpServletRequest.inputStream复用问题

2019-06-23

众所周知,request对象中的inputStream只能读取一次,下次再读就没有了,在一些场景中,这种特性是不适用的:比如需要在拦截器中读取请求体,然后做相关的参数校验,做完校验后请求打到Controller中就读取不到了。

解决的方法很简单,通过HttpServletRequestWrapperHttpServletRequest对象包装一下,保存requestBody的副本,最后通过过滤器将包装后的request对象替换掉。

附完整代码:

/**
 * 包装HttpServletRequest,使其inputStream可复用。
 * @author wuwenze
 * @date 2019-06-21
 */
@Configuration
@Order(1)
@WebFilter(urlPatterns = ["/**"])
class HttpServletRequestWrapperFilter : Filter {
    override fun doFilter(req: ServletRequest?, resp: ServletResponse?, chain: FilterChain?) {
        when (req) {
            is HttpServletRequest -> chain?.doFilter(MyHttpServletRequestWrapper(req), resp)
            else -> chain?.doFilter(req, resp)
        }
    }

    class MyHttpServletRequestWrapper(request: HttpServletRequest) : HttpServletRequestWrapper(request) {
        private var body: ByteArray = request.getBodyString().toByteArray(Charset.forName("UTF-8"))

        @Throws(IOException::class)
        override fun getReader(): BufferedReader {
            return BufferedReader(InputStreamReader(inputStream))
        }

        @Throws(IOException::class)
        override fun getInputStream(): ServletInputStream {
            val byteArrayInputStream = ByteArrayInputStream(body)
            return object : ServletInputStream() {

                @Throws(IOException::class)
                override fun read(): Int {
                    return byteArrayInputStream.read()
                }

                override fun isFinished(): Boolean {
                    return false
                }

                override fun isReady(): Boolean {
                    return false
                }

                override fun setReadListener(readListener: ReadListener) {

                }
            }
        }
    }
}

获取requestBody的扩展函数:

fun HttpServletRequest.getBodyString(): String {
    val result = StringBuffer()
    var reader: BufferedReader? = null
    try {
        var line: String? = null
        reader = BufferedReader(InputStreamReader(this.inputStream, Charset.forName("UTF-8")))
        while ({ line = reader.readLine();line }() != null) {
            result.append(line)
        }
    } catch (e: IOException) {
        // ignore
    } finally {
        reader?.close()
    }
    return result.toString()
}

这样就能多次使用了,每次读取的其实是存储在MyHttpServletRequestWrapper中的副本。

阅读全文...

MockMvc WebFilter不生效问题解决

2019-06-23

在SpringBoot项目中,配置了一个@WebFilter,正常启动没问题,但是通过MockMvc进行单元测试死活不生效

@Configuration
@Order(1)
@WebFilter(urlPatterns = ["/**"])
class HttpServletRequestWrapperFilter : Filter {
    override fun doFilter(req: ServletRequest?, resp: ServletResponse?, chain: FilterChain?) {
        when (req) {
            is HttpServletRequest -> chain?.doFilter(MyHttpServletRequestWrapper(req), resp)
            else -> chain?.doFilter(req, resp)
        }
    }
}

后来搞了半天,原来在构建MockMvc对象时,需要手动添加过滤器,这坑爹的玩意儿。

@Before
fun setUp() {
    mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
        .addFilters<DefaultMockMvcBuilder>(HttpServletRequestWrapperFilter())
        .build()
}
阅读全文...

JMeter以非GUI模式执行压力测试

2019-06-13

为什么要使用非GUI模式执行压力测试呢?

  1. 图形化界面消耗更多资源,CPU和内存
  2. 图形化界面不支持大型的负载测试和性能测试
  3. 命令行测试支持持续集成,例如放到Jenkins这样的CI工具上

相关参数

-h:帮助,打印出有用的信息并退出 -n:以非GUI形式运行Jmeter -t:Jmeter脚本路径 -l:输出结果路径,如果没有该文件就自动创建,可以生成csv或者jtl文件 -r:远程执行,启动远程服务 -H:代理主机,设置Jmeter使用的代理主机 -P:代理端口,设置Jmeter使用的代理主机的端口号 -e:在脚本运行结束后生成html报告 -o:保存html报告的地址,此文件夹中必须为空 -J:传递动态参数搭配, 在脚本中需要配合__P函数取值,如:-Jthreads=100 脚本取值为: ${__P(threads,1)}

阅读全文...

UI.Vision Kantu-基于Selenium的浏览器自动化工具

2019-06-06

之前简单介绍过Selenium框架在Java中的简单应用(查看文章),用来做基于webDriver的自动化测试非常方便。但是通过硬编码的方式虽然简单,但是也还是要写不少的代码的,今天发现了一款基于Selenium的浏览器自动化插件,支持录制和回放,完全可以将网页中的一些重复的操作录制下来,解放双手。

下载地址

https://chrome.google.com/webstore/detail/uivision-kantu-for-chrome/gcbalfbdmfieckjlnblleoemohcganoc?hl=zh-CN

使用案例:批量添加数据

1) 新建宏指令

在需要操作的页面,打开UI.Vision Kantu然后新建宏指令 image.png

阅读全文...

MySQL7新特性之JSON数据类型

2019-05-28

MySQL 5.7.8开始,原生提供了一个JSON类型的数据格式,在此之前类似的需求都是需要通过VARCHAR的方式来存储处理的。

  • JSON数据类型,拥有自动校验格式功能;
  • 提供操作JSON数据的内置函数;
  • 优化的存储格式,存储在JSON列中的JSON数据被转换成内部的存储格式,允许快速读取;
  • 支持修改JSON对象的特定属性,而不需要更新整个JSON内容;
  • 支持创建JSON对象的特定属性索引;

创建表

CREATE TABLE user
(
    id       INT AUTO_INCREMENT,
    username VARCHAR(12) NOT NULL COMMENT '用户名',
    password VARCHAR(32) NOT NULL COMMENT '密码',
    extends  JSON        NULL COMMENT '扩展信息',
    CONSTRAINT user_pk
        PRIMARY KEY (id)
)
    COMMENT '用户表';

CREATE UNIQUE INDEX user_username_uindex
    ON user (username);

直接指定字段的类型为JSON即可。

阅读全文...

Canal数据同步中间件初探

2019-05-21

MySQL本身是支持主从模式(Master/Slave)的,Master产生的日志(binary log)中记录了所有增删改语句,将日志发送到Slave执行即可完成数据库的增量数据同步操作。

Canal是阿里巴巴开源的一个中间件,他的作用就是解析binary log来完成数据同步的。 源码地址:https://github.com/alibaba/canal

Canal工作原理

image.png

  1. canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议
  2. mysql master收到dump请求,开始推送binary logslave(也就是canal)
  3. canal解析binary log对象(原始为byte流)
阅读全文...