【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));
}
}
}
四、整合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文件作为首页文件。
然后输入
localhost:8080/
就可以看到首页的内容了。
2、自定义favicon
转换网址
https://jinaconvert.com/cn/convert-to-ico.php
3、除去某个自动配置
@SpringBootApplication(exclude = {ErrorMvcAutoConfiguration.class})
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}