【SpringBoot整合web开发 下】

【SpringBoot整合web开发 下】

一、配置类与XML配置

1、配置类

​ SpringBoot推荐使用Java来完成相关的配置工作。但是不建议将所有的配置放在一个配置类中。这些配置类上都需要添加@Configuration注解,@ComponentScan注解会扫描所有的Spring组件,也包括@Configuration。

​ @ComponentScan注解在项目入口类的@SpringBootApplication注解中已经提供,因此在实际项目中只需要按照需要提供相关的配置类即可。

2、XML配置

  • 在resources提供配置文件,然后通过@ImportResource加载配置文件即可。

Hello类

package com.example.demo.controller;

public class Hello {
    public String sayHello(String name){
        return "hello" + name;
    }
}

在resources目录下新建beans.xml文件配置该类

<beans xmlns ="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean class="com.example.demo.controller.Hello" id="hello"/>

</beans>

然后创建Beans配置类,导入XML配置。

package com.example.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ImportResource("classpath:beans.xml")
public class Beans {

}

最后在Controller中就可以直接导入Hello类使用了

package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    Hello hello;
    @GetMapping("/hello")
    public String hello(){
        return hello.sayHello("darkerg学springboot");
    }
}

二、注册拦截器

SpringMVC提供了AOP风格的拦截器,拥有更加精细的拦截处理能力。SpringBoot中拦截器的注册更加方便,步骤如下:

1、创建springboot项目,添加web依赖

2、创建拦截器实现HandlerInterceptor接口

package com.example.demo.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor1 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("MyInterceptor1>>>preHandle");
        
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor1>>>afterCompletion");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("myInterceptor1>>>afterCompletion");
    }
}

拦截器中的方法将按照preHandle->Controller->postHandle->afterCompletion的顺序执行。注意,**只有preHandle方法返回true时,后面的方法才会执行。当拦截器链内存在多个拦截器时候,postHandle在所有拦截器返回成功时候才会调用,afterCompletion同理。

3、配置拦截器、定义配置类进行拦截器的配置

package com.example.demo.config;

import com.example.demo.interceptor.MyInterceptor1;
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 WebMvcConfig implements WebMvcConfigurer {

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

自定义的类实现WebMvcConfigurer接口,实现接口中的addInterceptors方法。其中,addPathPatterns表示拦截路径,excludePathPatterns表示排除的路径。

4、测试

在浏览器中提供/hello2和/hello接口分别进行访问,当访问/hello2接口时候,打印日志。

三、启动系统任务

​ 有些特殊的任务需要在系统启动时候执行,例如配置文件的加载、数据库初始化等操作。如果没有使用SpringBoot,这些问题可以在Listener中解决。SpringBoot对此提供了两种解决方案:CommandLineRunner和ApplicationRunner。CommandLineRunner和ApplicationRunner基本一致。

1、CommandLineRunner

​ 在SpringBoot web项目中添加两个CommandLineRunner:

package com.example.demo.runner;

import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Order(1)
public class MyCommandLineRunner1 implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("Runner1>>>"+ Arrays.toString(args));
    }
}
package com.example.demo.runner;

import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Order(2)
public class MyCommandLineRunner2 implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("Runner2>>>"+ Arrays.toString(args));
    }
}

代码解释:

  • @Order(1)注解用来描述CommandLineRunner的执行顺序,数字越小越先执行。
  • run方法中是调用的核心逻辑,参数是系统启动时传入的参数,即入口类中main方法的参数。

2、ApplicationRunner

​ 与CommandLineRunner的区别主要体现在run方法的参数上。新建两个ApplicationRunner

package com.example.demo.runner;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;

@Component
@Order(3)
public class MyApplicationRunner1 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        List<String> nonOptionArgs = args.getNonOptionArgs();
        System.out.println("1-nonOptionArgs>>>"+nonOptionArgs);
        Set<String> optionNames = args.getOptionNames();
        for (String optionName : optionNames) {
            System.out.println("1-key:"+optionName+";value:"+args.getOptionValues(optionName));
        }
    }
}
package com.example.demo.runner;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;

@Component
@Order(4)
public class MyApplicationRunner2 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        List<String> nonOptionArgs = args.getNonOptionArgs();
        System.out.println("2-nonOptionArgs>>>"+nonOptionArgs);
        Set<String> optionNames = args.getOptionNames();
        for (String optionName : optionNames) {
            System.out.println("2-key:"+optionName+";value:"+args.getOptionValues(optionName));
        }
    }
}

image-20210916101926264

image-20210916101941939

四、整合Servlet、Filter和Listener

​ 在SpringBoot的web项目中添加下面三个组件

Servlet

package com.example.demo.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/my")
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("name>>>"+req.getParameter("name"));
    }
}

Fileter

package com.example.demo.Filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("MyFilter>>>init");
    }

    @Override
    public void destroy() {
        System.out.println("MyFilter>>>destory");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("MyFilter>>doFilter");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

Listener

package com.example.demo.Linstener;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class MyListener implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("MyLinsener>>>requestDestroyed");
    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("MyLinster>>>requestInitialized");
    }
}

对于其他的HttpSessionListener、ServletContextListener等也是支持的。

在入口类上添加@ServletComponentScan注解,实现对这三个组件的扫描。

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan
public class DemoApplication {

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

}

最后、启动项目,访问http://localhost:8080/my?name=jackson,可以看到日志。

五、路径映射

​ 在使用了页面模板之后,用户需要通过控制器才能访问页面。有一些页面需要在控制器中家在数据。然后渲染,才能显示出来;还有一些页面在控制器中不需要加载数据,只是完成简单的跳转,对于这种页面,可以直接配置路径映射,提高访问速度。例如,有两个Thymeleaf做模板的页面login.html和index.html,直接在MVC配置中重写addViewControllers方法配置映射关系即可:

package com.example.demo.config;

import com.example.demo.interceptor.MyInterceptor1;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/index").setViewName("index");
    }
}

配置完成之后,就可以直接访问localhost:8080/login等地址了。

六、配置AOP

面向切面编程(Aspect-Oriented Programming,AOP)

  • 场景:公司有一个人力资源管理系统已经上限运行,但是系统不稳定,有时候运行很慢,为了检测哪个环境出现了问题,开发人员想要监控每一个方法的执行时间。如果手动修改运行时候的成千上万个方法,然后再移除掉,工作量太大了。如果能够再运行过程中动态添加代码,就能很好的解决这个问题。
  • Joinpoint(连接点):类里面可以被增强的方法即为连接点。例如:想修改哪个方法的功能,那么该方法就是一个连接点。
  • Pointcut(切入点):对Joinpoint进行拦截的定义即为切入点。例如:拦截所有以insert开始的方法,这个定义即为切入点。
  • Advice(通知):拦截到Joinpoint之后所要做的事情就是通知。例如,上文说到的打印日志监控。通知分为前置通知、后置通知、异常通知、最终通知和环绕通知。
  • Aspect(切面):Pointcut和Advice的结合
  • Target(目标对象):要增强的类称为Target

1、添加SpringBoot支持

①在SpringBoot web项目中导入依赖

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

②在Service包下新建一个UserService类

package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
    
    public String getUserById(Integer id){
        System.out.println("get....");
        return "user";
    }
    
    public void deleteUserById(Integer id){
        System.out.println("delete");
    }
    
}

③创建切面

package com.example.demo.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LogAspect {

    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void pc1(){

    }
    @Before(value = "pc1()")
    public void after(JoinPoint jp){
        String name = jp.getSignature().getName();
        System.out.println(name+"方法执行结束....");
    }
    @AfterReturning(value = "pc1()",returning = "result")
    public void afterReturning(JoinPoint jp,Object result){
        String name = jp.getSignature().getName();
        System.out.println(name+"方法返回值为:"+result);
    }
    @AfterThrowing(value = "pc1()",throwing = "e")
    public void afterThrowing(JoinPoint jp,Exception e){
        String name = jp.getSignature().getName();
        System.out.println(name+"方法抛出异常了,异常是:"+e.getMessage());
    }
    @Around("pc1()")
    public Object arround(ProceedingJoinPoint pjp) throws Throwable{
        return pjp.proceed();
    }

}

代码解释:

  • @Aspect表明这是一个切面类
  • pc1方法使用了@Pointcut注解,这是一个切入点定义。execution中的第一个*表示方法返回任意值,第二个*表示service包下的任意类,第三个*表示类中的任意方法。括号中的两个点表示方法参数任意。
  • @Before注解,表示这是一个前置通知,该方法在目标方法执行之前执行。通过JoinPoint参数可以获取目标方法的方法名。修饰符等信息。
  • @After,后置通知。
  • @AfterReturing,返回通知。可以获取目标方法的返回值。returning参数是指返回值的变量名。定义返回值的类型为Object,可以是任意类型。
  • @AfterThrowing注解,异常通知。
  • @Around注解,环绕通知,可以实现前置、后置、异常通知以及返回通知的功能。

2、测试

新建一个接口,分别调用UserService中的两个方法。

package com.example.demo.controller;

import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
    @Autowired
    UserService userService;

    @GetMapping("/getUserById")
    public String getUserById(Integer id){
        return userService.getUserById(id);
    }

    @GetMapping("/deleteUserById")
    public void deleteUserById(Integer id){
        userService.deleteUserById(id);
    }
}

七、其他

1、自定义欢迎页

​ SpringBoot启动后,首先回去静态资源路径下查找index.html作为首页文件。若查找不到,就回去查找动态的index文件作为首页文件。

image-20210916113417548

然后输入

localhost:8080/

就可以看到首页的内容了。

2、自定义favicon

转换网址

https://jinaconvert.com/cn/convert-to-ico.php

image-20210916113547753

3、除去某个自动配置

image-20210916113855077

@SpringBootApplication(exclude = {ErrorMvcAutoConfiguration.class})
public class DemoApplication {

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

}

image-20210916114337666

posted @ 2021-09-16 11:44  DarkerG  阅读(44)  评论(0编辑  收藏  举报