一:EasyMVC
1.MVC思想
三层架构:
WEB开发的最佳实践就是根据功能职责的不同,划分为控制层,业务层,持久层.
MVC原理
MVC图片说明:
MVC模型:是一种架构型的模型,本身不引入新功能,只是帮助我们将开发的结构组织的更加合理,使显示与模型分离,
流程控制逻辑,业务逻辑调用与展示逻辑分离----->责任分离
Model(模型):数据模型,包含要展示的数据与业务功能.(Service,domain)
View(视图):用户界面,在用户界面上显示模型数据
Controller(控制器):起调作用,接受用户请求,调用业务处理请求,共享模型数据并跳转界面.
MVC框架需要哪些功能:
设置请求编码,接受请求参数,输入校验参数类型转换,把参数封装到对象中,设置共享数据,文件上传,
文件下载,控制界面跳转,国际化处理,自定义标签.
2.前端控制器
什么是前端控制器:Front Controller模式要求在WEB应用系统的前端(Front)设置一个入口控制器(Controller),
是用来提供一个集中的请求处理机制,所有的请求都被发往该控制器统一处理,然后把请求分发给各自的处理程序.
一般用来做一个共同的处理,如权限检查,授权,日志记录等.
因为前端控制的集中处理请求的能力,因此提高了可重用性和可拓展性.
前端控制器的作用:
二:走进SpringMVC
1.SpringWeb框架:
什么是SpringMVC?
mvc框架,它解决WEB开发中常见的问题(参数接收,文件上传,表单验证,国际化,等等),而且使用简单,与Spring无缝集成,
Spring3.0后全面超越Strus2,成为最优秀的MVC框架(更安全,性能更好,更简单)
支持RESTful风格的URL请求,非常容易与其他视图技术集成,如Velocity,FreeMarker.
采用了松散耦合可插拔组件结构,比其他MVC框架更具扩展性和灵活性.
SpringMVC和Struts2对比
①:SpringMVC的前端控制器是Servlet,而Struts2是Filter.
②:SpringMVC会稍微比Struts2快些,SpringMVC是基于方法设计,处理器是单例,而Struts2是基于类
没发一次请求都会实例一个新的Action对象,Action是多例的.
③:SpringMVC更加简洁,开发效率SpringMVC比Struts2高,如支持JSR303校验,且处理AJAX请求更方便.
④:Struts2的OGNL表达式使页面的开发效率相比SpringMVC更高些,但是SpringMVC也不难差.
2.入门程序
准备环境:
搭建web项目,拷贝依赖的jar
开发步骤:
①:配置前端控制器:DispatcherServlet
②:配置处理器映射器:BeanNameUrlHandlerMapping
③:配置处理器适配器:SimpleControllerHandlerAdapter
④:配置视图解析器:InternalResourceViewResolver
⑤:开发和配置处理器:HelloController
helloword开发步骤:
<!-- 配置前端控制器 -->
<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:
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"/>
使用注解配置的好处,以及它们默认的配置信息:
RequestMapping注解详解:
前端控制器url-pattern拦截方式 及其 静态资源访问处理
<!--
解决静态资源访问问题:
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="/**"/>
两种处理形式,一般url用"/"
三:请求和响应
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中的拦截器和处理器一并处理.
拦截器原理图:
拦截器到底做了什么,我们可以通过考察拦截器的接口方法来进行了解.
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.
校验标签:
八;SpringMVC标签
SpringMvc标签
通过SpringMVC的表单标签可以实现将模型数据中的属性和HTML表单元素绑定,以实现表单数据更便捷编辑和表单的回写.
hidden标签的属性:
属性 描述
htmlEscape 接受true或者false,表示是否应该对被渲染的值进行HTML转义
path 要绑定的属性路径
表单标签库中的标签:
input标签的属性:
表单标签的属性:
password标签的属性:
员工的增加列表与编辑列表:
先添加标签:<%@ 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);
}
}