SpringMVC就是将servlet的过程封装起来,有自己的一套配置和使用方法,在SpringMVC中,用来处理请求的类叫做控制器类(controller),在servlet中也有相应的类。
关于视图定位
在xml中,配置所有视图的默认位置
在Beans标签中间加入下面代码,表示WEB-INF/page/为页面的默认位置。这个位置,不能从外部直接访问,当内部需要跳转到某个页面时,首先会到该目录下访问。
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/" />
<property name="suffix" value=".jsp" />
</bean>
在tomcat的server.xml中,也有一个配置过程起到类似的作用,下面配置代码指示了应用所在的位置。如果不配置的话,想要访问这个应用,就需要把整个项目放在tomcat中,这就很麻烦。
<Context path="/" docBase="D:\\Workspaces\\springmvc\\web" debug="0" reloadable="true" />
TIPS:
如果在地址栏输入:127.0.0.1/index.jsp 那么,就会在tomcat指定的应用目录中去寻找这个文件,注意,不是视图定位的位置,而是在tomcat的server.xml中配置的应用的位置。如果输入的是 1270.0.1/index 那么,应用的 controller 中,会有一个专门用来处理 index 的方法,如果没有这个处理的方法,就报 404 。
视图定位的位置表示的是 我在内部调用页面是使用的默认位置,是一个安全位置,外部是不能直接访问的,想访问这里面的页面,唯一的方法就是通过servlet进行内部跳转。例如:controller中有这么一个语句,表示跳转到showProduct这个文件,SpringMVC就会去视图定位的位置去寻找这个文件,配置中,可以指定文件的后缀之类的,那么跳转时,就不需要加上后缀,showProduct表示的是跳转到 showProduct.jsp这个文件,因为配置时,设置了后缀为 .jsp 。
ModelAndView mav = new ModelAndView("showProduct");
关于控制器类
下面是注解方式来配置SpringMVC中需要用到的控制器类,和Spring一样,也可以在xml中配置。
在控制器的类声明上面,加上注解 @Controller 表示,这个类是一个控制器类,可以用来处理客户端发来的请求
在相应的处理方法上面,加上注解 @RequestMapping("/addProduct") 表明该方法是用来处理前端发来的 addProduct 这个请求的,即html中的form表单的action的值。
在前端的表单中,会填写一些属性,比如name,age,相应的在后端就需要一个 bean 来装这些数据(接收数据的注入),就是下面的例子中的 Product 类,类中的成员变量,就有neme,age这些属性,而且还要有相应的get和set方法,bean的规范。
@Controller //代表这个是一个控制器类,用来处理请求 public class ProductController { @RequestMapping("/addProduct") //注解方式 表明该方法是用来处理 addProduct这个请求的 public ModelAndView add(Product product) throws Exception { //mav.addObject("product",product); 参数列表中有一个 Product类的对象,该值会自动赋值给mav,并不需要手动添加 ModelAndView mav = new ModelAndView("showProduct"); return mav; }
}
跳转方式:
上面的过程进行的是服务端跳转,例如:请求的是 addProduct ,在服务端跳转到了 showProduct ,
下面介绍下客户端跳转,和服务端跳转的区别在于,服务端跳转,浏览器地址栏显示的地址不会发生变化,跳转过程在服务端进行,前端没感觉,客户端跳转,在浏览器地址栏上可以看到访问的地址已经发生了变化,当你访问 /jump 时,地址栏的地址会跳转到 /index ,可以看出,已经进行了客户端跳转。
@RequestMapping("/jump") public ModelAndView jump() { ModelAndView mav = new ModelAndView("redirect:/index");//重定向请求 return mav; }
关于session
Session在用户登录,一些特殊场合在页面间传递数据的时候会经常用到,什么意思?session和servlet中的一样。
servlet 中的session,会话,用来标记同一用户的一系列动作。
我们使用Httpsession对象来标记不同的会话,也就是说,与一个特定客户的整个会话期间,Httpsession对象会持久存储。Httpsession对象中有许多的属性我们可以直接使用,比如 count 用来记录访问次数。
同一个用户,向同一个 servlet 发送多次请求,如果没有 session ,那么对于容器来说,就是不同的请求,容器并不知道这些请求是来自于同一个用户(因为http是面向无连接的,一次数据发送完毕,连接就会关闭,所以容器不会知道请求来自同一个客户),当加上session之后,当一个用户第一次建立连接时,容器就会给该客户一个ID,后面客户每次发送数据,都会用整个 ID 来标识自己,容器会根据 sessionID,得知,某些请求来自同一个用户,我们可以将每次用户发送的数据存储在Httpsession对象中,这样,servlet 就不仅仅知道用户的本次请求发送了什么数据,还可以获取之前请求的数据。
sessionID是存放在cookie中的,容器和客户的每次交互,都需要将cookie交给对方,而且整个过程,会由容器自动完成,我们只需要直接使用Httpsession对象就行了,原理还是要知道的。
想使用session这个对象,需要导入相关的包,session中的count属性记录的是同一个用户访问该页面的次数?
import
javax.servlet.http.HttpSession;
@RequestMapping("/check") public ModelAndView check(HttpSession session) { Integer i = (Integer) session.getAttribute("count"); if (i == null) i = 0; i++; session.setAttribute("count", i); ModelAndView mav = new ModelAndView("check"); return mav; }
关于中文问题
通过过滤器来实现这个配置过程,配置之后,页面显示中文不会乱码。
在项目的web.xml中配置过滤器来设置字符集,就可以解决。过滤器的作用和servlet中的一样,所以说先学servlet,再学框架真的是容易些
servlet 中的过滤器:
过滤器就是在夹在客户和servlet中间的部件,所有的请求必从过滤器经过才能到达servlet,同样的所有的响应必须经过过滤器才能回送给客户。把关的。
在servlet中,过滤器在DD中配置,某些请求运行特定组合的过滤器,过滤器的顺序等。过滤器的运行顺序,由FilterChain来定义,FilterChain请求到来时,沿着FilterChain来运行,目标servlet给出响应时,响应沿着FilterChanin的反方向出来。
配置文件web.xml:head first servlet&JSP的P678
<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>
关于如何上传文件
关于拦截器
对于业务类Controller来说,并不知道由拦截器的存在,
首先在SpringMVC的xml文件的beans标签中配置拦截器,可以配置多个,拦截器运行的顺序就是配置的顺序,例如下面的配置文件,在访问/index时,会先执行
IndexInterceptor和Interceptor2这两个拦截器。这两个拦截器都是针对/index这个请求的。
<!-- 配置拦截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/index"/> <!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 --> <bean class="interceptor.IndexInterceptor"/> </mvc:interceptor> <!-- 当设置多个拦截器时,先按顺序调用preHandle方法,然后逆序调用每个拦截器的postHandle和afterCompletion方法 --> <mvc:interceptor> <mvc:mapping path="/index"/> <!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 --> <bean class="interceptor.Interceptor2"/> </mvc:interceptor> </mvc:interceptors>
拦截器类如下:拦截器相当于把业务处理类包裹起来了,Controller处理前运行一部分,处理之后,再运行一部分
public class IndexInterceptor extends HandlerInterceptorAdapter { /** * 在业务处理器处理请求之前被调用 * 如果返回false * 从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链 * 如果返回true * 执行下一个拦截器,直到所有的拦截器都执行完毕 * 再执行被拦截的Controller * 然后进入拦截器链, * 从最后一个拦截器往回执行所有的postHandle() * 接着再从最后一个拦截器往回执行所有的afterCompletion() */ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle(), 在访问Controller之前被调用"); return true; } /** * 在业务处理器处理请求执行完成后,生成视图之前执行的动作 * 可在modelAndView中加入数据,比如当前时间,只有当这个处理函数运行结束之后,真正的响应才会返回给客户 */ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle(), 在访问Controller之后,访问视图之前被调用,这里可以注入一个时间到modelAndView中,用于后续视图显示"); modelAndView.addObject("date","由拦截器生成的时间:" + new Date()); } /** * 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等 * * 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion() */ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion(), 在访问视图之后被调用"); }