- 三层架构 : 表现层 业务层 持久层
- MVC设计模型: 模型 javabean 视图 jsp 控制器 servlet.
- Spring MVC 与Struts2区别
- 核心控制器不同 前者为Servlet,后者为Filter.
- Spring MVC基于方法设计 而Struts2基于类,Struts2 多例,每次执行都会创建一个类.
- Struts2 的OGNL表达式 开发效率更高,
Spring MVC 框架
过程:
- 环境搭建
- 导入依赖.
- 配置前端控制器. 配servlet. DispatcherServlet类.
- 配置Tomcat服务器.
编写代码
1… 首先前端jsp 文件需要调用后端servlet. 所以可以在java普通类中加注释@Controller. 注释需要扫描,所以还需要在xml配置文件中开启注解扫描.
2. 控制器类能扫描到了,要调用的方法怎么识别? 通过@RequestMapping 映射(path="/hello") ,映射hello路径为该方法的请求路径.
RequestMapping属性:value 与 path 是等价的; method属性指定请求的方法必须是哪些. params 指定请求要传过来哪些参数 headers指定要包含哪些请求头.
3. xml配置文件还没被加载,所以注解扫描无法生效.要让Tomcat服务器启动时就加载,需要在web.mxl里面全局初始化.
<init-param>
<param-name>contextConifgLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
- 视图类 在WEB-INF 下创建pages文件夹, 需要配置 视图解析器对象.
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
需要内嵌两个属性.<property name="prefix" value="/WEB-INF/pages/"/>
视图类的目录,<property name="suffix" value=".jsp" />
后缀名.
流程分析
- 启动服务器,加载配置文件.
- DispatcherServlet对象创建
- 初始化参数 : springmvc.xml被加载
- 扫描注解,Controller注解类被创建成对象.
- 发送请求, 后台处理
- 首先请求经过DispatcherServlet类, 根据请求路径,找到相应映射的方法.
- return “success” 经前端控制器 DispatcherServlet 找视图解析器.
- 当返回值为void时,默认的返回页面为WEB-INF/pages/映射同名.jsp . 要转发到成功页面, 用request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
- 重定向 response.sendRedirect(request.getContextPath+“index.jsp”);
请求参数绑定
- 请求传的参数与方法参数对应时,会参数绑定,传的参数,方法就可以直接用.
- 请求参数绑定实体类型 方法的参数写要封装的javabean的类. 请求参数名字要同成员变量名相同.
- 当Javabean的类里面还有引用类时, 请求的参数 名字用user.name
- 当Javabean的类里面还有集合类时,请求的参数 : List< User > 用 list[0].name Map< String,User> 用 map[‘one’].name
- 请求传参中文乱码问题 用过滤器解决乱码问题.
<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>
自定义类型转换器
- SpringMVC框架提供了类型转换, 请求都传的是字符串,会给你转换成Integer 等相应类型. 但有些情况如date. 2099/9/9 会转换成功,但2099-9-9就会出现异常. 所以有些类型需要自定义转换器.
- 实现Converter< String,Target>接口,实现convert方法.用target 要转换类型的格式化方法写相应格式. 如Date格式 用
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
- 主配置文件中配置自定义类型转换器
<!--配置自定义类型转换器-->
<bean id="conversion" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<!--注册自定义类型转换器-->
<bean class="com.hk.utils.TestConverter"/>
</set>
</property>
</bean>
<!-- 添加自定义类型转换器支持-->
<mvc:annotation-driven conversion-service="conversion"/>
SpringMVC常用注解
- @RequestParam : 当传参与方法参数名称不匹配时, 需要在方法的参数前面@RequestParam(“传参的名称.”)
- @RequestBody: Get请求方式不适用, 因为Get方式不会有请求体,内容直接写在URI后面. 在控制器类的方法参数(@RequestBody String body) 即得到body为请求体的内容.
- @PathVariable:
RESTful架构:URL定位资源,用HTTP动词(GET,POST,PUT,DELETE)描述操作。
增加一个朋友,uri: generalcode.cn/v1/friends 接口类型:POST
删除一个朋友,uri: generalcode.cn/va/friends 接口类型:DELETE
修改一个朋友,uri: generalcode.cn/va/friends 接口类型:PUT
查找朋友,uri: generalcode.cn/va/friends 接口类型:GET
通过HTTP请求类型 而不是在URI里面写动词来描述操作.
/friends/{id} PathVariable 即用于获取{id} 占位符的值.
@RequestMapping("/aa/{sid}") 在方法参数中用@ParVariable(name=“sid”) String id 获取. - HiddenHttpMethodFilter 由于浏览器form表单只支持GET与POST请求,而DELETE,PUT等method并不支持,Spring3.0添加了一个过滤器,可以将请求的方式改为指定方式,使得能支持DELETE,PUT方式.
- @RequestHeader 获取指定值的请求头
- @CookieValue
- @ModelAttribute 出现在方法上,表示当前方法会在控制器的方法执行之前先执行.有无具体返回值的方法都可修饰.
可以用于表单数据不完整时,通过返回User 类,由于先执行,使其能传给控制器类一个完整的User封装数据.
也可以没有返回值,通过Map< String,User> . 在控制器类方法参数上再加@ModelAttribute(“键”) - @SessionAttributes 为了降低耦合,控制器类的参数不会写 HttpRequest. 而是用提供的Model类. 底层会存储到request域对象中.
@SessionAttributes 只能加在类上面. 可以通过键 将值存入session中.
取session值 需要用到Model的实现类,ModelMap.
删掉session值,用到SessionStatus类. 使用方法setComplete()
响应返回值
- 返回值为ModelAndView类型 通过把user对象存储到ModelAndView对象中,同时也存入了request对象. (mv.addObject(“user”,user)).然后跳转页面,mv.setViewName(“success”); [用的视图解析器跳转] 返回String类型 底层是用的ModelAndView
响应JSON
使用jQuery,出现用不了的问题: 前端控制器拦截了静态资源. 在主配置文件中< mvc:resources mapping="/js/" location="/js/"/>
- 发送ajax请求
$("#btn").click(function () {
/*发送ajax请求*/
$.ajax({ //json格式
url:"account/testAjax", //要请求的java控制器方法路径.
contentType:"application/json;charset=UTF-8", //json默认类型
data:'{"userName":"aaa","password":"123","money":"300.5"}', //请求发送的数据
dataType:"json",
type:"post", //HTTP请求方法
success:function (data) { //请求成功后的回调函数
//data 服务器端响应过来的json数据,进行解析.
alert(data);
alert(data.userName);
alert(data.password);
alert(data.money);
}
})
});
- 控制器响应ajax请求.
@RequestMapping(path = "/testAjax") //映射该方法访问路径.
//接受发送过来的ajax请求的数据 ,要用@RequestBody注解
//导入Jackson相关jar包,可以自动将json数据封装成相应的javabean
//客户端发送ajax请求,后台处理请求,封装成相应javabean
public @ResponseBody Account testAjax(@RequestBody Account account){
//请求传过来的数据.
System.out.println(account);
// 对请求做相应,模拟查询数据库 然后返回数据
account.setUserName("bbb");
account.setMoney(120);
return account;
}
文件上传
准备:
首先要把form表单的enctype 取值改为: multipart/form-data 正文内容变为多个部分,每一部分都是MIME类型描述.
method属性取值必须为post 因为get限制了大小64kb
提供一个文件选择域 < input type=“file”>
借助第三方组件 解析内容. commons-fileupload commons-io
编程:
准备前端内容 : 一个提交表单,准备部分的内容.然后写要调用后台的方法.
传统方式的主要步骤: 获取路径,要存储的文件夹是否存在,解析request对象.对上传文件项进行判断,然后写入.
//获取上传路径
String path = request.getSession().getServletContext().getRealPath("/uploads/");
//判断是否有该文件夹
File file = new File(path);
if(!file.exists()){
file.mkdirs();
}
//解析request对象,获取上传文件项
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//解析request
List<FileItem> fileItems = fileUpload.parseRequest(request);
for (FileItem item : fileItems){
//判断item对象是否为上传文件项
if(item.isFormField()){
//说明为普通表单项
}else{//说明为上传文件项
//获取上传文件名
String filename =item.getName();
//完成上传
System.out.println(item.getName());
System.out.println(path);
item.write(new File(path,filename));
//删除临时文件
item.delete();
}
}
SpringMVC方式 上传文件.
首先:配置文件解析器 用于解析request,然后返回update 上传文件项.
<!--配置文件解析器 id名必须为multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--大小 : 10MB-->
<property name="maxUploadSize" value="10485760"></property>
</bean>
然后写上传方法.
//upload 与选择文件 input 标签的 name 必须一致. 使用的是参数绑定.
public String testupload2(HttpServletRequest request, MultipartFile upload) throws Exception {
System.out.println("文件上传");
//获取上传文件到什么路径下
String path = request.getSession().getServletContext().getRealPath("/uploads/");
//判断是否有该文件夹
File file = new File(path);
if(!file.exists()){
file.mkdirs();
}
String filename =upload.getOriginalFilename();
//文件名称设置唯一值
String uuid = UUID.randomUUID().toString().replace("-","");
filename = uuid+"_"+filename;
upload.transferTo(new File(path,filename));
return "success";
}
跨服务器上传文件,一般开发会有各个功能专用的服务器. 如应用服务器接受请求,向文件服务器存文件.
需要用到Jersey-core 以及 Jersey-client jar包
启动两个Tomcat 即可模拟两个服务器
两个服务器出现的一个问题就是 无法写入 需要在Tomcat的配置文件web.xml下的servlet标签下
<init-param> <param-name>readonly</param-name> <param-value>false</param-value> </init-param>
需要向另一个服务器将接受的上传文件路径传过去.所以需要创建一个客户端对象.
//定义跨服务器上传路径
String path ="http://localhost:9090/uploads/";
String filename =upload.getOriginalFilename();
//文件名称设置唯一值
String uuid = UUID.randomUUID().toString().replace("-","");
filename = uuid+"_"+filename;
//创建客户端对象
Client client = Client.create();
//连接服务器
WebResource resource = client.resource(path + filename);
//作为客户端向图片服务器发送文件.
resource.put(upload.getBytes());
return "success";
拦截器
SpringMVC的处理拦截器类似于servlet的过滤器Filter,用于对处理器进行预处理和后处理.
区别:
过滤器是servlet规范的一部分,所有java web工程都可以使用
拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的才能用
过滤器在url-pattern配置了/*后,可对所有资源进行拦截.
而拦截器只会拦截访问控制器的方法,
-
异常处理器组件
- 编写自定义异常类(做提示信息)
- 编写异常处理器
- 配置异常处理器(跳转到提示页面)
-
拦截器
- 编写拦截器类,实现HandlerInterceptor
里面有三个方法
preHandle postHandle afterCompletion
控制器之前 控制器之后 跳转jsp页面之后
return true: 放行,下一个拦截器,若没有,执行controller中方法.
return false: 不放行,用request的getRequestDispatcher方法跳转到一个页面.controller不执行. - 配置拦截器
- 编写拦截器类,实现HandlerInterceptor
<mvc:interceptors>
<!--配置拦截器-->
<mvc:interceptor>
<!--要拦截的方法 当请求/user下的方法时,拦截器拦截.-->
<mvc:mapping path="/user/*"/> <!--或者/** 访问的所有方法-->
<!--不要拦截的方法-->
<!--<mvc:exclude-mapping path=""/>-->
<!--拦截器对象.-->
<bean class="com.hk.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
SSM整合
三层:
视图层 SpringMVC 业务层 Spring 持久层 Mybatis 用业务层去整合其他两个框架.
- 先maven,用坐标配置jar包.
- 配置spring, resources/applicationContext.xml 开启扫描,排除SpringMVC注释.
- 先写Javabean,再写Dao,Service接口,Dao的实现类由Spring实现,不需要实现类,Service要自己写实现类.
- 配置SpringMVC,视图层用于与页面进行交互,首先要在web.xml里面配置前端控制器,让其在服务器加载时加载springmvc.xml文件.然后写上解决中文乱码的过滤器.
- 配置springmvc.xml.四步: 开启注解扫描,只包含Controller; 配置视图解析器InternalResourceViewResolver,属性prefix,suffix;过滤静态资源:mvc:resources;开启SpringMVC注解支持: mvc:annotation-driven.
- 用Spring框架整合SpringMVC:如下图:
Spring自带有监听器,所以直接在web.xml下配置监听器就行.
<!--配置spring的监听器 默认只加载WEB-INF目录下的applicationContext.xml配置文件-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--设置配置文件路径-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
- 当配置spring监听器以后,Controller在启动时,现在已经加载了spring的配置文件,可以用spring的IOC容器. 于是就可以依赖注入后,通过对象调用业务层方法.
- 整合Mybatis . 首先写SqlMapConfig.xml 配置文件. ----> 配置环境(Mysql,JDBC…)—>引入映射文件.
1.(mappers标签,推荐用package,可以映射dao包下所有方法,以后添加也不用再修改.)
2.(连接Mysql时,数据库字符集设为了utf8,在url后面加上 ?characterEncoding=utf8 ) 避免数据库中文显示乱码问题.
3.对于增删改的SQL操作,需要自己进行提交事务操作 . (Spring用声明式事务管理) - Spring整合Mybatis. 在spring的配置文件applicationContext.xml文件下配置. [其实就是Mybatis配置文件内容放到Spring来整合]
1.配置连接池. [c3p0连接池,Druid连接池] c3p0 用ComboPooledDataSource. Druid用DruidDataSource.
2.Dao层需要用到代理对象. SqlSessionFactoryBuilder—>SqlSessionFactory—>SqlSession—>Session—>xxxxDao–>调用Dao方法.要配置Factory对象,用到SqlSessionFactoryBean类,以及需要代理的Dao对象所在包,用到MapperScannerConfigurer类.
3.声明式事务管理配置. 配置事务管理器---->配置事务通知---->配置AOP增强.(即对xxxServiceImp AOP增强,需要用到通知.)
<!--Spring整合Mybatis-->
<!--配置连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8"></property>
<property name="user" value="root"></property>
<property name="password" value="dz4663479"></property>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置扫描Dao接口所在包-->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.hk.dao"></property>
</bean>
<!--配置Spring框架声明式事务管理-->
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" isolation="DEFAULT"></tx:method>
</tx:attributes>
</tx:advice>
<!--配置AOP增强-->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.hk.service.impl.*ServiceImp.*(..))"></aop:advisor>
</aop:config>