作为一名Javaer领到电脑的第一件事就是需要配置Java环境,以及切换不同的JDK版本来进行日常开发,Node开发有nvm,python开发有pyenv, Java也提供了响应的工具sdkman(Linux, Unix, MacOS)。同时sdkman也可以管理Scala、Gradle、Maven等等软件不同的版本。

安装 SDKMan 要开始使用 SDKMan,

首先需要在你的系统上安装它。SDKMan 支持 Windows、macOS 和 Linux 等操作系统。

在 macOS 和 Linux 上安装

在终端中执行以下命令来安装 SDKMan:

1
2
3
curl -s "https://get.sdkman.io" | bash 
# 或者
curl -s "https://get.sdkman.io" | ZSH

执行

1
2
3
4
source "$HOME/.sdkman/bin/sdkman-init.sh"
$ sdk version
# 如下,如果显示版本则代表成功
sdkman 5.18.2

在 Windows 上安装

在 Windows 上,你可以使用 Git Bash 或 Cygwin 等工具来安装和使用 SDKMan。首先,下载并安装 Git Bash 或 Cygwin,并确保它们在系统的 PATH 环境变量中。然后,在 Git Bash 或 Cygwin 终端中执行以下命令来安装 SDKMan:

1
curl -s "https://get.sdkman.io" | bash

使用 SDKMan

一旦你安装好了 SDKMan,就可以开始使用它来管理你的开发工具了。

安装工具

使用 SDKMan 安装一个开发工具非常简单。比如,要安装最新版本的 Java,只需要在终端中执行以下命令:

1
2
sdk lis java #显示出java所有版本
sdk install java 17.0.3-oracle

切换版本

1
sdk use java 17.0.3-oracle

升级工具

SDKMan 也提供了升级已安装工具的功能。要升级某个已安装的工具,只需要执行以下命令:

1
sdk upgrade <tool>

其中 <tool> 是你想要升级的工具名称,比如 javascala等。

列出可用版本

如果你想查看某个工具的所有可用版本,可以使用以下命令:

1
sdk list <tool>

这将列出该工具所有可用的版本号供你选择。

删除工具

如果你想删除某个已安装的工具,可以使用以下命令:

1
sdk uninstall <tool> <version>

其中 <tool> 是工具的名称,<version> 是要删除的版本号。

结语

SDKMan 是一个非常实用的开发工具管理工具,可以帮助开发人员轻松安装、升级和切换不同版本的开发工具。它简化了我们在不同项目中使用不同工具版本的过程,让开发变得更加便捷和高效。

如果你还没有尝试过 SDKMan,我强烈推荐你去官方网站(https://sdkman.io/)了解更多信息,并在你的开发环境中安装和使用它。相信它会成为你的好帮手!

ChatGpt真强大这篇文章是它写的。

Comment and share

番茄架构是遵循Common Sense Manifesto的软件架构的一种方法

Common Sense Manifesto

  • 选择适合你的软件的架构而不是流行的的架构: 在盲目追随流行人物的建议之前,要考虑软件最佳实践。
  • 不要过度设计: 努力保持简单,而不是过度工程化,试图猜测未来十年的需求。
  • 进行研究与开发,选择一项技术并拥抱它,而不是为了可替代性而创建抽象层。
  • 确保你的解决方案作为整体工作正常,而不仅仅是单个组件。

架构图

tomato-architecture.png

实施指南(架构核心:关注点分离):

  1. 按功能进行打包

    将代码按照功能分成不同的包是一种常见的模式,通常会根据技术层次(如 controllers, services, repositories等)进行划分。如果你正在构建一个专注于特定模块或业务能力的微服务,那么这种方法可能是可行的。

如果你正在构建一个单体应用或者模块化单体应用,强烈建议首先按照功能而非技术层进行划分。

详细信息请阅读链接: https://phauer.com/2020/package-by-feature/

  1. “应用核心”独立于交付机制(Web, Scheduler Jobs, CLI)

    应用核心应该公开可以从主方法调用的API。为了实现这一点,“应用核心”不应该依赖于其调用上下文。这意味着“应用核心”不应依赖于任何HTTP/Web层的库。同样,如果你的应用核心被用于定时任务或命令行接口,任何调度逻辑或命令行执行逻辑都不应泄露到应用核心中。

  2. 将业务逻辑执行与输入源(Web Controllers, Message Listeners, Scheduled Jobs等)分离

    输入源,如Web Controllers, Message Listeners, Scheduled Jobs等,应该是很薄的一层,在提取请求数据后将实际的业务逻辑执行委托给“应用核心”。

比如:

坏味道:

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
class CustomerController {
private final CustomerService customerService;

@PostMapping("/api/customers")
void createCustomer(@RequestBody Customer customer) {
if(customerService.existsByEmail(customer.getEmail())) {
throw new EmailAlreadyInUseException(customer.getEmail());
}
customer.setCreateAt(Instant.now());
customerService.save(customer);
}
}

纠正:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RestController
class CustomerController {
private final CustomerService customerService;

@PostMapping("/api/customers")
void createCustomer(@RequestBody Customer customer) {
customerService.save(customer);
}
}

@Service
@Transactional
class CustomerService {
private final CustomerRepository customerRepository;

void save(Customer customer) {
if(customerRepository.existsByEmail(customer.getEmail())) {
throw new EmailAlreadyInUseException(customer.getEmail());
}
customer.setCreateAt(Instant.now());
customerRepository.save(customer);
}
}

采用这种方法,无论你是通过REST API调用还是通过CLI创建一个客户,所有的业务逻辑都会集中在应用核心中。

坏味道:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
class OrderProcessingJob {
private final OrderService orderService;

@Scheduled(cron="0 * * * * *")
void run() {
List<Order> orders = orderService.findPendingOrders();
for(Order order : orders) {
this.processOrder(order);
}
}

private void processOrder(Order order) {
...
...
}
}

纠正:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component
class OrderProcessingJob {
private final OrderService orderService;

@Scheduled(cron="0 * * * * *")
void run() {
List<Order> orders = orderService.findPendingOrders();
orderService.processOrders(orders);
}
}

@Service
@Transactional
class OrderService {

public void processOrders(List<Order> orders) {
...
...
}
}

采用这种方法,你可以将订单处理逻辑与调度程序解耦,可以在没有通过调度程序触发的情况下独立进行测试。

  1. 不要让“外部服务集成”对“应用核心”产生太大影响

    从应用核心,我们可能需要与数据库、消息代理或第三方Web服务等进行通信。必须注意的是,业务逻辑执行器不应过度依赖于外部服务集成。

    例如,假设你正在使用Spring Data JPA进行持久化,而你想从CustomerService中使用分页获取客户。

    坏味道:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Service
    @Transactional
    class CustomerService {
    private final CustomerRepository customerRepository;

    PagedResult<Customer> getCustomers(Integer pageNo) {
    Pageable pageable = PageRequest.of(pageNo, PAGE_SIZE, Sort.of("name"));
    Page<Customer> cusomersPage = customerRepository.findAll(pageable);
    return convertToPagedResult(cusomersPage);
    }
    }

    纠正:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Service
    @Transactional
    class CustomerService {
    private final CustomerRepository customerRepository;

    PagedResult<Customer> getCustomers(Integer pageNo) {
    return customerRepository.findAll(pageNo);
    }
    }

    @Repository
    class JpaCustomerRepository {

    PagedResult<Customer> findAll(Integer pageNo) {
    Pageable pageable = PageRequest.of(pageNo, PAGE_SIZE, Sort.of("name"));
    return ...;
    }
    }

    这种方式下,任何持久化库的修改都只会影响到持久化层。

  2. 将领域(domain)逻辑处理放在领域对象中

    如果一个方法仅仅影响领域对象中的状态或者是根据领域对象状态计算某些内容的方法,则该方法应该是属于领域对象。

    坏味道:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Cart {
    List<LineItem> items;
    }

    @Service
    @Transactional
    class CartService {

    CartDTO getCart(UUID cartId) {
    Cart cart = cartRepository.getCart(cartId);
    BigDecimal cartTotal = this.calculateCartTotal(cart);
    ...
    }

    private BigDecimal calculateCartTotal(Cart cart) {
    ...
    }
    }

    纠正:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Cart {
    List<LineItem> items;

    public BigDecimal getTotal() {
    ...
    }
    }

    @Service
    @Transactional
    class CartService {

    CartDTO getCart(UUID cartId) {
    Cart cart = cartRepository.getCart(cartId);
    BigDecimal cartTotal = cart.getTotal();
    ...
    }
    }
  3. 不要创建没有必要的接口

    不要创建接口并希望有一天我们可以为此接口添加另一个实现。如果那一天真的到来,那么利用我们现在拥有的强大的 IDE,只需敲击几下键盘即可提取界面。如果创建接口的原因是为了使用 Mock 实现进行测试,我们有像 Mockito 这样的模拟库,它能够在不实现接口的情况下模拟类。

    因此,除非有充分的理由,否则不要创建接口。

  4. 拥抱框架强大的功能和灵活性

    通常,创建库和框架是为了满足大多数应用程序所需的常见要求。因此,您应该选择一个库/框架来更快地构建应用程序时。

    与其利用所选框架提供的功能和灵活性,不如在所选框架之上创建间接或抽象,并希望有一天您可以将框架切换到其他框架,这通常是一个非常糟糕的主意。

    例如,Spring框架为处理数据库事务、缓存、方法级安全性等提供了声明性支持。引入我们自己的类似注释并通过将实际处理委托给框架来重新实现相同的功能支持是不必要的。

    相反,最好直接使用框架的注释,或者根据需要使用附加语义来组合注释。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Transactional
    public @interface UseCase {
    @AliasFor(
    annotation = Transactional.class
    )
    Propagation propagation() default Propagation.REQUIRED;
    }
  5. 不仅单元测试,也需要集成测试。

    我们绝对应该编写单元测试来测试单元(业务逻辑),如果需要,可以通过模拟外部依赖项。但更重要的是验证整个功能是否正常工作。

    即使我们的单元测试以毫秒为单位运行,我们是否可以放心地投入生产?当然不是。我们应该通过测试实际的外部依赖项(例如数据库或消息代理)来验证整个功能是否正常工作。这让我们更有信心。

    我想知道“我们应该拥有完全独立于外部依赖项的核心域”哲学的整个想法来自于使用真实依赖项进行测试非常具有挑战性或根本不可能的时代。

    幸运的是,我们现在拥有更好的技术(例如:testcontainer 运行测试时动态启动依赖的中间件容器技术)来测试真正的依赖关系。使用真正的依赖项进行测试可能会花费稍多的时间,但与好处相比,这是一个可以忽略不计的成本。

学习于原文:https://github.com/wenPKtalk/tomato-architecture

Comment and share

背景

电脑强制重启后,docker里的mongo不能正常使用,索引不能位置被破坏,错误log是这样的: error: WT_ERROR: non-specific WiredTiger error”}

解决方法

  1. 简历另外一个容器,volume挂载和上一个容器一样的位置:
1
2
3
4
5
6
7
8
9
docker run -it -d --name="mongofix" \
-p 27017:27017 \
-p 27018:27018 \
-p 27019:27019 \
-e TIMEZONE="Australia/Brisbane" \
-e TZ="Australia/Brisbane" \
-v {本地挂载目录}/configdb:/data/configdb \
-v {本地挂载目录}/db:/data/db \
mongo:latest /bin/bash
  1. 删除几个lock文件

    1
    2
    3
    $ rm -r journal
    $ rm -r mongod.lock
    $ rm -r WiredTiger.lock
  2. 进入mongofix容器

1
2
docker exec -it mongo /bin/sh
# mongod --repair

总结

这也提供了一条思路,如果因为挂载文件损坏,容器不能正常启动,所以不能进入容器运行修复命令,可以先创建其它容器,进入容器后进行修复。然后restart老的容器。

引用

https://gist.github.com/mtrunkat/203c33347cc96db51a754a45bb902669?permalink_comment_id=2693735

Comment and share

  1. 常用团队规范,包含Git 使用原则、分支设计、单线分支原则、开发规范
    https://blog.meathill.com/git/git-1-team-sop.html

  2. 常见问题解决,包含处理 hotfix、git rebase dev -i、git reset –hard COMMIT
    https://blog.meathill.com/git/2023-git-2-faq.html

  3. Git推荐配置与小技巧,包含
    https://blog.meathill.com/git/2023-git-3-tips.html

  4. Mac用户使用Item2+zsh中的主题自带了Git的常见alias,可以使用 alias |grep git进行查看:

    常用的有:

    1
    2
    3
    4
    5
    gup = git  pull --rebase
    gp = git push
    gl = git pull
    gcam = git commit -a -m
    gca! = git commit -v -a --amend

Comment and share

  • page 1 of 1
Author's picture

Topsion

Fullstack Developer


Coder


Xi'an China