SpringBoot框架
SpringBoot框架基础介绍
通过昨天的SpringBoot应用程序的实例,可以看到Boot程序和核心就是@SpringBootConfiguration注解类,该类是SpringBoot的核心Spring容器,并且还能实现自动配置和扫描其他文件的注解
昨天提到SpringBoot的核心配置文件就是Resource目录下的application.yml或者properties;其中主推的就是yml类型
在实际开发中,项目会经历很多的阶段【开发–> 测试 --> 上线】,每个阶段的配置是不同的,比如【端口,上下文根、数据库等】,这个时候,为了方便环境的切换,SpringBoot提供了多环境配置;还是在application.yml中实现
这个时候就创建多个配置文件【一个配置文件,对应一个环境 】,命名都是application-环境标识.yml
application-dev.yml //开发环境的核心配置文件
application-product.yml //生产环境核心配置文件
application-test.yml //测试环境核心配置文件
然后总的配置文件就是application.yml; 想要激活使用哪个配置文件,就在 applicaiton.properties中进行指定
spring.profiles.active=dev
这样就指定激活开发环境的配置文件
@Value("${key}"), 可以来自外部的文件,比如application.properties;
也就是说,在application中除了使用系统自带的配置之外,可以自定义一些数据,然后供容器的DI使用
#######Spring Boot配置文件#######
##设置端口号####
server:
port: 8081
servlet:
context-path: /mySpringBoot #上下文路径
###自定义的数据配置,SpringBoot中不需要再使用PropertiesSource导入,已经内置了####
school:
name: CfengIT
adress: Peking
这里自定义了数据,然后在Controller中进行注入
@Controller
public class SimpleTest {
@Value("${server.port}")
private int stuNo;
@Value("${school.name}")
private String stuName;
@Value("${school.adress}")
private String stuClass;
这里是可以正常访问到数据的
这里博主出现了一个报错: java.lang.IllegalArgumentException: Could not resolve placeholder ‘stu.no’ in value “${stu.no}”
这个如果是mybatis就是名称不对应、实体类找不到等; 这里问题是不能用no、name、class这种类型的; 换成num或者stuno都是可以正常读取的emmm。。。。
这个注解可以将整个文件映射成为一个对象【和之前的JavaConfig思想相同】;将整个文件映射成为一个对象,用于自定义配置项比较多的情况 ; 注解放在创建的Info类上,通过prefix自定前缀
— 这样就会在配置文件中寻找prefix为XX下面的配置属性; 如果没有前缀,那么就和前面的是相同的
@Component("myStudent")
@ConfigurationProperties(prefix = "stu")
public class Student {
private int stuNo;
private String stuName;
private String stuClass;
运行bootAppliction,就可以注入这个对象;这里测试bootAppliction时,
@Test
public void testContext() {
ApplicationContext container = new AnnotationConfigApplicationContext(BootApplication.class);
Arrays.stream(container.getBeanDefinitionNames()).forEach(name -> System.out.println(name + " 值为 : " + container.getBean(name)));
}
这里确不能正常进行属性注入【配置文件】;后面博主会再解释这里的问题
适用这个注解的时候,IDEA会给出提示信息: Spring Boot Annotation Processor not config; 也就是没有配置annotation-processor注解处理器依赖项; 在官网中找到依赖添加到pom文件即可
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
这个处理器就是处理和ConfigurationProperties有关的元数据的信息的
和之前的SSM创建的项目不同,SpringBoot默认是不支持JSP的,推荐使用模板来代替,所以要想在SpringBoot项目中使用JSP,需要加入SpringBoot内嵌的Tomcat对JSP的解析包
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-jasperartifactId>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifact>jstlartifact>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifact>javax.servlet-apiartifact>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifact>javax.servlet.jsp-apiartifact>
dependency>
webapp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
使用jsp
测试 ${data}
<resources>
<resource>
<directory>src/main/webappdirectory>
<targetPath>META-INF/resourcestargetPath>
<includes>
<include>**/*.*include>
includes>
resource>
resources>
这里的资源插件的配置就和之前在SSM中配置资源是相同的,是为了能够将这些文件从原理的目录中放到执行的target的classes中;targetpath就是指代的target/classes; 之前为了移动java下面的mapper配置文件也进行了配置;同时还要配置resources下面的文件
(可以发现这样又需要webapp了,META-INF之前也提到过,和WEB-INF在一级)
package cfeng.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class MyController {
@RequestMapping("/hello")
public String getJsp(HttpServletRequest req) {
req.setAttribute("data","Hello, study Spring Boot with Cfeng");
return "index";
}
}
##配置端口号和根路径
server:
port: 8081
servlet:
context-path: /myboot
##配置视图解析器
spring:
mvc:
view:
prefix: / #这里的/代表的就是webapp
suffix: .jsp
创建一个普通的springBoot的web项目,在pom中加入依赖
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-jasperartifactId>
dependency>
因为Boot项目是没有webapp文件夹的,需要创建一个(和之前的SSM的相同);然后要修改这个文件夹的类型,在项目结构中配置web;这样webapp就可以出现蓝色小点,就可以存放jsp文件
在浏览器中访问hello就可以成功得到结果
在main方法中SpringApplication.run方法获取返回的Spring容器对象,再获取业务bean进行调用;【比如之前的SSM项目为了在一开始获取到数据,就配置了监听器获取Spring的容器对象】,那么在SpringBoot中怎么进行?
查看Application的run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
返回值是一个ConfigurableApplicationContext类型的对象,那么看看这个对象到底是什么类型
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable
可以看到继承了ApplicationContext接口;就是一个Spring的容器;【 之前还提过SpringBootConfiguration注解就可以让变为容器】
这里可以用来测试,获取容器的方法就是run的返回值;所以这里可以解答上面的问题
@Test
public void testContext() {
// ApplicationContext container = new AnnotationConfigApplicationContext(BootApplication.class);
ApplicationContext container = SpringApplication.run(BootApplication.class);
Arrays.stream(container.getBeanDefinitionNames()).forEach(name -> System.out.println(name + " 值为 : " + container.getBean(name)));
}
上面博主的写法就是使用之前的Annotation的写法,但是那是获取的自定义的配置类的方法,在SpringBoot项目中,使用@SpringBootConfirguration注解的起始类,虽然BootApplication本身就可以当作容器使用,可以直接使用@Bean创建对象,但是如果通过new Annotation的方式就不能正常扫描到其他的注解对象;所以之前就一直抛出异常
正确的方法就是使用返回值
ApplicationContext container = SpringApplication.run(BootApplication.class);
如果有参数可以接收,没有就只用放入BootApplicaiton.class
simpleTest 值为 : Cfeng.controller.SimpleTest@51dae791
myStudent 值为 : Student{stuNo=22, stuName='Cfeng', stuClass='HC2001'}
student 值为 : Sun Mar 27 15:19:38 CST 2022
------这里就自动进行了扫描并且属性通过application.yml进行DI----------而student就是在BootApplication中使用@Bean进行声明的对象; simpleTest就是controller对象---
除了这几个之外,还有就是自动配置的许多对象
比如:
dispatcherServlet 值为 : org.springframework.web.servlet.DispatcherServlet@4eba373c
taskExecutorBuilder 值为 : org.springframework.boot.task.TaskExecutorBuilder@10947c4e
applicationTaskExecutor 值为 : org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor@3667faa8
beanNameViewResolver 值为 : org.springframework.web.servlet.view.BeanNameViewResolver@b46e103
requestMappingHandlerAdapter值为: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter@705a8dbc
requestMappingHandlerMapping 值为 : org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@40729f01
characterEncodingFilter 值为 : org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter@646c0a67
这几个对象都是之前SSM中就提到过的对象,中央处理器,任务执行链,后面如果有时间会对这几个对象的作用进一步解释
在开发中,在容器对象创建好之后,可能会进行一些操作,比如读取配置文件,数据库的连接,SpringBoot提供了两个接口CommandRunner和ApplicationRunner接口实现这种操作;
这两个接口的执行时机都是容器启动完成的时候,接口中都有一个run方法,只需要实现该方法即可,两个接口的不同之处: ApplicaitonRunner的run方法参数为ApplicationArguments; 而CommandLineRunner接口的run方法的参数就是String数组
依赖这两个接口,就可以在容器对象创建好后来自定义执行一些操作【不加web依赖,项目会自动加入spring-boot-starter】比web-starter少一些包
package Cfeng;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.Date;
@SpringBootApplication
public class BootApplication implements CommandLineRunner {
public static void main(String[] args) {
//运行SpringBoot程序,启动内置的Tomcat,并且返回值为容器对象
System.out.println("准备创建容器");
SpringApplication.run(BootApplication.class,args);
System.out.println("容器创建完成");
}
@Bean("student")
public Date createDate() {
return new Date();
}
@Override
public void run(String... args) throws Exception {
//在这里可以执行各种操作
System.out.println("创建容器后,执行了各种操作");
}
}
这里查看控制台,发现Tomcat启动后,在控制台输出该语句,说明run方法的操作会在容器创建之后执行
准备创建容器
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.5)
创建容器后,执行了各种操作
容器创建完成
可以简单看成在main中的run执行的时候,会调用下面的run方法,run方法中可以使用容器创建的对象;可以直接通过id来使用
SpringBoot中在Spring和SpringMVC的基础上进一步的封装简化,不需要手动配置中央调度器
之前在SpringMVC中使用拦截器,拦截器是SpringMVC中的一种对象,当时将各种操作: 比如全局的异常处理,拦截器都放在handler包下面,全局异常处理依靠的是aop,配置切入点即可;而interceptor前面在vue中也提过,vue中就是拦截请求,向请求头中加入数据,或者执行操作Loading效果; 而MVC中的拦截器实现需要implements HandlerInterceptor接口; 按需实现接口的几个方法就可以拦截,比如preHandle ---- 请求处理之前【权限验证】; postHandle ----- 处理器方法处理之后【可以对数据二次修正】; afterCompletion ---- 本次请求完成之后,forward【资源释放】
并且需要在MVC的配置文件中注册该拦截器对象
SpringBoot中除了核心的Application.yml之外是没有其他的配置文件的,那么是实现的拦截器如何进行注册呢? SpringBoot中大量运用JavaConfig来代替配置文件,所以注册拦截器需要在WebMvcConfigurer实现类中进行注册;
public interface WebMvcConfigurer {
default void configurePathMatch(PathMatchConfigurer configurer) {
}
default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}
default void addFormatters(FormatterRegistry registry) { //格式化操作
}
default void addInterceptors(InterceptorRegistry registry) { //添加注册拦截器
}
default void addResourceHandlers(ResourceHandlerRegistry registry) { //添加静态资源解析器 【之前需要配置默认】
}
default void addCorsMappings(CorsRegistry registry) {
}
default void addViewControllers(ViewControllerRegistry registry) { //处理视图
}
default void configureViewResolvers(ViewResolverRegistry registry) {
}
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}
default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) { //处理异常
}
default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
@Nullable
default Validator getValidator() {
return null;
}
@Nullable
default MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
这里的每一个方法就是对应的之前的配置文件的配置操作
首先先实现一个拦截器
package Cfeng.handler;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("执行了preHandle方法进行拦截");
return true; //返回值为真,放行
}
}
之后就是编写mvc的配置类,在配置类中注册拦截器
package Cfeng.config;
import Cfeng.handler.MyInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration //表明该类为配置类
public class myMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加拦截器对象,注入到容器中,参数就是一个注册器
HandlerInterceptor interceptor = new MyInterceptor();
//addPathPattern就是要拦截的路径,下面的exclude就是不拦截的路径
String[] path = {"/user/**"};
String[] exPath = {"/user/login"};
registry.addInterceptor(interceptor).addPathPatterns(path).excludePathPatterns(exPath);
}
}
直接实现addInterceptor方法就可以注册拦截器,创建一个拦截器对象注册,通过方法addPathPatterns指定拦截的路径;excludePathPatterns指定不拦截的路径【排除的】
在mvc中,就需要在配置文件中
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<bean class="Jning.handler.MyInterceptorHandler"/>
mvc:interceptor>
mvc:interceptors>
访问account时可以看到进行了拦截;login的时候没有进行拦截
容器创建完成
2022-03-27 16:44:48.788 INFO 9764 --- [nio-8082-exec-1] o.a.c.c.C.[.[localhost].[/mySpringBoot] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-03-27 16:44:48.788 INFO 9764 --- [nio-8082-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-03-27 16:44:48.788 INFO 9764 --- [nio-8082-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
执行了preHandle方法进行拦截
访问了路径/user/account
这里表明,当创建容器完成之后,并没有马上Initializing Spring DispatcherServlet;而是当发生第一次访问之后,才会进行初始化;
之前在没有使用SSM框架的时候,就是纯粹的Servle来处理请求,但是一个Servlet只能处理一个地址的请求,有些不便;
在SpringBoot框架中使用Servlet对象只需要创建一个Servlet类,并且进行注册即可;将对象放入容器
package Cfeng.controller;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
//创建响应的应答对象,数据输入响应体
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
out.println("Hello,Cfeng, study Spring Boot");
out.flush();
out.close();
}
}
接下来就是注册Servlet对象,之前没有SSM的时候,注册Servlet可以在web.xml中进行注册,相当于放入Tomcat的容器;注解式开发可以在Servlet上面通过@WebServlet来指定pattern,创建对象
在框架中,Servlet对象类型为ServletRegistrationBean,这就是Servlet注册对象;构造函数的参数就是Servlet对象和url的pattern
package Cfeng.config;
import Cfeng.controller.MyServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class myWebServletConfig {
//注册Servlet对象进入容器
@Bean("myServlet")
public ServletRegistrationBean setServlet() {
//创建一个对象;有参构造,分别是Servlet对象和其url
//public ServletRegistrationBean(T servlet, String... urlMappings)
ServletRegistrationBean bean = new ServletRegistrationBean(new MyServlet(),"/user/hello");
return bean;
}
}
这样之后就可以成功使用Servlet的方式访问成功;
这里也可以使用无参的构造方法来创建对象
@Configuration
public class myWebServletConfig {
//注册Servlet对象进入容器
@Bean("myServlet")
public ServletRegistrationBean setServlet() {
//创建一个对象;有参构造,分别是Servlet对象和其url
//public ServletRegistrationBean(T servlet, String... urlMappings)
ServletRegistrationBean bean = new ServletRegistrationBean();
bean.setServlet(new MyServlet());
bean.addUrlMappings("/user/hello","/user/nihao");//这里是可变的参数,可以设置为多个地址
return bean;
}
}
请求会先经过过滤器,然后再进行应答,是Servlet规范的,对请求的参数、属性进行调整,经常用来进行字符集的设置
过滤器的使用和Servlet的使用十分类似,都是需要注册,将注册过滤器对象放入容器
package Cfeng.filter;
import javax.servlet.*;
import java.io.IOException;
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("执行了过滤器方法");
filterChain.doFilter(servletRequest,servletResponse);
}
}
之后还是在配置类中将注册对象放入容器,这里和Servlet类似;所以就放在同一个类中
package Cfeng.config;
import Cfeng.controller.MyServlet;
import Cfeng.filter.MyFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyWebConfig {
//注册Servlet对象进入容器
@Bean("myServlet")
public ServletRegistrationBean setServlet() {
//创建一个对象;有参构造,分别是Servlet对象和其url
//public ServletRegistrationBean(T servlet, String... urlMappings)
ServletRegistrationBean bean = new ServletRegistrationBean();
bean.setServlet(new MyServlet());
bean.addUrlMappings("/user/hello","/user/nihao");//这里是可变的参数,可以设置为多个地址
return bean;
}
//注册Filter过滤器对象
@Bean("myFilter")
public FilterRegistrationBean setFilter() {
//创建一个FilterRegistrationBean对象
FilterRegistrationBean bean = new FilterRegistrationBean();
//指定拦截的地址和过滤器对象
bean.setFilter(new MyFilter());
bean.addUrlPatterns("/user/*");
return bean;
}
}
注意;这里的匹配路径是/* 不是/**; 成功注册之后过滤器正常使用
执行了过滤器方法
访问了路径/user/account
CharactorEncodingFilter;解决post请求中文乱码问题;之前的Servlet开发中,post请求经常中文乱码,【因为传输的时候默认的字符格式为ISO-8859-1】所以为了解决这个问题,就需要设置字符集过滤器;在SpringMVC中,在web.xml中注册过滤器
<filter>
<description>字符集过滤器description>
<filter-name>encodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
<init-param>
<param-name>forceEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
这是MVC中自带的CharacterEncodingFilter类
在springBoot中要使用这个类,还是像上面的Filter注册相同,还是在config中注册【springBoot使用JavaConfig代替的配置文件】
//注册字符Filter过滤器对象
@Bean("myFilter")
public FilterRegistrationBean setFilter() {
//创建一个FilterRegistrationBean对象
FilterRegistrationBean bean = new FilterRegistrationBean();
//指定拦截的地址和过滤器对象 --- mvc提供的字符过滤器
CharacterEncodingFilter characterFilter = new CharacterEncodingFilter();
characterFilter.setEncoding("UTF-8");
characterFilter.setForceEncoding(true);
bean.setFilter(characterFilter);
bean.addUrlPatterns("/*");
return bean;
}
可以看到,注册很easy,因为这个类是mvc框架提供的,不需要自己写;在filter中还是需要使用之前的操作设置一下字符的编码方式
同时为了能够让修改生效,需要在application.yml中设置emcoding.enadbled
server.servlet.encoding.enabled = false
因为SpringBoot中默认已经配置了CharacterEncodingFilter,编码默认是ISO-8859-1,需要关闭默认的过滤器
SpirngBoot中已经有一个默认的过滤器,只是编码方式不对,所以只需要设置一下编码方式即可
server.servlet.encoding.enabled= true
#指定使用的编码方式
server.servlet.encoding.charset= utf-8
#强制请求和应答对象都使用charset
server.servlet.encoding.force= true
这样就也可以正常进行字符的过滤
接下来就是整合Mybatis了