Spring提供的环境类详解( ApplicationRunner,ApplicationListener,ApplicationEvent,ApplicationListener...)

这篇文章用以总结Spring体系中提供的一些特殊类,并总结其中的用法

1.CommandLineRunner 和 ApplicationRunner

  • 用处
    他们的用处一样,都是可以实现他们的方法,来做一些应用启动以后执行的动作,区别有一点他们的run方法的入参不一样,CommandLineRunner 的run方法接收的是启动类 main函数传递进来的参数 ,很好验证就不验证了
    ApplicationRunner 的run方法接收的是ApplicationArguments 该类可以用于更为方便的操作启动项目时传递进来的参数,当然也是包含main传进来的参数,还有其他命令行传进来的参数,通过ApplicationArguments 的各种get方法都可以获取到。这里作用大差不差,使用哪个都没有大区别,如无特殊需要推荐使用 CommandLineRunner
  • 注意
    使用时一定得将他们的实现类注入到容器,大部分场景下他们的应用都是在系统启动后做一些初始化处理,一般不太会用run的入参
  • 举例
    CommanLineRunner:
    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 做一些需要系统初始化后需要做的动作 
        }
    }
    
    ApplicationRunner:
    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 注解表明该实现类会被优先使用:
    @Primary
    @Service
    public class OrderServiceOne {
    
    }
    
    Qualifier 声明bean的name,从而限定Bean的使用(bean的name默认是类名首字母小写):
    @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注解就会方便一些,同时也可以防止不知道的同事,直接使用该注解,不使用自定义的线程池了。
  • 举例:
    项目增加配置类:
    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注解就可以默认用这个线程池了,有如下代码进行测试:
    @Async
    public void test4() {
        System.out.println("test4 执行了...: "+Thread.currentThread().getName());
    }
    
    通过接口调用后,执行结果如下:
    在这里插入图片描述

10.Environment

  • 作用:
    Spring提供了多样化的配置获取方式,包括注解Value,注解@ConfigurationProperties等等,此外我们还可以直接使用Environment来获取到项目里的配置,项目的配置都可以通过这个环境类获取到。
  • 举例:
    controller中获取一些配置信息进行展示:
    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"));
        }
    }
    
    下面是执行结果,我并没有指定配置文件的属性spring.profiles.active所以第一个是空集合:
    在这里插入图片描述

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());
        }
    }
    
    有如下的测试代码:
    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);
        }
    }
    
    预想结果是时间的格式应该和我们自己定义的一致,Long类型应该变为String类型,结果如下(和预期一致了):
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

归去来 兮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值