这篇文章用以总结Spring体系中提供的一些特殊类,并总结其中的用法
Spring提供的各种环境类详解
- 1.CommandLineRunner 和 ApplicationRunner
- 2.ApplicationEvent 和 ApplicationListener 和 ApplicationEventPublisher
- 3.EventListener 和 TransactionalEventListener
- 4.InitializingBean 和 DisposableBean
- 5.Ordered 和 Order 和 PriorityOrdered 和 Priority
- 6.Primary 和 Qualifier
- 7.TransactionSynchronizationManager
- 8.AopContext
- 9.AsyncConfigurer
- 10.Environment
- 11.Aware-ApplicationContextAware
- 12.Aware-EnvironmentAware
- 13.WebMvcConfigurer
1.CommandLineRunner 和 ApplicationRunner
- 用处
他们的用处一样,都是可以实现他们的方法,来做一些应用启动以后执行的动作,区别有一点他们的run方法的入参不一样,CommandLineRunner 的run方法接收的是启动类 main函数传递进来的参数 ,很好验证就不验证了
ApplicationRunner 的run方法接收的是ApplicationArguments 该类可以用于更为方便的操作启动项目时传递进来的参数,当然也是包含main传进来的参数,还有其他命令行传进来的参数,通过ApplicationArguments 的各种get方法都可以获取到。这里作用大差不差,使用哪个都没有大区别,如无特殊需要推荐使用 CommandLineRunner - 注意
使用时一定得将他们的实现类注入到容器,大部分场景下他们的应用都是在系统启动后做一些初始化处理,一般不太会用run的入参 - 举例
CommanLineRunner:
ApplicationRunner:import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; /** * @Author: pcc * @Description: 一句话描述该类 */ @Component public class MyCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("MyCommandLineRunner"); // TODO 做一些需要系统初始化后需要做的动作 } }
运行截图:import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; /** * @Author: pcc * @Description: 一句话描述该类 */ @Component public class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("ApplicationRunner, 接收参数:"+args.getSourceArgs()[0]); // TODO... } }
2.ApplicationEvent 和 ApplicationListener 和 ApplicationEventPublisher
注意:如果一个事件被多个监听器监听了,那就是一个类似广播的行为,当事件发布后,所有监听改事件的监听器都会收到消息
-
用处
ApplicationEvent 是Spring中定义事件的抽象类,Spring中有太多太多的动作是基于事件来完成的了,比如容器初始化时就会发布很多事件,他们都是ApplicationEvent的子类。下面举例一些场景的事件类:ContextRefreshedEvent: 当 Spring 应用程序上下文(ApplicationContext)被刷新时,即所有 beans 都被创建和初始化之后,会触发这个事件。这通常发生在应用程序启动时。
ContextStartedEvent: 当 Spring 应用程序上下文被启动时触发这个事件。这通常发生在应用程序启动完成后,并且所有必要的资源都已经被加载。
ContextStoppedEvent: 当 Spring 应用程序上下文被停止时触发这个事件。这通常发生在应用程序关闭或停止运行之前。
ContextClosedEvent: 当 Spring 应用程序上下文被关闭时触发这个事件。这通常发生在应用程序完全停止运行之后。
ApplicationStartedEvent: 当 Spring 应用程序启动完成时触发这个事件。这通常发生在应用程序上下文被创建、启动和所有必要的资源被加载之后。
ApplicationReadyEvent: 当 Spring 应用程序准备就绪时触发这个事件。这通常发生在所有 beans 都被创建和初始化,并且所有必要的资源都已经被加载之后。
ApplicationFailedEvent: 当 Spring 应用程序启动失败时触发这个事件。这通常发生在应用程序上下文创建、启动或初始化过程中遇到错误时。
ServletRequestHandledEvent: 当 Spring MVC 控制器处理一个 HTTP 请求并返回响应时触发这个事件。这通常用于跟踪和记录请求处理时间。
RequestHandledEvent: 当 Spring 应用程序中的任何类型的请求(不仅仅是 HTTP 请求)被处理并返回响应时触发这个事件。这通常用于跟踪和记录请求处理时间。
RequestStartedEvent: 当 Spring 应用程序中的任何类型的请求开始处理时触发这个事件。这通常用于开始记录请求处理时间。
RequestEndedEvent: 当 Spring 应用程序中的任何类型的请求处理完成并返回响应时触发这个事件。这通常用于结束请求处理时间记录。ApplicationListener 则是为了监听对应事件的
ApplicationEventPublisher 则是为了发布事件使用的 -
举例 - 使用场景一:监听RequestHandledEvent等原生事件,举例监听RequestHandledEvent,可以获取到请求触发的信息
import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; import org.springframework.web.context.support.RequestHandledEvent; /** * @Author: pcc * @Description: 一句话描述该类 */ @Component public class MyApplicationListenerOne implements ApplicationListener<RequestHandledEvent> { @Override public void onApplicationEvent(RequestHandledEvent requestHandledEvent) { System.out.println("MyApplicationListenerOne: "+requestHandledEvent); } }接口请求后截图如下:

-
举例 - 使用场景二:自定义一个Event,然后自定义个一个监听来监听这个事件。在业务中如果为了解耦,也可以使用事件的机制,来分离不相干的逻辑。
自定义事件:import org.springframework.context.ApplicationEvent; /** * @Author: pcc * @Description: 一句话描述该类 */ public class MyEvent extends ApplicationEvent { // source 可以是任何对象,不过需要再接收时进行强转,主要用于存储事件的上下文 public MyEvent(Object source) { super(source); } }自定义监听,监听上面的事件:
import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * @Author: pcc * @Description: 一句话描述该类 */ @Component public class MyEventApplicationListener implements ApplicationListener<MyEvent>{ @Override public void onApplicationEvent(MyEvent myEvent) { System.out.println("收到自定义事件: "+myEvent.getSource()); } }然后还需要一个触发的场景,也就是什么业务场景下发布事件,这里演示就只在controller中进行演示了
import cn.felord.generatecode.testspring.MyEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController @RequestMapping("/order") public class Order0Controller { @Resource ApplicationEventPublisher applicationEventPublisher; @GetMapping("/action") public void test(){ System.out.println("我是test接口,执行了..."); MyEvent myEvent = new MyEvent( "hello"); applicationEventPublisher.publishEvent(myEvent); } }下面是执行截图,可以看到事件被成功通知到了监听者:

注意:监听器一定要注入到Spring容器,事件因为只是信息的载体,无需注入 -
举例 - 使用场景三:使用普通类实现事件,然后进行监听
ApplicationEventPublisher 提供了一个发布事件的方法入参是Object,所以我们也可以定义一个普通类作为事件类,下面是示例
事件类(普通的java类):import java.util.List; /** * @Author: pcc * @Description: 一句话描述该类 */ @Data public class MyTwoEvent { List<String> ids; }监听类,注意这里因为没有使用ApplicationEvent自然也不能使用ApplicationListener了,所以需要使用注解来实现,如下:
import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; /** * @Author: pcc * @Description: 一句话描述该类 */ @Component public class MyApplicationListenerTwo{ @EventListener public void onApplicationEvent(MyTwoEvent event) { System.out.println("不使用ApplicationEvent的事件,收到参数:"+event.getIds()); } }触发场景的代码,和普通事件类似,不过调用的是他的另一个重载方法,如下:
import cn.felord.generatecode.testspring.MyEvent; import cn.felord.generatecode.testspring.MyTwoEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.Arrays; @RestController @RequestMapping("/order") public class Order0Controller { @Resource ApplicationEventPublisher applicationEventPublisher; @GetMapping("/action") public void test(){ System.out.println("我是test接口,执行了..."); MyEvent myEvent = new MyEvent( "hello"); applicationEventPublisher.publishEvent(myEvent); // 普通类作为事件类 MyTwoEvent myTwoEvent = new MyTwoEvent(); myTwoEvent.setIds(Arrays.asList("1","2")); applicationEventPublisher.publishEvent(myTwoEvent); } }下面是调用接口后的执行截图,如下图,可以看到两种形式的自定义事件都正常执行了:

3.EventListener 和 TransactionalEventListener
这俩是注解,可以用于普通事件监听和事务事件监听。EventListener 的使用举例在上面的“举例 - 使用场景三”已经举过例子了,这里不重复说了,这里就主要说说 TransactionalEventListener,该注解是在EventListener 的基础上增加了对事务的监听,通过这个注解我们可以实现发布的事件和事务进行建立关系,比如我们想要发布一个事件,这个事件在事务提交以后执行,就可以为事件的监听方法增加这个注解:@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
当然也可以监听别的事务状态作为事件的触发条件。
-
用处
TransactionalEventListener 可以实现根据事务的状态来作为事件的触发条件 -
举例
这里事件类就使用一个普通的类就行:import lombok.Data; import java.util.List; /** * @Author: pcc * @Description: 一句话描述该类 */ @Data public class MyTwoEvent { List<String> ids; }监听代码:
import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; /** * @Author: pcc * @Description: 一句话描述该类 */ @Component public class MyApplicationListenerThree { @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void onApplicationEvent(MyTwoEvent event){ System.out.println("事务事件触发了:"+event.getIds()); } }触发场景代码如下,同样使用ApplicationEventPublisher,注意使用事务:
import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.Arrays; /** * @Author: pcc * @Description: 一句话描述该类 */ @Service public class TestService { @Resource ApplicationEventPublisher applicationEventPublisher; /** * 测试事务事件监听 */ @Transactional(rollbackFor = Exception.class) public void test(){ System.out.println("testService 执行了..."); MyTwoEvent myTwoEvent = new MyTwoEvent(); myTwoEvent.setIds(Arrays.asList("你好","事务监听")); applicationEventPublisher.publishEvent(myTwoEvent); // int i = 1/0; } }下面是接口执行时的结果展示:

4.InitializingBean 和 DisposableBean
相关文章推荐:
Spring的Bean生命周期详解
Bean的初始化和销毁的多种方式
- 用处
这俩接口主要是Spring提供了可以在Bean的生命周期中执行一些我们想要的逻辑,比如监控当前Bean的状态和属性变化等。Bean生命周期的大致过程:加载Bean---->实例化Bean---->属性填充---->初始化Bean---->InitializingBean---->PostProcessor---->进入单例池---->销毁(关闭容器)---->DisposableBean。他们使用起来也比较简单,就直接实现接口,重写方法就行 - 举例
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; /** * @Author: pcc * @Description: 一句话描述该类 */ @Component public class TestBean implements InitializingBean, DisposableBean { @Override public void destroy() throws Exception { System.out.println("destroy"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet"); } }
5.Ordered 和 Order 和 PriorityOrdered 和 Priority
-
用处
这几个注解都是定义Bean加载顺序的,就是Spring容器加载Bean时的顺序。作用就是Bean的加载优先级。其中Ordered 和 Order 和 PriorityOrdered都是Spring提供的类或者注解,Priority 是基于 JSR-330 的 javax.annotation.Priority 注解,它不仅可以在 Spring 中使用,还可以在其他支持 JSR-330 的环境中使用。
Ordered 和 Order 和 PriorityOrdered 他们的使用具有相似性,都是值越小优先级越高。如果同时使用了Ordered 和 Order 和 PriorityOrdered 他们三个,那么Ordered 和 Order 的优先级一样,也就是说值越小加载的位置越靠前,但是PriorityOrdered这个属性定义的Bean一定会在Ordered 和 Order 之前加载,他是Ordered的一个扩展注解。PriorityOrdered 他的值即使比较大,也会优先加载,不过多个使用PriorityOrdered修饰的Bean的加载顺序还是越小越先加载 -
举例
Ordered 接口示例:import org.springframework.core.Ordered; import org.springframework.stereotype.Service; /** * @Author: pcc * @Description: 一句话描述该类 */ @Service public class TestService2 implements Ordered { @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } }Order注解示例:
@Order(Ordered.HIGHEST_PRECEDENCE) @Service public class TestService2 { }PriorityOrdered 注解使用举例:
@Service public class TestService2 implements PriorityOrdered { @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } }Priority 使用示例:
import org.springframework.core.Ordered; import org.springframework.stereotype.Service; import javax.annotation.Priority; /** * @Author: pcc * @Description: 一句话描述该类 */ @Priority(Ordered.HIGHEST_PRECEDENCE) @Service public class TestService2 { }
6.Primary 和 Qualifier
- 作用:
这里都是注解,都是用来解决同一个类多个Bean时的注入问题的。假设场景OrderService 拥有两个实现类OrderServiceImplOne、OrderServiceImplTwo。如果我们想要使用在使用OrderService 时指定使用哪个实现类,有两种方式,第一种使用Primary注解,声明哪个实现类是主要的,声明为主要的类会优先使用。 - 举例:
Primary 注解表明该实现类会被优先使用:
Qualifier 声明bean的name,从而限定Bean的使用(bean的name默认是类名首字母小写):@Primary @Service public class OrderServiceOne { }@Qualifier("orderServiceOne ") @Autowired OrderService iService;
7.TransactionSynchronizationManager
- 作用:
这是一个事务管理器,可以声明事务各个阶段的函数,Spring会帮我们在各个阶段进行回调我们声明的函数,这个和上面的TransactionalEventListener有一些类似,不过这个不是事件,而且支持同时定义多个阶段的方法,还是很好用的。使用他恶意很好的解决,事务完成之后才可以做的一些动作,比如:事务完成后做三方通知,如果完成前通知可能通知发出去了,最后事务失败了,导致信息不一致。 - 举例:
新版写法:
执行截图:import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import javax.annotation.Priority; import javax.annotation.Resource; import java.util.Arrays; /** * @Author: pcc * @Description: 一句话描述该类 */ @Priority(1) @Service public class TestService implements IService{ @Resource ApplicationEventPublisher applicationEventPublisher; @Transactional(rollbackFor = Exception.class) @Override public void test3() { System.out.println("test3 执行了..."); TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){ @Override public void afterCommit() { System.out.println("事务提交了..."); } }); } }

老版Spring的写法(在新版已经不推荐使用TransactionSynchronizationAdapter了):import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronizationAdapter; import org.springframework.transaction.support.TransactionSynchronizationManager; import javax.annotation.Priority; import javax.annotation.Resource; import java.util.Arrays; /** * @Author: pcc * @Description: 一句话描述该类 */ @Priority(1) @Service public class TestService implements IService{ @Resource ApplicationEventPublisher applicationEventPublisher; @Transactional(rollbackFor = Exception.class) @Override public void test3() { System.out.println("test3 执行了..."); TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { System.out.println("事务提交了..."); } }); } }
8.AopContext
- 作用:
AopContext 类只能在一个AOP通知方法内部调用。如果在非AOP上下文中调用,它会抛出 IllegalStateException. 可以利用该类解决代理事务失效等因为获取不到代理对象而导致的AOP问题。 - 示例:
假设有一个a方法是事务方法,a事务方法需要调用同类的b事务方法,如果直接调用,b事务方法会失效,此时可以使用AopContext解决这个问题,如下:import org.springframework.aop.framework.AopContext; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /** * @Author: pcc * @Description: 一句话描述该类 */ @Component public class TestAopContext { @Transactional(rollbackFor = Exception.class) public void testA(){ System.out.println("testA"); // 调用B事务方法,这么写B方法将无实物:this.testB(); ((TestAopContext)AopContext.currentProxy()).testB(); } @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) public void testB(){ System.out.println("testB"); } }
9.AsyncConfigurer
- 作用:
未Async注解提供全局的配置,如果使用了该类进行配置,则不需要再注解中声明线程池了,这样每次写Async注解就会方便一些,同时也可以防止不知道的同事,直接使用该注解,不使用自定义的线程池了。 - 举例:
项目增加配置类:
当增加以上配置以后,再使用Async注解就可以默认用这个线程池了,有如下代码进行测试:import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; /** * @Author: pcc * @Description: 一句话描述该类 */ @Configuration @EnableAsync public class TestAsyncConfigurer implements AsyncConfigurer { // 当前机器的CPU核心数 private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors(); @Bean(name = "asyncTaskExecutor") public ThreadPoolTaskExecutor asyncTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(AVAILABLE_PROCESSORS+1); executor.setMaxPoolSize(AVAILABLE_PROCESSORS*2); executor.setQueueCapacity(100); executor.setThreadNamePrefix("自定义-"); executor.initialize(); return executor; } @Override public Executor getAsyncExecutor() { return asyncTaskExecutor(); } }
通过接口调用后,执行结果如下:@Async public void test4() { System.out.println("test4 执行了...: "+Thread.currentThread().getName()); }

10.Environment
- 作用:
Spring提供了多样化的配置获取方式,包括注解Value,注解@ConfigurationProperties等等,此外我们还可以直接使用Environment来获取到项目里的配置,项目的配置都可以通过这个环境类获取到。 - 举例:
controller中获取一些配置信息进行展示:
下面是执行结果,我并没有指定配置文件的属性spring.profiles.active所以第一个是空集合:import cn.felord.generatecode.testspring.IService; import cn.felord.generatecode.testspring.MyEvent; import cn.felord.generatecode.testspring.MyTwoEvent; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.Arrays; /** * * @author pcc * @since 2024-08-09 */ @ConfigurationProperties @RestController @RequestMapping("/order") public class Order0Controller { @Resource private Environment environment; @GetMapping("/actionTra5") public void test5(){ System.out.println(Arrays.toString(environment.getActiveProfiles())); System.out.println(environment.getProperty("spring.application.name")); } }

11.Aware-ApplicationContextAware
- 作用:
可以通过实现 ApplicationContextAware 接口的 setApplicationContext 方法来获取 ApplicationContext 的引用,利用这个接口的特性可以写一个Spirng的工具类,通过这个接口可以拿到一个上下文的副本供我们使用 - 示例:
下面是一个工具类的示例代码,可以直接使用:
注意:这种方式获取到的上下文是原始上下文的副本,并不会随着原始上下文的变化而变化,如果需要获取实时的上下文,可以直接注入ApplicationContext来进行使用,下面这个适用于获取不会更改的一些变量。
验证代码如下:import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.PriorityOrdered; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; /** * @Author: pcc * @Description: Spirng工具类 */ @Component public class SpringUtil implements ApplicationContextAware, PriorityOrdered { public static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringUtil.applicationContext = applicationContext; } public static <T> T getBean(Class<T> cla) { return applicationContext.getBean(cla); } public static <T> T getBean(String name, Class<T> cal) { return applicationContext.getBean(name, cal); } public static String getProperty(String key) { return applicationContext.getBean(Environment.class).getProperty(key); } @Override public int getOrder() { return PriorityOrdered.LOWEST_PRECEDENCE; } }
执行结果如下:import cn.felord.generatecode.testspring.IService; import cn.felord.generatecode.testspring.MyEvent; import cn.felord.generatecode.testspring.MyTwoEvent; import cn.felord.generatecode.testspring.SpringUtil; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.Arrays; /** * * @author pcc * @since 2024-08-09 */ @ConfigurationProperties @RestController @RequestMapping("/order") public class Order0Controller { @GetMapping("/actionTra6") public void test6(){ Order0Controller controller = SpringUtil.getBean(Order0Controller.class); // 利用获取到的bean再执行下面的方法 controller.test7(); // 利用工具类获取下配置信息 String name = SpringUtil.getProperty("spring.application.name"); System.out.println(name); } public void test7(){ System.out.println("我是test7接口,执行了..."); } }

12.Aware-EnvironmentAware
-
作用:
EnvironmentAware 作用与ApplicationContextAware类似,都是可以获取到对应的上下文的,EnvironmentAware 则是用来获取配置信息的上下文的,不过这个功能ApplicationContextAware也可以做,所以一般不用,而且上面的工具类已经包含了这个功能 -
示例:
简单示例:import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; /** * @Author: pcc * @Description: 一句话描述该类 */ @Component public class EnvironmentUtil implements EnvironmentAware { public static Environment environment; public static String getProperty(String key) { return environment.getProperty(key); } @Override public void setEnvironment(Environment environment) { EnvironmentUtil.environment = environment; } }使用举例:
@GetMapping("/actionTra6") public void test6(){ // 测试EnvironmentAware System.out.println(EnvironmentUtil.getProperty("spring.application.name")); }
13.WebMvcConfigurer
- 作用:
Spring提供的可以供使用者自定义各种MVC层的自定义配置的一个接口,我们只需要实现这个接口并重写对应的方法就可以实现对MVC层的控制,他包含的方法很多,这里列举常用的处理 - 示例-解决JDK8时间类的序列化与Long的序列化问题:
JDK8的默认时间格式都是需要做序列化处理的,还有就Long类型在JS端会失真,也需要序列化处理,下面就是一个通过MVC层的统一序列化处理方案:
有如下的测试代码:import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.stereotype.Component; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.concurrent.atomic.AtomicReference; /** * @Author: pcc * @Description: 一句话描述该类 */ @Component public class WebMvcConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { AtomicReference<MappingJackson2HttpMessageConverter> jackson2HttpMessageConverter = new AtomicReference<>(new MappingJackson2HttpMessageConverter()); /* 使用之前的转换器 */ converters.forEach(converter -> { if(converter instanceof MappingJackson2HttpMessageConverter){ jackson2HttpMessageConverter.set((MappingJackson2HttpMessageConverter)converter); } }); /* 设置Long类型序列化方式 */ ObjectMapper objectMapper = new ObjectMapper(); SimpleModule simpleModule = new SimpleModule(); simpleModule.addSerializer(Long.class, ToStringSerializer.instance); simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); objectMapper.registerModule(simpleModule); /* JDK8日期的序列化 和反序列化 */ JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); objectMapper.registerModule(javaTimeModule); jackson2HttpMessageConverter.get().setObjectMapper(objectMapper); converters.add(0,jackson2HttpMessageConverter.get()); } }
预想结果是时间的格式应该和我们自己定义的一致,Long类型应该变为String类型,结果如下(和预期一致了):import cn.felord.generatecode.entity.TestDTO; import cn.felord.generatecode.testspring.*; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.PriorityOrdered; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; /** * * @author pcc * @since 2024-08-09 */ @ConfigurationProperties @RestController @RequestMapping("/order") public class Order0Controller { @GetMapping("/actionTra8") public TestDTO test8(){ return new TestDTO() .setTime(LocalDateTime.now()) .setId(1829678483673235457L) .setAge(100); } }

2729

被折叠的 条评论
为什么被折叠?



