SpringMVC框架--学习笔记(下)

接上篇:SpirngMVC框架--学习笔记(上):https://blog.csdn.net/a745233700/article/details/81038382


17、全局异常处理:

    系统中异常包括两类,预期异常和运行时异常RuntimeException,前者通过捕获异常从而获得异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。

    系统的dao、service、controller出现异常都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:


springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。

(1)自定义异常类:

对不同的异常类型定义异常类,继承Exception

//自定义异常:
//针对预期的异常,需要在此类中抛出此类的异常
public class CustomException extends Exception{
	
	private String message;
	public CustomException(String message)
	{
		super(message);
		this.message=message;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
}

(2)全局异常处理器:实现HandlerExceptionResolver接口

思路:

    系统遇到异常,在程序中手动抛出,dao抛给service、service给controller、controller抛给前端控制器,前端控制器调用全局异常处理器。

    全局控制器处理思路:

    ①解析出异常类型;

    ②如果该异常类型是系统自定义的异常,直接取出异常信息,在错误页面展示。

    ③如果该异常不是系统自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)

//自定义异常:
//针对预期的异常,需要在此类中抛出此类的异常
public class CustomException extends Exception{
	
	private String message;
	public CustomException(String message)
	{
		super(message);
		this.message=message;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
}

在springmvc文件中注册全局异常处理器:

       <!-- 全局异常处理器 -->
       <!-- 不用写id,系统根据是否实现HandlerExceptionResolver接口,
       		只要实现,就是全局异常处理器,如果配置多个,只有一个起作用 -->
       <bean class="com.zwp.ssm.exception.CustomExceptionResolver"></bean> 


如果与业务功能相关的异常,建议在service中抛出异常。

与业务功能没有关系的异常,建议在controller中抛出。


18、上传图片:

(1)创建图片虚拟目录:

①第一种:


②第二种:

也可以直接修改tomcat的配置:在conf/server.xml文件,添加虚拟目录:


注意:在图片虚拟目录中,一定将图片目录分级创建(提高I/O性能),一般我们采用按日期进行分级创建。

(2)加入上传图片的jar包:


(3)在页面的form表单中加入enctype="multipart/form-data"


(4)在springmvc.xml文件中配置解析器:

       <!-- 上传文件 -->
       <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
       		<!-- 设置上传文件最大尺寸 -->
       		<property name="maxUploadSize">
       			<value>5242880</value>
       		</property>
       </bean>

(5)编写Controller:

	//MultipartFile items_pic用于接收图片
	@RequestMapping(value="/editItemsSubmit",method={RequestMethod.POST,RequestMethod.GET})
	public String editItemsSubmit(Model model,HttpServletRequest request,Integer id,
			@ModelAttribute("itemsCustom") @Validated(value={ValidGroup1.class}) ItemsCustom itemsCustom,
			BindingResult bindingResult,MultipartFile items_pic) throws Exception
	{
		
		if(bindingResult.hasErrors())
		{
			List<ObjectError> allErrors=bindingResult.getAllErrors();
			//自定义一个list接受自己编码后的提示字符串,在把自己定义的list传到界面,
            //这样就解决了把乱码传到界面的问题了
            List<String> listErrors=new ArrayList<>();

			for(ObjectError objectError:allErrors)
			{
				//System.out.println(objectError.getDefaultMessage());
				//把返回错误的提示再次编码
				String strError=new String(objectError.getDefaultMessage().getBytes("ISO-8859-1"),"UTF-8"); 
	            listErrors.add(strError);//把编码好的错误提示信息加自己定义好list集合里面去
			}
			model.addAttribute("allErrors", listErrors);
			return "Items/editItems";
		}
		
		//原始名称
		String originalFilename=items_pic.getOriginalFilename();
		System.out.println(originalFilename);
		//上传图片:
		if(items_pic!=null && originalFilename!=null && originalFilename.length()>0)
		{
			//存储图片的物理路径:
			String pic_path="D:\\Tomcat 5.5\\pictures\\";
		
			//新的图片名称
			String newFileName=UUID.randomUUID()+originalFilename.substring(originalFilename.lastIndexOf("."));
			//新图片:
			File newFile=new File(pic_path+newFileName);
	
			//将内存中的数据写入磁盘
			items_pic.transferTo(newFile);
			
			//将新图片名称写到itemsCustom中
			itemsCustom.setPic(newFileName);
		}
		
		//调用service更新商品信息,页面需要将商品信息传到此方法中
		itemsService.updateItems(id, itemsCustom);
				
		//页面转发:
		return "forward:queryItems.action";
	}

(6)页面显示:



19、json交互:

@RequestBody:将json串转成java对象

@ResponseBody:将java对象转成json输出。

(1)请求是Json串,输出也是json串。


(2)请求是key/value,输出是json(常用)


最终结果都是输出json数据,为了在前端页面方便对请求结果进行解析。

步骤:

第一步:导入jar包依赖:


第二步:配置json转化器:

在注解适配器中加入messageConverters


注意:如果使用<mvc:annotation-driver />则不用定义上边的内容。

第三步:页面和控制器:









20、springmvc对RESTful的支持:

(1)对url进行规范,写RESTful格式的url


(2)http的方法规范 :

不管是删除、添加、更新。。使用的url是一致的,如果进行删除,需要设置http的方法为delete,同理添加。

后台controller方法:判断http方法,如果是delete执行删除,如果是post执行添加。

(3)对http的contentType规范:

请求是指定contentType,要json数据,设置成json格式的type..


需求:查询商品信息,返回json数据。

思路:定义方法,进行url映射使用RESTful风格的url,将查询商品的信息的id传入controller。输出json是,使用@ResponseBody将java对象输出json。

(1)Controller:


	//根据id查找商品信息,使用RESTful风格,并返回json格式的数据
	//("/itemView/{id}")的id要和@PathVariable("id")的id一致,表示将("/itemView/{id}")的id绑定到@PathVariable后面的参数上面
	@RequestMapping("/itemView/{id}")
	public @ResponseBody ItemsCustom itemView(@PathVariable("id") Integer id) throws Exception
	{
		ItemsCustom itemsCustom=itemsService.findItemsById(id);
		return itemsCustom;
	}

(2)前端控制器配置:

	<!-- RESTful规范要用第二种方式: -->
	<servlet>
		<servlet-name>springmvc_restful</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
			
		<!-- contextConfigLocation:配置springmvc加载的配置文件(处理处映射器、处理器适配器等等) -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:/spring/springmvc.xml</param-value>
		</init-param>	 
	</servlet>
	<servlet-mapping>
		<servlet-name>springmvc_restful</servlet-name>
		<!-- 
		第一种:*.action,访问以.action结尾由DispatcherServlet进行解析
		第二种:/,所有访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置
				不让DispatcherServlet进行解析,使用此种方法可以实现RESTful风格的url
		第三种:/*,这种配置不对,使用这种配置,最终要转发到一个jsp页面,仍然会由
					DispatcherServlet解析jsp,不能根据jsp页面找到Handle,会报错-->
		<url-pattern>/</url-pattern>
	</servlet-mapping>

(3)访问的url:


(4)json结果数据:



--对静态资源的解析:

在springmvc.xml中添加静态资源的解析:

      <!-- 对静态资源的解析,如js,css,img... -->
      <!--  <mvc:resources location="/js/" mapping="/js/**"></mvc:resources> -->
      <!--  <mvc:resources location="/css/" mapping="/css/**"></mvc:resources> -->

对静态资源的访问:



21、springmvc拦截器:

(1)定义拦截器:实现HandlerInterceptor接口

public class HandlerInterceptor1 implements HandlerInterceptor{
	
	//进入Handler方法之前
	//用于身份认证,身份授权
	//比如身份认证,如果认证不通过表示当前用户没有登陆,需要此方法拦截不再向下执行
	@Override
	public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {	
		//return false:表示拦截,不向下执行
		//return ture:表示放行
		System.out.println("HandlerInterceptor1...preHandle");
		return true;
	}

	//进去Handler方法之后,返回modelAndView之前执行
	//应用场景:将公用的模型数据(比如菜单导航)传到视图,也可以在这里统一制定视图
	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
		System.out.println("HandlerInterceptor1...postHandle");
	}
	
	//执行Handler完成执行此方法
	//应用场景:统一异常处理,统一日志处理
	@Override
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		System.out.println("HandlerInterceptor1...afterCompletion");
	}
}

(2)配置拦截器:

①针对HandlerMapping进行拦截配置:(一般不用)

如果在某个HandlerMapping中配置,经过该HandlerMapping映射成功的Handler最终使用该拦截器。


②类似全局的拦截器:

springmvc配置类似全局的拦截器,springmvc框架将配置的类似全局的拦截器注入到每个HandlerMapping中。

       <!-- 拦截器 -->
       <mvc:interceptors>
       		<!-- 多个拦截器:顺序执行 -->
      	 	<mvc:interceptor>
      	 		<!-- /**:表示拦截所有url包括子url路径 -->
       			<mvc:mapping path="/**"/>
       			<!-- 拦截器路径 -->
       			<bean class="com.zwp.ssm.intercept.HandlerInterceptor1"></bean>
      	 	</mvc:interceptor>
      	 	
      	 	<mvc:interceptor>
       			<mvc:mapping path="/**"/>
       			<bean class="com.zwp.ssm.intercept.HandlerInterceptor2"></bean>
      	 	</mvc:interceptor>    	 	
       </mvc:interceptors>

(3)测试:

①两个拦截器都放行:

总结:preHandle方法按顺序执行;postHandle和afterCompletion按拦截器配置的逆向顺序执行。

②拦截器1放行,拦截器2不放行:


总结:拦截器1放行,拦截器2的preHandle才会执行;

拦截器2的preHandle不放行,拦截器2的postHandle和afterCompletion不会执行;

只要有一个拦截器不放行,postHandle不会执行。

③拦截器1不放行,拦截器2不放行:


总结:拦截器1的preHandle不放行,postHandler和afterCompletion不会执行。

拦截器1的preHandle不放行,拦截器2不执行。

(4)拦截器小结:

根据测试结果,对拦截器应用。

比如:统一日志处理拦截器,需要该拦截器的preHandle一定要放行,且将它放在拦截器链中第一个位置。

比如:登陆认证拦截器,放在拦截器链中第一个位置;权限校验拦截器,放在登陆认证拦截器之后。(因为登陆通过之后才检验权限)

(5)拦截器应用:

需求:

①用户请求url

②拦截器进行拦截校验:

    --如果请求的url是公开地址(无需登陆即可访问的url),让放行。

    --如果用户session不存在,跳转到登陆页面

    --如果用户session存在,放行,继续操作。

登陆页面:

<form action="${pageContext.request.contextPath }/login.action" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
<input type="submit" value="登陆"/>
</form>

拦截器:

public class LoginInterceptor implements HandlerInterceptor{
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {	
		//获取请求的url
		String url=request.getRequestURI();
		//判断url是否是公开地址(实际使用时,将公开地址配置在配置文件中)
		//这里公开地址是登陆提交的地址
		if(url.indexOf("login.action")>=0){
			//如果进行登陆提交,放行
			return true;
		}
		//判断session
		HttpSession session=request.getSession();
		//从session中取出用户身份信息:
		String username=(String) session.getAttribute("username");
		if(username!=null)
		{	//身份存在,放行
			return true;
		}	
		//执行到这里,表示用户身份需要认证,跳转到登陆界面
		request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);	
		return false;		
	}
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView arg3) throws Exception {

	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception arg3)
			throws Exception {
	}
}

注册拦截器:

       <!-- 拦截器 -->
       <mvc:interceptors>
       		<!-- 多个拦截器:顺序执行 -->
       		<mvc:interceptor>
      	 		<!-- /**:表示拦截所有url包括子url路径 -->
       			<mvc:mapping path="/**"/>
       			<!-- 拦截器路径 -->
       			<bean class="com.zwp.ssm.intercept.LoginInterceptor"></bean>
      	 	</mvc:interceptor>
       </mvc:interceptors>

编写Controller:

@Controller
public class LoginController {
	@RequestMapping("/login")
	public String login(HttpSession sesison,String username,String password) throws Exception{
		//调用service进行身份验证
		//...
		
		//保存用户身份信息
		sesison.setAttribute("username", username);
		//重定向到商品列表
		return "redirect:items/queryItems.action";	
	}
	
	@RequestMapping("/logout")
	public String logout(HttpSession sesison) throws Exception{
		
		//清除session
		sesison.invalidate();
		//重定向商品列表
		return "redirect:items/queryItems.action";		
	}
}

登陆成功页面:




猜你喜欢

转载自blog.csdn.net/a745233700/article/details/81045960