Spring MVC
Spring Web MVC是基于Java的轻量级Web框架,使用了MVC架构模式的思想。Spring Web MVC核心架构为:
- 用户发送的请求到达前端控制器DispatcherServlet,前端控制器根据请求信息来决定使用哪一个页面控制器,并将处理请求转给该控制器。
- 页面控制器收到请求后,可以完成请求的逻辑(这里的逻辑复杂了),处理完毕后返回一个ModelAndView(模型数据和逻辑视图名)。
- 前端控制器收回控制权,然后根据返回的逻辑视图名,选择相应的视图进行渲染,渲染时会将返回的模型数据填充到视图中,即形成响应。
- 前端控制器将响应返回给用户。
示例应用
- 创建一个MAVEN的webapp项目,使用eclipse会默认生成需要的目录
- 通过tomcat可以部署该webapp项目,该项目的入口即为web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Archetype Created Web Application</display-name>
<!-- 全局上下文的参数从classpath中的applicationContext.xml获取 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- 定义拦截session的过滤器 -->
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 前端控制器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
spring-mvc.xml配置文件定义了前面提到的ModelAndView,只需要在classpath中能够找到即可,不固定文件位置。通常放在src/main/resources目录下:
代码语言:txt复制<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven />
<mvc:default-servlet-handler />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:viewClass="org.springframework.web.servlet.view.JstlView"
p:prefix="/WEB-INF/views/"
p:suffix=".jsp" />
<context:component-scan base-package="sample" />
</beans>
全局的上下文配置文件applicationContext.xml主要用于Redis的连接配置,如下:
代码语言:txt复制<?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:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<context:component-scan base-package="sample" />
<context:property-placeholder location="classpath:redis.properties"/>
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis.maxActive}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="poolConfig" ref="poolConfig" />
<property name="port" value="${redis.port}" />
<property name="hostName" value="${redis.host}" />
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
<bean id="redisHttpSessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="1800" />
</bean>
</beans>
在上面的配置中有配置component-scan的base-package为sample,也就是搜索页面控制器的包。因此所有的页面控制器都需要放到该包下面。如:
代码语言:txt复制package sample;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class AppController {
@RequestMapping("/")
public String home() {
return "index";
}
@RequestMapping("/put")
public String put(HttpSession session) {
session.setAttribute("name", "test");
return "index";
}
@RequestMapping("/get")
@ResponseBody
public String get(HttpSession session) {
return (String) session.getAttribute("name");
}
}
其中使用到的Controller和RequestMapping装饰器是必要的,否则无法处理请求逻辑。
测试&问题
- Redis服务未开启时,报错Could not get a resource from the pool。由于无法连接到Redis,因此可用连接耗完。
- Redis服务开启时,不支持config命令(可以通过配置文件rename-command屏蔽config命令),报错为:
Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enableRedisKeyspaceNotificationsInitializer' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Unable to configure Redis to keyspace notifications. See http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent
Caused by: java.lang.IllegalStateException: Unable to configure Redis to keyspace notifications. See http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: ERR unknown command 'CONFIG'; nested exception is redis.clients.jedis.exceptions.JedisDataException: ERR unknown command 'CONFIG'
从报错信息中可以看出,由于enableRedisKeyspaceNotificationInitializer的报错而无法初始化,导致程序启动不了。而造成enableRedisKeyspaceNotificationInitializer报错的原因是无法使用CONFIG命令。解决方案:1)通过申请设置notify-keyspace-events为Egx;2)在配置文件中添加如下配置(这个配置和JedisConnectionFactory的放置在一起):
代码语言:txt复制<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 上面的两条util和schemaLocation需要填写,否则会报错util:constant没有匹配 -->
<util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
</beans>
测试时,打开浏览器输入 http://localhost:8080/springsession/put 即可设置session,在Redis中可以查看到:
然后通过 http://localhost:8080/springsession/get 请问的时候会发现,在请求cookie中会有:
到此,spring session的简单使用和测试就已经完成了。网上对于这块的资料很杂,写法也有很多种,这里只是使用了其中一种来进行测试。
参考
- http://jinnianshilongnian.iteye.com/blog/1594806
- http://blog.csdn.net/xiao__gui/article/details/52706243
- https://github.com/13babybear/bounter-springsession