SpringMVC 的总结

一:EasyMVC

1.MVC思想

三层架构:

    WEB开发的最佳实践就是根据功能职责的不同,划分为控制层,业务层,持久层.

    ![这里写图片描述](https://img-blog.csdn.net/20180413203038991?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

MVC原理

MVC模型:是一种架构型的模型,本身不引入新功能,只是帮助我们将开发的结构组织的更加合理,使显示与模型分离,

        流程控制逻辑,业务逻辑调用与展示逻辑分离----->责任分离

Model(模型):数据模型,包含要展示的数据与业务功能.(Service,domain)

View(视图):用户界面,在用户界面上显示模型数据

Controller(控制器):起调作用,接受用户请求,调用业务处理请求,共享模型数据并跳转界面.

    ![MVC图片说明](https://img-blog.csdn.net/20180414183004539?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

MVC框架需要哪些功能:

        设置请求编码,接受请求参数,输入校验参数类型转换,把参数封装到对象中,设置共享数据,文件上传,

        文件下载,控制界面跳转,国际化处理,自定义标签.

2.前端控制器

什么是前端控制器:Front Controller模式要求在WEB应用系统的前端(Front)设置一个入口控制器(Controller),

    是用来提供一个集中的请求处理机制,所有的请求都被发往该控制器统一处理,然后把请求分发给各自的处理程序.

    一般用来做一个共同的处理,如权限检查,授权,日志记录等.

    因为前端控制的集中处理请求的能力,因此提高了可重用性和可拓展性.

    ![前端控制器的作用](https://img-blog.csdn.net/20180414184718822?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

二:走进SpringMVC

1.SpringWeb框架:

什么是SpringMVC?

    mvc框架,它解决WEB开发中常见的问题(参数接收,文件上传,表单验证,国际化,等等),而且使用简单,与Spring无缝集成,

    Spring3.0后全面超越Strus2,成为最优秀的MVC框架(更安全,性能更好,更简单)

    支持RESTful风格的URL请求,非常容易与其他视图技术集成,如Velocity,FreeMarker.

    采用了松散耦合可插拔组件结构,比其他MVC框架更具扩展性和灵活性.

SpringMVC和Struts2对比

:SpringMVC的前端控制器是Servlet,而Struts2Filter.

    ②:SpringMVC会稍微比Struts2快些,SpringMVC是基于方法设计,处理器是单例,而Struts2是基于类

       没发一次请求都会实例一个新的Action对象,Action是多例的.

    ③:SpringMVC更加简洁,开发效率SpringMVCStruts2高,如支持JSR303校验,且处理AJAX请求更方便.

    ④:Struts2OGNL表达式使页面的开发效率相比SpringMVC更高些,但是SpringMVC也不难差.

2.入门程序

准备环境:

        搭建web项目,拷贝依赖的jar

开发步骤::配置前端控制器:DispatcherServlet:配置处理器映射器:BeanNameUrlHandlerMapping:配置处理器适配器:SimpleControllerHandlerAdapter:配置视图解析器:InternalResourceViewResolver:开发和配置处理器:HelloController

    ![helloword开发步骤](https://img-blog.csdn.net/20180414200641953?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
<!-- 配置前端控制器 -->
    <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:mvc.xml</param-value>
        </init-param>
        <!-- 在Tomcat启动的时候就就初始化SpringMVC容器 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>        
    </servlet-mapping>
<!-- 1:处理器映射器 -->
    <!-- 
        目的:选择哪一个处理器(controller)来处理当前请求
        BeanNameUrlHandlerMapping
        会按照处理器的id/name作为URL去查找

        /hello  去匹配id或name为/hello的bean
     -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!-- 2:处理器适配器 -->
    <!-- 
        目的:调用处理器(Controller)的处理请求的方法
        1:所有的适配器都实现HandlerAdapter接口
        2:处理器(Controller)类必须实现org.springframework.web.servlet.mvc.Controller
     -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    <!-- 3:视图解析器 -->
    <!-- 处理视图 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
    <!-- 4:处理器 
        在SpringMVC中Handler(框架)和Controller(自己)是同一个东西.
    -->
    <bean id="/hello" class="cn.wolfcode.hello.HelloController"/>
    <bean id="/hello2" class="cn.wolfcode.hello.HelloController2"/>

3.执行流程

    ![执行流程示意图,返回处理器中含有拦截器Filter](https://img-blog.csdn.net/20180414210440182?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

4.使用注解开发

Spring版型注解

    JavaEE应用三层架构:

    控制层(mvc)           @Controller

    业务逻辑层(Service)   @Service

    数据持久层(DAO)       @Resposotory

    其他组件使用通用注解   @Component
mvc.xml中的注解

    <!--在哪个包中扫描注解-->
    <context:component-scan base-package="cn.wolfcode"/>

    <!--MVC注解解析器-->
    <mvc:annotation-driven/>

    它很强大:会自动去注册RequestHandlerMapping,RequestMappingHandlerAdapter,ExeceptionHandlerExceptionResolver三个bean

            除此之外,SpringMVC中几乎没有注解,都需要改注解解析器来解析.

            支持类型转换ConversionService

            @NumberFormat,DateTimeFormate注解

            @Validdate JSR303校验.

            @RequestBody @ResponseBody等等.
其实不管用注解还是xml都可以不配置这三个,因为在springmvc包中有缺省的配置信息:

        <!-- 1:处理器映射器 -->
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

        <!-- 2:处理器适配器 -->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

        <!-- 3:视图解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>

        ![使用注解配置的好处,以及它们默认的配置信息](https://img-blog.csdn.net/20180414220304562?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
RequestMapping注解详解

    ![RequestMapping中的注解解析](https://img-blog.csdn.net/20180414221934621?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
前端控制器url-pattern拦截方式 及其 静态资源访问处理

    ![两种处理形式,一般url用"/",](https://img-blog.csdn.net/20180414225339222?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    <!-- 
        解决静态资源访问问题:
            1:排除法,非常SB
            2:实用<mvc:default-servlet-handler/>开开发中用的比较多
            原理:将SpringMVC上下文中定义一个D额DefaultServlcetHttpRequestHandler.对所有前端控制器的请求做筛选和盘查.
                如果发现没有经过映射请求,就交给Tomcat的默认servlet来处理.

            3:资源映射时:<mvc:resources location="/" mapping="/**">
     -->

    <!-- 方式一  -->
    <!-- <mvc:default-servlet-handler/> -->

    <!-- 方式二  -->
    <mvc:resources location="classpath:/static/" mapping="/**"/>

三:请求和响应

1.处理方法设计

    Controller方法可以有多个不同类型的参数,以及一个多种类型的返回结果.

2.处理响应

@Controller
@RequestMapping("/response")
public class HandlerResponseController {

    //返回void类型,此时可以把Controller方法当做Servlet来使用
    @RequestMapping("/test1")
    public void test1(HttpServletRequest request,HttpServletResponse response) throws Exception{
        //请求转发
        request.getRequestDispatcher("").forward(request,response);
        //URL重定向
        response.sendRedirect("");
        //设置共享数据
        request.setAttribute("","");
        //输出JSON格式
        response.setContentType("test/json:charset=utf-8");
        response.getWriter().println("");
    }

    //返回void:文件下载
    @RequestMapping("test2")
    public void test2(OutputStream out) throws IOException{
        Files.copy(Paths.get("C:/123/04.Java集合框架-Iterable接口.avi"),out);
    }

    //返回ModelAndView
    @RequestMapping("test3")
    public ModelAndView test3(){
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "返回ModelAndView");//设置共享数据的Key和Value
        mv.addObject("随便写点什么是吧");//设置共享数据的value,此时会把value类型的首字母小写的作为key:string
        mv.setViewName("result");
        return mv;
    }
    //前缀: /WEB-INF/views/
    //后缀: .jsp
    //物理视图=前缀+逻辑视图+后缀
    /**

     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
     </bean>
     */
    @RequestMapping("test4")
    public String test4(Model model){
        model.addAttribute("msg", "返回String");
        model.addAttribute("苗畦春17岁");
        return "result";
    }

3.请求跳转

//请求转发,浏览器地址栏不变,可以共享请求中的数据
    //原理:request.getRequestDispatcher("").forward(request,response);
    @RequestMapping("/test5")
    public String text5(Model model){
        return "forward:/hello.html";
    }

    //重定向,浏览器地址栏会变,不能共享请求中数据
    //原理:response.sendRedirect(""   );
    @RequestMapping("/test6")
    public String test(Model model){
        return "redirect:/hello.html";
    }
    /**
        请求转发和URL重定向的选择
                        请求转发           URL重定向
        --------------------------------------------
        地址栏                              不变     变化
        共享数据              可以        不能?
        表单重复提交        导致        不会

        请求资源路径问题:  以后访问资源你的时候都,使用/开头
            重定向redirect:/hello.html       redirect:hello.html

            访问资源的时候前面带上/.表示绝对路径,从根路径开始去寻找资源.
            访问资源的时候签名不加/,表示从先对路径,从上一级上下文路径中去寻找资源

            /response/test6------>"redirect:/hello.html";---->localhost:/hello.html 
            /response/test6------>"redirect: hello.html";---->localhost:/response/hello.html

            传统的方式,在URL重定向的时候,因为是两次不同的请求,所以不能共享请求中的数据,
            在开发中有的时候,真的需要重定向跳转后共享数据.
            Sprint3.1开始提供了Flash属性.  中能使用从Controller重定向到Controller,不能到JSP
     */
    @RequestMapping("/a")
    public String a(RedirectAttributes ra){
        ra.addAttribute("msg1","msg1");
        ra.addFlashAttribute("msg2","msg2");
        return "redirect:/response/b";
    }

    @RequestMapping("/b")
    public ModelAndView b(String msg1,@ModelAttribute("msg2")String msg2){
        System.out.println("msg1:"+msg1);
        System.out.println("msg2:"+msg2);
        return null;
    }

四:拦截器

1.拦截器原理:

当收到请求时,DispatcherServlet将请求交给处理器映射(HandlerMapping),让他找出对应该请求的HandlerExeutionChain对象.

HandlerExecutionChain对象是一个执行链,包含一个处理该请求的处理器(Handler),同时包括多个对该请求实施拦截的拦截器(HandlerInterceptor),

当HandlerMapping返回HandlerExecutionChain后,DispatcherServlet将请求交给定在HandlerExecutionChain中的拦截器和处理器一并处理.

    ![拦截器原理图](https://img-blog.csdn.net/20180415222059183?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
拦截器到底做了什么,我们可以通过考察拦截器的接口方法来进行了解.

1.preHandle方法

    在请求达到Handler方法之前,先执行这个前置处理方法,当该方法返回false时,请求直接返回,不会传递到链

中的下一个拦截器,更不会传递到处理链末端的Handler方法中,只有返回true时,请求才向链中的下一个处理节点传递.


2.postHandler方法:

    控制器方法执行后,视图渲染之前执行(可以加入统一的响应信息).

3.afterCompletion方法:视图渲染之后执行(处理Controller异常信息,记录操作日志,清理资源等)

2.拦截器开发

开发SpringMVC拦截器步骤:

    1.定义拦截器类,实现接口org.springframework.web.servlet.HandlerInterceptor 

    2.applicationContext.xml文件中配置拦截器

五:SpringMVC综合案例

domain

@Data
public class Employee {

    private Long id;
    //@NotNull(message="用户名不能为空")
    //@Size(max=10,min=4,message="用户名在4-10位之间")
    private String username;
    //@NotNull(message="密码不能为空")
    //@Size(max=10,min=4,message="密码在4-10位之间")
    private String password;
    //@NotNull(message="年龄不能为空")
    //@Min(value=18,message="最小岁数是18")
    //@Max(value=60,message="最大岁数是60")
    private Integer age;
    @DateTimeFormat(pattern="yyyy-MM-dd")
    private Date hiredate;

}

dao

public interface EmployeeDAO {

    void save(Employee emp);

    void update(Employee emp);

    void delete(Long id);

    Employee get(Long id);

    List<Employee> listAll();

    Employee checkLogin(String username, String password);
}

impl

@Repository
public class EmployeeDAOImpl implements EmployeeDAO{

    private JdbcTemplate jdbcTemplate;

    @Autowired
    public void setDataSource(DataSource dataSource){
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void save(Employee emp) {
        jdbcTemplate.update("INSERT INTO employee (username,password,age,hiredate) VALUES (?,?,?,?)",
                emp.getUsername(),emp.getPassword(),emp.getAge(),emp.getHiredate());
    }

    public void update(Employee emp) {
        jdbcTemplate.update("UPDATE employee SET username=? ,password=?,hiredate=? WHERE id=?",
                emp.getUsername(),emp.getPassword(),emp.getHiredate(),emp.getId());
    }

    public void delete(Long id) {
        jdbcTemplate.update("DELETE FROM employee where id = ?",id);
    }

    public Employee get(Long id) {
        List<Employee> list = jdbcTemplate.query("SELECT id,username,password,age,hiredate from employee where id = ?",new Object[]{id},
                (rs,rowNum)->{
                    Employee e = new Employee();
                    e.setId(rs.getLong("id"));
                    e.setUsername(rs.getString("username"));
                    e.setPassword(rs.getString("password"));
                    e.setAge(rs.getInt("age"));
                    e.setHiredate(rs.getDate("hiredate"));
                    return e;
        });
        return list.size()==1? list.get(0):null;
    }

    public List<Employee> listAll() {
        return jdbcTemplate.query("SELECT id,username,password,age,hiredate from employee",new Object[]{},new RowMapper<Employee>(){
            //把每一行结果集映射成一个Employee对象
            public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
                Employee e = new Employee();
                e.setId(rs.getLong("id"));
                e.setUsername(rs.getString("username"));
                e.setPassword(rs.getString("password"));
                e.setAge(rs.getInt("age"));
                e.setHiredate(rs.getDate("hiredate"));
                return e;
            }});
    }

    public Employee checkLogin(String username, String password) {
        List<Employee> list = jdbcTemplate.query(
            "SELECT id,username,password,age,hiredate from employee WHERE username = ? AND password = ?",
            new Object[]{username,password},
                (rs,rowNum)->{
                    Employee e = new Employee();
                    e.setId(rs.getLong("id"));
                    e.setUsername(rs.getString("username"));
                    e.setPassword(rs.getString("password"));
                    e.setAge(rs.getInt("age"));
                    e.setHiredate(rs.getDate("hiredate"));
                    return e;
                });
        return list.size()==1 ? list.get(0): null;
    }
}

service

public interface IEmployeeService {

    void save (Employee emp);

    void update (Employee emp);

    void delete (Long id);

    Employee get(Long id);

    List<Employee> listAll();

    void login(String username, String password);
}

impl

@Service
@Transactional
public class EmployeeServiceImpl implements IEmployeeService{

    @Autowired
    private EmployeeDAO dao;

    public void save(Employee emp) {
        dao.save(emp);
    }

    public void update(Employee emp) {
        dao.update(emp);
    }

    public void delete(Long id) {
        dao.delete(id);
    }

    @Transactional(readOnly=true)
    public Employee get(Long id) {
        Employee employee = dao.get(id);
        return employee;
    }

    @Transactional(readOnly=true)
    public List<Employee> listAll() {
        List<Employee> listAll = dao.listAll();
        return listAll;
    }

    @Transactional(readOnly=true)
    public void login(String username, String password) {
        Employee current = dao.checkLogin(username,password);
        if(current == null){
            throw new RuntimeException("账号或密码错误");
        }
        UserContext.setCurrentUser(current);
    }
}

util

//封装当前登录用户的上下文信息.
public class UserContext {

    private static final String USER_IN_SESSION = "user_in_session";

    //获取HttpSession对象
    public static HttpSession getSession(){
        return ((ServletRequestAttributes) (RequestContextHolder.getRequestAttributes())).getRequest().getSession();
    }

    public static void setCurrentUser(Employee current){
        if(current==null){
            getSession().invalidate();
        }else{
            getSession().setAttribute(USER_IN_SESSION,current);
        }
    }

    public static Employee getCurrentUser(){
        return (Employee) getSession().getAttribute(USER_IN_SESSION);
    }
}

exception

//处理异常
@ControllerAdvice
public class HandlerExceptionAdvice {

    @ExceptionHandler()
    public String error(Exception ex,Model model){
        model.addAttribute("errorMsg",ex.getMessage());
        return "commons/error";
    }
}

web.interceptor

//拦截器
public class CheckLoginInterceptor implements HandlerInterceptor{

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        //没有登录的情况
        if(UserContext.getCurrentUser()== null){
            //因为此处的拦截器找不到存错了的session对象,所以导致跳转到/login.jsp页面,并且得不到放行,因为/employee/list被拦截器设置拦截了
            response.sendRedirect("/login.jsp");
            return false;//阻止往后放行
        }
        System.out.println("放行");
        return true;//放行,放行给下一个拦截器或最终的处理器.
    }
}

web.controller

@Controller
@RequestMapping("/employee")
public class EmployeeController {

    @Autowired
    private IEmployeeService service;

    @RequestMapping("/list")
    public String list(Model model){
        //int a = 1/0;
        List<Employee> listAll = service.listAll();
        model.addAttribute("employees",listAll);
        return "employee/list";
    }

    @RequestMapping("/input")
    public String input(Model model,Long id){
        if(id!=null){
            Employee e = service.get(id);
            model.addAttribute("emp",e);
        }
        return "employee/input";
    }

    @RequestMapping("/saveOrUpdate")
    public String saveOrUpdate(Employee e){
        if(e.getId() == null){
            service.save(e);
        }else{
            service.update(e);
        }
        return "redirect:/employee/list";
    }

    @RequestMapping("/delete")
    public String delete(Long id){
        if(id!=null){
            service.delete(id);
        }
        return "redirect:/employee/list";
    }
}

@Controller
public class LoginController {

    @Autowired
    private EmployeeServiceImpl service;
    /**
        此处的作用就是先让login.jsp放行到login中,然后从login中存储session对象,
        这样他在进入/employee/list之前的拦截器就会有了session对象,给/employee/list放行
     */
    @RequestMapping("/login")
    public String login(String username,String password,HttpSession session){
        try {
            service.login(username,password);
        } catch (Exception e) {
            session.setAttribute("errorMsg",e.getMessage());
            return "redirect:/login.jsp";
        }
        //既然能来到这,就说明账号跟密码正确,但是进去/employee/list之前要经过拦截器
        System.out.println("LoginController.login()");
        return "redirect:/employee/list";
    }
}
@Controller
public class FileUploadController {

    @Autowired
    private ServletContext servletContext;

    @RequestMapping("/save")
    public ModelAndView save(Employee e,MultipartFile pic) throws Exception{
        System.out.println(e);
        String fileName = pic.getOriginalFilename();
        System.out.println(fileName);
        String saveDir = servletContext.getRealPath("/upload");
        Files.copy(pic.getInputStream(),Paths.get(saveDir, fileName));
        return null;
    }
}
@Controller
public class FileDownLoadController {

    //没有处理IE看着以前WEB的视频设置就行
    @RequestMapping("/download1")
    public void download1(String fileName,HttpServletRequest request,HttpServletResponse response) throws Exception{
        String dir = request.getServletContext().getRealPath("/WEB-INF/down");
        //设置响应头,下载文件
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        //设置建议保存名称
        response.setHeader("Content-Disposition","attachment;filename="+fileName);
        Files.copy(Paths.get(dir,fileName),response.getOutputStream());
    }


    //第二种方法了解就行,看龙哥视频去了解
    @RequestMapping("/download2")
    public ResponseEntity<byte[]> download2(String fileName,HttpServletRequest request,HttpServletResponse 
            response) throws Exception{
        String dir = request.getServletContext().getRealPath("/WEB-INF/down");
        HttpHeaders headers = new HttpHeaders();
        //设置响应头,下载文件
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        //设置建议保存名称
        headers.setContentDispositionFormData("attachment",fileName);
        return new ResponseEntity<>(FileUtils.readFileToByteArray(new File(dir,fileName)),headers,
                HttpStatus.CREATED);
    }
}

web.xml

<!-- 编码设置器 -->
    <filter>
        <filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>       
    </filter-mapping>


    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <multipart-config>
            <location>c:/temp</location>
            <max-file-size>10248466</max-file-size>
            <max-request-size>10249568</max-request-size>
            <file-size-threshold>10240</file-size-threshold>
        </multipart-config>
    </servlet>

    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

mvc.xml

<import resource="classpath:applicationContext.xml"/>
    <context:component-scan base-package="cn.wolfcode"/>
    <mvc:default-servlet-handler/>
    <mvc:annotation-driven/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 注册拦截器 -->
    <!-- 
        对哪些资源做拦截
        /*   :只能拦截一级路径,如/delete, /save,如果/employee/list就没有效果了
        /**  :可以拦截一级或多级路径. 
     -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <mvc:exclude-mapping path="/login"/>
            <bean class="cn.wolfcode.web.interceptor.CheckLoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

    <!-- 配置异常处理器 -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 设置错误的视图 -->
        <property name="defaultErrorView" value="commons/error"></property>
        <!-- 在错误页面,获取异常信息对象变量名称,缺省exception -->
        <property name="exceptionAttribute" value="ex"></property>
        <!-- 根据不同的类型异常,跳转到不同的错误页面 -->
        <property name="exceptionMappings">
            <!-- xxxx.serviceException=commons/nopermissi -->
        </property>
    </bean>

    <!-- Apache方式文件上传解析器  -->
    <!-- <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        限制文件大小
        <property name="maxUploadSize" value="1048576"></property>
    </bean> -->

    <!-- servlet3.0的文件上传解析器  所以要交给web.xml的来配置他的属性,initParam下面配置-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>

applicationContext.xml

        <context:component-scan base-package="cn.wolfcode"/>        
        <context:property-placeholder location="classpath:db.properties" system-properties-mode="NEVER"/>

        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <property name="driverClassName" value="${jdbc.driverClassName}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
            <property name="initialSize" value="${jdbc.initialSize}"></property>
        </bean>

        <!-- 1:WHAT:配置JDBC事务 管理器-->
        <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>

         <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>

db.properties

#key=value
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/springdemo
jdbc.username=root
jdbc.password=111111
jdbc.initialSize=2

login.jsp


        <h3>登录界面</h3>
        <span style="color: red">${errorMsg}</span>
        <%
            session.invalidate();
        %>
        <form action="/login" method="POST">
            账号:<input type="text" name="username" /><br/>
            密码:<input type="text" name="password" /><br/>
            <input type="submit" value="提交表单"/>
        </form>

input.jsp

        <form action="/save" method="POST" enctype="multipart/form-data">
            姓名:<input type="text" name="username"/><br/>
            年龄:<input type="text" name="age"/><br/>
            文件:<input type="file" name="pic"/><br/>
                <input type="submit" value="提交"/>
        </form>
        <hr/>

        <a href="/download2?fileName=butter.rar">butter.rar</a>

views-employee

list.jsp

        <h4>员工列表,当前登录用户为:${user_in_session.username}</h4>
        <a href="/login.jsp">注销登录</a>
        <hr/>
        <a href="/employee/input" >新增</a>
        <table border="1" cellspacing="0" cellpadding="0" width="500">
            <tr>
                <th>ID</th>
                <th>用户名</th>
                <th>密码</th>
                <th>年龄</th>
                <th>入职日期</th>
                <th>操作</th>
            </tr>
        <c:forEach items="${employees}" var="e">
            <tr>
                <td>${e.id}</td>
                <td>${e.username}</td>
                <td>${e.password}</td>
                <td>${e.age}</td>
                <td>${e.hiredate}</td>
                <td>
                    <a href="/employee/delete?id=${e.id}">删除</a>
                    <a href="/employee/input?id=${e.id}">编辑</a>
                </td>
            </tr>
        </c:forEach>        
        </table>

input.jsp

        <h3>添加员工</h3>
        <form action="/employee/saveOrUpdate" method="POST">
            <input type="hidden" name="id" value="${emp.id}">
            账号:<input type="text" name="username" value="${emp.username}"/><br/>
            密码:<input type="text" name="password" value="${emp.password}"/><br/>
            年龄:<input type="text" name="age" value="${emp.age}"/><br/>
            入职:<input type="text" name="hiredate" value="${emp.hiredate}"/><br/>
            <input type="submit" value="提交表单"/>
        </form>

        <hr/>
        <!-- 使用SpringMVC表单标签,需要先引入taglib标签库,然后再引入springmvc
                中的form标签 然后就可以看着小龙老师的视频使用了-->

views-commons

error.jsp

    出错啦:${errorMsg}

六:异常处理:

SpringMVC处理异常的3种方式:

    (1):使用SpringMVC提供的简单异常处理SimpleMappingExceptionResolver; (有例子)

    (2):实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器;

    (3):使用@ExceptionHandler注解实现异常处理;  (使用最多)

七:数据校验

数据校验是WEB开发任务之一,在SpringMVC中有两种方式可以实现,分别是使用Spring自带的验证框架和

使用JSR 303实现,也称为Spring-validator和jsr303-validator.

    开发中更建议使用jsr303-validator.

    ![校验标签](https://img-blog.csdn.net/20180415235415385?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    ![校验标签](https://img-blog.csdn.net/20180415235524801?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

八;SpringMVC标签

SpringMvc标签

    通过SpringMVC的表单标签可以实现将模型数据中的属性和HTML表单元素绑定,以实现表单数据更便捷编辑和表单的回写.

    ![表单标签库中的标签](https://img-blog.csdn.net/20180416000140179?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    ![input标签的属性](https://img-blog.csdn.net/20180416000248152?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    ![表单标签的属性](https://img-blog.csdn.net/20180416000546145?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    ![password标签的属性](https://img-blog.csdn.net/20180416000628113?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    hidden标签的属性:

    属性                        描述

    htmlEscape         接受true或者false,表示是否应该对被渲染的值进行HTML转义

    path               要绑定的属性路径
员工的增加列表与编辑列表:

    先添加标签:<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>

    此时的属性名就是employee跟,表名一致
    <form:form action="/employee/saveOrUpdate" modelAttribute="employee">
            <form:hidden path="id"/><br/>
            账号:<form:input path="username"/><form:errors path="username"/><br/>
            密码:<form:input path="password" showPassword="true"/><form:errors path="password"/><br/>
            年龄:<form:input path="age"/><form:errors path="age"/><br/>
            入职:<form:input path="hiredate"/><form:errors path="hiredate"/><br/>
            <input type="submit" value="提交表单"/>
    </form:form>

    虽然不是编辑人,也需要传入一个employee,
    @RequestMapping("/input")
    public String input(Model model,Long id){
        if(id!=null){
            Employee e = service.get(id);
            model.addAttribute("employee",e);
        }else{
            model.addAttribute("employee",new Employee());
        }
        return "employee/input";
    }

九:文件的上传和下载

1.文件上传

SpringMVC的文件上传两种方式:

1:基于Apache的上传组件

    先拷贝jar包:

            com.springsource.org.apache.commons.fileupload-1.2.0.jar

            com.springsource.org.apache.commons.io-1.4.0.jar

    MVC.XML中配置--文件上传解析器:

        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
                限制文件大小
                <property name="maxUploadSize" value="1048576"></property>
        </bean>

2:基于Servlet3.0  不需要拷贝jar包, SpringBoot里默认支持,不依赖Apacher,围绕MultipartConfig注解,Part接口,webService中讲过.

    MVC.XML中配置--文件上传解析器:

        <!-- servlet3.0的文件上传解析器  所以要交给web.xml的来配置他的属性,initParam下面配置-->
        <bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>

    WEB.XML中配置文件属性等:

        <servlet>
            <servlet-name>springDispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:mvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
            <!--文件上传的配置-->
            <multipart-config>
                <location>c:/temp</location>
                <max-file-size>10248466</max-file-size>
                <max-request-size>10249568</max-request-size>
                <file-size-threshold>10240</file-size-threshold>
            </multipart-config>
        </servlet>

上传跟下载的JSP文件

    1.请求方式必须是post   2.使用multipart/form-data形式   3.有上传文件file

        <form action="/save" method="POST" enctype="multipart/form-data">
            姓名:<input type="text" name="username"/><br/>
            年龄:<input type="text" name="age"/><br/>
            文件:<input type="file" name="pic"/><br/>
                <input type="submit" value="提交"/>
        </form>
        <hr/>

        <a href="/download2?fileName=butter.rar">butter.rar</a>

Controller代码

    @Controller
    public class FileUploadController {

        @Autowired
        private ServletContext servletContext;

        @RequestMapping("/save")
        public ModelAndView save(Employee e,MultipartFile pic) throws Exception{
            System.out.println(e);
            String fileName = pic.getOriginalFilename();
            System.out.println(fileName);
            String saveDir = servletContext.getRealPath("/upload");
            Files.copy(pic.getInputStream(),Paths.get(saveDir, fileName));
            return null;
        }
    }

2.文件下载

@Controller
public class FileDownLoadController {

    //没有处理IE看着以前WEB的视频设置就行
    @RequestMapping("/download1")
    public void download1(String fileName,HttpServletRequest request,HttpServletResponse response) throws Exception{
        String dir = request.getServletContext().getRealPath("/WEB-INF/down");
        //设置响应头,下载文件
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        //设置建议保存名称
        response.setHeader("Content-Disposition","attachment;filename="+fileName);
        Files.copy(Paths.get(dir,fileName),response.getOutputStream());
    }

    //第二种方法了解就行,看龙哥视频去了解
    @RequestMapping("/download2")
    public ResponseEntity<byte[]> download2(String fileName,HttpServletRequest request,HttpServletResponse 
            response) throws Exception{
        String dir = request.getServletContext().getRealPath("/WEB-INF/down");
        HttpHeaders headers = new HttpHeaders();
        //设置响应头,下载文件
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        //设置建议保存名称
        headers.setContentDispositionFormData("attachment",fileName);
        return new ResponseEntity<>(FileUtils.readFileToByteArray(new File(dir,fileName)),headers,
                HttpStatus.CREATED);
    }
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页