首页 > 其他分享 >SpringBoot配置类

SpringBoot配置类

时间:2024-11-17 21:14:23浏览次数:3  
标签:SpringBoot 配置 springframework shore org import com public

在Spring Boot中,配置类是一种特殊的类,用于定义和配置Spring应用程序的各种组件、服务和属性。这些配置类通常使用Java注解来声明,并且可以通过Spring的依赖注入机制来管理和使用。

Spring 容器初始化时会加载被@Component、@Service、@Repository、@Controller等注解标识的类,除了注解标识的类,Spring容器还会加载配置类中定义的Bean。配置类通常使用@Configuration注解标识,它们中定义的方法会被Spring容器识别为Bean的创建方法。

本篇主要讲 Springboot 配置类一些常见的用法,按照 SpringBoot 加载顺序配置监听器,过滤器,拦截器,定时器以及读取自定义配置文件。

定义配置类

package com.shore.confittestdemo.config;

import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {

}

监听器

`ServletContextListener接口

主要用于监听ServletContext对象的创建和销毁事件。它关注的是Web应用的启动和关闭,以及ServletContext对象的状态变化。

创建一个监听器类,实现ServletContextListener接口

package com.shore.configdemo.config.listener;

import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;

@WebListener
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        sce.getServletContext().setAttribute("myApp", "config-demo");
        System.out.println("MyServletContextListener:监听器启动");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println(sce.getServletContext().getAttribute("myApp"));
        System.out.println("MyServletContextListener:监听器销毁");
    }
}

注解 @WebListener 标记为一个监听器类(没啥用)

@Bean
    public ServletContextListener servletContextListener() {
        return new MyServletContextListener();
    }

系统启动时上下文添加 app 信息,系统退出时,获取上下文的 app 信息

ApplicationListener‌ 接口

主要用于监听Spring应用生命周期中的事件,如应用启动、上下文刷新、自定义事件等。它关注的是Spring容器内部的事件。

监听上下文刷新事件

创建一个监听器类,实现 ApplicationListener 接口,指定要监听的事件类型,ContextRefreshedEvent 或者 ContextClosedEvent 或者其他自定义类型的事件,重写 onApplicationEvent 方法,编写监听到事件后需要执行的逻辑。

package com.shore.configdemo.config.listener;

import jakarta.servlet.annotation.WebListener;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@WebListener
//@Component
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("MyApplicationListener: 监听到上下文刷新" + event);
    }

    @Override
    public boolean supportsAsyncExecution() {
        return ApplicationListener.super.supportsAsyncExecution();
    }
}

配置类中注册 监听器

@Bean
    public MyApplicationListener listener() {
        return new MyApplicationListener();
    }

项目启动创建上下文时被 ServletContextListener 监听到,更新上下文信息,然后被 ApplicationListener 监听器捕获到。

监听上下文刷新和上下文关闭事件

有时候我们希望在一个监听器中监听多个事件,而不是每个事件增加一个监听器类,这时我们可以采用在自定义方法上增加注解 @EventListener,指定要监听的事件类型,一个类中可以有多个注解监听多个事件。

package com.shore.configdemo.config.listener;

import jakarta.servlet.annotation.WebListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;

@WebListener
public class MyMultiEventListener {

    @EventListener
    public void handleContextRefreshEvent(ContextRefreshedEvent event) {
        System.out.println("MyMultiEventListener.handleContextRefreshEvent:监听到上下文刷新");
    }

    @EventListener
    public void handleContextClosedEvent(ContextClosedEvent event) {
        System.out.println("MyMultiEventListener.handleContextClosedEvent:监听到上下文关闭");
    }
}

监听多个自定义事件

实际业务中,我们可能需要监听一些其他的业务,比如类初始化等,可以通过自定义事件来完成。

创建一个类继承 ApplicationEvent 定义一个事件

package com.shore.configdemo.config.event;

import org.springframework.context.ApplicationEvent;

public class MyCustomEvent extends ApplicationEvent {
    private String message;

    public MyCustomEvent(Object source) {
        super(source);
    }

    public MyCustomEvent(Object source, String message) {
        super(source);
        this.message = message;
    }
    public String getMessage() {
        return message;
    }
}

定义一个类,调用 ApplicationEventPublisher 接口发布事件

package com.shore.configdemo.config.publisher;

import com.shore.configdemo.config.event.MyCustomEvent;
import jakarta.annotation.Resource;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
public class MyEventPublisher {
    @Resource
    private ApplicationEventPublisher eventPublisher;

    public void publishEvent() {
        MyCustomEvent myCustomEvent = new MyCustomEvent(this, "Hello, this is custom event");
        eventPublisher.publishEvent(myCustomEvent);
    }
}

在启动类或其他地方调用发布事件方法

package com.shore.configdemo;

import com.shore.configdemo.config.publisher.MyEventPublisher;
import jakarta.annotation.Resource;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ConfigDemoApplication implements CommandLineRunner {
    @Resource
    private MyEventPublisher myEventPublisher;

    public static void main(String[] args) {
        SpringApplication.run(ConfigDemoApplication.class, args);
    }


    @Override
    public void run(String... args) throws Exception {
        myEventPublisher.publishEvent();
    }
}

监听自定义事件

@EventListener
    public void handleMyCustomEvent(MyCustomEvent event) {
        System.out.println("MyMultiEventListener.handleContextClosedEvent:监听到自定义事件:" + event.getMessage());
    }

过滤器

Spring Boot 中的过滤器(Filter)主要用于对请求和响应进行预处理或后处理,常用于处理诸如日志记录、身份验证、头信息处理、请求修改等任务。

新建一个过滤器类,继承 javax.servlet.Filter 接口,也可以继承 OncePerRequestFilter 类,它确保在一次请求中只通过一次filter,无需显式检查是否已过滤。

package com.shore.configdemo.config.filter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

public class MyCustomFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 过滤器前执行的操作
        System.out.println("MyCustomFilter: Before doFilter");
        System.out.println("Request URI: " + request.getRequestURI());
        System.out.println("Request Method: " + request.getMethod());


        // 继续过滤器链
        filterChain.doFilter(request, response);

        // 过滤器后执行的逻辑
        System.out.println("MyCustomFilter: After doFilter");
        System.out.println("Response Status: " + response.getStatus());
    }
}

在配置类中注册过滤器

@Bean
    public FilterRegistrationBean<MyCustomFilter> myCustomFilter() {
        FilterRegistrationBean<MyCustomFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new MyCustomFilter());

        // 设置特定过滤路径
        filterRegistrationBean.addUrlPatterns("/*");

        // 设置过滤器顺序
        filterRegistrationBean.setOrder(1);
        return filterRegistrationBean;
    }

通过postman 或者 http 请求在配置类中配置范围的控制器

拦截器

Spring Boot 中的拦截器(Interceptor)主要用于拦截和处理 Spring MVC 框架中的 HTTP 请求和响应。与过滤器(Filter)相比,拦截器更侧重于对 Spring MVC 框架内的请求进行处理,例如,处理控制器方法的前后逻辑。

创建一个拦截器类实现 HandlerInterceptor 接口,重写接口的三个方法 preHandlepostHandleafterCompletion,分别用于在请求处理前、请求处理后(但在视图渲染之前)以及整个请求完成后进行拦截。

package com.shore.configdemo.config.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MyCustomInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在控制器方法执行之前调用
        System.out.println("MyCustomInterceptor: Pre Handle method is Calling");
        // 如果返回 false,将中断请求,不会调用后续的拦截器和控制器方法
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 在控制器方法执行之后,视图渲染之前调用
        System.out.println("yCustomInterceptor: Post Handle method is Calling");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 在整个请求完成之后调用(包括视图渲染和异常处理)
        System.out.println("Request and Response is completed");
    }
}

配置类中注册拦截器,与监听器和过滤器不同,注册拦截器的配置类需要实现 WebMvConfigurer 接口,并重写他的 addInterceptors 方法。

package com.shore.configdemo.config;

import com.shore.configdemo.config.filter.MyCustomFilter;
import com.shore.configdemo.config.interceptor.MyCustomInterceptor;
import com.shore.configdemo.config.listener.MyApplicationListener;
import com.shore.configdemo.config.listener.MyMultiEventListener;
import com.shore.configdemo.config.listener.MyServletContextListener;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletContextListener;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Resource
    private MyCustomInterceptor myCustomInterceptor;

    @Bean
    public FilterRegistrationBean<MyCustomFilter> myCustomFilter() {
        FilterRegistrationBean<MyCustomFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new MyCustomFilter());

        // 设置特定过滤路径
        filterRegistrationBean.addUrlPatterns("/*");

        // 设置过滤器顺序
        filterRegistrationBean.setOrder(1);
        return filterRegistrationBean;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myCustomInterceptor)
                .addPathPatterns("/**") // 拦截特定的请求
                .excludePathPatterns("/login"); // 排除特定的请求
    }
}

一旦拦截器被注册到 Spring Boot 应用中,它就会自动对匹配的请求起作用。根据你的配置(如路径模式),每次符合条件的请求都会先经过拦截器,然后再继续处理。

我们在控制器新增两个方法,一个 post 方法用来访问,一个 get 方法用来拦截器重定向,拦截器建议重定向 get 方法

package com.shore.configdemo.web;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyCustomController {
    @PostMapping("/sayHello")
    public String sayHello() {
        System.out.println("sayHello");
        return "Hello World";
    }

    @GetMapping("/login")
    public String login() {
        System.out.println("login");
        return "login success";
    }
}

我们来分析以下上图的链路,首先我们调用 sayHllo 方法走到过滤器的链路,打印过滤器链之前的逻辑,然后继续后面的过滤器链,接着请求被拦截器拦截到,执行拦截器 preHandler 方法,该方法 重定向到 login 并结束当前链路,不再访问 sayHello,接着 login 又被过滤器捕获到,执行过滤前逻辑,然后被拦截器放行,访问到 login 接口,正常返回又被过滤器捕获到,执行过滤器后置逻辑,至此链路结束。

我们将拦截器中判断 session 返回 false 的逻辑删掉,重新访问 sayHello,执行一次完整的拦截器逻辑.

这里我们可以看到先走到过滤器前置逻辑,接着拦截器 preHandle 方法,然后走到sayHello 接口,然后走到拦截器 postMethod 方法,然后是拦截器 afterCompletion 方法,最后走到过滤器后值逻辑。

定时器

在Spring Boot中,实现定时器功能通常有几种方法,包括使用@Scheduled注解、TaskScheduler接口以及通过@EnableScheduling和配置类来设定定时任务。

@Scheduled注解

@Scheduled注解提供了最简便的方式来声明定时任务。你只需在相应的方法上添加此注解,并指定相应的cron表达式或其他定时规则。

首先,确保你的Spring Boot应用已经启用了定时任务支持,这通常通过在主应用类或配置类上添加@EnableScheduling注解来实现。

package com.shore.configdemo;

import com.shore.configdemo.config.publisher.MyEventPublisher;
import jakarta.annotation.Resource;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class ConfigDemoApplication implements CommandLineRunner {
    @Resource
    private MyEventPublisher myEventPublisher;

    public static void main(String[] args) {
        SpringApplication.run(ConfigDemoApplication.class, args);
    }


    @Override
    public void run(String... args) throws Exception {
        myEventPublisher.publishEvent();
    }
}

创建一个定时任务类,方法使用 @Scheduled 注解标记

package com.shore.configdemo.config.schedule;

import org.springframework.scheduling.annotation.Scheduled;

import java.util.Date;

public class MyCustomScheduleTask {

    @Scheduled(cron = "0 * * * * *")
    public void myScheduleTask() {
        System.out.println("MyCustomScheduleTask: " + new Date());
    }
}

配置类注册定时任务类

@Bean
    public MyCustomScheduleTask myCustomScheduleTask() {
        return new MyCustomScheduleTask();
    }

TaskScheduler接口

如果你需要更细粒度的控制,比如任务池、线程管理等,你可以直接使用TaskScheduler接口。首先,定义一个TaskScheduler的bean

@Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        return taskScheduler;
    }

然后,你可以注入TaskScheduler并使用它来调度任务

package com.shore.configdemo.service.impl;

import com.shore.configdemo.service.MyService;
import jakarta.annotation.Resource;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class MyServiceImpl implements MyService {
    @Resource
    private TaskScheduler taskScheduler;

    @Override
    public void sayHello(String name) {

    }

    public void scheduleTask() {
        taskScheduler.scheduleWithFixedDelay(() -> {
            System.out.println("执行定时任务: " + new Date());
        }, 5000); // 任务间隔5秒
    }
}

@EnableScheduling和配置类

前面已经提到了@EnableScheduling注解,它用于启用Spring的计划任务调度功能。通常,你只需在主应用类或任何配置类上添加此注解,然后就可以在你的应用中使用@Scheduled注解来声明定时任务了。

除了简单的启用定时任务外,你还可以通过实现SchedulingConfigurer接口来自定义定时任务的配置,比如设置任务执行器(TaskExecutor)或任务调度器(TaskScheduler)的属性。

package com.shore.configdemo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;

import java.util.Date;

@Configuration
@EnableScheduling
public class MyCustomSchedulingTask implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(
                () -> System.out.println("执行基于触发器的任务: " + new Date()),
                triggerContext -> {
                    // 自定义触发器逻辑,比如根据时间或条件来决定是否执行任务
                    return new CronTrigger("0 * * * * *")
                            .nextExecutionTime(triggerContext)
                            .toInstant();
                }
        );
    }
}

配置文件属性

自定义配置文件 custom-config.yaml

my-app:
  auth-filter:
    secretKey: mySecretKey
    expirationTime: 864000000 # 10 天
    excludes:
      - /shore/demo/user/login
      - /shore/demo/user/register
      - /shore/demo/user/query
  snowflake:
    workerId: 1
    datacenterId: 1

application.yaml 文件配置自定义配置文件的路径

spring:
  config:
    import: classpath:custom-config.yaml

创建配置文件类,指定要读取的标签,定义要读取的属性

package com.shore.my_spring_demo.web.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;

@ConfigurationProperties(prefix = "my-app.auth-filter")
@Data
public class CustomAuthFilterConfig {
    private String secretKey;
    private long expirationTime;

    private List<String> excludes;
}

配置类中注册配置文件类

@Bean
    public CustomAuthFilterConfig customAuthFilterConfig() throws NoSuchAlgorithmException {
        return new CustomAuthFilterConfig();
    }

最后就可以在服务中生命使用了

package com.shore.my_spring_demo.service.jwt;

import com.shore.my_spring_demo.common.enums.ErrorEnums;
import com.shore.my_spring_demo.exception.UsersException;
import com.shore.my_spring_demo.web.config.CustomAuthFilterConfig;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.security.KeyPair;
import java.util.Date;

@Slf4j
@Service
public class JwtServiceImpl implements JwtService {
    @Resource
    private CustomAuthFilterConfig customConfig;

    @Resource
    private KeyPair keyPair;

    @Override
    public String generateTokenAsymmetric(String username) {
        return Jwts.builder()
                .claim("sub", username).claim("role", "admin")
                .expiration(new Date(System.currentTimeMillis() + customConfig.getExpirationTime()))
                .signWith(SignatureAlgorithm.RS256, keyPair.getPrivate())
                .compact();
    }
}

总结

上述所有在配置类中注册的类,大多都可以直接通过注解 @Compent 标记这种简单的方法进行注册,但是如果想要更加精细化的操作,还是需要在配置类中通过 bean 管理注册。

标签:SpringBoot,配置,springframework,shore,org,import,com,public
From: https://blog.csdn.net/qq_35201802/article/details/143825017

相关文章