一、Spring常用注解
@Configuration 把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean
@Scope 注册 作用域
@Lazy(true) 表示延迟初始化
@Service 用于标注业务层组件
@Controller 用于标注控制层组件
@Repository 用于标注数据访问组件,即DAO组件
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
@Scope用于指定scope作用域的(用在类上)
@PostConstructor 用于指定初始化方法(用在方法上)
@PreDestroy 用于指定销毁方法(用在方法上)
@Resource 默认按名称装配,当找不到名称匹配的bean时,才会按照类型装配。
@DependsOn 定义Bean初始化及销毁时的顺序
@Primary 自动装配时,如果出现多个Bean的候选者,被注解为@Primary的Bean将作为首选者,否则将抛出异常
@Autowired 默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。(@AutoWired @Qqualifier("personDaoBean"))
@Async 异步方法调用
@ComponentScan 自动扫描包路径下面的所有@Controller、@Service、@Repository、@Component的类
二、SpringMVC配置拦截器
1.自定义拦截器,实现HandlerInterceptor接口。
public class AuthInterceptor extends HandlerInterceptorAdapter {
/**
* 进入Handler方法之前执行
* 用于身份认证、身份授权
* 比如身份认证,如果认证没有通过,表示当前用户没有登录,需要此方法拦截不再向下执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
/**
* 执行完Handler完成执行此方法
* 应用场景:统一异常处理,统一日志处理
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
/**
* 进入Handler方法之后,返回modelAndView之前执行
* 应用场景从modelAndView出发,将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
*/
@Override
public void postHandle(HttpServletRequest request,HttpServletResponse,Object handler,ModelAndView modelAndView) throws Exception {
}
}
2.mvc配置文件中指定拦截器Bean
applicationContext-mvc.cml:
<mvc:interceptors>
<!-- 国际化操作拦截器 如果采用基于(请求/Session/Cookie)则必需配置 -->
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
<!-- 如果不定义 mvc:mapping path 将拦截所有的URL请求 -->
<bean class="com.fescotech.common.base.interceptor.AuthInterceptor"></bean>
</mvc:interceptors>
三、如何处理异常?
Spring提供了多种方式将异常转为响应:
- 特定的Spring异常将会自动映射为指定的HTTP状态码;
- 异常上添加@ResponseStatus注解,从而将其映射为某个HTTP状态码;
- 在方法上可以添加@ExceptionHandler注解,使其用来处理异常
3.1将异常映射为HTTP状态码
在默认情况下,Spring会将自身的一些异常自动转换为合适的状态码。
Spring异常 | HTTP状态码 |
---|---|
BindException | 400-Bad Request |
ConversionNotSupportException | 500-Interal Server Error |
HttpMediaTypeNotAcceptableException | 406-Not Acceptable |
HttpMediaTypeNotSupportdException | 415-Unsupported Media Type |
HttpMessageNotReadableException | 400-Bad Request |
HttpMessageNotWritableException | 500-Internal Server Error |
HttpRequestMethodNotSupportedException | 405-Method Not Allowed |
MethodArgumentNotValidException | 400-Bad Request |
MissingServletRequestParameterException | 400-Bad Request |
MissingServletRequestPartException | 400-Bad Request |
NoSuchRequestHandlingMethodException | 404-Not Found |
TypeMismatchException | 400-Bad Request |
上表的异常一般回由Spring自身抛出,作为DispatcherServlet处理过程中或执行校验时出现问题的结果。
以上异常为内置的映射结果,对于应用出现的问题无能为力。此时需要Spring提供的@ResponseStatus注解,将异常映射为HTTP状态码。
3.2 @ResponseStatus注解将异常映射为HTTP状态码
以下为Controller代码示例,方法中抛出一个自定义异常SpittleNotFoundExeption
@RequestMapping(value="/{spittleId}",method=RequestMethod.GET)
public String spittle(@PathVariable("spittleId") long spittleId,Model model){
Spittle spittle = spittleRepository.findOne(spittleId);
if(spittle == null){
//抛出自定义异常
throw new SpittleNotFoundException();
}
model.addAttribute(spittle);
return "spittle";
}
创建一个自定义的非检查型异常,继承RuntimeException,类上加注解@ResponseStatus。该注解的两个属性:value是被映射成的HTTP状态码,reason是自定义的异常出现原因。
package spittr.web;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value=HttpStatus.NOT_FOUND,reason="Spittle Not Found")
public class SpittleNotFoundException extends RuntimeException{
//自定义逻辑
}
如此的话,当控制器抛出SpittleNotFoundException异常时,响应会具有404状态码,原因是“Spittle Not Found”。
3.3编写异常处理的方法
上面提到的两种处理情况,都是将项目中的异常转为HTTP错误。如果我们想在响应中还包含所产生的错误的时候,就需要按照处理请求的方式来处理异常。
下面是我们直接在方法中处理异常的方法,先捕获到DuplicateException异常,然后在处理逻辑中将响应转至duplicate路径。
@RequestMapping(method=RequestMethod.POST)
public String saveSpittle(SpittleForm form,Model model){
try{
spittleRepository.save(new Spittle(null,form.getMessage(),new Date(),form.getLongitude(),form.getLatitude()));
return "redirect:/spittles";
}catch(DuplicateSpittleException e){
return "error/duplicate";
}
}
这个方法没有问题,但是我想让这个方法更简单,让saveSpittle()方法只关注正确的路径,让其他方法去处理异常。
把异常方法处理逻辑去掉:
@RequestMapping(method=RequestMethod.POST)
public String saveSpittle(SpittleForm form,Model model){
spittleRepository.save(new Spittle(null,form.getMessage(),new Date(),form.getLongitude(),form.getLatitude()));
return "redirect:/spittles";
}
再在Controller中添加一个新方法,处理抛出的DuplicateException:
@ExceptionHandler(DuplicateException.class)
public String handleDuplicateSpittle(){
return "error/duplicate";
}
在handleDuplicateException方法上添加ExceptionHandler注解,当抛出DuplicateException异常时,将会委托这个方法来处理。
ExceptionHandler标注的方法,能处理同一个控制器中所有处理器方法抛出的异常。
所以SpittleController中所有处理器方法抛出的DuplicateException异常,都会被handleDuplicateSpittle这个方法来处理。
3.4控制器通知实现统一处理异常
控制器通知(Controller Advice)不是用来专门处理异常,我只描述通过控制器通知方式来处理异常的实现。
控制器通知要解决的问题是:将控制器类的特定切面能运用到整个应用程序的所有控制器中,但又避免出现重复相同的代码。
Spring3.2引入了控制器通知,它是一个带有@ControllerAdvice注解的类,这个类包含一个或多个如下类型方法:
- @ExceptionHandler注解标注的方法
- @InitBinder注解标注的方法
- ModelAttribute注解标注的方法。
在带有@ControllerAdvice注解的类中,以上所述的这些方法会运用到整个应用程序所有控制器中带有@RequestMapping注解的方法上。
@ControllerAdvice注解本身已经使用了@Component,因此@ControllerAdvice注解所标注的类会自动被组件扫描获取到,就像带有@Component注解的类一样。
@ControllerAdvice最实用的场景就是将所有的@ExceptionHandler方法收集到一个类中,这样所有控制器的异常就能在一个地方进行一致的处理。
例如,我们将DuplicateSpittleException的处理方法应用到整个应用程序的所有控制器上,下面AppWideExceptionHandler就能做到:
package spittler.web;
import org.spring.framework.web.bind.annotation.ControllerAdvice;
import org.spring.framework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class AppWideExceptionHandler{
@ExceptionHandler(DuplicateSpittleException.class)
public String duplicateSpittleHandler(){
return "error/duplicate";
}
@ExceptionHandler(RecordNotFoundException.class)
public String recordNotFoundHandler(){
return "error/notfound";
}
//后面还可以再补充其他的异常处理方法
}
现在,如何任何控制器方法抛出DuplicateSpittleException异常,或者RecordNotFoundException异常,都会来这个控制器通知类中来匹配相应的方法来进行处理。