springmvc:是一个表现层框架,用于代替struts2框架
1.springmvc工作原理: //三大组件:1.处理器映射器(HandlerMapping) 2.处理器适配器(HandlerAdapter) 3.视图解析器(ViewReslover)
1)DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。
2)HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3)Handler:处理器 //对应controller中的方法
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
4)HandlAdapter:处理器适配器
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
5)viewResolver:视图解析器
View Resolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
6)View:视图
springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
1、 用户发送请求至前端控制器DispatcherServlet
2、 DispatcherServlet收到请求调用处理器映射器。处理器映射器根据请求url找到具体的处理器,生成处理器(Handler)对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
3、 DispatcherServlet通过处理器适配器(HandlerAdapter)调用处理器,执行处理器(Controller,也叫后端控制器)。
5、 Controller执行完成返回ModelAndView,HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
6、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器
7、 ViewReslover解析后返回具体View
8、 DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
9、 DispatcherServlet响应用户
2.springMVC:使用入门
1)建工程,导jar包,核心包:spring-webmvc.jar
2)配置springmvc前端控制器:是一个servlet,这个servlet会获取到请求 //需要配置参数指定springmvc.xml配置文件的位置
<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> //配置springmvc.xml文件的位置
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
3)配置springmvc.xml文件:只需要配置包扫描:
<context:component-scan base-package="cn.itcast.springmvc.controller"/>
4)创建controller类
1)类上添加@controller :让这个类被spring管理
2)方法上添加@RequestMapping :配置方法对应的访问路径
3)返回值:ModelAndView:两个重要方法
modelAndView.addObject(String,Object) :添加键值对,底层会将键值对存入到request域对象中
modelAndView.setViewName("") :添加视图,参数为视图(jsp文件)所在的路径
@Controller
public class ItemController {
@RequestMapping("/itemList.action") //括号中为访问路径
public ModelAndView itemList() {
List<Items> itemList = new ArrayList<>(); //保存要显示的商品数据
itemList.add(new Items(1, "imac", 20000, new Date(), "苹果本很贵"));
itemList.add(new Items(2, "imac1", 20000, new Date(), "苹果本很贵"));
ModelAndView modelAndView = new ModelAndView(); //创建ModelAndView对象
modelAndView.addObject("itemList", itemList); //添加数据到modelAndView,底层会保存数据到request
modelAndView.setViewName("/WEB-INF/jsp/itemList.jsp");//设置展示数据的jsp
return modelAndView;//返回结果
}
}
3)编写jsp页面:在页面上可以用el表达式获取到request域中的数据
执行流程:服务器启动时,加载配置文件,创建controller.页面发送请求(localhost:8080/springmvc/itemList.action),被配置的servlet获取,
匹配controller类中方法上配置的请求路径,执行对应方法.ModelAndView对象封装数据和视图.数据会被放入request中,跳转到结果视图后,通过el表达式显示数据到页面
3.三大组建的配置:
1)默认情况下,在jar包中存在配置文件,已经配置了三大组件:
处理器映射器:默认为DefaultAnnotationHandlerMapping类,已过期
处理器适配器:默认为AnnotationMethodHandlerAdapter类,已过期
视图解析器:默认为InternalResourceViewResolver
2)这些组件如果不配置,会使用默认值,也可以手动配置.由于前两个组件默认的类已经过期:因此可以手动配置:
在springmvc.xml中配置两个组件:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> //处理器映射器
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/> //处理器适配器
上面配置的处理器映射器和适配器也可采用下面一行配置代替:
<mvc:annotation-driven> //相当于上面的两行配置
3)可以配置视图解析器,来指定跳转视图路径的前缀后缀:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" /> //前缀,会在modelAndView.setViewName("itemList");的参数路径的前面加上配置的/WEB-INF/jsp/
<property name="suffix" value=".jsp" /> //后缀,会在modelAndView.setViewName("itemList");的参数路径的后面加上配置的.jsp
</bean>
可以简化视图路径的编写:
原来的路径:modelAndView.setViewName("/WEB-INF/jsp/itemLIst.jsp");
现在的路径:modelAndView.setViewName("itemList");
4.ssm三大框架的整合:
1)建工程,导jar包:
2)编写配置文件:
1)dao层,mybatis的配置:
sqlMapConfig.xml:空文件即可,需要文件头.如果需要别名,可以配
applicationContext-dao.xml:
a)数据库连接池
b)SqlSessionFactory对象,需要注入连接池,注入sqlMapConfig.xml文件位置。
c)配置mapper接口或包扫描器。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> //连接池
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="10" />
<property name="maxIdle" value="5" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> //让spring管理sqlsessionfactory
<property name="dataSource" ref="dataSource" /> //注入数据库连接池
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" /> //注入SqlMapConfig.xml位置
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> //配置包扫描器
<property name="basePackage" value="cn.itcast.springmvc.mapper"/>
</bean>
2)service层:
a)配置包扫描器
<context:component-scan base-package="cn.itcast.springmvc.service"/>
b)事务相关配置
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> //事务管理器
<property name="dataSource" ref="dataSource" /> //注入连接池
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager"> //配置通知
<tx:attributes> //配置方法的事务信息
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config> //配置切面
<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.itcast.springmvc.service.*.*(..))" />
</aop:config>
3)springmvc的配置,springmvc.xml:
<context:component-scan base-package="cn.itcast.springmvc.controller" /> //扫描带Controller注解的类
<mvc:annotation-driven/> //注解驱动,相当于手动配置处理器映射器和处理器适配器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> //视图解析器
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" /> // jsp前缀
<property name="suffix" value=".jsp" /> //jsp后缀
</bean>
4)web.xml中配置springmvc的servlet,并配置spring的监听器:
<context-param>//配置spring的监听器
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext-*.xml</param-value> //配置spring配置文件路径,*为通配符
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> //加载spring容器的监听器
</listener>
<servlet> //springmvc中央控制器
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
3)编写controller类,以及处理请求的方法
4)编写service dao层代码
5.controller中处理请求方法默认可以使用的数据类型
1)在controller类中的处理请求的方法中,可以直接添加参数,框架会自动将这些处理请求的对象传给这些参数,包括:
request,response,session,model
2)使用Model和方法返回值代替ModelAndView对象 //即在方法参数中添加Model,并设定方法返回值为String
Model为一个接口,可以存放结果数据.本质上也是将数据保存到request中,这个接口的方法addAttribute()相当于ModelAndView的addObject()
可以让方法返回一个string,这个字符串指定结果视图的路径,用于代替ModelAndView的setViewName()方法
@RequestMapping("/itemEdit")
public String editItem(HttpServletRequest request,
HttpServletResponse response, HttpSession session, Model model) { //请求发出后,框架会自动将请求中的对象赋值给这些参数,在方法中可以直接使用这些对象
String strId = request.getParameter("id"); //从request中取参数
int id = new Integer(strId);
Items items = itemService.getItemById(id);
model.addAttribute("item", items); //设置返回结果
return "editItem"; //返回结果视图jsp
}
6.参数绑定:即接收controller类接收请求参数的方法
1)request对象获取请求参数:
@RequestMapping("/itemEdit")
public String itemEdit(HttpServletRequest request, Model model) {
String strId = request.getParameter("id"); //从Request中取id
2)通过方法形参直接获取请求参数: //只支持简单数据类型
@RequestMapping("/itemEdit")
public String itemEdit(Integer id) { //会获取到请求参数中参数名为id的参数值.这里的参数名(id)必须和请求参数名称一致
Items items = itemService.getItemById(id);
当请求参数名和形参参数名一致时,可以通过这个参数直接获取到请求参数值.支持的数据类型:
整形:Integer、int
字符串:String
单精度:Float、float
双精度:Double、double
布尔型:Boolean、boolean
如果方法参数名和请求参数名称不一致,但还想接收时可以使用注解:@RequestParam
@RequestParam的三个属性:
value:请求参数的名称,会将这个请求参数名称的参数值赋值到方法的参数中
required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报错
defaultValue :默认值,表示如果请求中没有同名参数时的默认值.配置这个属性可以防止请求中没有此参数时报错
public String editItem(@RequestParam(value="item_id",required=true) String id) { ... } //相当于手动将请求参数中的item_id映射到方法中的id,会将参数名为item_id的参数值赋值给id
3)pojo类型获取请求参数:
可以在方法参数中使用一个pojo直接接收请求参数,条件:参数名和pojo中属性名相同
@RequestMapping("/updateitem")
public String updateItem(Items items) { //可以接收到和这个pojo items对象中属性名相同的参数
itemService.updateItem(items);
//返回成功页面
return "success";
}
4)包装类型pojo接收参数:
如果接收参数的类为包装类,则请求参数名应该写为:
items.name //会在接收参数的包装类中找到items,然后在items中找到name属性接收参数
public class QueryVo { //为一个Items的包装类,
private Items items;
@RequestMapping("/queryitem")
public String queryItem(QueryVo queryVo) { //包装类接收参数
页面上参数名应该为:items.id items.name 等 //参数为:被包装的类.属性
7.自定义转换器:当参数中包含日期类型的参数时,由于日期格式多变,框架无法直接解析和接收日期参数,需要自定义转换器 //转换器时将参数中的参数转换为需要的数据类型
1)自定义一个类实现Converter接口:
public class DateConverter implements Converter<String, Date> { //这里的两个泛型指定转换前后的数据类型,即将字符串转换为date
@Override
public Date convert(String source) { //通过这个方法将字符串转换为日期类型数据
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = simpleDateFormat.parse(source);
return date;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
2)配置自定义转换器:
<mvc:annotation-driven conversion-service="conversionService"/> //配置转换器
<bean id="conversionService" //配置转换器
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.itheima.springmvc.converter.DateConverter"></bean> //配置自定义转换器
</set>
</property>
</bean>
原理:配置完自定义转换器后,参数中的日期类型数据会被自动解析并完成参数传递
注意:当页面有日期参数,需要封装日期数据,而没有配置日期转换器时,发送请求时会报400错误
sqlmapconfig.xml能不能不要
jsp中对日期进行格式化后显示:
<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>
springmvc使用时post请求乱码问题解决:在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>
1.绑定数组:多个相同参数名参数的接收,两种方式:
1)方法参数接收:方法参数中使用数组接收 //参数名要和数组名相同
2)pojo属性接收:参数为pojo时,可以在pojo中定义数组接收 //数组属性名要和数组名相同
public class QueryVo{
private string[] ids;
...
}
@RequestMapping("/queryitem")
public String queryItem(QueryVo queryVo, String[] ids) { //两种方式均可封装到请求参数中的多个ids,
return "success";
}
2.将表单参数绑定到list
在pojo中定义接收参数的List集合itemList
参数名:
itemList[index].属性 //会将这个参数封装到pojo中itemList的索引为index的元素的指定属性中
1)pojo类的定义:
public class QueryVo { //pojo类
private List<Items> itemList; //封装参数的集合
...
2)controller中方法:
@RequestMapping("/queryitem")
public String queryItem(QueryVo queryVo) { //queryVo封装参数
3)页面:
<input type="text" name="itemList[${s.index}].name" value="${item.name }">
3.requestMapping注解: //value属性也可写成path
1)加在类上,表示访问路径 //类似于struts类上的namespace
2)加在方法上,表示访问路径 //类似于struts方法上的@action
如果类上有requestMapping,访问路径为类上的路径+方法上的路径
3)@requestMapping的method属性:指定请求方式
默认情况下,任何请求方式均可
可以定义一个或多个:
@requestMapping(method={RequestMethod.POST,RequestMethod.GET}) //添加这个注解后,只有post和get请求才能访问到这个方法
@Controller
@RequestMapping("/item")
public class ItemController {
@RequestMapping(value="/updateitem", method={RequestMethod.POST,RequestMethod.GET}) //访问路径应该为:/item/updateitem
//支持post,get两种请求方式
public String updateItem(Items items) throws Exception {
4.controller方法的返回值:
1)ModelAndView:可以返回数据和视图 //参考第一天
2)String
1)表示转发的结果逻辑视图 //默认为请求转发
return "itemList"; //会加上路径的前缀后缀然后转发到指定视图
2)手动请求转发: //可以添加forward指定为请求转发
return "forward:/item/itemList.action"; //可请求转发到另外一个路径,只能服务器端内部跳转
2)实现重定向 //redirect指定为重定向
return "redirect:/item/itemList.action"; //如果有Model会自动将model中数据拼装到路径后面
3)void :使用原始的request response完成请求的处理和响应
5.全局异常处理器: //在dao service controller中直接抛出异常,统一将异常抛给前端控制器,在前端控制器通过异常控制器统一处理
1)自定义异常类:定义一个类实现接口:Exception
public class CustomerException extends Exception {
private String expMessage; //保存错误信息
2)定义全局异常处理器,实现接口:HandlerExceptionResolver
public class GlobalExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception exception) { //exception获取到异常对象
String msg = null;
if (exception instanceof CustomerException) { //判断是否为自定义的异常
CustomerException custExp = (CustomerException) exception;
msg = custExp.getExpMessage();
} else {
exception.printStackTrace(); //如果是运行时异常,取错误的堆栈。
StringWriter s = new StringWriter();
PrintWriter printWriter = new PrintWriter(s);
exception.printStackTrace(printWriter);
msg = s.toString();
}
//写日志、发短信、发邮件
//...
//返回一个错误页面,显示错误消息
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", msg);
modelAndView.setViewName("error");
return modelAndView; //将错误信息响应到页面
3)在springmvx.xml中配置全局异常处理器
<bean class="com.itheima.springmvc.exception.GlobalExceptionResolver"/>
6.图片上传:需要两个jar包:commons-fileupload和commons-io
1)页面上:post请求,file输入框,设置enctype为"multipart/form-data"
<form id="itemForm" action="${pageContext.request.contextPath }/item/updateitem.action"
method="post" enctype="multipart/form-data">
<input type="file" name="picture"/>
2)springmvc.xml中配置多媒体文件解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> //这里id的值固定
<!-- 设置上传文件的最大尺寸为5MB -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>
3)controller方法中:
方法中采用MultipartFile接收上传的图片文件,MultipartFile的方法:
multipartFile.getOriginalFilename(); 获取上传的文件名
multipartFile.transferTo(); 将文件保存到服务器端
@RequestMapping(value="/updateitem", method={RequestMethod.POST,RequestMethod.GET})
public String updateItem(Items items, MultipartFile picture) throws Exception { //MultipartFile接收文件
String picName = UUID.randomUUID().toString();
String oriName = picture.getOriginalFilename(); //取文件名
String extName = oriName.substring(oriName.lastIndexOf("."));
picture.transferTo(new File("C:\\temp\\images\\" + picName + extName)); //保存文件到服务器
//把文件名保存到数据库
items.setPic(picName + extName);
itemService.updateItem(items);
return "forward:/item/itemEdit.action";
}
7.json处理:两个注解:@RequestBody @ResposeBody
1)导入jackson jar包
2)在处理器适配中,配置json转换器 //Springmvc默认用MappingJacksonHttpMessageConverter对json数据进行转换
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
</bean>
如果配置了<mvc:annotation-driven /> 则不用定义上边的内容。
2)@RequestBody :接收http请求的json数据,将json数据转换为java对象
3)@ResposeBody :实现将controller方法返回对象转换为json响应给客户端
@RequestMapping("/jsontest")
@ResponseBody //会将返回的items对象转换为json并返回给浏览器
public Items jsonTest(@RequestBody Items items) { //会将请求的json数据解析并封装到items对象中
return items;
}
注意:使用$.ajax发送请求,要想传递json数据,需要设置请求头: contentType:"application/json;charset=utf-8"
8.springmvc实现resultful服务
resultful是一种编程的风格:使用put、delete、post、get,使用不同方法对资源进行操作。
1)在请求路径后面可以直接拼接上请求参数
@RequestMapping("/itemEdit/{id}") //这里{id} 为指定路径后面的参数
public String editItem(@PathVariable("id") Integer iid, Model model) { //使用@PathVariable将请求路径上的参数(id)映射到方法参数(iid)上
//如果路径上的参数名称和方法名一样,@PathVariable可不指定value属性
return "editItem";
}
2)使用restful实现功能时请求路径后面没有.action,因此需要在web.xml中修改DispatcherServlet的配置:
<servlet>
<servlet-name>springmvc-servlet-rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping> //添加这个映射
<servlet-name>springmvc-servlet-rest</servlet-name>
<url-pattern>/</url-pattern> //这里的"/"不能改为"/*","/"可以拦截所有的请求,但不会拦截.jsp,"/*"也会拦截.jsp
</servlet-mapping>
3)静态资源访问的问题:
当DispatcherServlet中设置url-pattern为 /,那么静态资源如/js/.. /css/.. 等无法访问.必须对静态资源进行访问处理。在springmvc.xml文件中配置:
<mvc:resources location="/WEB-INF/js/" mapping="/js/**"/> ///当访问路径为/js/..时会自动到/WEB-INF/js/下获取到相关资源
9.拦截器:
1)定义拦截器:
定义一个拦截器实现接口:HandlerInterceptor,需要实现的三个方法:
public boolean preHandle(HttpServletRequest request, //参数包含request,response和请求的handler
HttpServletResponse response, Object handler) //controller方法执行前调用此方法返回true表示继续执行,返回false中止执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) //controller方法执行后但未返回视图前调用此方法这里可在返回用户前对模型数据进行加工处理
public void afterCompletion(HttpServletRequest request, //包含异常对象Exception
HttpServletResponse response, Object handler, Exception ex) //controller执行后且视图返回后调用此方法,这里可得到执行controller时的异常信息,可记录操作日志,资源清理等
2)配置拦截器:在springmvc.xml中配置 //可配置多个,配置多个时,按照配置的顺序执行
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/> ///配置要拦截的路径
<bean class="cn.itcast.springmvc.filter.HandlerInterceptor1"></bean> //配置对应的拦截器
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.itcast.springmvc.filter.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
补充:
1)jstl中数组循环的序号的获取
<c:forEach items="${itemList }" var="item" varStatus="s">
<input type="hidden" name="itemList[${s.index}].id" value="${item.id }"> //s.index获取到序号
2)web.xml中配置servlet时,可配置多个<servlet-mapping>