长链接转短链接

应用场景:之前手机上收到的垃圾短信都是一大串的连接;现在收到的基本上都是很短的连接
* 大部分长连接转短连接都是采用新浪微博平台的开放接口;下方实际也是采用微博提供的接口
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>生成短链接</title>
</head>
<!-- <script src="jquery-2.1.1.min.js"></script> -->
<body>
    <input type="text" name="" id="url">
    <button id="get_short">生成</button>
    <a id="myDiv">点我</a>
</body>
<script type="text/javascript">
 
    // 绑定按钮事件
    btn_getshort = document.getElementById('get_short');
    btn_getshort.onclick = function (){
        var source = '3271760578';
        var url = document.getElementById('url').value;
        var all = 'http://api.t.sina.com.cn/short_url/shorten.json?source=' + source
        +'&url_long='+ url;
        document.getElementById("myDiv").href = all;
    }
</script>
</html>

微博长链转短链的文档

Spring切面处理日志

业务场景:在web服务中添加日志,要求在刚进入方法和结束方法的时候打印开始和结束日志;进入方法好说,直接在第一行打印即可,但是return的时候可能会有很多分支,所以我们必须要在更上一层进行处理

实现步骤:
1.创建TestRequestBodyAdvice并实现RequestBodyAdvice接口,类上添加注解@ControllerAdvice
2.创建TestResponseBodyAdvice并实现ResponseBodyAdvice接口,类上添加注解@ControllerAdvice
3.修改其中的默认返回值;例如null改为对应值,false改为true(这个不要盲目的改)
4.引入logger类
5.在关键位置写入要打印的日志
具体代码如下:

@ControllerAdvice
public class TestRequestBodyAdvice implements RequestBodyAdvice {

    private Logger logger= LoggerFactory.getLogger(TestRequestBodyAdvice.class);

    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        return httpInputMessage;
    }

    public Object afterBodyRead(Object object, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        logger.debug("即将进入{}方法",methodParameter.getMethod().getName());
        return object;
    }

    public Object handleEmptyBody(Object object, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return object;
    }
}




@ControllerAdvice
public class TestResponseBodyAdvice implements ResponseBodyAdvice {

    private Logger logger= LoggerFactory.getLogger(TestResponseBodyAdvice.class);

    public boolean supports(MethodParameter methodParameter, Class aClass) {
        if (aClass.isAssignableFrom(MappingJackson2CborHttpMessageConverter.class)){
            return true;
        }
        return false;
    }

    public Object beforeBodyWrite(Object object, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        logger.debug("即将进入{}方法",methodParameter.getMethod().getName());
        return object;
    }
}
* 以上两个类并非只是打印日志的作用,他可以在所有请求的进入和返回时刻进行处理,例如进行简单的参数校验,或者查询为空时在此处理给前端一个默认返回值等

* 以上代码采用了适配器模式,通过supports函数来判断是否进入下面的函数进行逻辑处理

Java8特性Stream中map与forEach的区别

//Map接口的定义
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
//forEach接口的定义
void forEach(Consumer<? super T> action);

通过接口定义可以看出,两者最明显的区别就是map有返回值,forEach没有返回值

所以在具体调用时,map调用时可以return一个对象到外部,如下: read more

Oracle over函数的简单使用

OVER() 在使用时一般会配合其他函数一起使用
1.row_number() OVER (PARTITION BY COL1 ORDER BY COL2)
表示根据COL1分组,在分组内部根据 COL2排序,而此函数计算的值就表示每组内部排序后的顺序编号
2.rank() OVER (PARTITION BY COL1 ORDER BY COL2)
跳跃排序,有两个第二名时接下来就是第四名(同样是在各个分组内)
3.dense_rank() OVER (PARTITION BY COL1 ORDER BY COL2)
连续排序,有两个第二名时仍然跟着第三名。相比之下row_number是没有重复值的
4.lag(arg1,arg2,arg3)
arg1是从其他行返回的表达式;arg2是希望检索的当前行分区的偏移量;arg3是在arg2表示的数目超出了分组的范围时返回的值.
5.FIRST_VALUE(arg) OVER (PARTITION BY COL1 ORDER BY COL2)
返回组中数据窗口的第一个值,可以不进行order by
6.LAST_VALUE(arg) OVER (PARTITION BY COL1 ORDER BY COL2)
返回组中数据窗口的最后一个值,可以不进行order by

现在拿row_number举例: read more

获取MyBatis执行SQL

工具类实现代码片段

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    public String showSql(Class<?> cls,String methodName,Object param) {
        String absoluteMetgodName=getAbsoluteMetgodName(cls,methodName);
        MappedStatement mappedStatement=sqlSessionFactory.getConfiguration().getMappedStatement(absoluteMetgodName);
        Configuration configuration=mappedStatement.getConfiguration();
        BoundSql boundSql=mappedStatement.getBoundSql(param);
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        //CollectionUtils如无对应pom引用可换其他方法,仅判空用
        if (CollectionUtils.isNotEmpty(parameterMappings)&& Objects.nonNull(parameterObject)) {
            TypeHandlerRegistry typeHandlerRegistry=configuration.getTypeHandlerRegistry();
            MetaObject metaObject=configuration.newMetaObject(parameterObject);

            //object取值判断逻辑参考mybatis源码ParameterHandler.setParameters()
            Object[] values=parameterMappings.stream().map(it->{
                String property=it.getProperty();
                Object result;
                if (boundSql.hasAdditionalParameter(property)){
                    result=boundSql.getAdditionalParameter(property);
                }else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())){
                    result=parameterObject;
                }else{
                    result=metaObject.getValue(property);
                }
                //Object转String并拼接进Sql的逻辑可根据自己业务修改或补充
                return getParameterValue(result);
            }).toArray();
            sql=String.format(sql.replace("?","%s"),values);
        }
        return sql;
    }

    private String getAbsoluteMetgodName(Class<?> cls,String mothodName){
        return cls.getCanonicalName()+"."+mothodName;
    }

    private String getParameterValue(Object obj) {
        String value = null;
        if (obj instanceof String) {
            value = "'" + obj.toString() + "'";
        } else if (obj instanceof Date) {
            SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            value="to_date('"+sdf.format(obj)+"','yyyy-mm-dd hh24:mi:ss')";
        } else {
            if (obj != null) {
                value = obj.toString();
            } else {
                value = "\'\'";
            }

        }
        return value;
    }

调用代码片段
xxx.class为对应mapper的文件名 read more

采用java8Stream对集合进行处理

//测试数据
        List<Map<Integer,Object>> list=new ArrayList<>();
        list.add((Map<Integer, Object>) new HashMap<>().put(222,"222"));
        list.add((Map<Integer, Object>) new HashMap<>().put(111,"111"));
        //筛选数据
        List<Map<Integer,Object>> a=list.stream().filter(info -> info.get("key").equals("123")).collect(Collectors.toList());
        //排序 针对String排序系统给出了更好的解决方案,如list中为自定义对象可通过以下方法进行排序
        list=list.stream().sorted(Comparator.comparing(Map::keySet)).collect(Collectors.toList());//升序
        list=list.stream().sorted(Comparator.comparing(Map::keySet).reversed()).collect(Collectors.toList());//降序
        //遍历集合进行操作,{}内可写入大量代码块
        list.forEach((Map<Integer, Object> info)->{info.put(333,"333");});
        //筛选出来的数据条数
        System.out.println(list.stream().filter(info->info.equals("123")).count());
* 当前只列举出了本人近期使用频率较高的几种语法,如无特殊的业务逻辑,以上几种的组合使用已经可以在一定程度上减少大量的开发代码
* 后续如遇到其他使用频率较高的写法会持续更新进来

采用java8新特性实现自定义lambda编程

* lambda语法 Lambda允许把函数作为一个方法的参数传递进方法中
* 适用场景:在一个大块的共用逻辑中间,穿插一块非共用的逻辑,在java8之前也可以采用传入某个字段在适当位置通过switch语句进行区分
* lambda可以应用到很多的业务场景中,本文只简要说明本人所遇到的业务场景
* 代码换环境执行一定要注意jdk的版本是否大于等于1.8
import org.junit.Test;

/**
 * @ClassName LambdaTest
 * @Author justin.Sun
 * @Date 2019/2/18 10:18
 **/
public class LambdaTest {

    //声明接口
    interface  Executor{
        String execute(String arg);
    }

    //调用时需要传入代码块的方法
    private Object runExecute(Executor executor,String arg){
        // TODO: 2019/2/18 公用代码块...

        //传入arg参数并执行代码块
        return executor.execute(arg);
    }

    @Test
    public void test(){
        //传入参数
        String param="test";
        //{}中的代码块相当于实现了上面定义的接口中的方法
        System.out.println(runExecute((String arg)->{return getResult(arg);},param));
    }

    //传入的代码块
    public String getResult(String arg){
        String result=arg;
        // TODO: 2019/2/18 逻辑块...
        return result;
    }
}
* 对于自定义lambda本人也尚处于初步阶段,上文仅用于记录,如有不正之处还请指正

实现一键部署Java服务

说一键部署有些夸张了,本文主要描述是通过linux脚本启动SpringBoot服务的一些细节
#!/bin/bash echo "---shutdown---" app_name=service-$1 dir=/A/B pid=`ps -ef | grep $app_name.jar | grep -v "grep" | awk '{print $2}'` if [ -n "$pid" ];then echo "kill -9的pid:" $pid kill -9 $pid fi echo "---upload---" cd $dir/$app_name rm -rf $dir/$app_name/$app_name.jar rz echo "---startup---" nohup java -Dactive=dev -Deureka.url=http://localhost:0001/eureka/ -jar $dir/$app_name/$app_name.jar >$dir/$app_name/nohup.out & tail -f $dir/$app_name/nohup.out

每行脚本对应的中文翻译:
1:指此脚本使用/bin/bash来解释执行。
2:输出字符串
3:声明变量为service-加上传入的第一个参数
4:声明绝对路径
5:“里面的内容表示参数输入;
管道符“|”用来隔开两个命令,管道符左边命令的输出会作为管道符右边命令的输入;
grep 是查找含有指定文本行的意思;
grep -v 是反向查找的意思,比如 grep -v grep 就是查找不含有 grep 字段的行;
awk是行处理器,awk ‘{print $2}’的意思是以空格作为分隔符,打印第二个字段;
第五行的意思就是:先查看app_name.jar包相关的信息并排除”grep”然后取第二个字段也是就是pid码

6:pid不为空则进入if,否则不进入
7:打印信息
8:杀掉进程
9:if结束符 read more

记一个BUG

在一次发包后,没有修改接口A的任何代码,但是接口A却不能正常返回数据,
INFO级别日志一切正常,后换为DEBUG级别日志,看到一些信息:
Could not find result map java.lang.String
于是全局扫描ResultMap标签,发现的确有一处代码写错了,位置就在A接口的下边,是一段正处于开发阶段的代码
错误的原因就是我在ResultMap中写了java.lang.String,改成ResultType后正常

造成错误的原因:
1.没有通过纯净分支发布服务,因为我所开发的服务一般都是由我发布的,所以一般情况下都是在我本地的分支上发布的,正在开发的时候需要发布的情况是会经常出现的;
2.不了解Mybatis的加载机制,没有很明确的了解到与A接口无关的代码也会影响到A接口的运行 read more