spring mvc 札记
spring mvc 笔记
最近练习了一下spring mvc,计划以后多用,所以做做笔记。
● 第一个头疼的问题
模仿官方的例子搭建一个工程,居然死在不能访问首页上,折腾了两天才发现问题所在。
在web.xml里配置:
spring-mvc配置文件里:
然后访问首页,一直打不开,访问其它页面完全没问题。再做一个测试,加上一行配置:
然后访问http://localhost:8080/a,很正常。但http://localhost:8080/就是不行。不管怎么google都找不到答案,郁闷了两天后突然发觉了问题:在web的根目录有一个index.html文件,删掉了就没事!
● 简化RequestMapping
如何去掉每个方法头顶的RequestMapping(value="xxx")中的xxxx,因为一般都会与方法名相同,再写就多余了。像struts2的convention插件那样就挺好。Google了很久,别人也找不到答案。
●事务问题
在Controller中调用service层的方法访问数据库时,出现当前线程没有session的问题,很诡异,之前用struts2时很正常。在老外的论坛中找到一个解决方法:在DispatcherServlet的配置文件中添加<tx:annotation-driven/>。问题是解决了,但这个方法很奇怪,因为已经在applicationContext.xml中配置了tx。还不明白是怎么回事。
后来找到解决方案。造成事务失效的原因是,mvc的配置文件先被处理,Service类此时还没有进行事务增强处理,所以不具备事务的能力,而轮到applicationContext.xml被处理时,已经被处理过的组件将被忽略。解决方法是,mvc的配置文件中不进行Service类的扫描:
后来发现,其实更好的方法是,既然同时用了spring和spring MVC,没必要搞出一个applicationContext.xml和一个spring-mvc.xml,应该统一,不要重复配置<context:component-scan/>
●中文乱码问题
直接用@ResponseBody输出字符串会有中文乱码出现,原因可Google到,其中一个解决方法是:在mvc的配置文件中添加以下配置(注意,必须添加在<mvc:annotation-driven />之前。并且,只添加StringHttpMessageConverter是不行的,那样输出json时会报错,所以只好也添加了MappingJacksonHttpMessageConverter,如果用到其它的Converter估计同样要添加。这不是一个好的解决办法。)
目前比较好的方法可能就是修改源码并重新编译了,将StringHttpMessageConverter类中的Charset.forName("ISO-8859-1");修改为Charset.forName("utf-8");这样最保险,不会引起其它错误,只是编译源码有点麻烦。
● 配置异常处理
● 页面传入集合参数
● 页面传入bean
● 页面传入List和Map
● 向页面传出json
记得添加相应的jackson依赖包,此外无需额外配置。
●hibernate代理对象的json转换
用session.get(id)来取出非代理对象,而不要用session.load(id)来取出代理对象。
●让Controller接受日期参数
在Controller类中添加如下方法:
最近练习了一下spring mvc,计划以后多用,所以做做笔记。
● 第一个头疼的问题
模仿官方的例子搭建一个工程,居然死在不能访问首页上,折腾了两天才发现问题所在。
在web.xml里配置:
<servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
spring-mvc配置文件里:
<mvc:view-controller path="/" view-name="index"/>
然后访问首页,一直打不开,访问其它页面完全没问题。再做一个测试,加上一行配置:
<mvc:view-controller path="/a" view-name="index"/>
然后访问http://localhost:8080/a,很正常。但http://localhost:8080/就是不行。不管怎么google都找不到答案,郁闷了两天后突然发觉了问题:在web的根目录有一个index.html文件,删掉了就没事!
● 简化RequestMapping
如何去掉每个方法头顶的RequestMapping(value="xxx")中的xxxx,因为一般都会与方法名相同,再写就多余了。像struts2的convention插件那样就挺好。Google了很久,别人也找不到答案。
●事务问题
在Controller中调用service层的方法访问数据库时,出现当前线程没有session的问题,很诡异,之前用struts2时很正常。在老外的论坛中找到一个解决方法:在DispatcherServlet的配置文件中添加<tx:annotation-driven/>。问题是解决了,但这个方法很奇怪,因为已经在applicationContext.xml中配置了tx。还不明白是怎么回事。
后来找到解决方案。造成事务失效的原因是,mvc的配置文件先被处理,Service类此时还没有进行事务增强处理,所以不具备事务的能力,而轮到applicationContext.xml被处理时,已经被处理过的组件将被忽略。解决方法是,mvc的配置文件中不进行Service类的扫描:
<context:component-scan base-pagckage="..."> <context:exclude-filter type="annotation" expression="ogr.springframework.stereotype.Service"/> </context:component-scan>
后来发现,其实更好的方法是,既然同时用了spring和spring MVC,没必要搞出一个applicationContext.xml和一个spring-mvc.xml,应该统一,不要重复配置<context:component-scan/>
●中文乱码问题
直接用@ResponseBody输出字符串会有中文乱码出现,原因可Google到,其中一个解决方法是:在mvc的配置文件中添加以下配置(注意,必须添加在<mvc:annotation-driven />之前。并且,只添加StringHttpMessageConverter是不行的,那样输出json时会报错,所以只好也添加了MappingJacksonHttpMessageConverter,如果用到其它的Converter估计同样要添加。这不是一个好的解决办法。)
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <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.MappingJacksonHttpMessageConverter"> <property name = "supportedMediaTypes"> <list> <value>application/json;charset=UTF-8</value> </list> </property> </bean> </list> </property> </bean>
目前比较好的方法可能就是修改源码并重新编译了,将StringHttpMessageConverter类中的Charset.forName("ISO-8859-1");修改为Charset.forName("utf-8");这样最保险,不会引起其它错误,只是编译源码有点麻烦。
● 配置异常处理
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="defaultErrorView" value="error/unexpected"/> <property name="exceptionMappings"> <props> <prop key="org.sninwo.test.AppException">error/expected</prop> <prop key="java.lang.Throwable">error/unexpected</prop></props> </property> </bean>
● 页面传入集合参数
//: test/params?xxIds=2,3,4 或 test/param2?xxIds=2&xxIds=3&xxIds=4 @RequestMapping("/test/params") public @ResponseBody String params(int[] xxIds){ for (int id: xxIds){ //.... } return "xxx"; }
● 页面传入bean
//:test/bean?s1=abc&s2=ddd&i=45&bool=1 @RequestMapping("/test/bean") public @ResponseBody String bean(Bean bean){ return bean != null ? bean.toString() : "null"; }
● 页面传入List和Map
/* List和Map不能直接传,必须绑定到某个对象上,而且,在url中指定参数时,格式有点怪异:List必须指定下标,格式为listName[i].prop=xx,Map的键也必须用中括号[],不能直接用点,格式为mapName['keyName']=value */
● 向页面传出json
记得添加相应的jackson依赖包,此外无需额外配置。
//:test/json @RequestMapping("/test/json") public @ResponseBody Bean json(){ Bean bean = new Bean(); bean.setS1("abc"); bean.setS2("ddd"); return bean; }
●hibernate代理对象的json转换
用session.get(id)来取出非代理对象,而不要用session.load(id)来取出代理对象。
●让Controller接受日期参数
在Controller类中添加如下方法:
@InitBinder protected void initBinder(WebDataBinder binder){ binder.registerCustomerEditor(Date.class, new PropertyEdiotrSupport(){ public void setAsText(String value){ setValue(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value)); } public void getAsText(){ return (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date)getValue())); } }); }