我上学那会流行的是struts,但是等我13年毕业工作的时候springMVC开始变得更加流行了,所以我又学了学springMVC,学的不算深入,但是应付开发没问题了。之前搭建的是spring-3.x的,今天下午又搭建了一次spring-4.x,耽误了很久才彻底弄好,这里还是记一下笔记吧,方便我,也方便大家。我这里只是记录搭建的步骤,不是具体的深入讲解springMVC,所以不适合那些刚刚接触springMVC的读者,不过如果只是看搭建的过程的话,完全没问题。
提前说一下,我这里使用的版本是spring-4.1.6.RELEASE版本。
1、建立工程
先建立一个maven工程,在eclipse中(这里我使用的是spring集成的eclipse,spring tool suite,简称sts,在spring的官网spring,io中可以下载到),new maven project ,选择web骨架,然后在pom中添加如下的dependency,
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
2、编辑web.xml
因为springMVC是一个servlet,所以要在web.xml里面写上springMVC的dispatchServlet,代码如下:
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
里面参数的意思是
contextConfigLocation:springMVC的配置文件,这里写的是classpath下的mvc.xml,名字随意,然后把这个文件放在maven项目的resource文件夹下就可以了。mvc.xml的结构如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
</beans>
其中的明明空间先不用管,等到我们使用到特定的标签的时候再加也可以的。
load-on-startup的意思是这个servlet在tomcat启动的时候就创建,而不是等到有访问的时候才会创建,一般的servlet如果不配置load-on-startup则只有在访问的时候才会实例化,而且在整个tomcat的声明周期中只实例化一次。其中参数1表示实例化的先后顺序,越小,越早创建。
然后对应的springMVC的servlet的servlet-mappiing的配置,这里配置的是*.do,表示所有的以.do结尾的请求被这个servet拦截。这里的配置是有讲究的,不要配置为/,这样的话就会覆盖tomcat的默认的servlet,而这个默认的servlet的作用十分强大,我们对js css 图片 html等静态资源的访问全部依靠这个servet,具体一点的,当访问失败,比如404 500错误时,返回的那个页面,就是依靠的这个缺省的servlet。当然我们可以将springMVC的servlet配置为/,但是这样的话对于静态资源的访问我们必须还要配置别的东西,我印象中里是配置<resouce>什么的,如果你非要配置为/,那么可以去查看<resource>,这里我配置为*.do。
3、测试
现在就可以启动tomcat测试了,这里我们先进入到dispatcherServlet的源码中,在他的构造方法处设置断点,然后debug启动tomcat,如果发现在设置的断点处停下,就说明配置的成功,至少是dispatcherServlet启动了,然后跑完程序后发现tomcat启动没有报错,就可以了写Hello World了。
4、继续配置
先修改mvc.xml,我依次讲解每个的意思。
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
上面配置的是viewResolver,有很多个viewResolver,因为我们从controller跳出后转向的是jsp,所以使用InternalResourceViewResolver,如果跳出后转向的是velocity,那么就要使用不同的viewResolver,这个等集成velocity的时候再细说。viewResolver的配置
·prefix 要跳转的view的位置,也就是在controller中我们返回的ModelAndView或者是String前面补充的前缀,这里/WEB-INF/jsp表示的是WEB-INF下的jsp文件件下。
·suffix 要跳转的view的后缀,也就是在controller中我们返回的ModelAndView或者是String前面补充的前缀,这里用的是jsp,如果是velocity,则可以配置为.vm。
在上面的配置中,如果我在controller中返回的是string “hello”,且这个controller没有标注@responseBody,那么将跳转到 /WEB-INF/jsp/hello.jsp中。这里的跳转类似于servlet中的forward。
继续配置controller的扫描路径:
<!-- 开启注解 -->
<mvc:annotation-driven />
<!-- 配置扫描路径 -->
<context:component-scan base-package="com.controller"
use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
这个配置很重要,在spring的配置文件中,我们可以定义很多的bean,其中就包括我们自己定义的bean,比如service dao controller,但是spring给我们提供了扫描的机制,也就是不用自己在配置文件中写上bean了,而是提供一个机制让spring去自动得将我们需要的bean放入spring的容器中,spring的解决办法是提供一个路径,然后spring会在这个路径中搜索所有的类,如果满足一定的条件就会实例化这个类,然后放入spring的容器中,这里的条件就是用的注解,即如果这个类上有@controller @service @dao @conmponent之一的话就会实例化放入ioc容器。springMVC这里的配置也是一样的。
<mvc:annotation-driven/> 这个的意思是开启注解的功能,
<context:component-scan ...../>这个的作用是配置扫描的路径,use-default-filters 这个的配置很有来头,默认的配置是true,表示只要这个类上面有@controller @service @dao @component这四个的一个,就会放入springmvc的容器,我们这里配置了false,表示禁用这个默认的值,而是指定扫描的注解为@controller,也可以使用排除 即<context:exlude-filter..../>,但是具体的exclude和include怎么配合使用我没有研究,这里只使用<include >的话,的确是不会扫描@service @dao @component的。我已经做过实验。
完成这个配置,可以做个hello world的测试了,我的测试代码如下:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class Test1 {
@RequestMapping("/hello.do")
public String hello(){
return "hello";
}
}
在 WEB-INF/jsp文件夹下创建 hello.jsp,然后访问 localhost:8080/app/hello.do 其中app表示你的web应用的contextPath,如果出现正常的页面,则表示配置成功。
5、不跳转到页面的配置
有时候,我们在前段html可能仅仅是请求一个true false,或者是一个对象的json字符串,这个时候就不需要再跳转到view了,这里需要在controller中配置@responseBody,表示执行完这个方法后不再跳转到view了。关于这个方法的返回值有很多讲究,比如返回的是个string,或者是返回的是个list又或者是map,或者是个bean,还要涉及到编码格式,返回的http响应头等。
这个就要涉及到MessageConvertor了,我们继续在xml中配置如下:
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
</list>
</property>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>applicaton/json;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
先注意:一定要把这个配置放在<mvc:annotation-driven/>前面,否则这个配置不起作用。在配置spring的xml时我总结的经验就是一定要将<bean/>的配置放在其他的配置的前面,因为其他的配置会使用到bean的配置。
这个bean RequestMappingHandlerAdaptor里面有个叫做messageConverters的list的属性,表示配置的消息转换器,springMVC自动的就会带有很多个消息转换器,比如这里配置的String的转换器 StringHttpMessageConverter,但是我们做实验的时候发现她对中文的支持不了,中文的全部是乱码,我做的实验室这样的:先注释掉上面配置的<bean RequestMappingHandlerAdaptor/> 然后访问如下的代码
@Controller
@RequestMapping("/hello")
public class HelloWorldController {
@RequestMapping("/world.do")
@ResponseBody
public String hello(){
System.out.println("fsafas");
return "hello 你好";
}
}
请一定要将刚刚配置的bean注释掉,然后访问 这个路径,发现返回的是 hello ??,中文的乱码了,说明他的默认的编码字符集不支持中文,我们到StringHttpMessageConverter的源码中一看,他有个属性:DEFAULT_CHARSET 是 ISO-8859-1的,不用多想,一定时没有指定编码他才是用了这个不支持中文的字符集。再打开我们配置的bean,返现就可以返回正常的中文了。
至于下面的MappingJackson2HttpMessageConverter,这个是处理bean的,像我们定义的person类,ArrayList啊,HashMap啊,这个转化器是转化为json字符串返回给浏览器,我们到这个类的源码中看一下他的javadoc
Implementation of {@link org.springframework.http.converter.HttpMessageConverter HttpMessageConverter} that can read and write JSON using <a href="http://jackson.codehaus.org/">Jackson 2.x's</a> {@link ObjectMapper}. <p>This converter can be used to bind to typed beans, or untyped {@link java.util.HashMap HashMap} instances. <p>By default, this converter supports {@code application/json} and {@code application/*+json}. This can be overridden by setting the {@link #setSupportedMediaTypes supportedMediaTypes} property. <p>The default constructor uses the default configuration provided by {@link Jackson2ObjectMapperBuilder}. <p>Compatible with Jackson 2.1 and higher.
大致的翻译是能够转化bean和hashamap,将其转化为json,能够通过覆写supportedMediaTypes属性来改变转化的格式。也就是说我们在配置中写的<property name="supportedMediaTypes".../>默认就是application/json类型的,然后我们再看看他的编码是什么,我们到他的父类中AbstractJackson2HttpMessageConverter,发现有个属性DEFAULT_CHARSET 就是UTF-8的,所以貌似我们这里的配置只要写上<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>即可,不用配置属性。。这样的猜测是正确的,在下面的实验中证明了这一点。
在实验之前,有一个要注意的地方,这个类使用了jackson的jar包,从他的类最上面的import中就可以发现,所以我们要在pom中加入jackson的依赖,但是jackson有两个版本,1.xx是codihaus的,2.x是fastxml的,javadoc中的Compatible with Jackson 2.1 and higher这句话提示我们要使用2.x的,也就是fastxml的,所以我在pom中加入的是如下的依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.2</version>
</dependency>
实验的java代码如下:
//我们新建立一个person类
public class Person {
public Person(){}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
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;
}
}
//controller如下 我们是通过ajax的方式访问的world2.do
@Controller
@RequestMapping("/hello")
public class HelloWorldController {
@RequestMapping("world2.do")
@ResponseBody
public Person person(){
return new Person("张三", 20);
}
@RequestMapping("/world.do")
@ResponseBody
public String hello(){
System.out.println("fsafas");
return "hello 你好";
}
}
访问的ajax代码如下,我们使用的是jquey访问的请一定要加入jquey的js
<script type="text/javascript" src="jquery-1.11.3.js"></script>
<script type="text/javascript">
$(function(){
$.ajax({
url:"/yourApp/hello/world2.do",
success:function(data){
if(data){
alert(data.name);
}else{
alert("失败");
}
}
});
});
</script>
我们访问这个jsp,然后就会alert出 ”张三“。
然后我们把配置的 <property name="supportedMediaTypes"> 注释掉,发现,结果一样,证明了我们的猜想。。
我们在测试一个map的,修改之前的HelloWorldController ,添加如下的模块
@RequestMapping("map.do")
@ResponseBody
public Map<String,String> map(){
Map<String,String> m = new HashMap<String, String>();
m.put("name", "李四");
m.put("age", "22");
return m;
}
然后仍然使用ajax访问,我的ajax的代码如下:
<script type="text/javascript" src="jquery-1.11.3.js"></script>
<script type="text/javascript">
$(function(){
$.ajax({
url:"/yourApp/hello/map.do",
success:function(data){
if(data){
alert(data.name);
}else{
alert("失败");
}
}
});
});
</script>
我的实验是可以的。
我记得之前的springMVC3中是可以序列化list的,再次做个实验:继续在上面的HelloWorldController中加入如下代码:
@RequestMapping("list.do")
@ResponseBody
public List<String> list(){
List<String> l = new ArrayList<String>();
l.add("中国");
l.add("捷克");
l.add("日本");
return l;
}
然后修改之前的jsp,如下
<script type="text/javascript" src="jquery-1.11.3.js"></script> <script type="text/javascript"> $(function() { $.ajax({ url : "/velocity/hello/list.do", success : function(data) { if (data) { for(var i=0;i<data.length;i++){ alert(data[i]); } } else { alert("失败"); } } }); }); </script>
运行后访问,可以发现,正常,说明是可以序列化list的。