Abstract
服务端开发课程第4讲:Web开发框架和上下文配置的XML方式与Java配置类方式
Spring MVC
的流程图
![]()
- 用户请求DispathcerServlet。
- DispatcherServlet接受到请求,将根据请求信息交给处理器映射器。
- 处理器映射器(HandlerMapping)根据用户的url请求查找匹配该url的Handler,并返回一个执行链。
- DispacherServlet再根据执行链请求处理器适配器(HandlerAdapter)。
- 处理器适配器调用相应的handle进行处理。
- 对应的handler处理完成后返回ModelAndVIew给处理器适配器。
- 处理器适配器将接受的ModelAndView返回给DispatcherServlet。
- DispatcherServlet请求视图解析器来解析视图。
- 视图解析器处理完后返回View对象给DispacherServlet。
- 最后前端控制器对View进行视图渲染(即将模型数据填充至视图中)。
项目的包图
![]()
Web.xml
首先是Web容器启动的上下文配置
它提示 在 classpath
的根路径(resource
)下面有一个对应的文件 (applicationContext.xml
)。
1 2 3 4 5 6 7 8 9 10 11
| <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>
|
多个配置文件可用逗号或空格分隔;Web容器监听器在Web容器启动时自动运行。
对应的上下文。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.example.dao"/> <context:component-scan base-package="com.example.service"/> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/exampledb" p:username="root" p:password="root" />
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSource" /> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" /> <aop:config proxy-target-class="true"> <aop:pointcut id="serviceMethod" expression=" execution(* com.example.service..*(..))" /> <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" /> </aop:config> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" /> </tx:attributes> </tx:advice> </beans>
|
此外,需要将log4J.propertis
日志配置文件放置在类路径下,以便日志引擎自动生效。
1 2 3 4
| log4j.rootLogger=DEBUG,A1 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
|
接着我们看一下Spring MVC配置的相关信息。Spring MVC像Struts一样,也通过一个Servlet
来截获URL请求,然后再进行相关的处理。
1 2 3 4 5 6 7 8 9 10 11 12 13
| <servlet> <servlet-name>viewspace</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>3</load-on-startup> </servlet>
<servlet-mapping> <servlet-name>viewspace</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping>
|
它会根据 Servlet 的名字(viewspace)加上 servlet 的后缀(viewspace-servlet
),在/WEB-INF
目录下找到对应的 servlet 的 xml 文件(契约式规定)。
在*
处对这个Servlet的URL路径进行定义,在这里让所有以.html
为后缀的URL都能被viewspace Servlet
截获,进而转由Spring MVC框架进行处理(注意:对于那些无需任何动态处理的静态网页,则可以使用.htm
后缀进行区分,以避免被框架截获)。
我们来看一下viewspace-servlet.xml
文件的内容
servlet
它的主要内容包括:
1 2 3 4 5 6 7 8 9
| <context:component-scan base-package="com.example.web"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:viewClass="org.springframework.web.servlet.view.JstlView" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/>
|
会去对应的文件里寻找 Controller。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @Controller @RequestMapping(value = "/admin") public class LoginController {
private UserService userService;
@Autowired public LoginController(UserService userService) { this.userService = userService; }
@RequestMapping(value = "/login.html") public String loginPage() {
return "login"; }
@RequestMapping(value = "/loginCheck.html") public ModelAndView loginCheck(HttpServletRequest request, LoginInfo loginInfo) { boolean isValidUser = userService.hasMatchUser(loginInfo.getUserName(), loginInfo.getPassword()); if (!isValidUser) { return new ModelAndView("login", "error", "用户名或密码错误。"); } else { User user = userService.findUserByUserName(loginInfo .getUserName()); user.setLastIp(request.getLocalAddr()); user.setLastVisit(new Date()); userService.saveLog(user); request.getSession().setAttribute("user", user); return new ModelAndView("main"); } } }
|
另一个就是 视图解析器,解析对应的视图文件名。
1 2 3 4 5 6
| <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:viewClass="org.springframework.web.servlet.view.JstlView" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/>
|
应用程序上下文配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.example.dao"/> <context:component-scan base-package="com.example.service"/> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/exampledb" p:username="root" p:password="root" />
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSource" /> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" /> <aop:config proxy-target-class="true"> <aop:pointcut id="serviceMethod" expression=" execution(* com.example.service..*(..))" /> <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" /> </aop:config> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" /> </tx:attributes> </tx:advice> </beans>
|
Dao 层代码
1
| <context:component-scan base-package="com.example.dao"/>
|
业务层代码
1
| <context:component-scan base-package="com.example.service"/>
|
配置数据源
1 2 3 4 5 6 7
| <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/exampledb" p:username="root" p:password="root" />
|
访问数据库的 jdbc 模板
1 2 3
| <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSource" />
|
事务处理器
1 2 3 4
| <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" />
|
事务增强处理器 - 切面定义
1 2 3 4 5 6 7 8 9 10 11
| <aop:config proxy-target-class="true"> <aop:pointcut id="serviceMethod" expression=" execution(* com.example.service..*(..))" /> <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" /> </aop:config> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" /> </tx:attributes> </tx:advice>
|
以上组装出了一个 可运行的 Web 应用程序。
Java 配置类
初始化类。一个 Web 初始化器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class AddressBookWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[]{RootConfig.class}; }
@Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[]{WebConfig.class}; }
@Override protected String[] getServletMappings() { return new String[]{"/"}; }
}
|
在配置中继承了AbstractAnnotationConfigDispatcherServletInitializer
这个抽象类,这个类在父类AbstractContextLoaderInitializer
中注册了ContextLoadListener
监听器,它会监听项目的启动,在项目启动时调用容器初始化方法完成Spring容器的初始化
将配置信息通过 @Override
继承复写的方式告诉它。
例如,如果你需要引入 Servlet 的配置信息。
1 2 3 4
| @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[]{WebConfig.class}; }
|
通知 该 Web 其它的 Bean。
1 2 3 4
| @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[]{RootConfig.class}; }
|
Config
实例化 Component 路径 为 example.web
@EnableWebMvc
通知需要 Mvc 的框架。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Configuration @EnableWebMvc @ComponentScan("example.web") public class WebConfig extends WebMvcConfigurerAdapter {
@Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); return resolver; }
@Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); }
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { super.addResourceHandlers(registry); }
}
|
视图解析器
1 2 3 4 5 6 7
| @Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); return resolver; }
|
RootConfig
导入了其它类作为配置的补充( DataConfig
)。
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Configuration @Import(DataConfig.class) @ComponentScan(basePackages = {"example"}, excludeFilters = { @Filter(type = FilterType.CUSTOM, value = WebPackage.class) }) public class RootConfig { public static class WebPackage extends RegexPatternTypeFilter { public WebPackage() { super(Pattern.compile("example\\.web")); } } }
|
定义组件搜索路径。除了 web 以外的所有路径下寻找是否有需要实例化的 Bean 。(排除行为)
1 2 3 4
| @ComponentScan(basePackages = {"example"}, excludeFilters = { @Filter(type = FilterType.CUSTOM, value = WebPackage.class) })
|
DataConfig
Dao 层所需要的的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Configuration public class DataConfig {
@Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("schema.sql") .build(); }
@Bean public JdbcOperations jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); }
}
|
运行Web应用
有两种方式
将项目打包成.war
文件,放入到本机tomcat/webapps/
目录下,然后启动tomcat
在pom.xml
文件中配置Web应用服务器插件(Jetty
)(注意!Jetty
版本要支持Servlet 3
才能由Java Config
类配置启动)(此处是8.1.15.v20140411
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>8.1.15.v20140411</version> <configuration> <scanIntervalSeconds>3</scanIntervalSeconds> <webAppSourceDirectory>src/main/webapp</webAppSourceDirectory> <webApp> <contextPath>/section4</contextPath> </webApp> <connectors> <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector"> <port>8088</port> <maxIdleTime>60000</maxIdleTime> </connector> </connectors> <stopKey>shutdown</stopKey> <stopPort>9998</stopPort> </configuration> </plugin>
|