新手编码指北(持续更新...)

news/2024/7/5 5:06:16

新手编码指北

    • 常用代码
      • Json转换
      • 获取当前时间
      • 计时器
      • 判空相关
      • 字符串aes加解密工具类
    • 编码技巧
      • 事务提交后再处理某些方法
      • 某个方法新加参数时,写重载方法
      • 处理同一类型业务,具体逻辑有差别的代码:策略模式变种

本文主要记录了一些常用的Java编码中使用到的类或方法,或编码的小技巧,以期望写出更容易,更安全,更优美的代码。本文中的内容是本人工作中总结和记录,不一定适合所有人,也不一定是最优的,欢迎大家共同讨论学习进步。
本文会持续更新…

常用代码

此部分记录一些编码中常用的接口或者方法,在开发中可以复制粘贴直接使用,或简单的修改可以直接使用。

Json转换

public class DemoApplication {

    @Getter
    @Setter
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Range implements Serializable {
        private Integer min;
        private Integer max;
        private List<Integer> enums;
        private Integer length;

        @Override
        public String toString() {
            return "Range{" +
                    "min=" + min +
                    ", max=" + max +
                    ", enums=" + enums +
                    ", length=" + length +
                    '}';
        }
    }

    public static void main(String[] args) throws Exception {

        List<Integer> e = new ArrayList<Integer>(Arrays.asList(0, 1, 2, 3));
        Range range = Range.builder().max(100).min(0).length(999).enums(e).build();

        // 类转json
        String toJson = JSON.toJSONString(range);
        System.out.println(toJson);
        // json转类
        Range object = JSON.parseObject(toJson, Range.class);
        System.out.println(object);
    }   
}

输出结果

{"enums":[0,1,2,3],"length":999,"max":100,"min":0}

Range{min=0, max=100, enums=[0, 1, 2, 3], length=999}

获取当前时间

Long time = System.currentTimeMillis();

计时器

有时会需要计算某个方法或某些代码执行了多少毫秒,可以使用org.springframework.util.StopWatch

public void receive(String data){
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // 执行代码
    stopWatch.stop();
    long timeMillis = stopWatch.getTotalTimeMillis();
    System.out.println(timeMillis);
}

判空相关

// javax.validation.constraints.NotNull  @NotNull 判断接口入参是否为空的注解
@NotNull
private Integer type;

// javax.validation.constraints.NotEmpty @NotEmpty 判断接口入参的集合是否为空的注解
@NotEmpty
List<String> codes;

// java.util.Optional 如果type为空就赋值orElse里的值,相当于if(为空){赋值} 的逻辑的简写
Optional.ofNullable(type).orElse(0)

// org.apache.commons.collections4.CollectionUtils 判断集合是否为空的工具类
CollectionUtils.isNotEmpty(collect)

// org.apache.commons.lang3.StringUtils 判断字符串是否为空或空串的工具类
StringUtils.isNotBlank(name)

字符串aes加解密工具类

public class TransformUtil {

    private final static String AES_KEY = "abcdefghijklmnop";

    private final static String AES_IV = "0123456789012345";

    /**
     * AES对称加密
     */
    public static String aesEncryptStr(String content) {
        AES aes = new AES(Mode.OFB, Padding.PKCS5Padding, AES_KEY.getBytes(), AES_IV.getBytes());
        return aes.encryptHex(content);
    }

    /**
     * AES对称解密
     */
    public static String aesDecryptStr(String content) {
        AES aes = new AES(Mode.OFB, Padding.PKCS5Padding, AES_KEY.getBytes(), AES_IV.getBytes());
        return aes.decryptStr(content);
    }
}

使用:

public static void main(String[] args) throws Exception {

    String test = "Hello world!";
    String encrypt= TransformUtil.aesEncryptStr(test);
    String decrypt= TransformUtil.aesDecryptStr(encrypt);
    System.out.println("原字符串:" + test + "\n加密后:" + encrypt + "\n解密后:" + decrypt);
}

输出:

原字符串:Hello world!
加密后:0ac139cbb73c29c07d459e53d822986b
解密后:Hello world!

编码技巧

事务提交后再处理某些方法

有些时候,可能在增删改之后,需要执行某些方法,处理某些逻辑,比如新注册了一个用户,在用户信息入库后,可能需要执行再赠送该用户3个月会员的业务逻辑,而在赠送的接口里,可能需要去查库拿到用户信息,此时,如果刚刚用户信息入库的事务还未提交,这个时候很可能查不到该用户,导致无法赠送会员,产生业务上的bug。
这个时候我们需要先保证前面插入数据的事务提交了,再执行后续的方法,下面这个单例类可以打到此目的:在执行一个方法时判断当前是否有事务,如果没有则立即执行,如果有则事务提交完后执行。

单例类TransactionSupport,采用枚举方式实现单例

public enum TransactionSupport {
    INSTANCE;
    public TransactionSupport getInstance(){
        return INSTANCE;
    }
    private TransactionAfterCommitExecutor afterCommitExecutor
            = new TransactionAfterCommitExecutor();

    public void addJobAfterTransaction(Runnable runnable) {
        afterCommitExecutor.execute(runnable);
    }

    public void addJobAfterTransaction(List<Runnable> runnable) {
        if (CollectionUtil.isEmpty(runnable)) {
            return;
        }
        runnable.forEach(r -> afterCommitExecutor.execute(r));
    }
}

执行的线程池:

public class TransactionAfterCommitExecutor extends ThreadPoolExecutor {

    public TransactionAfterCommitExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public TransactionAfterCommitExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public TransactionAfterCommitExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public TransactionAfterCommitExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    public TransactionAfterCommitExecutor() {
        this(
                10, 500,
                500L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(1024),
                new ThreadFactoryBuilder().setNameFormat("transaction-after-commit-executor-pool-%d").build(),
                new DiscardOldestPolicy());
    }

    private ThreadLocal<List<Runnable>> currentRunables = ThreadLocal.withInitial(() -> new ArrayList<>(5));

    private ThreadLocal<Boolean> registed = ThreadLocal.withInitial(() -> false);

    @Override
    public void execute(final Runnable runnable) {
        //如果事务同步未启用则认为事务已经提交,马上进行异步处理
        if (!TransactionSynchronizationManager.isSynchronizationActive()) {
            System.out.println("no transaction");
            super.execute(runnable);
        } else {
            //同一个事务的合并到一起处理
            currentRunables.get().add(runnable);
            //如果存在事务则在事务结束后异步处理
            if (!registed.get()) {
                TransactionSynchronizationManager.registerSynchronization(new AfterCommitTransactionSynchronizationAdapter());
                registed.set(true);
            }
        }
    }


    private class AfterCommitTransactionSynchronizationAdapter extends TransactionSynchronizationAdapter {
        @Override
        public void afterCompletion(int status) {
            final List<Runnable> txRunables = new ArrayList<>(currentRunables.get());
            currentRunables.remove();
            registed.remove();
            if (status == STATUS_COMMITTED) {
                TransactionAfterCommitExecutor.super.execute(() -> {
                    for (Runnable runnable : txRunables) {
                        try {
                            System.out.println("after transaction");
                            runnable.run();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    }

}

关于TransactionSynchronizationManager的具体用法,可以学习这篇文章:TransactionSynchronizationManager事务同步器的使用
这里简单介绍一下代码逻辑:
execute()方法中,先调用TransactionSynchronizationManager.isSynchronizationActive()判断是否有事务,有事务则调用TransactionSynchronizationManager的registerSynchronization方法注入一个TransactionSynchronization接口实例,通常是使用TransactionSynchronizationAdapter适配器类,重写其afterCommit()方法,实现在事务提交后执行对应的逻辑。

使用和测试,随便写一个service方法,里面调用dao层,存储数据,在方法加上事务注解,在service层方法最后使用该工具执行其他方法,看输出打印。

    @Transactional
    public void updateMsgConfig(String account) {
        // 存储一个用户信息,简写了此处
        configInfoRepository.saveAndFlush();
        
        // 事务提交后打印haha!!!
        TransactionSupport.INSTANCE.addJobAfterTransaction(()->{
            System.out.println("haha!!!");
        });
    }

结果输出,证明是事务提交后,执行的打印。

after transaction
haha!!!

某个方法新加参数时,写重载方法

有业务场景如下:比如原来有一个老接口func处理一些固定逻辑,而现在新增了一个接口,其实是func里面大部分逻辑相同,只有少部分需要走新逻辑,代码大致如下:

void func(String name, String age){
    // 前置校验name和age是否合法
    // 中间存入库表中
    // 结尾处理会员赠送逻辑
}

// 调用的地方
userService.func("abc", 18);

现在要新增一种送会员方式,比如web登录用户还是按照原来规则赠送,app用户登录按照新的赠送方式。
分析:实际上整个func方法中,只有最后处理会员赠送的逻辑需要判断一下按哪种方式赠送,前面都是一样的逻辑
不好的处理方式:(仅个人看法,觉得不够简洁优美)

  • CV一个func1,只修改最后的逻辑
void func(String name, String age){
    // 前置校验name和age是否合法
    // 中间存入库表中
    // 老逻辑:结尾处理会员赠送逻辑
}
void func1(String name, String age){
    // 前置校验name和age是否合法
    // 中间存入库表中
    // 新逻辑:结尾处理会员赠送逻辑
}

// 调用的地方
userService.func("abc", 18);

userService.func1("abc", 18);
  • CV一个func1,把公共的部分提炼出一个方法,func和func1都调用公共的方法,最后写各自的逻辑
void checkAndSave(String name, String age){
    // 前置校验name和age是否合法
    // 中间存入库表中
}

void func(String name, String age){
    checkAndSave(name, age);
    // 老逻辑:结尾处理会员赠送逻辑
}
void func1(String name, String age){
    checkAndSave(name, age);
    // 新逻辑:结尾处理会员赠送逻辑
}

// 调用的地方
userService.func("abc", 18);

userService.func1("abc", 18);

好的处理方式:重载该方法,增加同名方法,加一个参数,此处举例使用布尔类型,也可以加枚举实现。老的方法调用新的重载方法,并传入指定参数。相当于在老方法外包了一层。代码大致如下:

void func(String name, String age){
    // 调用另一个重载方法
    this.func(name, age, false);
}

void func(String name, String age, boolean isApp){
    // 前置校验name和age是否合法
    // 中间存入库表中
    if(isApp){
        // 新逻辑处理会员赠送逻辑
        return;
    }
    // 老逻辑处理会员赠送逻辑
}

// 调用处,老的地方不变
userService.func("abc", 18);
// 新的地方
userService.func("abc", 18, true);

处理同一类型业务,具体逻辑有差别的代码:策略模式变种

有些业务场景,需要处理同一类业务,但是因为类型不同,各自有各自处理的逻辑,此时可以采用策略模式,有一个共同基类,子类继承基类,实现父类的方法,完善自己的业务处理逻辑。
传统的策略模式,在外层使用时一般会采用switch case的方式,根据不同类型,new不同子类,获取实例,调用父类方法进行逻辑处理。
关于策略模式,可以参考该文章简单了解:Java 策略模式 模板方法模式 状态模式

改进:通过spring的自动注入,把不同的类型放入到map中,取代switch case,这样后续新增一个子类,只需要新增这个子类,无需修改已有代码

代码示例如下:
接口和子类如下:

// 接口,定义类型和具体需要子类重写的方法
public interface VIPHandler {
    String getType();

    void process(String data);
}
// 具体实现的子类,注明自己的类型,实现自己的逻辑
@Component
public class SilverVIPHandler implements VIPHandler{
    @Override
    public String getType() {
        return VIPTypeEnum.SILVER.getDesc();
    }

    @Override
    public void process(String data) {
        // 转换成具体的类
        //User user = JSON.parseObject(data, User.class);
        // 执行白银会员处理相应的逻辑
        System.out.println("SilverVIPHandler processing...");
    }
}
// 具体实现的子类,注明自己的类型,实现自己的逻辑
@Component
public class BronzeVIPHandler implements VIPHandler{
    @Override
    public String getType() {
        return VIPTypeEnum.BRONZE.getDesc();
    }

    @Override
    public void process(String data) {
        // 转换成具体的类
        //User user = JSON.parseObject(data, User.class);
        // 执行青铜会员处理相应的逻辑
        System.out.println("BronzeVIPHandler processing...");
    }
}
// 具体实现的子类,注明自己的类型,实现自己的逻辑
@Component
public class GoldVIPHandler implements VIPHandler{
    @Override
    public String getType() {
        return VIPTypeEnum.GOLD.getDesc();
    }

    @Override
    public void process(String data) {
        // 转换成具体的类
        //User user = JSON.parseObject(data, User.class);
        // 执行黄金会员处理相应的逻辑
        System.out.println("GoldVIPHandler processing...");
    }
}

使用handler的service中:

public interface VIPService {
    void dealVIPData(String type, String data);
}
@Service
public class VIPServiceImpl implements VIPService {

    // key:handler的类型 value:handler实例
    private static final Map<String, VIPHandler> routerMap = Maps.newHashMap();

    @Resource
    private List<VIPHandler> vipHandlers;

    // 初始化map
    @PostConstruct
    public void init() {
        for (VIPHandler handler : vipHandlers) {
            routerMap.put(handler.getType(), handler);
        }
    }
    
    @Override
    public void dealVIPData(String type, String data) {
        VIPHandler handler = routerMap.get(type);
        if(ObjectUtil.isNotNull(handler)){
            handler.process(data);
        }
    }
}

插曲:关于@PostConstruct

Java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。

通常我们会是在Spring框架中使用到@PostConstruct注解 该注解的方法在整个Bean初始化中的执行顺序:
Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)

进行测试,controller中调用service层方法:

    @Resource
    private VIPService vipService;

    @GetMapping(value = "/v1/handler/test")
    public void testHandler(){
        vipService.dealVIPData(VIPTypeEnum.BRONZE.getDesc(), "abc");
        vipService.dealVIPData(VIPTypeEnum.SILVER.getDesc(), "abc");
        vipService.dealVIPData(VIPTypeEnum.GOLD.getDesc(), "abc");
    }

输出结果:

BronzeVIPHandler processing...
SilverVIPHandler processing...
GoldVIPHandler processing...

http://lihuaxi.xjx100.cn/news/45267.html

相关文章

攻防演练中防守方的防护措施.

红队常用的防护手段 防护手段是落地防护策略的基础&#xff0c;但“不知攻&#xff0c;焉知防”&#xff0c;近年 随着网络攻击的手段、方法的层出不穷&#xff0c;攻击技术的不断发展&#xff0c;红队 的网络防御难度也越来越大&#xff0c;需要不断更新才能更好地保障网络安…

指令的类型

目录 1. 传送类指令 2. 输入/输出&#xff08;I/O&#xff09;类指令 3. 算术逻辑运算类指令 4. 程序控制类指令 传送类指令输入/输出&#xff08;I/O&#xff09;类指令算术逻辑运算类指令程序控制类指令1. 传送类指令 将源地址里保存的数据传送到目的地址中保存&#xff…

java计算机毕业设计网上书店进销存管理系统源程序+mysql+系统+lw文档+远程调试

java计算机毕业设计网上书店进销存管理系统源程序mysql系统lw文档远程调试 java计算机毕业设计网上书店进销存管理系统源程序mysql系统lw文档远程调试本源码技术栈&#xff1a; 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 开发软件&#xff1a;idea eclipse…

数据库管理工具Navicat16版本,功能优化全新详解

Navicat16版本具备多项改进和新功能&#xff0c;能满足你对数据库开发的需求。Navicat拥有过百种增强功能和耳目一新的界面&#xff0c;为你提供构建、管理和维护数据库的新方法&#xff0c;提升你的工作表现 一、崭新的设计 相比上一个版本&#xff0c;Navicat16带来了许多U…

windows下Mysql多实例部署

当存在多个项目的时候&#xff0c;需要同时部署时&#xff0c;且只有一台服务器时&#xff0c;哪么就需要部署Mysql多个实例&#xff0c;原理很简单&#xff0c;多个mysql服务运行使用不同的配置及数据管理。 具体操作如下&#xff1a; 1、找到mysql.ini配置并复制为一个新的…

Zookeeper 集群安装

一、Java环境的安装 1. 下载jdk Java Downloads | Oracle 2. 解压并配置环境变量 # 上传到/usr/local目录下 tar -zxvf jdk-8u341-linux-x64.tar.gz # 配置Java环境变量 vi /etc/profile export JAVA_HOME/usr/local/jdk1.8.0_341 export PATH$PATH:$JAVA_HOME/bin #验证是否…

SpringMVC-02 MVC模式介绍

文章目录1 Java Web开发模型2 JSPJavaBean开发模型&#xff08;model1&#xff09;3 MVC开发模式&#xff08;model2&#xff09;3.1 MVC模式基础3.1.1 模型、视图、控制器各部分的作用3.1.2 MVC与三层结构3.2 MVC发展3.2.1 ServletJSPJavaBean的模型3.2.2 Strust13.2.2 Strust…

rust的struct

定义struct 使用struct关键字&#xff0c;并为整个struct命名在花括号内&#xff0c;为所有字段&#xff08;field&#xff09;定义名称和类型 struct User{username: String,email: String,sign_in_count: u64,active: bool, }实例化struct 想要使用struct&#xff0c;需要…