Spring MVC,顾名思义是MVC框架。相关的jar包包含在Spring Framework的lib文件夹下。MVC编程思想和Spring MVC的简介网上多的是,可以随便找几篇看一下了解个大概。这里主要介绍使用全注解方式搭建Spring MVC工程,实现返回JSP和字符串以及json字符串。源码下载地址https://github.com/S-taotao/SpringMVCA。
使用Eclipse新建动态web工程
在src目录下创建如下目录结构及类,结构不是必须的,只要该有的类都有
其中HelloWorldInitializer这个类是web应用的启动入口,个人总结大体上有两种写法:
package springmvc.configuration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/*public class HelloWorldInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(HelloWorldConfiguration.class);
ctx.setServletContext(container);
ctx.refresh(); // TTTT
ServletRegistration.Dynamic servlet = container.addServlet(
"dispatcher", new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
}
}*/
public class HelloWorldInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { HelloWorldConfiguration.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
无论哪一种写法都需要指明配置文件,RootConfigClass可以没有。下面看一下HelloWorldConfiguration的内容
package springmvc.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "springmvc")
public class HelloWorldConfiguration {
@Bean(name="HelloWorld")
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
//viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
这里@Configuration表明该类(文件)是个配置类,@EnableWebMvc启用WebMvc的功能,@ComponentScan指明类扫描范围,配置类里面定义了一个ViewResolver来设置返回页面的匹配规则,结合下面的Controller类可以看到具体怎么工作的。
Controller类大体上有两种,一种是针对请求返回页面(如html、JSP),另一种是返回字符串(通常是json,再配合上JavaScript的Ajax技术可以方便前端html页面与后台的通信,取代传统的JSP等技术)。先看第一种:
package springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/")
public class HelloWorldController {
@RequestMapping(method = RequestMethod.GET)
public String sayHello() {
return "welcome";
}
}
这里使用@Controller注解来标示,返回字符串“welcome”实际上是页面的名称,配合上面的ViewResolver,实际返回/WEB-INF/views/文件夹下名为welcome并且以.jsp为后缀的页面文件。其中这个views文件夹以及welcome.jsp都是自己手工创建的。有了这些内容,工程已经可以运行,选择HelloWorldInitializer在Tomcat上运行,然后在浏览器中可以进行访问:
看到的正是自己编写的JSP页面。
另一种Controller如下:
package springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
@RequestMapping("/rest")
public class RestController {
@RequestMapping("/user")
public String index() {
return "user";
}
}
上面的代码虽然能运行,但实际上是有问题的,RestController与Spring Framework里的org.springframework.web.bind.annotation.RestController注解重名,这会导致在整个工程里无法使用@RestController注解(导入报错),而且这种命名方式也很不规范,可能导致其他问题。@ResponseBody注解表明将函数返回内容(一般是字符串)直接返回到前端,而不是去匹配页面名称。该注解可以作用于类也可以作用于函数,例如将前面的HelloWorldController修改为:
package springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/")
public class HelloWorldController {
@RequestMapping(method = RequestMethod.GET)
public String sayHello() {
return "welcome";
}
@ResponseBody
@RequestMapping("/justString")
public String HelloString() {
return "welcome";
}
}
此时重新运行工程,访问http://localhost:8080/springMVC/justString得到
仅返回字符串。
当作用于类时,@Controller和@ResponseBody可以合并成@RestController,正是前面提到过的注解。于是将RestController改名为ResController,将@Controller和@ResponseBody可以合并成@RestController。Spring Boot的Get Start示例中就演示了一个将对象自动转化为json字符串返回到前端的功能。这个功能的实现仅需要在Spring Framework的基础上引入jackson相关的jar包即可。将ResController修改为:
package springmvc.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/rest")
public class ResController {
@RequestMapping("/user")
public User index() {
User u = new User();
u.setAge(11);
u.setName("tao");
return u;
}
}
并创建User类
package springmvc.controller;
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
重启工程,访问http://localhost:8080/springMVC/rest/user,得到
至此,已经基于全注解的方式搭建简单的Spring MVC工程,实现了Spring MVC的最简单功能。
最后强调几个注意事项:
- Tomcat使用7,最好是8以上的版本,低版本不支持Servlet3.0,进而不支持注解;我是使用的JDK1.8、Spring Framework5以及Tomcat9。
- 对于Web应用,一些依赖的jar包需要复制WebContent/WEB-INF/lib目录下,不然启动会报错;
- 使用Eclipse时,多个Web工程都配置了同一个Tomcat,启动时会相互影响。一个工程里有报错,启动另一个工程也会有报错信息,因为Console信息应该是Tomcat Server的启动和运行日志。多个工程之间可能会形成冲突,例如有多个Context同时启动。我之前就遇到过这种问题,针对一个工程排查了很久都没有问题,后来把其他的Web工程删了就好了。
- 不要将自定义的类名与JDK或者框架中的类名同名,会导致引入报错。
- 运行Web工程的Initializer的时候会报404错误,只要把路径修改成正确的路径即可
- Spring官网上的代码示例里并没有ctx.setServletContext(container);这句,但我试过不加这句会报错,除非去掉@EnableWebMvc注解。有兴趣的朋友可以研究一下。
- 后面会附工程代码,导入工程后,由于JDK版本等原因,可能在有些文件上会有红色的X提示报错,有时候甚至可以运行但还有报错提示。可以切换到java视图,然后依次选择Eclipse的Window->Show View->Problems,打开Problems视图下按提示来修复。