作业一:
手写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.
- 定义注解@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则激活该方法上的用户权限.
两者都激活的话则方法上则为两者的并集.
- 定义测试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); } }
- 改造 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 下的路径
配置加在了方法上的注解
- 任何用户都无权访问:
http://localhost:8080/methodDemo/query1?username=zhangsan
http://localhost:8080/methodDemo/query1?username=lisi
- 注解了单个用户: 张三
http://localhost:8080/methodDemo/query2?username=zhangsan 只张三可访问
http://localhost:8080/methodDemo/query2?username=lisi 不可访问
- 配置了注解中所有用户张三和李四皆可访问
http://localhost:8080/methodDemo/query3?username=zhangsan 可访问
http://localhost:8080/methodDemo/query3?username=lisi 可访问
- 继续改造
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<>(); } ...
- 测试 /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)实现登录页面及相关功能
- 创建
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
- 新建 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"; }
- 新建 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); } }
- 新建 ResumeDao 类
package dao; public interface ResumeDao extends JpaRepository<Resume, Long>, JpaSpecificationExecutor<Resume> { }
- 补充pom 中对应的 jar 包依赖 和 POJO的创建
spring 和 spring mvc 相关包 hibernate相关jar包 整合包 mysql 数据库驱动jar 阿里 druid 数据库连接池 jackson json包, 给前端返回json数据 javax.servlet 拦截器需用到 junit 测试包 spring test增强测试包
- 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>
- 再集成 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>
- 测试数据库连接/操作是否正常
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(); } }
- 静态页面部分
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>
- 前端页面部分
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
- 写一个login页面
StaticController 类中新建方法
@RequestMapping("/login.htm") public ModelAndView login(String name, String password) { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("login"); return modelAndView; }
- 拦截器增强
定义 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; }
自定义类型转换器
- 自定义类型转换器
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; } }
- 注册自定义类型转换器
<!-- 注册自定义类型转换类--> <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 需求
- 有一个登录页面,写一个Handler用于跳转登录页面
- 登录页面有一提交表单的动作。需要在Controller中处理
a) 判断用户名密码是否正确(admin/admin)
b) 如果正确,向session中写入用户信息(写入用户名username)
c) 跳转到登录成功页面
- 开发拦截器
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分析
- Junit测试jar(4.12版本)
- Mybatis的jar
- Spring相关jar(spring-context、spring-test、spring-jdbc、spring-tx、spring-aop、aspectjweaver)
- Mybatis/Spring整合包jar
原来这个整合包是由Spring提供维护的,后来由Mybatis自己维护
包名是mybatis-spring开头的
- Mysql数据库驱动jar
- Dbcp数据库连接池的jar
- Log4j日志jar
l 业务需求:查询商品表的全部数据
l 步骤
- Dao层代码开发
- Service层代码开发
- 按照整合目标完成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 集成