一、springMVC全注解开发步骤
1.导坐标:
spring-context
spring-webmvc
javax.servlet-api
javax.servlet.jsp-api
log4j
spring-context-support
…
2.配置SpringConfiguration(代替springapplication.xml)
@Configuration
@ConponentScan(value="cn.edu.szu.mvc",excludeFilters=@ConponentScan.Filter(type=FilterType.Annotation,classes=Controller.class))
public class SpringConfiguration{
}
3.配置SpringMvcConfiguration(代替springmvc.xml)
@Configuration
@ConponentScan("cn.edu.szu.mvc.controller")
public class SpringMvcConfiguration implements WebMvcConfigurer{
//添加资源处理规则
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/js/**")
.addResourceLocations("/public")
.resourceChain(true)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
}
//配置视图解析器
@Bean
public ViewResolver createViewResolver(){
InternalResouceViewResolver viewResolver=new InternalResouceViewResolver();
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
4.配置config(初始化spring和spring-ioc容器的配置类)
注意:
由于我们使用全注解开发,不再存在web.xml文件,根据Servlet3.0使用规范,在使用时都必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。
spring在spring-web当中已经创建文件并指定好实现类org.springframework.web.SpringServletContainerInitializer
该类使用@HandlesTypes指定要加载到SpringServletContainerInitializer接口实现中的字节码,也就是说只要我们提供一个WebApplicationInitializer的实现类即可。
我们使用WebApplicationInitializer实现的一个抽象类AbstractAnnotationConfigDispatcherServletInitializer。来继承使用
public class config extends AbstractDispatcherServletInitializer{
/**
* 添加字符集过滤器
* @param servletContext
* @throws ServletException
*/
@Override
public void onStartup(ServletContext servletContext) throws ServletException
{
// servletContext.gets
super.onStartup(servletContext);
CharacterEncodingFilter characterEncodingFilter = new
CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
FilterRegistration.Dynamic registry = servletContext.addFilter("characterEncodingFilter",characterEncodingFilter);
registry.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),false,"/*");
}
/**
* 创建web的Ioc容器
* @return
*/
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext acw = new
AnnotationConfigWebApplicationContext();
acw.register(SpringMvcConfiguration.class);
return acw;
}
/**
* 配置servlet的映射
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 创建根容器(非web层的对象容器)
* @return
*/
@Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext acw = new
AnnotationConfigWebApplicationContext();
acw.register(SpringConfiguration.class);
return acw;
}
}
二、springMVC常用注解
1、基础注解
@Controller
将标注的类加入ioc容器当中,是@Conponent的衍生注解之一。
这类注解尽量遵循spring的规范。
持久层使用@Repository
业务层使用@Service
控制层使用@Controller
工具类使用@Conponent
配置类使用@Configuration
@RequesetMapping
源码:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
//给请求URL提供一个名称。
String name() default "";
//指定url
@AliasFor("path")
String[] value() default {};
//同上
@AliasFor("value")
String[] path() default {};
/**
* 用于指定当前的方法支持什么样的请求方式。它支持以下这些类型:
* GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
* 这些值是通过RequestMethod枚举指定的。
*/
RequestMethod[] method() default {};
/**
* 用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数key和value必须和配置的一模一样。
* 例如:
* params = {"accountName"},表示请求参数必须有accountName
* params = {"moeny!100"},表示请求参数中money不能是100。
*/
String[] params() default {};
/**
* 用于指定限制请求消息头的条件。
* 例如:
* RequestMapping(value = "/something", headers = "content-type=text/*")
*/
String[] headers() default {};
/**
* 用于指定可以接收的请求正文类型(MIME类型)
* 例如:
* consumes = "text/plain"
* consumes = {"text/plain", "application/*"}
*/
String[] consumes() default {};
/**
* produces:用于指定可以生成的响应正文类型。(MIME类型)
* 例如:
* produces = "text/plain"
* produces = {"text/plain", "application/*"}
* produces = MediaType.APPLICATION_JSON_UTF8_VALUE
*/
String[] produces() default {};
}
作用:
用于建立请求URL和处理请求方法之间的对应关系。
注意:
属性只要出现2个或以上时,他们的关系是与的关系。表示必须同时满足条件。
出现位置:
写在类上:
请求URL的第一级访问目录。此处不写的话,就相当于应用的根目录。
它出现的目的是为了使我们的URL可以按照模块化管理,使我们的URL更加精细。
例如:
用户模块:
/user/add
/user/update
/user/delete
方法上:
请求URL的第二级访问目录。
衍生注解:
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
@RequestParam
源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
//指定映射参数名
@AliasFor("name")
String value() default "";
//指定映射参数名
@AliasFor("value")
String name() default "";
//改参数是否必须有值。为true时,参数没有值会报错。
boolean required() default true;
//指定参数没有值的时候的默认值
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
此注解是从请求正文中获取请求参数,给控制器方法形参赋值的。
当请求参数的名称和控制器方法形参变量名称一致时,无须使用此注解。
同时,当没有获取到请求参数时,此注解还可以给控制器方法形参提供默认值。
注意:
它只能出现在方法的参数上
示例:
@RequestMapping("useRequestParam")
public String useRequestParam(@RequestParam(value = "username",required
return "";
}
@InitBinder
源码:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InitBinder {
//指定哪些参数可以进行绑定操作,若不指定则为所有参数。
String[] value() default {};
}
作用:
用于初始化表单请求参数的数据绑定器。
使用:
//将表单提交的字符串日期转化为Date
@InitBinder("user")
public void dateBinder(WebDataBinder dataBinder){
dataBinder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}
注意:该注解声明在某个controller当中,只对该controller有效。
@DateTimeFormat
源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
public @interface DateTimeFormat {
String style() default "SS";
DateTimeFormat.ISO iso() default DateTimeFormat.ISO.NONE;
//指定转换格式
String pattern() default "";
public static enum ISO {
DATE,
TIME,
DATE_TIME,
NONE;
private ISO() {
}
}
}
作用:
实现表单日期字符串转为Date
使用:
首先在SpringMvcConfiguration添加@EableWebMvc。
@Configuration
@ConponentScan("...")
@EableWebMvc
public class SpringMvcConfiguration{}
将该注解标注在需要转换的实体类的属性上。
public class User{
...
@DateTimeFormat("yyyy-MM-dd")
privated Date birthday;
...
}
@ControllerAdvice
源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
//指定需要增强的控制器所在的包名
@AliasFor("basePackages")
String[] value() default {};
//指定需要增强的控制器所在的包名
@AliasFor("value")
String[] basePackages() default {};
//指定需要增强的类的字节码数组
Class<?>[] basePackageClasses() default {};
//指定特定的类型提供增强。
Class<?>[] assignableTypes() default {};
//用于指定给特定注解提供增强。
Class<? extends Annotation>[] annotations() default {};
}
作用:
用于给控制器提供一个增强的通知。
以保证可以在多个控制器之间实现增强共享。
它可以配合以下三个注解来用:
@exceptionhandler
@initbinder
@modeltattribute
使用:
@ControllerAdvice
public class InitBinderAdvice{
@InitBinder("user")
public void dateBinder(WebDataBinder dataBinder){
dataBinder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}
}
@RequestHeader
源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestHeader {
// 用于指定请求消息头的名称。
@AliasFor("name")
String value() default "";
// 用于指定请求消息头的名称。
@AliasFor("value")
String name() default "";
//用于指定是否必须有此消息头。当取默认值时,没有此消息头会报错。
boolean required() default true;
//用于指定消息头的默认值。
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
作用:
类似@RequestParam,作用在方法参数上面。
@CookieValue
源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CookieValue {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
作用:
获取Cookie的值,作用在方法参数上面,类似@RequestParam。
@ModelAttribute
源码:
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ModelAttribute {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean binding() default true;
}
作用:
它可以用于修饰方法,或者是参数。
当修饰方法时,表示执行控制器方法之前,被此注解修饰的方法都会执行。
当修饰参数时,用于获取指定的数据给参数赋值
使用:
由于被改注解修饰的方法会在控制器方法之前执行,故可以使用该注解修饰的方法对获取的参数进行预处理,处理得到的新参数放到model当中,再使用被@ModelAttribute修饰的参数获取到被处理过后的参数。
预处理参数有两种写法:
第一种:
@ModelAttribute
public void showModel(String oldName,Model model){
//TODO--预处理
model.addAttribute("newName",oldName);
}
第二种:
@ModelAttribute("newName")
public String showModel(String oldName){
//TODO--预处理
return oldName;
}
获取预处理后的参数:
@RequestMapping("/useModelAttribute")
public String useModelAttribute(@ModelAttribute("newName") String username) {
return "success";
}
@SessionAttribute和@SessionAttributes
源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SessionAttribute {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
}
作用:
从session域当中获取指定名称的session值。类似@RequestParam
源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SessionAttributes {
//指定存入session域当中的名称
@AliasFor("names")
String[] value() default {};
@AliasFor("value")
String[] names() default {};
//指定类型
Class<?>[] types() default {};
}
此注解是用于让开发者和ServletAPI进行解耦。
通过此注解即可实现把数据存入会话域,而无需在使用HttpSession的setAttribute方法。
注意:
当我们在控制器方法形参中加入Model或者ModelMap类型参数时,往model或者ModelMap当中存数据的操作是默认存入请求域的。而当控制器类上使用了此注解,以上操作在加入请求域当中的同时也会往session域中添加数据。
@ExceptionHandler
源码:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
//指定需要捕获的异常类型
Class<? extends Throwable>[] value() default {};
}
作用:
表明当前方法是控制器执行产生异常后的处理方法
用法:
异常捕获控制器当中添加类似方法
@ExceptionHandler(Exception.class)
public String handleException(Exception e){
System.err.println(e.getMessage());
return "error";
}
2、JSON数据交互相关注解
@RequestBody
源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
boolean required() default true;
}
作用:
获取全部的请求体
使用:
@RequestMapping("/useRequestBody")
public String useRequestBody(@RequestBody(required=false) String body){
System.out.println(body);
return "success";
}
一个应用场景:
对于以下的方法:
@RequestMapping("/add")
public String useRequestBody(User user){
//TODO--
}
当请求的参数是json格式的数据时(如{“id”:1,“username”:“szu”,“password”:“123”})
spring无法将数据封装到user当中,此时可以使用@RequestBody获取请求体(jason数据的字符串格式),再结合jackson(jackson-core)工具类可以实现参数的封装。
@ResponseBody
源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
}
作用:
用该注解标注的方法返回值是用流输出的,也就是说在下面的这个例子当中就不是跳转到success的页面,而是返回一个字符串"success",但使用ajax异步请求的时候,得到的数据就是"success"这字符串。
使用:
@RequestMapping("useResponseBody")
//@ResponseBody
public @ResponseBody String useResponseBody(String name){
return "success";
}
写在方法上,表示这个方法的返回值用流输出。
写在类上表示该控制器所有的方法都用流输出。
@RestController
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
/**
* 用于指定存入ioc容器时bean的唯一标识。
*/
@AliasFor(annotation = Controller.class)
String value() default "";
}
作用:
它具备@Controller注解的全部功能,同时多了一个@ResponseBody注解的功能
该注解标注的类上所有的方法返回值都是以流输出。
@RestControllerAdvice
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] assignableTypes() default {};
Class<? extends Annotation>[] annotations() default {};
}
作用:
它和@ControllerAdvice注解的作用一样,并且支持@ResponseBody的功能
当我们希望需要增强的方法返回值以流的方式输出时可使用该注解。
@RestControllerAdvice
public class ExceptionHandlerAdvice {
@ExceptionHandler(Exception.class)
public String handleException(Exception e){
System.err.println(e.getMessage());
return "error";
}
}
3、Rest风格URL请求相关注解
REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
传统方式:
http://localhost:8080/user/save POST
http://localhost:8080/user/update POST
http://localhost:8080/user/delete?id=1 GET
http://localhost:8080/user/find ?id=1 GET
REST
http://localhost:8080/user/ POST
http://localhost:8080/user/ PUT
http://localhost:8080/user/1 DELETE
http://localhost:8080/user/1 GET
@PathVariable
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {
//指定url映射中占位符的名称
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
}
作用:
它是springmvc框架支持rest风格url的标识。
它可以用于获取请求url映射中占位符对应的值。
使用:
@Controller
public class PathVariableController {
@RequestMapping("/usePathVariable/{id}")
public String usePathVariable(@PathVariable("id") Integer id){
System.out.println(id);
return "success";
}
}
4、跨域访问
跨域访问即跨站 HTTP 请求(Cross-site HTTP request),它是指发起请求的资源所在域不同于该
请求所指向资源所在的域的 HTTP 请求。
比如说,域名A(http://www.itheima.example)的某 Web 应用程序中通过标签引入了域名
B(http://www.itheima.foo)站点的某图片资源(http://www.itheima.foo/image.jpg),域名A的
那 Web 应用就会导致浏览器发起一个跨站 HTTP 请求。在当今的 Web 开发中,使用跨站 HTTP 请求加载
各类资源(包括CSS、图片、JavaScript 脚本以及其它类资源),已经成为了一种普遍且流行的方式。
@CrossOrigin
源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
/** @deprecated */
@Deprecated
String[] DEFAULT_ORIGINS = new String[]{"*"};
/** @deprecated */
@Deprecated
String[] DEFAULT_ALLOWED_HEADERS = new String[]{"*"};
/** @deprecated */
@Deprecated
boolean DEFAULT_ALLOW_CREDENTIALS = false;
/** @deprecated */
@Deprecated
long DEFAULT_MAX_AGE = 1800L;
@AliasFor("origins")
String[] value() default {};
@AliasFor("value")
String[] origins() default {};
String[] originPatterns() default {};
String[] allowedHeaders() default {};
String[] exposedHeaders() default {};
RequestMethod[] methods() default {};
String allowCredentials() default "";
long maxAge() default -1L;
}
作用:
指定是否支持跨域访问。
使用:
@RestController
@CrossOrigin
public class CrossOriginController {
@RequestMapping("/useCrossOrigin")
public String useCrossOrigin()throws Exception{
System.out.println("支持跨域访问");
return "success";
}
}
5、其他
纯注解开发配置拦截器步骤
1.编写拦截器类,并将类配置到ioc容器当中:
@Conponent
public class InterceptorDemo implements HandlerInterceptor {
//控制器方法之前执行,对执行方法做前置增强。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//TODO--
return true;
}
//控制器方法执行之后,结果视图执行之前执行,可以对相应数据进行增强。
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
//结果视图执行之后,相应之前执行,可以做一些清理的操作。
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
2.注册到InterceptorRegistry当中。
@Configuration
@ConponentScan()
public class SpringMvcConfiguration implements WebMvcConfigurer{
@Autowired
private InterceptorDemo interceptorDemo;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptorDemo);
}
}
执行顺序:preHandle->控制器方法->postHandle->视图渲染->afterCompletion
当有多个拦截器的时候,执行顺序为在springMvc配置类当中用加入拦截器注册的顺序
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptorDemo1);//先执行
registry.addInterceptor(interceptorDemo2);//后执行
}
当有多个拦截器的时候,执行的方法顺序:
执行成功:
preHandle1 – true
preHandle2 --true
preHandle3 --true
…
preHandle(n) --true
控制器方法
postHandle(n)
postHandle(n-1)
…
postHandle1
afterCompletion(n)
afterCompletion(n-1)
…
afterCompletion1
执行失败:
preHandle1 – true
preHandle2 --true
preHandle3 --true
…
preHandle(n) --false
afterCompletion(n-1)
afterCompletion(n-2)
…
afterCompletion1
责任链模式
责任链模式是一种常见的行为模式。它是使多个对象都有处理请求的机会,从而避免了请求的发送者和接
收者之间的耦合关系。将这些对象串成一条链,并沿着这条链一直传递该请求,直到有对象处理它为止。
优势:
解耦了请求与处理;
请求处理者(节点对象)只需关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,直接转发给下一级节点对象;
具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果;
链路结构灵活,可以通过改变链路结构动态地新增或删减责任;
易于扩展新的请求处理类(节点),符合 开闭原则;
弊端:
责任链路过长时,可能对请求传递处理效率有影响;
如果节点对象存在循环引用时,会造成死循环,导致系统崩溃;
类型转换器Converter
public interface Converter<S, T> {
/**
* 提供类型转换的逻辑
*/
@Nullable
T convert(S source);
}
定义转化器:
public class StringToDateConverter implements Converter<String,Date>{
private String pattern;
public void setPattern(String pattern) {
this.pattern = pattern;
}
private DateFormat format;
@Override
public Date convert(String source) {
try{
//1.实例化format对象
if(StringUtils.isEmpty(pattern)){
pattern = "yyyy-MM-dd";
}
format = new SimpleDateFormat(pattern);
//2.转换字符串
return format.parse(source);
}catch (Exception e){
throw new IllegalArgumentException("给定的日期格式不对!");
}
}
}
注册类型转换器:
@ControllerAdvice
public class InitBinderAdvice {
@Autowired
private Converter stringToDateConverter;
@InitBinder
public void initBinder(WebDataBinder dataBinder){
ConversionService conversionService = dataBinder.getConversionService();
if(conversionService instanceof GenericConversionService){
GenericConversionService genericConversionService =(GenericConversionService)conversionService;
genericConversionService.addConverter(stringToDateConverter);
}
}
}
异常处理器HandlerExceptionResovler
@Component
public class CustomeExceptionHandlerResovler implements HandlerExceptionResolver
{
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
//1.创建返回值
ModelAndView mv = new ModelAndView();
//2.判断当前的ex是系统异常还是业务异常
CustomeException ce = null;
if(ex instanceof CustomeException){
//业务异常
ce = (CustomeException)ex;
//设置错误信息
mv.addObject("errorMsg",ce.getMessage());
}else{
//系统异常
//设置错误信息
mv.addObject("errorMsg","服务器忙!"+ex.getMessage());
//只输出系统异到控制台
ex.printStackTrace();
}
//3.设置响应视图
mv.setViewName("error");
return mv;
}
}
SpringMVC文件上传
MultipartResolver