lagou 爪哇 1-3 spring mvc 笔记

作业一:
手写MVC框架基础上增加如下功能
1)定义注解@Security(有value属性,接收String数组),该注解用于添加在Controller类或者Handler方法上,表明哪些用户拥有访问该Handler方法的权限(注解配置用户名)

2)访问Handler时,用户名直接以参数名username紧跟在请求的url后面即可,比如http://localhost:8080/demo/handle01?username=zhangsan

3)程序要进行验证,有访问权限则放行,没有访问权限在页面上输出

注意:自己造几个用户以及url,上交作业时,文档提供哪个用户有哪个url的访问权限

解题思路

流程梳理

// 1 加载配置文件 springMVC.properties
// 2 扫描相关的类,扫描注解
// 3 初始化bean对象(实现ioc容器,基于注解)
doInstance();
// 4 实现依赖注入
// 5 构造一个 HandlerMapping 处理器映射器,将配置好的url和Method建立映射关系
initHandlerMapping();

LgDispatcherServlet 类中要改造的方法主要涉及上述的步骤3 和 步骤 5.

  1. 定义注解@Security
package com.lagou.edu.mvcframework.annotations;

import java.lang.annotation.*;

// 可以加在类上, 也可以加在方法上
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LagouSecurity {
    String[] value() default {};
}

该注解用于添加在Controller类或者Handler方法上,表明哪些用户拥有访问该Handler方法的权限

若不配置则无权访问该路径

  • 配置在Controller类上, 表示这些用户激活该 controller 下面的所有方法

  • 既配置了 Controller类上, 由配置了 LagouRequestMapping则激活该方法上的用户权限.
    两者都激活的话则方法上则为两者的并集.

  1. 定义测试controller, 并加载在类或者方法上
package com.lagou.demo.controller;

import com.lagou.demo.service.IDemoService;
import com.lagou.edu.mvcframework.annotations.LagouAutowired;
import com.lagou.edu.mvcframework.annotations.LagouController;
import com.lagou.edu.mvcframework.annotations.LagouRequestMapping;
import com.lagou.edu.mvcframework.annotations.LagouSecurity;

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

@LagouController
@LagouSecurity({"wangwu"})
@LagouRequestMapping("/classDemo")
public class ClassTestController {

    @LagouAutowired
    private IDemoService demoService;

    @LagouRequestMapping("/query1")
    public String query1(HttpServletRequest request, HttpServletResponse response, String username) {
        return demoService.get(username);
    }

    @LagouSecurity({"lisi"})
    @LagouRequestMapping("/query2")
    public String query2(HttpServletRequest request, HttpServletResponse response, String username) {
        return demoService.get(username);
    }
}
package com.lagou.demo.controller;

import com.lagou.demo.service.IDemoService;
import com.lagou.edu.mvcframework.annotations.LagouAutowired;
import com.lagou.edu.mvcframework.annotations.LagouController;
import com.lagou.edu.mvcframework.annotations.LagouRequestMapping;
import com.lagou.edu.mvcframework.annotations.LagouSecurity;

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

// 只在方法上添加 @LagouSecurity 注解
@LagouController
@LagouRequestMapping("/methodDemo")
public class MethodTestController {

    @LagouAutowired
    private IDemoService demoService;

    @LagouRequestMapping("/query1")
    public String query1(HttpServletRequest request, HttpServletResponse response, String username) {
        return demoService.get(username);
    }

    @LagouSecurity({"zhangsan"})
    @LagouRequestMapping("/query2")
    public String query2(HttpServletRequest request, HttpServletResponse response, String username) {
        return demoService.get(username);
    }

    @LagouSecurity({"zhangsan", "lisi"})
    @LagouRequestMapping("/query3")
    public String query3(HttpServletRequest request, HttpServletResponse response, String username) {
        return demoService.get(username);
    }
}
  1. 改造 Handler类, 添加 存储用户名的set 以及 在invoke方法中进行逻辑判断
package com.lagou.edu.mvcframework.pojo;

import com.lagou.edu.mvcframework.excpetion.MvcException;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * 封装handler方法相关的信息
 */
public class Handler {

    ...
    private final Set<String> userNameSet; // 用户名存储集合
    ...

    public Handler(Object controller, Method method, Pattern pattern, Set<String> userNameSet) {
        ...
        this.userNameSet = userNameSet;
        ...    
  }

    public Object invoke(String userName, Object... args)
            throws IllegalAccessException, IllegalArgumentException,
            InvocationTargetException, MvcException {
        // 若不匹配则 throw new MvcException
        if (userNameSet == null || !userNameSet.contains(userName)) {
            throw new MvcException(userName + " 未授权登录");
        }
        return this.method.invoke(this.controller, args);
    }
}

自定义异常类

package com.lagou.edu.mvcframework.excpetion;

public class MvcException extends Exception{

    public MvcException(String message) {
        super(message);
    }
}

4.改造 LgDispatcherServlet 类, 解析 @Security 时添加对注解在方法上的支持

package com.lagou.edu.mvcframework.servlet;

public class LgDispatcherServlet extends HttpServlet {

    /*
     * 构造一个HandlerMapping处理器映射器
     * 最关键的环节
     * 目的:将url和method建立关联
     */
    private void initHandlerMapping() {
        for(Map.Entry<String, Object> entry: ioc.entrySet()) {
                ...

                // 如果标识,就处理
                LagouRequestMapping annotation = method.getAnnotation(LagouRequestMapping.class);
                String methodUrl = annotation.value();  // /query
                String url = baseUrl + methodUrl;    // 计算出来的url /demo/query

                Set<String> userNameSet = new HashSet<>();
                // 若方法上有 @LagouSecurity 注解
                if (method.isAnnotationPresent(LagouSecurity.class)) {
                    LagouSecurity lagouSecurity = method.getAnnotation(LagouSecurity.class);
                    String[] value = lagouSecurity.value();
                    userNameSet.addAll(Arrays.stream(value).collect(Collectors.toSet()));
                    System.out.println("userNameSet" + userNameSet);
                }

                // 把method所有信息及url封装为一个Handler
                Handler handler = new Handler(entry.getValue(), method, Pattern.compile(url), userNameSet);
            }
        }
    }

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

        // 按类型添加
        int requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName()); // 0
        paraValues[requestIndex] = req;

        int responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName()); // 1
        paraValues[responseIndex] = resp;

        // 最终调用 handler 的 method 属性
        Object result = null;
        resp.setContentType("text/html; charset=utf-8");
        PrintWriter printWriter = resp.getWriter();
        try {
            final String queryString = getQueryString(req.getQueryString(), "username");
            result = handler.invoke(queryString, paraValues);
        } catch (MvcException e) {
            result = e.getMessage();
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 打印文字到页面上
        printWriter.print(result);
    }

    private String getQueryString(String queryString, String key) {
        String value = null;
        final String[] split = queryString.split("&");
        for (String s : split) {
            final String[] split1 = s.split("=");
            if (key.equals(split1[0])) {
                value = split1[1];
                break;
            }
        }
        return value;
    }
}

5.测试 /methodDemo 下的路径
配置加在了方法上的注解

  1. 继续改造
private void initHandlerMapping() { 
            ...
            // 解析 @Security 时添加对注解在类上的支持
            final Set<String> classUserNameSet;
            if (aClass.isAnnotationPresent(LagouSecurity.class)) {
                LagouSecurity lagouSecurity = aClass.getAnnotation(LagouSecurity.class);
                String[] value = lagouSecurity.value();
                classUserNameSet = Arrays.stream(value).collect(Collectors.toSet());
            } else {
                classUserNameSet = new HashSet<>();
            }
            ...
  1. 测试 /classDemo 下的路径
    配置分别加在了类上 和 方法上的注解:
    由于王五配置加类上, 则可访问所有方法
    http://localhost:8080/classDemo/query1?username=wangwu
    http://localhost:8080/classDemo/query2?username=wangwu

query1上没有配置 @Security, 则李四就不行
http://localhost:8080/classDemo/query1?username=lisi

query2 上面加上李四, 所有李四也能愉快的访问了
http://localhost:8080/classDemo/query2?username=lisi

作业2
需求:实现登录页面(简易版即可),实现登录验证功能、登录之后跳转到列表页,查询出 tb_resume 表【表数据和课上保持一致】的所有数据(列表不要求分页,在列表右上方有“新增”按钮,每一行后面有“编辑”和“删除”按钮,并实现功能),如果未登录就访问url则跳转到登录页面,用户名和密码固定为admin/admin

技术要求:根据SSM整合的思路,进行SSS整合(Spring+SpringMVC+SpringDataJPA),登录验证使用SpringMVC拦截器实现

【提交时统一数据库名test,用户名和密码root】

作业资料说明:

1、提供资料:代码工程、验证及讲解视频。(仓库中只有本次作业内容)

2、讲解内容包含:题目分析、实现思路、代码讲解。

3、效果视频验证
1)展示关键实现代码
2)有访问权限则放行,没有访问权限在页面上输出
3)实现登录页面及相关功能


  1. 创建 tb_resume 表的 sql
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for tb_resume
-- ----------------------------
DROP TABLE IF EXISTS `tb_resume`;
CREATE TABLE `tb_resume` (
 `id` bigint(20) NOT NULL AUTO_INCREMENT,
 `address` varchar(20) DEFAULT NULL,
 `name` varchar(20) DEFAULT NULL,
 `phone` varchar(20) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of tb_resume
-- ----------------------------
BEGIN;
INSERT INTO `tb_resume` VALUES (1, '北京', '张三', '131000000');
INSERT INTO `tb_resume` VALUES (2, '上海', '李四', '151000000');
INSERT INTO `tb_resume` VALUES (3, '⼴州', '王五', '153000000');
INSERT INTO `tb_resume` VALUES (4, '深圳', '小不点', '157000000');
INSERT INTO `tb_resume` VALUES (5, '九江', '大王八', '158000000');
INSERT INTO `tb_resume` VALUES (6, '衡阳', '绿豆汤', '159000000');
INSERT INTO `tb_resume` VALUES (7, '永州', '凉拌粉', '188000000');
INSERT INTO `tb_resume` VALUES (8, '南昌', '南昌拌粉', '18812345566');


COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

预先规划:
前端: 登陆页面 + 列表页面 + 编辑页面
后端: spring + springMVC + jpa(功能: 增删查改)

先集成spring + springmvc

  1. 新建 ResumeController 类
package controller;

@RequestMapping("/resume")
@Controller
public class ResumeController {

    @Autowired
    private ResumeService resumeService;

    /**
     * 查找所有数据
     * @return
     */
    @ResponseBody
    @RequestMapping("/findAll")
    public List<Resume> findAll() {
        return resumeService.findAll();
    }

    /**
     * 查找单条数据
     * @param id
     * @return
     */
    @ResponseBody
    @RequestMapping("/findOne")
    public Resume findOne(Long id) {
        return resumeService.findById(id);
    }

    /**
     * 删除单条数据
     * @param id
     * @return
     */
    @ResponseBody
    @RequestMapping("/deleteById")
    public String deleteById(Long id) {
        resumeService.deleteById(id);
        return STATUS_OK;
    }

    /**
     * 增加 或者 修改 单条数据
     * @param resume
     * @return
     */
    @ResponseBody
    @RequestMapping("/update")
    public String update(Resume resume) {
        Resume save = resumeService.save(resume);
        return save == null ? STATUS_NOT_OK : STATUS_OK;
    }

    private static String STATUS_OK = "OK";
    private static String STATUS_NOT_OK = "NotOk";

}
  1. 新建 ResumeServiceImpl 类及其接口
package service.impl;

@Service
public class ResumeServiceImpl implements ResumeService {

    @Autowired
    private ResumeDao resumeDao;

    @Override
    public List<Resume> findAll() {
        return resumeDao.findAll();
    }

    @Override
    public Resume findById(Long id) {
        return resumeDao.findById(id).get();
    }

    @Override
    public void deleteById(Long id) {
        resumeDao.deleteById(id);
    }

    /**
     * 如果有id就是修改, 否则就是新增
     * @param resume 之后的对象
     * @return
     */
    @Override
    public Resume save(Resume resume) {
        return resumeDao.save(resume);
    }

    @Override
    public List<Resume> saveAll(Iterable<Resume> iterable) {
        return this.resumeDao.saveAll(iterable);
    }

}
  1. 新建 ResumeDao 类
package dao;

public interface ResumeDao extends JpaRepository<Resume, Long>,
        JpaSpecificationExecutor<Resume> {
}
  1. 补充pom 中对应的 jar 包依赖 和 POJO的创建
spring 和 spring mvc 相关包
hibernate相关jar包
整合包 mysql 数据库驱动jar
阿里 druid 数据库连接池
jackson json包, 给前端返回json数据
javax.servlet  拦截器需用到
junit 测试包 spring test增强测试包
  1. applicationMvc 和 applicationContext(可具体分割为 dao 和 service)配置文件的整合
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation=
               "http://www.springframework.org/schema/beans
         https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         https://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/mvc
         https://www.springframework.org/schema/mvc/spring-mvc.xsd
    ">

    <!--配置spring包扫描-->
    <context:component-scan base-package="controller"/>

    <!--配置spring mvc 注解驱动, 自动注册最合适的处理器映射器,处理器适配器 -->
    <mvc:annotation-driven/>

    <mvc:default-servlet-handler/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/page/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <mvc:interceptors>
        <mvc:interceptor>
            <!--配置当前拦截器的url拦截规则,**代表当前⽬录下及其⼦⽬录下的所有url-->
            <mvc:mapping path="/**"/>
            <!--exclude-mapping可以在mapping的基础上排除⼀些url拦截-->
            <mvc:exclude-mapping path="/user/**"/>
            <mvc:exclude-mapping path="/page/login.htm"/>
            <bean class="interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>
  1. 再集成 hibernate. 包含了对 jdbc.properties 提取的配置.
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         https://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/data/jpa
         https://www.springframework.org/schema/data/jpa/spring-jpa.xsd
">

    <context:component-scan base-package="dao"/>

    <!--对Spring和SpringDataJPA进⾏配置-->
    <!--1、创建数据库连接池druid-->
    <!--引⼊外部资源⽂件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--第三⽅jar中的bean定义在xml中-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--2、配置⼀个JPA中⾮常重要的对象,entityManagerFactory
    entityManager类似于mybatis中的SqlSession
    entityManagerFactory类似于Mybatis中的SqlSessionFactory
    -->
    <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!--配置数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--配置包扫描(pojo实体类所在的包)-->
        <property name="packagesToScan" value="pojo"/>
        <!--指定jpa的具体实现,也就是hibernate-->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
        </property>
        <!--jpa⽅⾔配置,不同的jpa实现对于类似于beginTransaction等细节实现
       起来是不⼀样的,
        所以传⼊JpaDialect具体的实现类-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>
        <!--配置具体provider,hibearnte框架的执⾏细节-->
        <property name="jpaVendorAdapter" >
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!--定义hibernate框架的⼀些细节-->
                <!--配置数据表是否⾃动创建, 因为我们会建⽴pojo和数据表之间的映射关系
                    程序启动时,如果数据表还没有创建,是否要程序给创建⼀下-->
                <property name="generateDdl" value="false"/>
                <!--指定数据库的类型
               hibernate本身是个dao层框架,可以⽀持多种数据库类型
               的,这⾥就指定本次使⽤的什么数据库
                -->
                <property name="database" value="MYSQL"/>
                <!--
                配置数据库的⽅⾔
               hiberante可以帮助我们拼装sql语句,但是不同的数据库sql
               语法是不同的,所以需要我们注⼊具体的数据库⽅⾔
                -->
                <property name="databasePlatform"
                          value="org.hibernate.dialect.MySQLDialect"/>
                <!--是否显示sql 操作数据库时,是否打印sql-->
                <property name="showSql" value="true"/>
            </bean>
        </property>
    </bean>

    <!--3、引⽤上⾯创建的entityManagerFactory
    <jpa:repositories> 配置jpa的dao层细节 base-package:指定dao层接⼝所在包 -->
    <jpa:repositories base-package="dao" entity-manager-factory-ref="entityManagerFactory"
                      transaction-manager-ref="transactionManager"/>

</beans>
  1. 测试数据库连接/操作是否正常
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import pojo.Resume;
import service.ResumeService;

import java.util.Arrays;
import java.util.Collections;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext-dao.xml",
        "classpath:applicationContext-service.xml"})
public class JpaSpringTest {

    // 要测试IOC哪个对象注⼊即可, 如果爆红则需要配置注入扫描路径
    @Autowired
    private ResumeService resumeService;

    @Test
    public void testFindAll() {
        resumeService.findAll().forEach(System.out::println);
    }

    @Test
    public void testInsert() {
        Resume resume = new Resume("小布丁", "湖南路","13874768338");
        resume.setId(10L);
        System.out.println(resumeService.save(resume));
        this.testFindAll();
    }

    @Test
    public void testInsertBatch() {
        int length = 10;
        Resume[] array = new Resume[length];
        for (int i = array.length - 1; i >= 0; i--) {
            array[i] = new Resume("小布丁", "湖南路","1387471223" + i);
        }

        this.resumeService.saveAll(Arrays.asList(array));
    }

    @Test
    public void testUpdate() {
        Resume resume = new Resume();
        resume.setId(10L);
        resume.setPhone("13512345677");
        System.out.println(resumeService.save(resume));
        this.testFindAll();
    }

    @Test
    public void testFindById() {
        Long id = 100L;
        System.out.println(resumeService.findById(id));
    }

    @Test
    public void testDeleteById() {
        Long id = 10L;
        resumeService.deleteById(id);
        this.testFindAll();
    }
}
  1. 静态页面部分
package controller;

@RequestMapping("/page")
@Controller
public class StaticController {

    @Autowired
    private ResumeService resumeService;

    @RequestMapping("/list.htm")
    public ModelAndView findAll(ModelAndView modelAndView) {
        final List<Resume> all = resumeService.findAll();
        modelAndView.addObject("data", all);
        modelAndView.setViewName("list");
        return modelAndView;
    }

    // 最终的展现页为 edit.jsp
    @RequestMapping("/edit.htm")
    public ModelAndView edit(Resume resume) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("data", resume);
        modelAndView.setViewName("edit");
        return modelAndView;
    }

    // 内部调用 "/edit.htm" 接口
    @RequestMapping("/add.htm")
    public ModelAndView add() {
        return this.edit(null);
    }

    @RequestMapping("/login.htm")
    public ModelAndView login(String name, String password) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("login");
        return modelAndView;
    }
}

applicationMvc.xml 配置前后缀

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/page/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
  1. 前端页面部分
    http://localhost:4000/sss_war_exploded/page/list.htm
    http://localhost:4000/sss_war_exploded/page/edit.htm
    http://localhost:4000/sss_war_exploded/page/add.htm

实际上指向了两个jsp页面
list.jsp edit.jsp

  1. 写一个login页面
    StaticController 类中新建方法
 @RequestMapping("/login.htm")
    public ModelAndView login(String name, String password) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("login");
        return modelAndView;
    }
  1. 拦截器增强
    定义 MyInterceptor 类, 拦截 preHandle 方法.
package interceptor;

public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 返回值boolean代表是否放⾏,true代表放⾏,false代表中⽌
        Object user = request.getSession().getAttribute("session_login");
        if (user == null) {
            response.sendRedirect(request.getContextPath() + "/page/login.htm");
            return false;
        }
        return true;
    }
}

对应的页面 http://localhost:4000/sss_war_exploded/page/login.htm

期间遇到的问题

解决中文情况下乱码的问题

<%@page contentType="text/html; UTF-8" pageEncoding="UTF-8" %>

释义:
pageEncoding="utf-8" jsp编译为servlet时的编码
contentType="text/html; charset=utf-8" jsp响应内容的编码

启动项目, 组件注入不了

Type Exception Report

Message Servlet.init() for servlet [mvcServlet] threw exception

Description The server encountered an unexpected condition that prevented it from fulfilling the request.

Exception

javax.servlet.ServletException: Servlet.init() for servlet [mvcServlet] threw exception
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
    org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:660)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:798)
    org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:808)
    org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
    org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    java.base/java.lang.Thread.run(Thread.java:834)
Root Cause

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'resumeController': Unsatisfied dependency expressed through field 'resumeService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'service.ResumeService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:598)
    org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
    org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:376)

原因是这个 <listener> 中的 ContextLoaderListener 写成其他的一个类了, 导致注入失败.

<!--这个一定要写对呀,spring框架启动-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

maven 插件版 tomcat 7 与 hibernate 冲突, 解决办法是单独配置独立版的tomcat
学习到了有2种部署方式

  • war 部署
  • war_exploded 项目部署

用户登录权限验证的实现
使用session 级别就可以.

// 设置
request.getSession().setAttribute("session_login", "OK");
// 获取
Object user = request.getSession().getAttribute("session_login");

一些前端知识

主页面去刷新所有数据,并返给前端页面. 使用

// 刷新页面
window.location.reload();

使用 fetch api 去替代原生的xmlRequest 和 jQuery
js 操作 dom, js 函数的调用

课件和笔记

spring mvc 大致流程



前端控制器 dispatchServerlet

处理映射器 handlermapping: 存储url 和其关系

处理器适配器 handlerAdapter

处理器 handler

视图解析器 viewResolve

参数使用包装类型, 因为可以包含 null 值.

SpringMVC

[了解]MVC模式

​ M即Model数据模型,V即View视图,C即Controller控制器。

​ Model的广义和狭义之说

​ 广义:Model = Service层 +Dao层,由这两层来产生数据模型

​ 狭义:Model就是数据模型本身,就是Controller和View之间传递的数据,在这种说法下,MVC又可归为Web层。

​ 经典三层:web表现层(view+controller)、service业务层、dao持久层

[了解]SpringMVC概述

SpringMVC是什么

l SpringMVC是什么

​ SpringMVC是一个轻量级的Web表现层框架,作用:用来写Controller接收请求跳转页面的,它是Spring框架的一部分。

​ 说白了,用来替代servlet的。SpringMVC是对Servlet的封装和增强,简化了servlet的操作。它已经超越了Struts,成为目前最优秀的表现层框架。

​ 原始Servlet中

​ 第一步:取参

​ request.getParameter("param")

​ 第二步:数据类型转换

​ Integer.valueOf()

​ http协议:超文本传输协议

​ 在SpringMVC中,因为它是对servlet的封装,所以可以很方便的拿到整型数据

l 什么时候使用SpringMVC?

你觉得servlet繁琐的时候

l 在哪儿使用SpringMVC?

在经典三层的web表现层使用SpringMVC框架

l SpringMVC的优势

操作特简单,性能特别高,灵活性特别强

l 与Struts框架相比

解决了struts的安全性低,操作难,执行性能低的问题

拓展认识

Spring + Spring jdbctemplate + SpringMVC --> Spring全家桶 1.0 主流
Spring cloud + Spring Boot + Spring Data JPA + SpringMVC --> Spring全家桶2.0 潮流

Spring Cloud(微服务框架,单体工程拆分成很多个小(微)工程,订单服务、用户服务等)

​ dubbo(阿里巴巴微服务框架、hsf框架)

[理解]SpringMVC的处理流程

原始Servlet方式

一个项目中有很多个servlet,每个servlet负责不同的业务或者功能,前台请求各个servlet。

好比自己去找水电工、木工等。

SpringMVC全局只有一个servlet,这个servlet就好比装修队的头儿

[图片上传失败...(image-c7c515-1592497074318)]

SpringMVC的入门案例

入门案例需求

浏览器输入url发起请求,该url请求被SpringMVC框架拦截处理,把后台服务器的当前时间输出到jsp页面显示

入门案例实现分析

l jsp页面(显示当前系统时间)

l 前端控制器dispatcherServlet在web.xml中配置

l 写Handler获取当前系统时间(这里的Handler就是Controller)

l @Controller标识处理类,并配置扫描

入门案例步骤

l 使用骨架创建web应用,配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.springmvc</groupId>
  <artifactId>springmvc01_main</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>springmvc01_main Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

      <!--引入spring框架-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>

      <!--引入springmvc模块-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>

      <!--引入web开发所需要的两个jar,往往一起引入即可,start-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.0</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>3.0</version>
        <!--使用范围provide,只在开发、编译阶段有效,打包之后是由服务器环境提供-->
      <scope>provided</scope>
    </dependency>
      <!--引入web开发所需要的两个jar,往往一起引入即可,end-->
  </dependencies>

  <build>
    <finalName>springmvc01_main</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.7.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.20.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

l result.jsp页面开发

<%@ page isELIgnored="false" contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试结果页</title>
</head>
<body>
当前系统时间:${nowDate}
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
    <!--扫描Controller-->
    <context:component-scan base-package="com.springmvc.controller"/>

    <!--自动注册最优最合适的处理器适配器和处理器映射器-->
    <mvc:annotation-driven/>

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

入门案例执行过程

l tomcat启动加载读取web.xml

l 进一步读取到web.xml中配置的前端控制器DispatcherServlet

l DispatcherServlet会读取到springmvc.xml,进一步去实例化DefaultController对象

当url请求到来时,首先被DispatcherServlet前端控制器拦截,然后前端控制器去找能够处理当前url的处理器Handler(根据@RequestMapping的配置)

l jsp页面从request域当中获取到变量,进行显示

[理解]SpringMVC架构

框架结构

[图片上传失败...(image-f4a540-1592497074318)]

架构流程

第一步:用户发送请求至前端控制器DispatcherServlet

第二步:DispatcherServlet收到请求调用HandlerMapping处理器映射器

第三步:处理器映射器根据请求Url找到具体的Handler(后端控制器),生成处理器对象及处理器拦截器(如果有则生成)一并返回DispatcherServlet

第四步:DispatcherServlet调用HandlerAdapter处理器适配器去调用Handler

第五步:处理器适配器执行Handler

第六步:Handler执行完成给处理器适配器返回ModelAndView

第七步:处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的一个底层对象,包括 Model 和 View

第八步:前端控制器请求视图解析器去进行视图解析

根据逻辑视图名来解析真正的视图。

第九步:视图解析器向前端控制器返回View

第十步:前端控制器进行视图渲染

就是将模型数据(在 ModelAndView 对象中)填充到 request 域

第十一步:前端控制器向用户响应结果

组件说明

l DispatcherServlet:前端控制器

接收用户请求,响应结果,相当于中央处理器,DispatcherServlet是整个流程控制的中心,由它调用其它组件完成用户请求的处理。DispatcherServlet的存在降低了组件之间的耦合性。

l HandlerMapping:处理器映射器

理解为一个Map<url,Hanlder>

HandlerMapping负责根据用户请求的Url找到Handler即处理器,SpringMVC提供了不同的映射器来实现不同的映射方式,例如:实现接口方式,注解方式等。

l Handler:处理器

在SpringMVC当中有两层意思:Controller或者Controller当中的方法

Handler相对于前端控制器DispatcherServlet来说是*后端控制器*,执行具体业务处理的,它在DispatcherServlet的控制下处理用户的具体请求。

l HandlAdapter:处理器适配器

不同的接口类型转换成usb,体现了万物归一的思想

通过HandlerAdapter对Handler处理器进行执行,这是适配器模式的应用。

[图片上传失败...(image-beba6-1592497074318)] [图片上传失败...(image-927715-1592497074318)] [图片上传失败...(image-32edf6-1592497074318)]

l ViewResolver:视图解析器

ViewResolver进行视图解析,首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象。

[图片上传失败...(image-6eebd5-1592497074318)]

l View:视图

SpringMVC框架提供了很多的View视图类型的支持,包括:jsp、freemarkerView等。我们最常用的视图就是jsp

注意:在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。

需要我们开发的组件有handler(后台的处理逻辑)、view(前端的显示页面)

配置

SpringMVC对于三大件默认在底层已经进行了注册和使用(三大件已经存在于SpringMVC的运行机制中),默认使用的三大件已经过时了(虽然过时,但是依然能够保证程序运行的),建议我们使用更好更合适的三大件,只需要在SpringMVC的配置文件中添加一个标签<mvc:annotation-driven>,配置完成后会自动注册最合适的三大件

[掌握]RequestMapping使用

l 多个URL路径映射到同一个Handler(同一个方法)

[图片上传失败...(image-f61d32-1592497074318)]

l 添加到类上面(分类管理,限定类中方法访问的前缀),一看到url,就知道这个url所处理的业务领域

[图片上传失败...(image-c41c4f-1592497074318)]
[图片上传失败...(image-d659a3-1592497074318)]

l <method>属性限定请求方法,请求的url相同,请求方式(get/post)不同进入不同方法处理

访问的url是一样的(Handler名字是一样的),但是我根据你不同的请求方式(get/post)进入不同的方法处理

请求的url一样,但是请求方式不一样(get/post)

/*
 * 用例:url相同,根据请求方式(get/post)不同进入不同方法处理
 * */
@RequestMapping(value = "gotoResultSameUrl",method = {RequestMethod.GET})
public ModelAndView gotoResultSameUrlGet(ModelAndView modelAndView) {
    Date date = new Date();
    modelAndView.addObject("nowDate",date + "--->>>gotoResultSameUrlGet");
    modelAndView.setViewName("result");
    return modelAndView;
}


/*
 * 用例:url相同,根据请求方式(get/post)不同进入不同方法处理
 * */
@RequestMapping(value = "gotoResultSameUrl",method = {RequestMethod.POST})
public ModelAndView gotoResultSameUrlPost(ModelAndView modelAndView) {
    Date date = new Date();
    modelAndView.addObject("nowDate",date + "--->>>gotoResultSameUrlPost");
    modelAndView.setViewName("result");
    return modelAndView;
}

l <params>属性限定请求参数,支持简单的表达式语法

url一样,根据携带参数的不同进入不同的方法处理

url相同,请求方式相同,请求参数不同进入不同hanlder方法处理

param1:表示请求必须包含名为 param1 的请求参数!param1:表示请求不能包含名为 param1 的请求参数param1 != value1:表示请求包含名为 param1 的请求参数,但其值不能为 value1{“param1=value1”, “param2”}:请求必须包含名为 param1 和param2 的两个请求参数,且 param1 参数的值必须为 value1

[掌握]请求参数绑定(接收)

默认支持Servlet API作为方法参数

[图片上传失败...(image-be85f1-1592497074318)]

绑定简单类型参数

参数类型推荐使用包装数据类型,因为基础数据类型不可以为null

整型:Integer、int

字符串:String

单精度:Float、float

双精度:Double、double

布尔型:Boolean、boolean

说明:对于布尔类型的参数,请求的参数值为true或false。或者1或0

l 绑定简单数据类型参数,只需要直接形参声明

注意:形参的参数名和传递的参数名保持一致

​ 建议使用包装类型

参数绑定两步骤:1、从request中getParameter;2、根据形参定义的参数类型进行类型转换和赋值

@RequestParam使用

当形参和传递的参数名称不一致时使用RequestParam进行手动映射,类似于Mybatis当中国的resultMap的作用

/**
 * @RequestParam:从请求中把某一个参数取出赋值给当前形参
 * value属性:指定从请求中获取哪个参数赋值给当前形参
 * defaultValue:默认值,如果获取的参数值为空,则取该默认值(场景:分页的第一页)
 * @param modelAndView
 * @param id
 * @return
 */
@RequestMapping(value = "sendSimpleParams2")
public ModelAndView sendSimpleParams2(ModelAndView modelAndView,@RequestParam(value = "ids",defaultValue = "3") Integer id) {
    Date date = new Date();
    modelAndView.addObject("nowDate", date  + " id==>>" + id);
    modelAndView.setViewName("result");
    return modelAndView;
}

绑定pojo类型参数

直接形参声明pojo即可接收

要求:传递的参数名必须和pojo属性名对应

/**
 * 绑定pojo类型参数,只需要直接在形参中声明pojo即可
 * 要求:请求参数名和pojo属性名保持一致
 * @param modelAndView
 * @param user
 * @return
 */
@RequestMapping("sendPojo")
public ModelAndView sendPojo(ModelAndView modelAndView,User user) {
    Date date = new Date();
    modelAndView.addObject("nowDate", date  + " user==>>" + user);
    modelAndView.setViewName("result");
    return modelAndView;
}

绑定pojo包装对象参数

重点在于传参参数的命名

pojo包装对象首先就是一个普通的pojo,就应该按照上面绑定pojo的要求来,然后进一步处理

传参时参数名,首先应该定位到包装对象的属性名,如果不能确定数据,通过属性.的方式进一步锁定即可

l 前台传参

<fieldset>
    <p>传递(绑定)pojo包装类型参数</p>
    <a href="${pageContext.request.contextPath}/user/sendPojoPackage.action?user.id=1&user.username=lisi">测试</a>
</fieldset>

l 后台接收

/**
 * 绑定pojo包装类型参数,只需要直接在形参中声明pojo包装对象即可
 * 注意:绑定pojo包装类型参数,重点在于前台参数参数名的取法,首先定位到pojo的属性,然后通过.的方式进一步确定
 * @param modelAndView
 * @return
 */
@RequestMapping("sendPojoPackage")
public ModelAndView sendPojoPackage(ModelAndView modelAndView, QueryVo queryVo) {
    Date date = new Date();
    modelAndView.addObject("nowDate", date  + " queryVo==>>" + queryVo);
    modelAndView.setViewName("result");
    return modelAndView;
}

绑定pojo包含集合类型参数

传递绑定集合类型(List/Map),作为POJO的一个属性来传递

l 前端页面

<fieldset>
    <p>测试用例USE_CASE10:SpringMVC接收集合(List)类型参数</p>
    <form method="post" action="${pageContext.request.contextPath}/user/sendList.action">
        <table>
            <tr>
                <td>主键</td>
                <td>用户名</td>
                <td>性别</td>
            </tr>
            <tr>
                <td><input  type="text" name="userList[0].id"/></td>
                <td><input  type="text" name="userList[0].username"/></td>
                <td><input  type="text" name="userList[0].sex"/></td>
            </tr>
            <tr>
                <td><input  type="text" name="userList[1.id"/></td>
                <td><input  type="text" name="userList[1].username"/></td>
                <td><input  type="text" name="userList[1].sex"/></td>
            </tr>
            <tr>
                <td><input  type="text" name="userList[2].id"/></td>
                <td><input  type="text" name="userList[2].username"/></td>
                <td><input  type="text" name="userList[2].sex"/></td>
            </tr>

        </table>
        <input type="submit" value="批量修改提交">
    </form>
</fieldset>

<fieldset>
    <p>测试用例USE_CASE11:SpringMVC接收集合(Map)类型参数</p>
    <form method="post" action="${pageContext.request.contextPath}/user/sendMap.action">
        <table>
            <tr>
                <td>主键</td>
                <td>用户名</td>
                <td>性别</td>
            </tr>
            <tr>
                <td><input  type="text" name="userMap['key1'].id"/></td>
                <td><input  type="text" name="userMap['key1'].username"/></td>
                <td><input  type="text" name="userMap['key1'].sex"/></td>
            </tr>
            <tr>
                <td><input  type="text" name="userMap['key2'].id"/></td>
                <td><input  type="text" name="userMap['key2'].username"/></td>
                <td><input  type="text" name="userMap['key2'].sex"/></td>
            </tr>
            <tr>
                <td><input  type="text" name="userMap['key3'].id"/></td>
                <td><input  type="text" name="userMap['key3'].username"/></td>
                <td><input  type="text" name="userMap['key3'].sex"/></td>
            </tr>

        </table>
        <input type="submit" value="批量修改提交">
    </form>
</fieldset>

l pojo

[图片上传失败...(image-548516-1592497074318)]

l 后端handler

/**
 * 用例:springmvc绑定list集合
 * 重点在于前端参数的取值方式
 * @param modelAndView
 * @return
 */
@RequestMapping("sendList")
public ModelAndView sendList(QueryVo queryVo, ModelAndView modelAndView) {
    Date date = new Date();
    modelAndView.addObject("nowDate",date + "--->>>queryVo:" + queryVo);
    modelAndView.setViewName("result");
    return modelAndView;
}


/**
 * 用例:springmvc绑定map集合
 * 重点在于前端参数的取值方式
 * @param modelAndView
 * @return
 */
@RequestMapping("sendMap")
public ModelAndView sendMap(QueryVo queryVo, ModelAndView modelAndView) {
    Date date = new Date();
    modelAndView.addObject("nowDate",date + "--->>>queryVo:" + queryVo);
    modelAndView.setViewName("result");
    return modelAndView;
}

自定义类型转换器

  1. 自定义类型转换器
package com.springmvc.utils;

import org.springframework.core.convert.converter.Converter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 自定义类型转换器实现改接口,Converter<S,T>
 *    S:source,转换前的数据类型
 *    T:target,转换后的数据类型
 */
public class DateConverter implements Converter<String,Date> {
    @Override
    public Date convert(String source) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        try {
            return simpleDateFormat.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}
  1. 注册自定义类型转换器
<!-- 注册自定义类型转换类-->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.util.StringToDate"/>
            </set>
        </property>
    </bean>

[了解]消息头相关的两个注解

@RequestHeader

[图片上传失败...(image-b60091-1592497074318)]

@CookieValue

[图片上传失败...(image-c2ae18-1592497074318)]

[了解]扩展知识

乱码问题处理

l Post请求乱码,web.xml中加入过滤器

<!-- 解决post乱码问题 -->
<filter>
  <filter-name>encoding</filter-name>
  <filter-class>
    org.springframework.web.filter.CharacterEncodingFilter
  </filter-class>
  <!-- 设置编码参是UTF8 -->
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
  <init-param>
    <param-name>forceEncoding</param-name>
    <param-value>true</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>encoding</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

l Get请求乱码

Get请求乱码需要修改tomcat下server.xml的配置

<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

[掌握]Controller方法(Handler)的返回值

l 返回ModelAndView

l 返回字符串(直接返回逻辑视图名字符串,数据使用Model或者ModelMap封装)

ModelAndView = model + view(逻辑视图名)

/**Controller方法返回String使用Model
 * Model是接口
 * @param model
 * @return
 */
@RequestMapping("gotoResultReturnStrModel")
public String gotoResultReturnStrModel(Model model) {
    model.addAttribute("nowDate",new Date() + "--->>>model");
    return "result";
}


/**Controller方法返回String使用ModelMap
 * ModelMap是接口Model的实现
 * 注意:Model没有get方法,ModelMap方法有get方法,所以你要取值就声明ModelMap使用
 * @param model
 * @return
 */
@RequestMapping("gotoResultReturnStrModelMap")
public String gotoResultReturnStrModelMap(ModelMap model) {
    model.addAttribute("nowDate",new Date() + "--->>>modelMap");
    return "result";
}

l 返回void(了解)

​ 直接使用HttpServletRequest对象转发请求

​ 直接使用HttpServletResponse对象重定向请求

​ 直接使用HttpServletResponse输出结果

/**Controller方法返回void
 * 使用request进行请求转发
 * @param model
 * @return
 */
@RequestMapping("gotoResultReturnVoid")
public void gotoResultReturnVoid(ModelMap model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.getRequestDispatcher("/WEB-INF/jsp/result.jsp").forward(request,response);
}


/**Controller方法返回void
 * 使用response重定向
 * @param model
 * @return
 */
@RequestMapping("gotoResultReturnVoidRedirect")
public void gotoResultReturnVoidRedirect(ModelMap model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.sendRedirect("gotoResult.action");
}


/**Controller方法返回void
 * 使用response直接输出字符串
 * @param model
 * @return
 */
@RequestMapping("gotoResultReturnVoidWrite")
public void gotoResultReturnVoidWrite(ModelMap model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.getWriter().write("Hello SpringMVC!");
}

l 转发和重定向(返回字符串形式)

/**Controller方法返回void
 * 使用request进行请求转发
 * @param model
 * @return
 */
@RequestMapping("gotoResultReturnVoid2")
public String gotoResultReturnVoid2(ModelMap model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //request.getRequestDispatcher("/WEB-INF/jsp/result.jsp").forward(request,response);
    return "forward:/WEB-INF/jsp/result.jsp";
}


/**Controller方法返回void
 * 使用response重定向
 * @param model
 * @return
 */
@RequestMapping("gotoResultReturnVoidRedirect2")
public String gotoResultReturnVoidRedirect2(ModelMap model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //response.sendRedirect("gotoResult.action");
    return "redirect:gotoResult.action";
}

[应用]@ModelAttribute@SessionAttributes

l @ModelAttribute

场景有限

l @SessionAttributes

[图片上传失败...(image-155b15-1592497074318)]
/**
* 第一个handler:发起一个请求,后端把请求参数写入session
* 第二个handler:发起一个请求,取出session当中存入的数据
* 第三个handler:发起一个请求,清空session
*/
@RequestMapping("setSessionValue")
public String setSessionValue(Model model) {
   model.addAttribute("field1","value1");
   model.addAttribute("nowDate",new Date() + "--->>>该请求向session写入数据");
   return "result";
}


@RequestMapping("getSessionValue")
public String getSessionValue(ModelMap model) {
   model.addAttribute("nowDate",new Date() + "--->>>从session取出数据:" + model.get("field1"));
   return "result";
}


/*
   SessionStatus提供setComplete方法用于清空session中通过@SessionAttributes
共享的数据
*/
@RequestMapping("clearSession")
public String clearSession(Model model,SessionStatus sessionStatus) {
   sessionStatus.setComplete();
   model.addAttribute("nowDate",new Date() + "--->>>清空@SessionAttributes共享的数据");
   return "result";
}

[应用]SpringMVC对Restful风格URL的支持

直观而言,是传参风格的一种变化,原来参数跟在?后面,现在参数在url路径中

什么是RESTful

RESTful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种url设计风格。基于这个风格设计的软件可以更简洁,更有层次。

资源:互联网所有的事物都可以被抽象为资源 url(只要互联网上的事物可以用一个url来表示,那么它就是一个资源)

资源操作:使用POST、DELETE、PUT、GET不同方法对同一资源(同一url)进行操作。
分别对应 添加、 删除、修改、查询

Http主要的请求方式
get 主要是想做select
post 主要是想做insert
put 主要是想做update
delete 主要是想做delete

以上是http协议的标准请求方式,当然你用post请求也完全可以完成crud操作(因为归根结底无非就是把参数传递到后台对应处理即可)

传统方式操作资源

操作啥 (原来url)?操作谁(传入的参数)

url中先定义动作,然后传递的参数表明这个动作操作的是哪个对象(数据)

先定位动作,然后定位对象

http://localhost:8080/springmvc02/user/queryUserById.action?id=1 查询

http://localhost:8080/springmvc02/ user /saveUser.action 新增

http://localhost:8080/springmvc02/ user /updateUser.action 更新

http://localhost:8080/springmvc02/ user /deleteUserById.action?id=1 删除

使用RESTful操作资源

先定义对象

http://localhost:8080/springmvc02/user/1操作的对象) 查询,GET

http://localhost:8080/springmvc02/ user 新增,POST

http://localhost:8080/springmvc02/ user 更新,PUT

http://localhost:8080/springmvc02/ user /1 删除,DELETE

HiddentHttpMethodFilter过滤器

作用:由于浏览器 form 表单只支持 GET 与 POST 请求,而DELETE、PUT 等 method 并不支持,Spring3.0之后添加了一个过滤器,可以将浏览器请求改为指定的请求方式,发送给我们的控制器方法,使得支持 GET、POST、PUT 与DELETE 请求。

第一步:在web.xml中配置该过滤器

<!--HiddenHttpMethodFilter过滤器可以将POST请求转化为PUT\DELETE请求-->
<filter>
  <filter-name>methodFilter</filter-name>
  <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-name>methodFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

第二步:请求方式必须使用post请求

第三步:增加请求参数_method,该参数的取值就是我们需要的请求方式

<div>
    <h2>SpringMVC对Restful风格url的支持</h2>
    <fieldset>
        <p>测试用例Use_case11:SpringMVC对Restful风格url的支持</p>
        <%--<a href="${pageContext.request.contextPath}/user/queryUserById.action?id=11">点击测试</a>--%>
        <a href="${pageContext.request.contextPath}/user/15.action">rest_get测试</a>
        <form method="post" action="${pageContext.request.contextPath}/user.action">
            <input  type="text" name="id"/>
            <input type="text" name="username"/>
            <input type="submit" value="提交rest_post请求"/>
        </form>
        <form method="post" action="${pageContext.request.contextPath}/user.action">
            <input type="hidden" name="_method" value="PUT"/>
            <input  type="text" name="id"/>
            <input type="text" name="username"/>
            <input type="submit" value="提交rest_put请求"/>
        </form>
        <form method="post" action="${pageContext.request.contextPath}/user/15.action">
            <input type="hidden" name="_method" value="delete"/>
            <input type="submit" value="提交rest_delete请求"/>
        </form>
    </fieldset>
</div>

第四步:后台接收

@RequestMapping(value = "{id}",method = RequestMethod.GET)
public String queryUserById1(@PathVariable("id") Integer id, Model model) {
    System.out.println("======查询:" + id);
    model.addAttribute("nowDate",new Date());
    return "redirect:/default/gotoResult.action";
}
@RequestMapping(value = "{id}",method = RequestMethod.DELETE)
public String queryUserById2(@PathVariable("id") Integer id,Model model) {
    System.out.println("======删除:" + id);
    model.addAttribute("nowDate",new Date());
    return "redirect:/default/gotoResult.action";
}
@RequestMapping(value = "",method = RequestMethod.POST)
public String queryUserById3(User user,Model model) {
    System.out.println("======新增:" + user);
    model.addAttribute("nowDate",new Date());
    return "redirect:/default/gotoResult.action";
}
@RequestMapping(value = "",method = RequestMethod.PUT)
public String queryUserById4(User user,Model model) {
    System.out.println("======更新:" + user);
    model.addAttribute("nowDate",new Date());
    return "redirect:/default/gotoResult.action";
}

[应用]Json数据交互

Json数据是咱们企业级开发数据交互经常使用的一种方式,它比较轻量级,格式比较清晰(系统间接口调用/前后端调用,json数据格式都广为使用)

Json数据交互:前端传递json字符串到后台,后台如何能够自动转换为pojo对象;后台return 对象,能否前端直接接收到json格式的字符串

l @RequestBody注解

作用:用于获取请求体(按照http协议进行一个完整的封装,往往都是由请求头+请求体等组成)内容,不适用于Get请求方式,只能使用post请求方式

[图片上传失败...(image-5549c8-1592497074318)]

更多的是用于将JSON字符串转换为POJO对象

引入json相关jar坐标

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.9.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.9.0</version>
</dependency>

前端ajax传递json字符串(post)到后台,后台直接接收为对象

l @ResponseBody注解

作用:该注解用于将Controller的方法返回的对象转换为json字符串返回给客户端

代码

@RequestMapping("sendJsonStr")
public @ResponseBody User sendJsonStr(@RequestBody User user) {
    user.setAddress("上海");
    System.out.println("-------------->>>>sendJsonStr:" + user);
    return user;
}
$(function () {
    $("#ajaxBtn").bind("click",function () {
        $.ajax({
            url:"${pageContext.request.contextPath}/user/sendJsonStr.action",
            type:"post",
            data:'{"id":1,"username":"zhangsan"}',
            contentType:"application/json;charset=utf-8",
            dataType:"json",
            success:function (data) {
                alert(data);
                alert(data.address);
                console.log(data);
            }
        })
    })
})

[应用]SpringMVC实现文件上传

l 前提条件

l 引入坐标

<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.1</version>
</dependency>

l 编写Handler

@RequestMapping("testUploadFile")
public String testUploadFile(MultipartFile uploadFile, HttpServletRequest request) throws IOException {
    // 文件原名,如xxx.jpg
    String originalFilename = uploadFile.getOriginalFilename();
    // 获取文件的扩展名,如jpg
    String extendName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1, originalFilename.length());
    String uuid = UUID.randomUUID().toString();
    // 新的文件名字
    String newName = uuid + "." + extendName;
    String realPath = request.getSession().getServletContext().getRealPath("/uploads");
    // 解决文件夹存放文件数量限制,按日期存放
    String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
    File floder = new File(realPath + "/" + datePath);
    if(!floder.exists()) {
        floder.mkdirs();
    }
    uploadFile.transferTo(new File(floder,newName));
    return "success";
}
<!-- id的值是固定的-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--设置上传文件最大5M-->
    <property name="maxUploadSize" value="5242880"/>
</bean>

l 跨服务器上传

分服务器的目的

[图片上传失败...(image-a9e7a3-1592497074318)]

在文件服务器的tomcat配置中加入,允许读写操作

[图片上传失败...(image-581c10-1592497074318)]

主应用引入jar坐标

<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-core</artifactId>
  <version>1.18.1</version>
</dependency>
<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-client</artifactId>
  <version>1.18.1</version>
</dependency>

Handler处理程序

@RequestMapping("testUploadFileRemote")
public String testUploadFileRemote(MultipartFile uploadFile, HttpServletRequest request) throws IOException {
    // 文件原名,如xxx.jpg
    String originalFilename = uploadFile.getOriginalFilename();
    // 获取文件的扩展名,如jpg
    String extendName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1, originalFilename.length());
    String uuid = UUID.randomUUID().toString();
    // 新的文件名字
    String newName = uuid + "." + extendName;
    String realPath = request.getSession().getServletContext().getRealPath("/uploads");
    // 解决文件夹存放文件数量限制,按日期存放
    String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
    File floder = new File(realPath + "/" + datePath);
    if(!floder.exists()) {
        floder.mkdirs();
    }

    Client client = Client.create();
    String baseUrl = "http://localhost:8081/fileserver/uploads/";
    WebResource webResource = client.resource(baseUrl+newName);
    String result = webResource.put(String.class,uploadFile.getBytes());
    System.out.println(result);
    return "success";
}

[理解]SpringMVC中的异常处理

Controller—>Service—>Dao层,异常统一向上抛出,可以自定义全局异常处理器统一处理异常

异常类型:编译异常、运行时异常;运行时异常、预期异常(自定义异常)

l 自定义异常

package com.springmvc.exception;

public class MyException extends Exception {
    private String message;

    public MyException(String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }
}

l 自定义异常处理器

package com.springmvc.exception;

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

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

public class MyExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

        String message = null;
        if(ex instanceof MyException) {
            // 自定义异常
            message = ex.getMessage();
        }else {
            // 运行时异常
            message = "系统出现未知异常,请联系管理员";
        }

        // 跳转到一个友好的提示页面
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("exception",message);
        modelAndView.setViewName("exception");
        return modelAndView;
    }
}
<bean id="myExceptionResolver" class="com.springmvc.exception.ExceptionHandler"></bean>

[理解]SpringMVC中拦截器的使用

认识拦截器

l Servlet:处理Request请求和Response响应

l 过滤器(Filter):对Request请求起到过滤的作用,*作用在Servlet之前*,如果配置为/*可以对所有的资源访问(servlet、js/css静态资源等)进行过滤处理

l 监听器(Listener):实现了javax.servlet.ServletContextListener 接口的服务器端组件,它随Web应用的启动而启动,只初始化一次,然后会一直运行监视,随Web应用的停止而销毁

作用一:做一些初始化工作

作用二:监听web中的特定事件,比如HttpSession,ServletRequest的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增加处理,实现监控,比如统计在线人数,利用HttpSessionLisener等。

l 拦截器(Interceptor):是SpringMVC、Struts等表现层框架自己的,不会拦截jsp/html/css/image的访问等,只会拦截访问的控制器方法(Handler)。

从配置的角度也能够总结发现:serlvet、filter、listener是配置在web.xml中的,而interceptor是配置在表现层框架自己的配置文件中的

在Handler业务逻辑执行之前拦截一次

在Handler逻辑执行完毕但未跳转页面之前拦截一次

在跳转页面之后拦截一次

SpringMVC中自定义拦截器

l 实现HandlerInterceptor接口

package com.springmvc.interceptor;

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

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

/**
 * SpringMVC中自定义拦截器实现HandlerInterceptor接口
 */
public class MyInterceptor implements HandlerInterceptor {

    /**
     * 之前执行:Handler逻辑真正执行之前执行
     * @param request
     * @param response
     * @param handler
     * @return 代表是否放行,true放行,false中止
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        System.out.println("===============>>>preHandle0:" + ((HandlerMethod) handler).getMethod().getName());
        return true;
    }

    /**
     * 之中执行:Handler逻辑真正执行完成但尚未返回页面
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        //modelAndView.addObject("nowDate","123");
        //modelAndView.setViewName("error");
        System.out.println("===============>>>postHandle0");
    }


    /**
     * 之后执行:返回页面之后执行
     * @param request
     * @param response
     * @param handler
     * @param ex
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("===============>>>afterCompletion0");
    }
}

l 配置拦截器

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.springmvc.interceptor.MyHandlerInterceptor"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.springmvc.interceptor.MyHandlerInterceptor1"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.springmvc.interceptor.MyHandlerInterceptor2"/>
    </mvc:interceptor>
</mvc:interceptors>

l 拦截器链(Interceptor Chain)

[图片上传失败...(image-1ee56-1592497074318)]

拦截器链执行时,拦截器链正常流程测试

preHandle按照拦截器配置顺序执行

postHandle按照拦截器配置倒序执行

afterCompletion按照拦截器配置倒序执行

拦截器链中断流程测试

拦截器链中有中断时,整个链中的拦截器的postHandle都不会执行

拦截器案例(登录控制)

l 需求

  1. 有一个登录页面,写一个Handler用于跳转登录页面

  2. 登录页面有一提交表单的动作。需要在Controller中处理

    a) 判断用户名密码是否正确(admin/admin)

    b) 如果正确,向session中写入用户信息(写入用户名username)

    c) 跳转到登录成功页面

  3. 开发拦截器

    a) 拦截用户请求,判断用户是否登录(登录页面跳转请求和登录提交请求不能拦截)

    b) 如果用户已经登录则放行

    c) 如果用户未登录则跳转到登录页面

l 实现

login.jsp

<%@ page isELIgnored="false" contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>xxx系统登录</title>

    <style>
        div{
            width:300px;
            height:100px;
            position:absolute;
            top:50%;
            left:50%;
            margin-top:-50px;
            margin-left:-150px;
        }
    </style>
</head>
<body>
    <div>
        <form method="post" action="${pageContext.request.contextPath}/user/login.action">
            <table>
                <tr>
                    <td>用户名</td>
                    <td><input type="text" name="username"/></td>
                </tr>
                <tr>
                    <td>密码</td>
                    <td><input type="password" name="password"/><input type="submit" value="登录"/></td>
                </tr>
            </table>
        </form>
    </div>
</body>
</html>
package com.springmvc.controller;

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("user")
public class UserController {

    @RequestMapping("toLogin")
    public String toLogin(Model model) {
        return "login";
    }

    @RequestMapping("login")
    public String login(Model model, String username, String password, HttpServletRequest request,HttpSession session) {
        if("admin".equalsIgnoreCase(username) && "admin".equalsIgnoreCase(password)) {
            session.setAttribute("username",username);
            return "forward:/default/gotoResult.action";
        }
        return "redirect:toLogin.action";
    }
}

l 拦截器

package com.springmvc.interceptor;

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

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

public class RequestIntercepter implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession httpSession = request.getSession();
        Object username = httpSession.getAttribute("username");
        if(username == null) {
            // 未登录,跳转到登录页面
            response.sendRedirect(request.getContextPath() + "/user/toLogin.action");
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <!--不拦截user下的请求-->
        <mvc:exclude-mapping path="/user/**"/>
        <bean class="com.springmvc.interceptor.RequestIntercepter"></bean>
    </mvc:interceptor>
</mvc:interceptors>

l web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.action</url-pattern>
  </servlet-mapping>

  <!--应用启动直接跳转到登录页面-->
  <welcome-file-list>
    <welcome-file>/user/toLogin.action</welcome-file>
  </welcome-file-list>

</web-app>

[掌握]SSM整合

SSM = Spring+SpringMVC+Mybatis

整合策略

SSM = Spring+SpringMVC+Mybatis = (Spring+Mybatis)+SpringMVC

先整合Spring+Mybatis

然后再整合SpringMVC

Mybatis整合Spring

l 整合目标(目的)

1. Mybatis框架的数据库连接池以及事务管理都交给Spring容器来完成

2. Mybatis框架的SqlSessionFactory对象应该放到Spring容器中作为单例对象管理

3. Mybatis框架的Mapper动态代理对象交给Spring管理,我们从Spring容器中直接获得Mapper的代理对象

l 整合所需jar分析

  1. Junit测试jar(4.12版本)

  2. Mybatis的jar

  3. Spring相关jar(spring-context、spring-test、spring-jdbc、spring-tx、spring-aop、aspectjweaver)

  4. Mybatis/Spring整合包jar

原来这个整合包是由Spring提供维护的,后来由Mybatis自己维护

包名是mybatis-spring开头的

  1. Mysql数据库驱动jar

  2. Dbcp数据库连接池的jar

  3. Log4j日志jar

l 业务需求:查询商品表的全部数据

l 步骤

  1. Dao层代码开发

  2. Service层代码开发

  3. 按照整合目标完成Mybatis+Spring框架整合的后续工作

注意:Mybatis和Spring整合之后,在一定程度来说,Mybatis的全局配置文件不是必须

l 整合后Pom坐标

<!--junit-->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>
<!--mybatis-->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.4.5</version>
</dependency>
<!--spring相关-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.9</version>
</dependency>
<!--mybatis与spring的整合包-->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>1.3.1</version>
</dependency>
<!--数据库驱动jar-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.46</version>
</dependency>
<!--数据库连接池jar-->
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-dbcp2</artifactId>
  <version>2.2.0</version>
</dependency>
<!--Log4j日志jar-->
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

l 整合后Mybatis日志开启,在SqlMapConfig.xml文件中配置

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

l Spring配置文件

applicationContext-dao.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
">

    <!--数据库连接池配置-->
    <context:property-placeholder location="classpath:db.properties"/>
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--配置SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.ssm.pojo"/>
        <property name="configLocation" value="classpath:SqlMapConfig.xml"/>
    </bean>

    <!--Spring管理Mapper动态代理对象-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.ssm.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>
</beans>

applicationContext-service.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
">
    <context:component-scan base-package="com.ssm.service"/>
    <!--配置Spring事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

l Mybatis配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--日志配置,指定为LOG4J输出-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
</configuration> 

Mybatis整合Spring后整合SpringMVC

l 整合思路

在已有工程基础上,开发SpringMVC的入门案例即可

l pom坐标

<!--SpringMVC-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<!--jsp-api&servlet-api-->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jsp-api</artifactId>
  <version>2.0</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
  <scope>provided</scope>
</dependency>
<!--页面使用jstl表达式-->
<dependency>
  <groupId>jstl</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>
<dependency>
  <groupId>taglibs</groupId>
  <artifactId>standard</artifactId>
  <version>1.1.2</version>
</dependency>
<%@ page isELIgnored="false" contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title>商品列表页</title>
</head>
<body>
<table border="1" width="100%">
    <tr>
        <td>序号</td>
        <td>商品名称</td>
        <td>商品价格</td>
        <td>商品详情</td>
    </tr>
    <c:forEach items="${itemList}" var="item" varStatus="status">
        <tr>
            <td>${status.count}</td>
            <td>${item.name}</td>
            <td>${item.price}</td>
            <td>${item.detail}</td>
        </tr>
    </c:forEach>
</table>

</body>
</html>

l web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!--配置Spring配置文件-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext-service.xml,classpath:applicationContext-dao.xml</param-value>
  </context-param>

  <!--配置编码过滤器-->
  <filter>
    <filter-name>encoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!--配置Spring启动监听器-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!--SpringMVC配置-->
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.action</url-pattern>
  </servlet-mapping>
</web-app>

l springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
">

    <!--配置Controller扫描-->
<!--
    注意事项:Controller层的扫描需要配置在SpringMVC中,不要配置在Spring的扫描中
             让Spring只扫描service
-->
<context:component-scan base-package="com.springmvc.controller"/>


<!--注册组件-->
<mvc:annotation-driven/>


<!--视图解析器配置(视图路径的前后缀配置)-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <!--不拦截user下的请求-->
            <mvc:exclude-mapping path="/user/**"/>
            <bean class="com.ssm.interceptor.RequestIntercepter"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

l ItemController.java

package com.ssm.controller;

import com.ssm.service.ItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("item")
public class ItemController {

    @Autowired
    private ItemService itemService;

    @RequestMapping("itemList")
    public String itemList(Model model) throws Exception {
        model.addAttribute("itemList",itemService.queryItemList());
        return "itemList";
    }
}

文件上传

MultipartFile

spring jpa

jpa 集成

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350