Spring

Spring 的 beanName 设置可能会导致代理失败

一些使用小细节就是在不断的源码探索中逐步发现的,今天就来和大家研究一下通过 beanName 的设置,可以让一个 bean 拒绝被代理的问题! 1. 代码实践 假设我有如下一个切面: @Aspect @EnableAspectJAutoProxy @Component public class LogAspect { @Pointcut("execution(* org.javaboy.demo.service.*.*(..))") public void pc() { } @Before("pc()") public void before(JoinPoint jp) { String name = jp.getSignature().getName(); System.out.println(name + " 方法开始执行了..."); } } 这个切面要拦截的方法是 org.javaboy.demo.service 包下的所有类的所有方法,现在,这个包下有一个 BookService 类,内容如下: @Service("org.javaboy.demo.service.BookService.ORIGINAL") public class BookService { public void hello() { System.out.println("hello bs"); } } 这个 BookService 的 beanName 我没有使用默认的 beanName,而是自己配置了一个 beanName,这个 beanName 的配置方式是 类名的完整路径 + .ORIGINAL。 当我们按照这样的规则给 bean 取名之后,那么即使当前 bean 已经包含在切点所定义的范围内,这个 bean 也不会被代理了。

使用 Spring 注解实例化同一个类的多个 Bean

1、概览 Spring IoC 容器创建和管理 Spring Bean,这些 Bean 是应用的核心。创建一个 Bean 实例与从普通的 Java 类创建对象相同。然而,生成多个相同类的 Bean 可能会比较麻烦一点。 本文将带你了解如何在 Spring 中使用注解创建同一个类的多个 Bean。 2、使用 Java 配置 这是使用注解创建多个同类 Bean 的最简单易行的方法。 举一个简单的例子。我们有一个 Person 类,它有两个字段:firstName 和 lastName: public class Person { private String firstName; private String lastName; public Person(String firstName, String secondName) { super(); this.firstName = firstName; this.lastName = secondName; } @Override public String toString() { return "Person [firstName=" + firstName + ", secondName=" + lastName + "]"; } } 接下来,构建一个名为 PersonConfig 的配置类,并在其中定义 Person 类的多个 Bean:

使用 @NotNull 注解进行非空校验

1、概览 空指针异常 NullPointerException 是一个常见问题,避免这种问题的方法之一是在方法参数上添加 @NotNull 等校验注解。 给方法参数添加了 @NotNull 注解后,还需要其他的一些设置才能自动对参数进行非空校验。 2、给方法参数添加 @NotNull 注解 创建一个类,其中包含一个返回 String 长度的方法。 在 String 参数上添加 @NotNull 注解: public class NotNullMethodParameter { public int validateNotNull(@NotNull String data) { return data.length(); } } 注意,有多个包下都有 @NotNull 注解,我们使用的应该是 jakarta.validation.constraints 包。 创建 NotNullMethodParameter 实例,然后使用 null 参数调用方法。 NotNullMethodParameter notNullMethodParameter = new NotNullMethodParameter(); notNullMethodParameter.doesNotValidate(null); 尽管在参数上使用了 @NotNull,但还是出现了空指针异常:NullPointerException。 注解未生效,因为没有 Validator 来执行它。 3、添加 Validator 添加 Hibernate Validator(jakarta.validation 的实现)来识别 @NotNull。 <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>8.0.0.Final</version> </dependency> 使用默认的 ValidatorFactory 创建 validator。

WebClient 将 Flux 读取到 InputStream 中

1、概览 本文将会带你了解在 Java 响应式编程中如何将 Flux<DataBuffer> 读取到 InputStream。 2、请求设置 首先,使用 Spring Reactive WebClient 发起 GET 请求。使用由 gorest.co.in 托管的公共 API 端点来进行测试: String REQUEST_ENDPOINT = "https://gorest.co.in/public/v2/users"; 接下来,定义 getWebClient() 方法,用于获取 WebClient 类的新实例: static WebClient getWebClient() { WebClient.Builder webClientBuilder = WebClient.builder(); return webClientBuilder.build(); } 至此,我们就可以向 /public/v2/users 端点发出 GET 请求了。注意,必须以 Flux<DataBuffer> 对象的形式获取响应体。 3、BodyExtractors 和 DataBufferUtils 我们可以使用 spring-webflux 中 BodyExtractors 类的 toDataBuffers() 方法将响应体提取到 Flux<DataBuffer> 中。 将 body 构建为 Flux<DataBuffer> 类型的实例: Flux<DataBuffer> body = client .get( .uri(REQUEST_ENDPOINT) .exchangeToFlux( clientResponse -> { return clientResponse.

Spring Lifecycle 和 SmartLifecycle 的区别

当我们想在 Spring 容器启动或者关闭的时候,做一些初始化操作或者对象销毁操作,我们可以怎么做? 注意我这里说的是容器启动或者关闭的时候,不是某一个 Bean 初始化或者销毁的时候! 1、Lifecycle 对于上面提到的问题,如果你稍微研究过 Spring,应该是了解其里边有一个 Lifecycle 接口,通过这个接口,我们可以在 Spring 容器启动或者关闭的时候,做一些自己需要的事情。 我们先来看下 Lifecycle 接口: public interface Lifecycle { void start(); void stop(); boolean isRunning(); } 这个接口一共就三个方法: start:启动组件,该方法在执行之前,先调用 isRunning 方法判断组件是否已经启动了,如果已经启动了,就不重复启动了。 stop:停止组件,该方法在执行之前,先调用 isRunning 方法判断组件是否已经停止运行了,如果已经停止运行了,就不再重复停止了。 isRunning:这个是返回组件是否已经处于运行状态了,对于容器来说,只有当容器中的所有适用组件都处于运行状态时,这个方法返回 true,否则返回 false。 如果我们想自定义一个 Lifecycle,方式如下: @Component public class MyLifeCycle implements Lifecycle { private volatile boolean running; @Override public void start() { running = true; System.out.println("start"); } @Override public void stop() { running = false; System.out.println("stop"); } @Override public boolean isRunning() { return running; } } 需要自定义一个 running 变量,该变量用来描述当前组件是否处于运行/停止状态,因为系统在调用 start 和 stop 方法的时候,都会先调用 isRunning 方法,用以确认是否需要真的调用 start/stop 方法。

测试 Spring JMS

1、概览 在本文中,我们将创建一个简单的 Spring 应用,用于连接到 ActiveMQ 并发送和接收消息。我们将重点关注测试这个应用以及测试 Spring JMS 的不同方法。 Spring JMS 是 Spring 框架提供的一个模块,用于支持与 Java 消息服务(Java Message Service,JMS)提供者的集成。JMS是一种用于在分布式系统中发送、接收和传递消息的标准 API。 2、应用设置 首先,创建一个用于测试的基本 Spring 应用。添加必要的依赖,并实现消息处理。 2.1、依赖 在 pom.xml 中添加所需的依赖。我们需要 Spring JMS 来监听 JMS 消息。我们将在部分测试中使用 ActiveMQ-Junit 启动嵌入式 ActiveMQ 实例,并在其他测试中使用 TestContainers 运行 ActiveMQ Docker 容器: <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>4.3.4.RELEASE</version> </dependency> <dependency> <groupId>org.apache.activemq.tooling</groupId> <artifactId>activemq-junit</artifactId> <version>5.16.5</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> <version>1.17.3</version> <scope>test</scope> </dependency> 2.2、应用代码 现在,创建一个可以监听消息的 Spring 应用: @ComponentScan public class JmsApplication { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(JmsApplication.

@Spy 和 @SpyBean

1、简介 本文将带你了解 @Spy 和 @SpyBean 之间的区别和用法。 2、基本应用 本文中,我们使用一个简单的订单应用,其中包括一个用于创建订单的订单服务,以及一个用于在处理订单时发出通知的通知服务。 OrderService 有一个 save() 方法,用于接收 Order 对象,使用 OrderRepository 保存该对象,并调用 NotificationService: @Service public class OrderService { public final OrderRepository orderRepository; public final NotificationService notificationService; public OrderService(OrderRepository orderRepository, NotificationService notificationService) { this.orderRepository = orderRepository; this.notificationService = notificationService; } public Order save(Order order) { order = orderRepository.save(order); notificationService.notify(order); if(!notificationService.raiseAlert(order)){ throw new RuntimeException("Alert not raised"); } return order; } } 为简单起见,我们假设 notify() 方法仅在日志中输出订单信息。实际上,它可能涉及更复杂的操作,例如通过队列向下游应用发送电子邮件或消息。 我们还假设,每个创建的订单都必须通过调用 ExternalAlertService 来接收警报(Alert),如果警报成功,则返回 true,如果 OrderService 没有发出警报,则会失败:

Spring 6 中的 RSocket 接口

1、概览 本文将带你了解如何在 Spring 6 中使用 RSocket。 随着 Spring 6 引入声明式 RSocket 客户端,使用 RSocket 变得更加简单。这一功能消除了重复的模板代码,使开发人员能够更高效地使用 RSocket。 2、Maven 依赖 首先,创建 Spring Boot 项目,并在 pom.xml 文件中添加 spring-boot-starter-rsocket 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-rsocket</artifactId> <version>3.1.4</version> </dependency> 3、创建 RSocket Server 首先,创建一个 responder(应答器),通过 Controller 来管理传入的请求: @MessageMapping("MyDestination") public Mono<String> message(Mono<String> input) { return input.doOnNext(msg -> System.out.println("Request is:" + msg + ",Request!")) .map(msg -> msg + ",Response!"); } 接着,在 application.properties 文件中添加以下属性,以在 7000 端口启动服务( MyDestination): spring.rsocket.server.port=7000 4、客户端 现在,开发客户端。为了简单起见,我们在同一个项目中创建客户端,但将其放在一个单独的包中。实际开发中,它们应该放在一个单独的项目中。 创建客户端接口: public interface MessageClient { @RSocketExchange("MyDestination") Mono<String> sendMessage(Mono<String> input); } 在使用客户端接口时,通过 @RSocketExchange 来指定 RSocket 端点。基本上,这意味着我们需要一些信息来建立端点路径。可以在接口级别上通过分配一个共享路径来实现。这非常简单,帮助我们知道要使用哪个端点。

SpringRunner 和 @SpringBootTest

1、概览 无论是单元测试还是集成测试,测试对于任何应用程序都至关重要。SpringRunner 和 SpringBootTest 类是运行集成测试的基础。 在本教程中,我们将了解 SpringRunner 和 @SpringBootTest 的用法,以及它们之间的区别。 2、SpringRunner SpringRunner 是 SpringJUnit4ClassRunner 类的别名,可用于基于 JUnit4 的测试类。它加载 Spring TestContext,通过 Spring TestContext,Spring Bean 和 Configuration 可与 JUnit 注解一起使用。需要 JUnit 4.12 或更高版本才能使用它。 通过 @RunWith(SpringRunner.class) 注解测试类来使用此功能: @RunWith(SpringRunner.class) public class SampleIntegrationTest { @Test public void test() { // } } 3、SpringBootTest SpringBootTest 是 SpringRunner 的替代品,可与 JUnit5 配合使用。它还用于运行集成测试和加载 Spring TestContext。 它的功能非常丰富,可通过注解参数提供多种配置。它支持各种 Web 环境模式,如 MOCK、RANDOM_PORT、DEFINED_PORT 和 NONE。我们可以通过注解传递 application properties,在测试运行之前将其注入到 Spring Environment 中。 @SpringBootTest( properties = {"user.name=test_user"}, webEnvironment = MOCK) public class SampleIntegrationTest { @Test public void test() { // } } 要运行集成测试,必须在类级别添加注解 @SpringBootTest。

@Scope 注解失效的问题

scope 属性,相信大家都知道,一共有六种: 取值 含义 生效条件 singleton 表示这个 Bean 是单例的,在 Spring 容器中,只会存在一个实例。 prototype 多例模式,每次从 Spring 容器中获取 Bean 的时候,才会创建 Bean 的实例出来。 request 当有一个新的请求到达的时候,会创建一个 Bean 的实例处理。 web 环境下生效 session 当有一个新的会话的时候,会创建一个 Bean 的实例出来。 web 环境下生效 application 这个表示在项目的整个生命周期中,只有一个 Bean。 web 环境下生效 gloablsession 有点类似于 application,但是这个是在 portlet 环境下使用的。 web 环境下生效 这个用法也很简单,通过配置就可以设置一个 Bean 是否为单例模式。 1、问题呈现 今天我要说的不是基础用法,是另外一个问题,假设我现在有如下两个 Bean: @Service public class UserService { @Autowired UserDao userDao; } @Repository public class UserDao { } 在 UserService 中注入 UserDao,由于两者都没有声明 scope,所以默认都是单例的。 现在,如果我给 UserDao 设置 Scope,如下: