Spring MVC
1.Spring技术体系
Spring MVC 主要组件
1. DispatcherServlet
核心控制器,按照servlet标准定义,定义在web.xml中,所有请求先进入DispatcherServlet
2.HandlerMapping
处理器映射,负责根据请求映射到不同Controller
3.Controller
处理器,负责接收请求及参数,调用业务组件Service/DAO进行处理,然后返回处理结果
4. ModelAndView
负责存储模型数据和视图名称
5. ViewResolver
视图解析器,根据ModelAndView中视图名找JSP,生成HTML
Sprig MVC 处理流程
配置DispatcherServlet(web.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:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
-
配置HandlerMapping(applicationContext.xml)
<bean id="handlermapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/hello.do">hellocontroller</prop> </props> </property>
-
配置HelloController(applicationContext.xml)
<bean id="hellocontroller" class="cn.xdl.controller.HelloController"> </bean>
配置ViewResolver(applicationContext.xml)
<bean id="viewresolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
Spring MVC注解配置
1.框架可以采用xml配置和注解配置,平时混用,jar包中提供的组件采用XML定义;程序员定义的组件用注解置
2.handlerMapping 可以使用RequestMappingHandlerMapping 替代SimpleUrlHandlerMapping组件
SimpleUrlHandlerMapping工作
<bean id="handlermapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/hello.do">hellocontroller</prop>
<prop key="/login.do">logincontroller</prop>
</props>
</property>
RequestMappingHandlerMapping工作
<bean id="handlermapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
</bean>
<bean id="handlermapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
</bean>
可以使用下面配置替代上面< bean>定义
<mvc:annotation-driven/>
在Controller组件方法前使用@RequestMapping("/hello.do")
POST中文乱码解决办法,Spring框架提供了一个CharacterEncodingFilter过滤器。内部执行了request.setCharacterEncoding("**"),配置在web.xml中
<!-- 解决post中文乱码过滤器 -->
<filter>
<filter-name>characterfilter</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>characterfilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
RowMapper创建映射对象 ,SpringDAO 无法将查询到的结果映射成实体类对象,需要手动添加映射
RowMapper<Book> rowMapper = new BeanPropertyRowMapper<Book>(Book.class);
xml文件中定义c3p0连接池
<bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="SCOTT"></property>
<property name="password" value="TIGER"></property>
<property name="jdbcUrl" value="jdbc:oracle:thin:@locahost:1521:XE"></property>
<property name="driverClass" value="oracle.jdbc.OracleDriver"></property>
</bean>
定义JdbcTemplate对象
<bean id="template" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="c3p0"></property>
</bean>
Spring MVC拦截器组件
1.拦截器组件实现HandlerInterceptor接口,接口中有3个方法,分别在处理器执行前,执行后,请求处理额按笔响应输出前执行
public class CheckInterceptor implements HandlerInterceptor{
//postHandle和afterCompletion省略
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
//检查是否登录,session是否有指定信息
HttpSession session = request.getSession();
Object user = session.getAttribute("user");
if(user != null){//有登录信息
return true;
}else{
response.sendRedirect("/spring_05/login.do");
return false;
}
}
}
2. 在spring中配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/book/delete.do"/>
<mvc:mapping path="/book/modify.do"/>
<bean class="cn.xdl.interceptor.CheckInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
Spring MVC 异常处理
当流程调用Controller,Service,Dao组件抛出异常后,Spring MVC会调用ExceptionResolver异常处理器处理,异常处理器可以自己定义,也可以使用内置的;
1.内置异常处理器SimpleMappingExceptionResolver,xml文件中配置如下,即可
<bean id="handleException"
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props> /*发生java.lang.Exception异常跳转到error.jsp*/
<prop key="java.lang.Exception">error</prop>
</props>
</property>
编写实现类,实现HandlerExceptionResolver接口
public class MyExceptionResolver implements HandlerExceptionResolver{
//注入异常类型和视图名
private Properties exceptionMappings;
//set注入方法
public void setExceptionMappings(Properties exceptionMappings) {
this.exceptionMappings = exceptionMappings;
}
@Override
public ModelAndView resolveException(
HttpServletRequest request,
HttpServletResponse response,
Object arg2,
Exception ex) {
ModelAndView mav = new ModelAndView();
//根据ex类型封装成对应ModelAndView
String className = ex.getClass().getName();
String viewName = exceptionMappings.getProperty(className);
if(viewName == null){
viewName = "error";
}
System.out.println("className:"+className);
System.out.println("viewName:"+viewName);
mav.setViewName(viewName);
return mav;
}
}
<bean id="handleException"
class="cn.xdl.exception.MyExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.NumberFormatException">error</prop>
</props>
</property>
</bean>
局部异常处理,ExceptionResolver都是全局范围处理,如果某个Controller异常需要特殊处理,可以使用@ExceptionHandler局部处理
@ExceptionHandler
public ModelAndView handlerException(Exception e){...}
Spring AOP
-切面(aspect)(追加什么功能)
指的是在相同时机或者位置执行的相同处理。封装了共同功能的组件一般被称为切面组件,切面组件可以作用到其他组件上;
- 切入点(pointcut)(给谁加功能),Spring提供了切入点表达式,用于指定目标组件及方法
1.方法限定表达式
execution(修饰符? 返回类型 方法名(参数列表)throws异常?)
eg:execution(* cn.xdl.BookController.load*(..))
execution(* load*(..)) execution(* cn.xdl..(..))
2.类型限定表达式
within(包名.类型名) eg: within(cn.xdl.BookController)
3.组件名称限定表达式
bean(id) eg: bean(bookController) bean(*Controller)
-
通知(Advice)(什么时候加?)
指定切入功能执行的时机。例如目标方法前执行、目标方法后执行、目标方法抛异常后、目标方法执行前和执行后等。
- 环绕通知--目标方法前和后执行
- 前置通知--目标方法前执行
- 后置通知--目标方法后执行(无异常执行)
- 异常通知--目标方法抛异常后执行
- 最终通知--目标方法后执行(有无异常都执行)
-
目标(Target)
指的是需要追加功能的组件。
AOP案例:服务调用日志记录
需求:每一次调用服务,将调用时间,执行时间记录下来
1.加什么功能?
将调用时间,执行时间记录下来
2.给谁加?
bean(bookController)/within(cn.xdl.controller.BookController)
3.什么时间加?
在目标方法前和后都加逻辑(环绕通知)
4.实现过程
- 在spring中开启aop配置
<aop:aspectj-autoproxy/>
- 编写watchBean组件
@Component
@Aspect//指定该组件是切面组件
public class WatchBean {
@Around("within(cn.xdl.controller.BookController)")
public Object execute(ProceedingJoinPoint pjp) throws Throwable{
//记录调用时间、执行时间、调用服务方法名
//目标方法之前调用逻辑
Date callTime = new Date();//1.获取调用时间
String methodName = pjp.getSignature().getName();//获取服务方法名
StopWatch watch = new StopWatch();
watch.start();//开始计时
Object obj = pjp.proceed();//执行目标方法
//目标方法之后调用逻辑
watch.stop();//停止计时
long execTime = watch.getTotalTimeMillis();//3.获取执行时间
System.out.println("调用了"+methodName
+"服务,调用时间为:"+callTime+" 执行时间为:"+execTime);
return obj;
}
}
Spring AOP通知
1.前置/后置/最终/异常/环绕通知,其中后置通知是指在目标方法后加入追加功能,如果目标方法抛异常则不会追加,最终通知则无论是否抛异常,都会追加功能;
在目标方法前切入追加功能。方法格式如下:
@Before("切入点表达式")
@AfterReturning("切入点表达式")
@After("切入点表达式")
@AfterThrowing(...)
@Around("within(cn.xdl.controller.BookController)") public Object execute(ProceedingJoinPoint pjp) throws Throwable{
案例:采用AOP实现异常处理
需求:当服务调用中发生异常,将异常记录到日志文件中
@Component
@Aspect
public class ExceptionBean {
@AfterThrowing(throwing="ex",pointcut="within(cn.xdl.controller..*)")//方法抛异常执行
public void handle(Exception ex){
try {
FileWriter fw = new FileWriter("E:\\spring_07_error.log", true);
PrintWriter out = new PrintWriter(fw);
out.println("-----------发生了异常------------");
out.println("-异常类型:"+ex);
out.println("-发生时间:"+new Date());
out.println("-异常详情:");
StackTraceElement[] stes = ex.getStackTrace();
for(StackTraceElement s : stes){
if(s.toString().contains("cn.xdl")){
out.println(s.toString());
}
}
out.flush();
out.close();
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
案例2:AOP实现原理(动态代理模式)
原理:Spring容器配置AOP,容器返回的BookController对象实际是一个动态代理技术生成子类对象(CGLIB技术)。动态代理类将BookController所有方法重写,在重写时加入BookController功能和切入的新功能(切面组件)。
-
根据现有组件创建代理对象
Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(bean.getClass());//将bean类型做父类 enhancer.setCallback(this);//注册回调函数 return enhancer.create();
-
每次调用代理对象方法,都会采用回调机制调用原组件方法,可以在回调方法中做扩展操作
public class CglibProxyFactoryBean implements MethodInterceptor{ public Object intercept( Object obj, Method method, Object[] args, MethodProxy arg3) throws Throwable { //切入目标方法前面执行的逻辑 System.out.println("回首2017"); Object retval = method.invoke(target, args);//调用目标组件对象的method方法 //切入目标方法后面执行的逻辑 System.out.println("展望2018"); return retval; } }
上一篇: Spring MVC