第02课:Spring

第02课:Spring Boot 基础详解(二)

第01课中,主要为各位读者介绍了实际开发中常用注解和 Spring Boot 中的请求主要流程。本课中,主要带大家掌握 Spring Boot 开发时所需要的以下技能:

  • 简单的 RESTful 风格接口开发;
  • 多环境配置文件动态切换,并读取配置文件内容;
  • Listener 的使用;
  • Filter 的使用;
  • Interceptor 的使用。

开发简单的 RESTful 风格接口

首先,我们访问:http://start.spring.io/,选择构建工具 Maven,采用编程语言 Java,输入如图中的信息,下载项目压缩包:

enter image description here

将下载的项目压缩包解压后,导入 idea 中,然后在此基础项目结构中进行开发即可,在此实例中,项目结构如下图所示:

enter image description here

为了能够开发 RESTful 风格的接口,需要在 pom.xml 文件中添加如下依赖:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
</dependency>

继下来编写 Controller:

@RestController
@RequestMapping(value = "/restful")
public class TestRestfulInterfaceController{

    @RequestMapping("/test")
    public String testRestfulInterface(@RequestParam("name") String name){
        return "hello" + name;
    }
}

将程序启动起来,并利用 Postman 进行测试,输出如下结果:

enter image description here

多环境配置文件动态切换并读取配置文件内容

利用 Spring Boot 进行微服务开发时,配置文件既可以利用 *.properties 文件格式,又可以利用 *.yml 格式文件。不过在开发中,通常选择利用 yml 格式配置文件,该格式文件可读性更好。

下面分别新建 application.yml、application-dev.yml、application-test.yml、application-prod.yml 四个配置文件:

enter image description here

将配置文件切换到测试环境并启动程序后,程序监听的端口变为8100,利用 Postman 测试结果如下:

enter image description here

读取配置文件中的配置项,分别在 dev、test、prod 对应配置文件中添加 student 相关配置信息,在程序中读取配置文件并将其内容打印出来。

配置文件内容,如下图所示:

enter image description here

读取配置文件内容代码:

@Component
public class StudentConfig {
    @Value("${student.name}")
    private String name;

    @Value("${student.age}")
    private Integer age;

    ...此处省略get set方法
}

打印学生信息代码:

@RestController
@RequestMapping(value = "/restful")
public class TestRestfulInterfaceController{
    @Autowired
    private StudentConfig studentConfig;

    @RequestMapping("/test")
    public String testRestfulInterface(@RequestParam("name") String name){
        return "hello:" + name;
    }

    @RequestMapping("/printStudentInfo")
    public String getStudentInfo(){
            return "studentName:" + studentConfig.getName() + 
     "   studentAge:" + studentConfig.getAge();
    }
}

Postman 测试结果,见下图:

enter image description here

监听器 Listener 的使用

Listener 是 Spring 为开发人员提供的一种监听、订阅实现机制,其实现原理是设计模式之观察者模式,设计的初衷是为了系统业务之间进行解耦,以便提高系统可扩展性、可维护性。Listener 主要包括定义事件、事件监听、事件发布。下面分别对它们做介绍。

1. 定义用户注册事件

/**
 *事件定义类
 */
public class UserRegisterEvent extends ApplicationEvent {
    private User user;

    /**
     * source 表示事件源对象
     * user表示注册用户对象
     * @param source
     * @param user
     */
    public UserRegisterEvent(Object source,User user) {
        super(source);
        this.user = user;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

2. 用户注册服务实现类

@Service("userService")
public class UserServiceImpl implements IUserService {
    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 用户注册
     * @param user
     */
    @Override
    public void registerUser(User user) {
        if (user != null) {
            //调用持久层注册用户

            //发布用户注册事件
            applicationContext.publishEvent(new UserRegisterEvent(this, user));
        }
    }
}

3. 用户注册控制类

 @RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private IUserService userService;

    @RequestMapping("/register")
    public String registerUser(@NotNull String userName, @NotNull Integer age) {
        String msg = "success";

        try {
            userService.registerUser(new User(userName,age));
        } catch (Exception e) {
            msg = "error";
        }

        return msg;

    }
}

4. 监听器代码

利用 @EventListener 实现监听用户注册事件:

 /**
 * 利用@EventListener注解监听用户注册事件
 */
@Component
public class AnnotationUserRegisterListener {
    @EventListener
    public void sendMailToUser(UserRegisterEvent userRegisterEvent){
        System.out.println("利用@EventListener注解监听用户注册事件并向用户发送邮件");
        System.out.println("注册用户名:" + userRegisterEvent.getUser().getName());
    }
}

利用接口 ApplicationListener 实现监听用户注册事件:

/**
 * 实现ApplicationListener实现用户注册监听
 */
@Component
public class RegisterUserApplicationListener implements ApplicationListener<UserRegisterEvent> {
    @Override
    public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
        System.out.println("实现接口ApplicationListener监听用户注册事件并向用户发送邮件");
        System.out.println("注册用户名:" + userRegisterEvent.getUser().getName());
    }
}

利用接口 SmartApplicationListener 实现监听用户注册事件:

/**
 *实现接口SmartApplicationListener实现监听用户注册
 */
@Component
public class RegisterUserSmartApplicationListener implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        return aClass == UserRegisterEvent.class;
    }
    /**
     * 注意此处aClass不能与IUserService.class比较
     * @param aClass
     * @return
     */
    @Override
    public boolean supportsSourceType(Class<?> aClass) {
        return aClass == UserServiceImpl.class;
    }
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;

        System.out.println("实现接口SmartApplicationListener监听用户注册事件并向用户发送邮件");
        System.out.println("注册用户名:" + userRegisterEvent.getUser().getName());
    }
    /**
     * 返回值越小监听越靠前
     * @return
     */
    @Override
    public int getOrder() {
        return 1;
    }
}

利用 Postman 调用接口:

http://localhost:8100/user/register?userName=huangchaobing&age=32

实现用户注册,控制台输出监听相关信息:

enter image description here

以上三种监听都属于同步监听,必须等监听逻辑处理完成之后,用户注册业务逻辑才算完成。然而这样,当有用户操作时,会让用户进行等待,给用户的体验不太好,因此下面我们用 @Async 注解实现异步监控。

1. 为异步监听器设置异步线程池对象:

@Configuration
@EnableAsync
public class AsyncListenerConfiguration implements AsyncConfigurer {
    /**
     * 获取异步线程池执行对象
     * @return
     */
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();

        //设置线程数量
        threadPoolTaskExecutor.setCorePoolSize(10);
        threadPoolTaskExecutor.setMaxPoolSize(10);
        threadPoolTaskExecutor.setQueueCapacity(50);
        threadPoolTaskExecutor.initialize();

        return threadPoolTaskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return null;
    }
} 

2. 在需要执行异步监听方法上面添加 @Async注解:

@Component
public class AsyncAnnotationUserRegisterListener {
    @Async
    @EventListener
    public void sendMailToUser(UserRegisterEvent userRegisterEvent) {
        try {
            Thread.sleep(5000);
        } catch (Exception e) {

        }
        System.out.println("利用@EventListener注解监听用户注册事件并异步向用户发送邮件");
    }
}

3. 发送注册用户请求,测试异步监听:

enter image description here

以上是自定义事件、发布事件、监听事件的常用开发方式。为了方便开发者开发,在 Spring Boot 2.0 以后,为开发者定义了如下事件:

enter image description here

  • ApplicationFailedEvent:Spring Boot 启动失败时触发;
  • ApplicationPreparedEvent:上下文 Context 准备时触发;
  • ApplicationReadyEvent:上下文准备完毕的时触发;
  • ApplicationStartedEvent:Spring Boot 启动监听类;
  • SpringApplicationEvent:获取 SpringApplication;
  • ApplicationEnvironmentPreparedEvent:装配完参数和环境后触发的事件。

在工作中我们经常在程序上下文和 Bean 创建成功后做一些比如初始化缓存、缓存预热等操作,这时,便会通过监听 ApplicationReadyEvent 事件完成,该过程分两步完成。

1. 定义监听器:

/**
 1. 初始化redis缓存listener
 */
public class InitializeRedisCacheListener implements ApplicationListener<ApplicationReadyEvent> {

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        ConfigurableApplicationContext applicationContext = applicationReadyEvent.getApplicationContext();

        RedisUtil redisUtil = applicationContext.getBean(RedisUtil.class);

        if(redisUtil != null){
            redisUtil.initializeRedisData();
        }
    }
}

2. 注册监听器 Listener:

@SpringBootApplication
public class Lesson2Application {

    public static void main(String[] args) {

        SpringApplication springApplication = new SpringApplication(Lesson2Application.class);

        springApplication.addListeners(new InitializeRedisCacheListener());

        springApplication.run(args);
    }
}

启动程序后,开始初始化 Redis 缓存数据,如下图所示:

enter image description here

Filter 的使用

Filter 通常用于在请求过程中对用户身份进行认证、过滤等操作,开发过程如下。

1. 继承 Filter 接口定义过滤器:

 @WebFilter(filterName = "loginFilter",urlPatterns = "/*")
public class LoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("对登录进行过滤操作");

        //请求放行
     filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

2. 在程序启动类加上注解 @ServletComponentScan,将过滤器加入过滤器链:

 @SpringBootApplication
@ServletComponentScan
public class Lesson2Application {

    public static void main(String[] args) {

        SpringApplication springApplication = new SpringApplication(Lesson2Application.class);

        springApplication.addListeners(new InitializeRedisCacheListener());

        springApplication.run(args);
    }
}

启动程序利用 Postman 发送请求,测试结果如下:

enter image description here

除了利用 @ServletComponentScan 将 Filter 加入过滤器链,还可以在程序启动类中加入 @Bean 注解将 Filter 加入过滤器链:

@SpringBootApplication
//@ServletComponentScan
public class Lesson2Application {

    public static void main(String[] args) {

        SpringApplication springApplication = new SpringApplication(Lesson2Application.class);

        springApplication.addListeners(new InitializeRedisCacheListener());

        springApplication.run(args);
    }

    @Bean
    public FilterRegistrationBean securityFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();

        Filter loginFilter = new LoginFilter();

        registration.setFilter(loginFilter);
        registration.addUrlPatterns("/*");
        registration.setName("loginFilter");
        registration.setOrder(1);

        return registration;
    }
}

测试结果如下:

enter image description here

Interceptor 的使用

本节将大家利用拦截器 Interceptor 完成请求过滤、记录请求日志、判断用户是否登录等操作。

实现自定义拦截器需要如下三步:

1. 实现接口 HandlerInterceptor 并根据需要重载其方法。其方法主要有:

  • preHandle,该方法在处理器方法执行之前执行;
  • postHandle,该方法在处理器方法执行之后执行;
  • afterCompletion,该方法在视图渲染之后被执行;

示例代码如下:

public class WebInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse
            httpServletResponse, Object o) throws Exception {
        System.out.println("============== request before ==============");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse
            httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("==============  request  ==============");
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("==============  request completion  ==============");
    }
}

2. 创建类 DefInterceptorConfig,并继承于 WebMvcConfigurerAdapter,重写其 addInterceptors 方法,将自定义拦截器加入拦截器链中:

 @Configuration
public class DefInterceptorConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new WebInterceptor()).addPathPatterns("/**");
    }
}

3. 启动应用程序,访问接口,查看自定义拦截器输出如下:

enter image description here

本文就分享到此了,内容不少,各位好好消化下。下一课我将大家学习 Spring Boot 整合常用技术框架的方法。

上一篇
下一篇
目录