2021/07/24 SpringBoot2 Web开发快速入门

  • 环境:JDK13、IDEA、SpringBoot2.5.3、maven3.8.1

目录

一、辅助插件

1、lombok

2、spring-boot-devtools

3、Spring Initailizr(项目初始化向导)

二、配置文件

1、yaml文件赋值

基本语法

三、Web开发

1、SpringMVC自动配置概览

2、静态资源

3、欢迎页

4、常用参数注解

@PathVariable(路径变量)和 @RequestHeader(获取请求头)

@RequestParam(获取前端返回的参数)和@Cookie(获取Cookie)

@RequstBody(获取请求体【post请求才有】)

四、视图解析与模板引擎

1、模板引擎-Thymeleaf

thymeleaf简介

基本语法

开发步骤

页面跳转

检查用户是否登录

 抽取公共部分

表格的遍历

五、 拦截器

六、文件上传

七、错误处理

1、默认规则

八、Web原生组件注入(Servlet,Filter,Listener)

1、注解注入方式

①Servlet

②Filter

③Listener

2、使用RegistrationBean


一、辅助插件

1、lombok

可以生成get,set方法,有参无参构造器,日志等的注解,引入依赖后去在idea的插件商店中搜索lombok并安装即可

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.16</version>
    <scope>provided</scope>
</dependency>

2、spring-boot-devtools

热部署插件,改变项目代码不需要重新部署也可以生效,只需重新编译项目

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <version>2.5.2</version>
</dependency>

3、Spring Initailizr(项目初始化向导)

在idea中new project按照提示选择需要的组件创建一个工程,自动分配目录 

二、配置文件

SpringBoot项目给属性赋值的方式

1、yaml文件赋值

基本语法

  • key: value;kv之间有空格
  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • '#'表示注释
  • 字符串无需加引号,如果要加,''与""表示字符串内容 会被 转义/不转义

 1、创建两个实体类Person和Dog

package com.kuang.pojo;

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

@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class Dog {
    private String name;
    private Integer age;
}
package com.kuang.pojo;

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

import java.util.Date;
import java.util.List;
import java.util.Map;

@AllArgsConstructor
@Data
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}

 注意:Person类中的@ConfigurationProperties,与配置文件绑定,如果属性值中有  \n  被单引号包围,则原样输出,双引号包围输出换行

此注解即可以引用yaml文件中对应person开头的属性的值,以此为属性赋值,下面是yaml文件内容,yaml文件语法对空格要求极为严格,以下为:对peson对象的各个属性进行赋值,yaml中对象person的首字母必须小写,属性名与实体类要么一致,要么遵循松散绑定


person:
  name: Young
  age: 22
  happy: true
  maps: {k1: v1,k2: v2}
  lists:
    - 1
    - 2
    - 你好
  dog:
    name: 旺财
    age: 3
  birth: 2021/7/22


测试:

package com.kuang;

import com.kuang.pojo.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootApplicationTests {

    @Autowired
    private Person person;

    @Test
    void contextLoads() {
        System.out.println(person);
    }

}

结果:

Person(name=Young, age=22, happy=true, birth=Thu Jul 22 00:00:00 GMT+08:00 2021, maps={k1=v1, k2=v2}, lists=[1, 2, 你好], dog=Dog(name=旺财, age=3))

当然这只是一种方式,使用@Value注解与properties文件亦可

三、Web开发

1、SpringMVC自动配置概览

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.(大多场景我们都无需自定义配置)

The auto-configuration adds the following features on top of Spring’s defaults:

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
  • 内容协商视图解析器和BeanName视图解析器
  • 静态资源(包括webjars)
  • Automatic registration of Converter, GenericConverter, and Formatter beans.
  • 自动注册 Converter,GenericConverter,Formatter
  • 支持 HttpMessageConverters (后来我们配合内容协商理解原理)
  • 自动注册 MessageCodesResolver (国际化用)
  • Static index.html support.
  • 静态index.html 页支持
  • 自定义 Favicon
  • 自动使用 ConfigurableWebBindingInitializer ,(DataBinder负责将请求数据绑定到JavaBean上)

2、静态资源

默认情况下,Spring Boot 提供来自类路径中名为/static(或/public/resources/META-INF/resources)的目录作为静态资源目录,

默认情况下,资源映射在 上/**,但您可以使用spring.mvc.static-path-pattern属性对其进行调整。例如,将所有资源重新分配到/resources/**可以实现如下:

spring.mvc.static-path-pattern=/resources/**

即为:

spring:
  mvc:
    static-path-pattern: /res/**

 表示res文件夹下所有资源都为静态资源

 注意:如果controller与静态资源重合,请求会先找controller解决,如果没有找到对应的controller,则找静态资源,若找不到静态资源,则404

3、欢迎页

它首先在配置的静态内容位置中查找index.xml文件。如果没有找到,它就会寻找一个index模板。如果找到其中之一,它会自动用作应用程序的欢迎页面。

两种方式设置欢迎页

  • 默认静态资源路径下 index.html
  • ccontroller能处理/index
  • 可以配置静态资源路径
  • 但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
spring:
#  mvc:
#    static-path-pattern: /res/**   这个会导致welcome page功能失效

  resources:
    static-locations: [classpath:/haha/]

4、常用参数注解

@PathVariable(路径变量)和 @RequestHeader(获取请求头)

可用于restful风格

    /*
     * @PathVariable
     * 参数map默认可以返回请求参数的键值对形式
     */
    @GetMapping("/a/{user}/{pwd}")
    @ResponseBody
    public String a(@PathVariable("user") String name,
                    @PathVariable("pwd") String password ,
                    @PathVariable Map<String,String> map    ){
        return name+"|"+password+"|"+map;
    }

结果:

我们在次注解的底层代码中,可以找到这样一段注释

* <p>If the method parameter is {@link java.util.Map Map&lt;String, String&gt;}
 * then the map is populated with all path variable names and values.

 也就是说,当有一个参数为Map<String,String>类型时,自动用变量名和值填充映射

        /*
         * @PathVariable
         */
        @GetMapping("/a/{user}/{pwd}")
        public Map<String,Object> a(@PathVariable("user") String name,
                        @PathVariable("pwd") String password ,
                        @PathVariable Map<String,String> map,
                        @RequestHeader("User-Agent") String userAgent,
                        @RequestHeader Map<String,String> header){
            Map<String, Object> hashMap = new HashMap<>();
            hashMap.put("name",name);
            hashMap.put("password",password);
            hashMap.put("map",map);
            hashMap.put("user-agent",userAgent);
            hashMap.put("header",header);
            return hashMap;
        }

测试:

@RequestParam(获取前端返回的参数)和@Cookie(获取Cookie)

    @RequestMapping("/b")
    public Map<String,Object> hello(@RequestParam("hobby") String hobby,
                                    @RequestParam Map<String,String> params,
                                    @CookieValue("JSESSIONID") String JSESSIONID,
                                    @CookieValue("JSESSIONID") Cookie cookie){
        Map<String,Object> hashMap  = new HashMap<>();

        hashMap.put("hobby",hobby);
        hashMap.put("params",params);
        hashMap.put("JSESSIONID",JSESSIONID);
        hashMap.put("cookie",cookie);
        System.out.println(cookie.getName()+"---->"+cookie.getValue());
        return hashMap;
    }

测试:

@RequstBody(获取请求体【post请求才有】)

    @PostMapping("/save")
    public Map post(@RequestBody String body){
        Map<String,Object> hashMap  = new HashMap<>();
        hashMap.put("requestBody",body);
        return hashMap;
    }

表单如下

结果: 

四、视图解析与模板引擎

视图解析:SpringBoot默认不支持 JSP,需要引入第三方模板引擎技术实现页面渲染

1、模板引擎-Thymeleaf

thymeleaf简介

Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text.

现代化、服务端Java模板引擎

基本语法

1、表达式

表达式名字

语法

用途

变量取值

${...}

获取请求域、session域、对象等值

选择变量

*{...}

获取上下文对象值

消息

#{...}

获取国际化等值

链接

@{...}

生成链接

片段表达式

~{...}

jsp:include 作用,引入公共页面片段

2、字面量

文本值: 'one text' , 'Another one!' ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false

空值: null

变量: one,two,.... 变量不能有空格

3、文本操作

字符串拼接: +

变量替换: |The name is ${name}|

4、数学运算

运算符: + , - , * , / , %

5、布尔运算

运算符: and , or

一元运算: ! , not

6、比较运算

比较: > , < , >= , <= ( gt , lt , ge , le )等式: == , != ( eq , ne )

7、条件运算

If-then: (if) ? (then)

If-then-else: (if) ? (then) : (else)

Default: (value) ?: (defaultvalue)

开发步骤

1、引入启动器

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

 2、直接开发即可

编写一个controller

package com.young.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class ViewTestController {

    @GetMapping("/young")
    public String young(Model model){
        //model的数据会被放在request域中
        model.addAttribute("msg","你好young!");
        model.addAttribute("link","http://www.4399.com");
        return "success";
    }
}

编写跳转的网页success.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 th:text="${msg}">哈哈</h1>
    <a href="http://www.atguigu.com" th:href="${link}">去4399【1】</a>
    <a href="http://www.atguigu.com" th:href="@{link}">去4399【2】</a>
</body>
</html>

测试 

点击‘去4399【1】’

 成功跳转

点击‘去4399【2】’

404错误,将按钮改成:

<a href="http://www.atguigu.com" th:href="@{http://www.4399.com}">去4399【2】</a>

 可以跳转成功,以此区分@{}与${},前者是直接取值,后者是动态取值

页面跳转

利用重定向防止登录表单信息的重复提交

package com.young.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class IndexController {

    //去登录页
    @GetMapping({"/","/login"})
    public String loginPage(){
        return "login";
    }

    //处理登录请求
    @PostMapping("/login")
    public String login(String username, String pwd){
        //重定向,防止表单重复提交
        return "redirect:main.html";
    }

    @GetMapping("/main.html")
    public String main(){
        //刷新只会刷新main页面,而不会重复刷新login请求
        return "main";
    }
}

注意:SpringBoot中请求必须经过视图解析器,所有需要两个请求方法完成重定向

检查用户是否登录

    //处理登录请求
    @PostMapping("/login")
    public String login(User user, HttpSession session,Model model){
        if (!user.getUserName().isEmpty() && "root".equals(user.getPwd())){
            //重定向,防止表单重复提交
            session.setAttribute("loginUser",user);
            return "redirect:main.html";
        }
        model.addAttribute("msg","用户名或密码错误!");
        return "login";
    }

    @GetMapping("/main.html")
    public String main(HttpSession session, Model model){
        //是否登录,拦截器
        User user = (User) session.getAttribute("loginUser");
        if (user != null){
            //刷新只会刷新main页面,而不会重复刷新login请求
            return "main";
        }else {
            model.addAttribute("msg","信息已过期,重新登路");
            return "login";
        }

    }

 抽取公共部分

可以在抽取出的common页面(示例)的某标签中使用 th:fragment="名称"或者id为一个片段取名称,然后在要引入的页面内使用一下三种方式引用

注意:div标签为示例,common为公共页面名称(省略html后缀),两个冒号后面的参数为片段名

<div th:insert="common :: 名称/#id"></div>

 效果:head中有属性th:fragment,直接将head标签整个插入到页面中

  <div><head>
    <!--common-->
    <link href="/css/style.css" rel="stylesheet">
    <link href="/css/style-responsive.css" rel="stylesheet">
    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="/js/html5shiv.js"></script>
    <script src="/js/respond.min.js"></script>
    <![endif]-->
</head></div>

 <div th:replace="common :: 名称/#id"></div>

 效果:head中有属性th:fragment,直接替换掉了div标签

  <head>
    <!--common-->
    <link href="/css/style.css" rel="stylesheet">
    <link href="/css/style-responsive.css" rel="stylesheet">
    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="/js/html5shiv.js"></script>
    <script src="/js/respond.min.js"></script>
    <![endif]-->
</head>

<div th:include="common :: 名称/#id"></div>

效果:head中有属性th:fragment,head标签没了,直接将head的内容插入

  <div>
    <!--common-->
    <link href="/css/style.css" rel="stylesheet">
    <link href="/css/style-responsive.css" rel="stylesheet">
    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="/js/html5shiv.js"></script>
    <script src="/js/respond.min.js"></script>
    <![endif]-->
</div>

表格的遍历

th:each方法

在请求中放入一些User对象返回给前端

    @RequestMapping("/dynamic_table")
    public String dynamic_table(Model model){
        List<User> users = Arrays.asList(new User("root1", "123"), new User("root2", "123"),
                new User("root3", "123"), new User("root4", "123"));
        model.addAttribute("users",users);

        return "table/dynamic_table";
    }

如下方式进行遍历 

        <tr class="gradeX" th:each="user:${users}">
            <td ></td>
            <td th:text="${user.userName}"></td>
            <td th:text="${user.pwd}"></td>
        </tr>

其中,user为得到的users的单个对象名(自定义),用法与jsp中差不多,此外,还有如下用法

当我们想要对遍历出的对象计数输出在前端页面时,写法如下:

 如图,可以在user对象后面加逗号,任意定义一个名字,用${}调用,可以调用其方法,我们调用count计数输出

        <tr class="gradeX" th:each="user,sta:${users}">
            <td th:text="${sta.count}"></td>
            <td th:text="${user.userName}"></td>
            <td th:text="${user.pwd}"></td>
        </tr>

成功

五、 拦截器

写一个类实现HandlerInterceptor接口,并实现接口中的三个方法

/**
 * 登录检查
 * 1、配置好拦截器要拦截哪些请求
 * 2、把这些配置放在容器中
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    /**
     * 目标方法执行之前
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String requestURI = request.getRequestURI();
        log.info("preHandle拦截的请求路径是{}",requestURI);

        //登录检查逻辑
        HttpSession session = request.getSession();

        Object loginUser = session.getAttribute("loginUser");

        if(loginUser != null){
            //放行
            return true;
        }

        //拦截住。未登录。跳转到登录页
        request.setAttribute("msg","请先登录");
//        re.sendRedirect("/");
        request.getRequestDispatcher("/").forward(request,response);
        return false;
    }

    /**
     * 目标方法执行完成以后
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle执行{}",modelAndView);
    }

    /**
     * 页面渲染以后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion执行异常{}",ex);
    }
}

 在SpringMVC中,配置拦截器在配置文件中,这里可以用如下方法配置:

创建一个Config类,实现WebMvcConfigurer,关于mvc的配置都需要继承此类,实现类中的addInterceptor方法注册拦截器,并且注明拦截规则

/**
 * 1、编写一个拦截器实现HandlerInterceptor接口
 * 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
 * 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
 */
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")  //所有请求都被拦截包括静态资源
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求
    }
}

六、文件上传

前端代码如下:

<div class="form-group">
    <label for="exampleInputFile">头像</label>
    <input name="headImg" type="file" id="exampleInputFile">
</div>
                            <div class="form-group">
    <label for="exampleInputFile">生活照</label>
    <input name="photos" type="file" multiple>
</div>

当要选择多个图片时,加上属性multiple

    @RequestMapping(value = "/upload",method = RequestMethod.POST)
    public String upload(@RequestParam("email") String email,
                         @RequestParam("username") String username,
                         @RequestPart("headImg") MultipartFile headImg,
                         @RequestPart("photos") MultipartFile[] photos) throws IOException {

        if (!headImg.isEmpty()){
            //如果上传来的照片非空,保存到文件服务器,OOS服务器
            headImg.transferTo(new File("E:\\SpringBoot\\" + headImg.getOriginalFilename()));
        }
        if (photos.length > 0){
            //有长度说明有图片
            for (MultipartFile photo : photos) {
                if (!photo.isEmpty()){
                    //非空
                    photo.transferTo(new File("E:\\SpringBoot\\"+photo.getOriginalFilename()));
                }
            }
        }
        return "main";
    }

@RequestPart注解配合图片使用,MultipartFile类型可以接受并提供封装好的方法处理图片

调用        transferTo        方法可以将图片存在指定位置,getOriginalFilename方法可以得到本地图片名

七、错误处理

1、默认规则

  • 默认情况下,Spring Boot提供/error处理所有错误的映射
  • 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据
  • 要对其进行自定义,添加View解析为error
  • 要完全替换默认行为,可以实现 ErrorController 并注册该类型的Bean定义,或添加ErrorAttributes类型的组件以使用现有机制但替换其内容。
  • error/下的4xx,5xx页面会被自动解析;

八、Web原生组件注入(Servlet,Filter,Listener)

1、注解注入方式

①Servlet

必须使用@WebServlet注解和@ServletCompentScan注解配合

package com.young.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(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("6666");
    }
}
package com.young;

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

@ServletComponentScan(basePackages = "com.young.servlet")
@SpringBootApplication
public class BootWeb05Application {

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

}

@WebServlet(urlPatterns = "/my"):注册servlet类并且指定链接(无视拦截器)

@ServletComponentScan(basePackages = "com.young.servlet"):标在主类上进行包扫描

②Filter

package com.young.servlet;

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

@WebFilter(urlPatterns = "")
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

    }

    @Override
    public void destroy() {

    }
}


@WebFilter(urlPatterns = " "):指定过滤资源路径

③Listener

同样使用@WebListener注解

2、使用RegistrationBean

ServletRegistrationBean

FilterRegistrationBean

ServletListenerRegistrationBean

@Configuration
public class MyRegistConfig {

    @Bean
    public ServletRegistrationBean myServlet(){
        MyServlet myServlet = new MyServlet();

        return new ServletRegistrationBean(myServlet,"/my","/my02");
    }


    @Bean
    public FilterRegistrationBean myFilter(){

        MyFilter myFilter = new MyFilter();
//        return new FilterRegistrationBean(myFilter,myServlet());
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
        return filterRegistrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean myListener(){
        MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();
        return new ServletListenerRegistrationBean(mySwervletContextListener);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值