学习Spring很长时间了,但是一直也没有认真地总结一次,总是陷入学习了忘记的怪圈(其实也不是怪圈,就是学习了没有总结)。 今天我就写一点东西来总结我的Spring MVC学习之旅。 这个得从我的Sponsor给我布置的家庭作业讲起,在这里面我学会了很多Spring的知识。
可以假设Spring是一个大的容器,里面放着各种各样的网页、文件等以供一个个的request访问。所以,我想从web.xml的配置说起。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<listener>
<listener-class> org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name> contextConfigLocation</param-name>
<param-value> classpath:applicationContext-servlet.xml
classpath:applicationContextDataSource.xml
</param-value>
</context-param>
<servlet>
<servlet-name> bankingSystem2</servlet-name>
<servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup> 1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name> bankingSystem2</servlet-name>
<url-pattern> /</url-pattern>
</servlet-mapping>
上面的配置的意思是:servlet-mapping配置了需要被捕获的请求,url-pattern是匹配request中跟的那个地址,例如’/abc’。这里只是一个斜杠,意思是捕获所有的请求,捕获这个请求的servlet名字叫bankingSystem2。 在捕获一个请求后,Spring会根据servlet-name中的名字在这个xml中找到匹配的一个servlet,然后servlet-class是这个servlet文件的所在地。这里的DispatcherServlet是Spring的类,所有发往由Spring 容器管理的资源的请求都由它统一收集,然后它会求助Spring中内置的HandlerMapping已决定该请求被哪个controller处理。最后根据不同的请求目的地再转发给不同的controller,这个后面会讲到。这里有一点值得注意,我们在web.xml中定义了一个名字叫bankingSystem2的servlet,那么就需要在同一个文件夹下面定义一个名叫bankingSystem2-servlet.xml这样一个配置文Spring会默认到里面去读取配置和加载bean,如果没有会报错。
ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。 在上面的contextConfigLocation里面配置了想要在Spring容器启动的时候加载的bean。 看完了上述的web.xml,我们进入在那个配置文件中配置的applicationContext-servlet.xml中一探究竟。
1
2
3
4
5
6
7
<context:component-scan base-package= "bank.icbc.controller" />
<context:component-scan base-package= "bank.icbc.domain" />
<mvc:annotation-driven/>
<bean class= "org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name= "prefix" value= "/WEB-INF/jsp/" />
<property name= "suffix" value= ".jsp" />
</bean>
首先,在这个xml配置文件中定义了一个bean,里面存放着一个resolver,他是Spring中负责显示页面的。即controller处理完一个request后,会返回一个string告诉Spring现在该显示哪个页面了,然后Spring就找到InternalResourceViewResolver,让它告诉该到哪去找到相应的页面来显示。例如,如果一个controller返回字符串abc,那么根据上面的配置,在/WEB-INF/jsp/abc.jsp这个页面就应该被显示。 上面还有一个context:component-scan,它的作用是‘扫描’相应的package,把这些package中的所有类纳入Spring的管理范围来,这样的好处是,如果相应包中有autowire bean中的类,只有主类在Spring的管理范围, 被引用的bean才能被成功地autowire。
同时,有些类(例如服务类)会在类上面表示诸如@Service这样的annotation,它被扫描到后,也会被Spring纳入到管理,这样其他类也能autowire它了。 annotation-driven:表示支持annotation,不写的话所有的annotation注解都无效。
在说完配置后,我们来模拟一个请求,来看看Spring在这个过程中的运行过程。首先,我们来到系统的初始页面:
1
2
3
4
Welcome to the Banking System! <br/> <c:url value= "/addCustomer" var= "addCustomer" />
1. <a href= "${addCustomer}" > add Customer</a> <c:url value= "/withdraw" var= "withdraw" />
2. <a href= "${withdraw}" > withdraw</a> <c:url value= "/deposit" var= "deposit" />
3. <a href= "${deposit}" > deposit</a>
在这个页面中,我们定义了3个URL,请求的地址分别是/addCustomer, /withdraw和/deposit。这时就需要有相应的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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
@Controllerpublic
class CustomerController {
@Autowired
@Qualifier ( "bank" )
private Bank bank ;
@Autowired
@Qualifier ( "customer" )
private Customer customer ;
@RequestMapping ( value = "/addCustomer" , method = RequestMethod . GET )
public String addCustomer ( @ModelAttribute Customer customer ) {
return "AddCustomer" ;
}
@RequestMapping ( value = "/addCustomer" , method = RequestMethod . POST )
public String saveCustomer ( @ModelAttribute Customer customer , ModelMap modelMap ) {
bank . addCustomer ( customer );
Customer theCustomer = bank . getCustomer ( customer . getNickname ());
modelMap . addAttribute ( "nickname" , theCustomer . getNickname ());
modelMap . addAttribute ( "dateOfBirth" , theCustomer . getDateOfBirth ());
modelMap . addAttribute ( "emailAddress" , theCustomer . getEmailAddress ());
return "ShowCustomer" ;
}
@RequestMapping ( value = "/openAccount" , method = RequestMethod . GET )
public String openAccount ( @ModelAttribute Account account , ModelMap modelMap ) {
bank . addAccount ( customer );
Account account1 = bank . getAccount ( customer . getNickname ());
modelMap . addAttribute ( "nickname" , account1 . getBalance ());
modelMap . addAttribute ( "balance" , account1 . getBalance ());
modelMap . addAttribute ( "joinDate" , account1 . getJoinDate ());
modelMap . addAttribute ( "isPremium" , account1 . isPremium ());
return "ShowAccount" ;
}
@RequestMapping ( value = "/withdraw" , method = RequestMethod . GET )
public String goToWithdrawPage ( @ModelAttribute Customer customer ) {
return "Withdraw" ;
}
@RequestMapping ( value = "/deposit" , method = RequestMethod . GET )
public String goToDepositPage ( @ModelAttribute Customer customer ) {
return "Deposit" ;
}
@RequestMapping ( value = "/deposit" , method = RequestMethod . POST )
public String deposit ( final HttpServletRequest request , ModelMap modelMap ) {
String nickname = request . getParameter ( "nickname" );
Account account = bank . getAccount ( nickname );
account . deposit ( Integer . parseInt ( request . getParameter ( "balance" )), nickname );
Account account1 = bank . getAccount ( nickname );
modelMap . addAttribute ( "balance" , account1 . getBalance ());
return "ShowBalance" ;
}
@RequestMapping ( value = "/withdraw" , method = RequestMethod . POST )
public String withdraw ( final HttpServletRequest request , ModelMap modelMap ) {
String nickname = request . getParameter ( "nickname" );
Account account = bank . getAccount ( nickname );
account . withdraw ( account . getBalance (), nickname );
Account account1 = bank . getAccount ( nickname );
modelMap . addAttribute ( "balance" , account1 . getBalance ());
return "ShowBalance" ;
}
@RequestMapping ( value = "/welcome" )
public String backToWelcomePage () {
return "Welcome" ;
}
@ExceptionHandler ( Exception . class )
public String handleException () {
return "Exception" ;
}
}
这个类是一个controller(因为被标注了@Controller 这个注解)。然后,它会捕获上面那个jsp页面发出来的请求,而具体哪个方法捕获对哪一个URL的请求,请看具体方法上面标示的@RequestMapping 注解。它规定了handle的请求的类型和URL。在处理请求的controller方法的签名中,我们看到request和modelmap。它们都被Spring容器管理,所以直接在方法参数中加进来就能直接使用它们了。ModelMap 的左右是存放一些值,用于JSP页面显示。
这就是一次大致的Spring MVC 例子的讲解。