Spring给程序员提供了一个轻量级的构建技术框架。它主要的特性就是控制反转IOC和面向切面AOP编程。
它的作用是使用基本的Bean来完成EJB可以完成的工作,提高了代码的可重用性,简单、松耦合和可测试性。
一个简单的Spring例子:
public interface Action {
public String execute(String str);
}
实现类:
public class LowerAction implements Action{
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String execute(String str) {
return (getMessage()+str).toLowerCase();
}
}
spring配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="TheAction" class ="com.spring.LowerAction">
<property name="message">
<value>Hello</value>
</property>
</bean>
</beans>
测试:
public void testQickStart(){
ApplicationContext ctx=new FileSystemXmlApplicationContext("bean.xml");
Action action=(Action)ctx.getBean("TheAction");
System.out.println(action.execute("Jeff Cheng"));
}
在spring中的依赖注入有3种实现类型:接口注入、设值注入和构造子注入。所谓依赖注入,就是在运行期由容器将依赖关系注入到组件之中。而程序员只须负责对接口的编程而不必理会接口具体是如何实现的。
一个完整的Bean配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<description>Spring Bean Configuration Sample</description>
<bean id="TheAction"
class="com.spring.UpperAction"
singleton="true" //是否采用单例
init-method="init"
destroy-method="cleanup"
depends-on="ActionManager"> //是否依赖其他Bean
<property name="message">
<value>HeLLo</value>
</property>
<property name="dataSource">
<ref local="dataSource"/> //指定属性对其他Bean的引用关系
</property>
</bean>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/sample</value>
</property>
</bean>
</beans>
测试代码:
public void testQickStart(){
ApplicationContext ctx=new FileSystemXmlApplicationContext("bean.xml");
Action action=(Action)ctx.getBean("TheAction");
System.out.println(action.execute("Jeff Cheng"));
}
BeanFactory提供了针对Java Bean的管理功能,而ApplicationContext覆盖了BeanFactory的全部功能并提供了更为框架的实现。
1.国际化支持:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- id必须为messageSource -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!-- 或ReloadableResourceBundleMessageSource 无需重启加载-->
<property name="basenames">
<list>
<value>messages</value>
</list>
</property>
</bean>
</beans>
Spring会自动在classpath路径中搜索配置文件加载:
messages_zh_CN.properties,messages_zh.properties,message.properties
2.资源访问:Application.getResource("...");
3.事件传播:
可以通过设置listener实现
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<description>Spring Login Action</description>
<bean id="loginaction" class ="com.spring.LoginAction"/>
<bean id="listener" class="com.spring.ActionListener"/>
</beans>
LoginAction.java:
public class LoginAction implements ApplicationContextAware{
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext=applicationContext;
}
public int login(String username,String password){
ActionEvent event=new ActionEvent(username);
this.applicationContext.publishEvent(event);
System.out.println("finish");
return 0;
}
}
ActionListenser.java:
public class ActionListener implements ApplicationListener{
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof ActionEvent){
System.out.println(event.toString());
}
}
}
ActionEvent.java:
public class ActionEvent extends ApplicationEvent{
public ActionEvent(Object source) {
super(source);
}
}
测试类:
public void testLoginAction(){
ApplicationContext ctx=new FileSystemXmlApplicationContext("loginBean.xml");
LoginAction action=(LoginAction)ctx.getBean("loginaction");
action.login("Jeff Cheng", "mypass");
}
对于web应用,我们可以在web.xml中配置ContextLoaderListener或ContextLoderServlet
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
或
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Spring高级特性:与MVC框架的整合
MVC框架的运作流程:
1.将Web页面中的输入元素封装成一个请求数据对象。
2.根据请求不同,调度相应的逻辑处理单元,并将请求数据对象作为参数传入。
3.逻辑单元完成运算后,返回一个结果对象。
4.将结果数据对象中的数据与预先设计的表现层相融合并展现给用户。
Struts:成熟的设计,丰富的资源和开发群体
Webwork2:设计理念更加先进,代码与Servlet API分离,单元测试便利,BS与CS转换方便,基于对模板技术(Velocity、FreeMarker和XSLT)的支持。
SpringMVC:在依赖注入和AOP方面更加优秀,但MVC框架与底层框架的分离不如webwork,难以脱离servlet容器独立运行。
首先来看看与SpringMVC的搭配:
web.xml容器配置文件
<servlet>
<servlet-name>Dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet //负责调度的核心引擎
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/Config.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
spring核心配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- Definition of View Resolver -->
<bean id="viewResolver" class ="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 可选.servlet.view.freemarker.FreeMarkerViewResolver
或servlet.view.velocity.VelocityViewResolver -->
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix"> //设定名称前缀
<value>WEB-INF/view/</value>
</property>
<property name="suffix"> //设定名称后缀
<value>.jsp</value>
</property>
</bean>
<bean id="RegisterValidator" class="com.validator.RegisterValidator"/>
<!-- Request Mapping -->
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/register.do">RegisterAction</prop>
<prop key="/login.do">LoginAction</prop>
</props>
</property>
</bean>
<!-- Action Definition -->
<bean id="RegisterAction" class="com.action.RegisterAction">
<property name="commandClass">
<value>com.reqbean.RegisterInfo</value>
</property>
<property name="validator">
<ref local="RegisterValidator"/>
</property>
<property name="formView">
<value>register</value>
</property>
<property name="successView">
<value>RegisterSuccess</value>
</property>
</bean>
<bean id="LoginAction" class="com.action.LoginAction">
<property name="commandClass">
<value>com.action.LoginInfo</value>
</property>
<property name="fail_view">
<value>loginfail</value>
</property>
<property name="success_view">
<value>main</value>
</property>
</bean>
<!-- 对异常进行处理 -->
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView">
<value>failure</value>
</property>
<property name="exceptionMappings">
<props>
<prop key="java.sql.SQLException">showDBError</prop>
<prop key="java.lang.RuntimeException">showError</prop>
</props>
</property>
</bean>
<!-- 国际化支持 -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>messages</value>
</property>
</bean>
<!-- 根据浏览器设置语言种类 -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver">
</bean>
<!-- 根据session设置语言种类
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocalResolver.LOCALE">
</bean>
-->
<!-- 根据cookie设置语言种类
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="cookieName">
<value>browerLocale</value>//默认为org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE
</property>
<property name="cookiePath">
<value>mypath</value>//默认为 /
</property>
<property name="cookieMaxAge">
<value>999999</value>//默认为[2147483647]
</property>
-->
</beans>
LoginAction.java:
public class LoginAction extends SimpleFormController{
private String fail_view;
private String success_view;
protected ModelAndView onSubmit(Object cmd,BindException ex)throws Exception{
LoginInfo loginInfo=(LoginInfo)cmd;
if(login(loginInfo)==0){
HashMap result_map=new HashMap();
result_map.put("logininfo", loginInfo);
List msgList=new LinkedList();
msgList.add("msg1");
msgList.add("msg2");
msgList.add("msg3");
result_map.put("messages", msgList);
return new ModelAndView(this.getSuccess_view(),result_map);
}else{
return new ModelAndView(this.getFail_view());
}
}
private int login(LoginInfo loginInfo){
if("JeffCheng".equalsIgnoreCase(loginInfo.getUsername())
&&"123".equals(loginInfo.getPassword())){
return 0;
}
return 1;
}
public String getFail_view(){
return fail_view;
}
public String getSuccess_view(){
return success_view;
}
public void setFail_view(String fail_view) {
this.fail_view = fail_view;
}
public void setSuccess_view(String success_view) {
this.success_view = success_view;
}
}
RegisterAction.java:
public class RegisterAction extends SimpleFormController{
protected ModelAndView onSubmit(Object cmd, BindException ex)
throws Exception {
Map rsMap=new HashMap();
rsMap.put("logininfo", cmd);
return new ModelAndView(this.getSuccessView(), rsMap);
}
}
RegisterValidator.java:
public class RegisterValidator implements Validator{
public boolean supports(Class clazz) {
return RegisterInfo.class.isAssignableFrom(clazz);
}
public void validate(Object obj, Errors errors) {
RegisterInfo regInfo=(RegisterInfo)obj;
if(regInfo.getUsername().length()<4){
errors.rejectValue("username","less4chars",null,"用户名长度必须大于4个字母!");
}
/*检查用户名是否已经存在
if(UserDao.getUser(regInfo.getUsername())!=null){
errors.rejectValue("username","existed",null,"用户已存在");
}
*/
if(regInfo.getPassword1().length()<6){
errors.rejectValue("password1","less6chars",null,"密码长度必须大于6个字母!");
}
if(!regInfo.getPassword2().equals(regInfo.getPassword1())){
errors.rejectValue("password2","notsame",null,"两次输入密码不一致!");
}
}
}
RegisterInfo.java:
public class RegisterInfo {
private String username;
private String password1;
private String password2;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword1() {
return password1;
}
public void setPassword1(String password) {
this.password1 = password;
}
public String getPassword2() {
return password2;
}
public void setPassword2(String password) {
this.password2 = password;
}
}
LoginInfo.java:
public class LoginInfo {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
main.jsp:
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c"%>
<body>
<p>Login Success!</p>
<p>Current User:
<c:out value="${logininfo.username}" /><br>
</p>
<p>Your current messages:</p>
<c:forEach items="${messages}" var="item" begin="0" end="9" step="1" varStatus="var">
<c:choose>
<c:when test="${var.index%2==0}">
*
</c:when>
<c:otherwise>
!
</c:otherwise>
</c:choose>
<c:out value="${item}" /><br>
</c:forEach>
</body>
register.jsp:
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<body style="text-align:center">
<form action="/SpringMVC/register.do" method="post">
<spring:bind path="command.*">
<font color="#FF0000">
<c:forEach items="${status.errorMessages}" var="error">
<spring:message code="error_msg" /><c:out value="${error}" /><br>
</c:forEach>
</font>
</spring:bind>
<table border="0" width="450" height="101" cellspacing="0" cellpadding="0">
<tr>
<td height="27" width="408" colspan="2">
<p align="center"><b><spring:message code="register_title" /></b></td>
</tr>
<tr>
<td height="23" width="104"><spring:message code="username_label" />:</td>
<td height="23" width="450">
<spring:bind path="command.username">
<input type="text"
value="<c:out value='${status.value}'/>"
name="<c:out value='${status.expression}'/>">
(<spring:message code="less4chars" />)
<br>
<c:if test="${status.error}">
<font color="#FF0000">
<spring:message code="error_msg" />
<c:forEach items="${status.errorMessages}" var="error">
<c:out value="${error}"/>
</c:forEach>
</font>
</c:if>
</spring:bind>
</td>
</tr>
<tr>
<td height="23" width="104"><spring:message code="password_label" />:</td>
<td height="23" width="450">
<spring:bind path="command.password1">
<input type="password"
value="<c:out value='${status.value}'/>"
name="<c:out value='${status.expression}'/>">
(<spring:message code="less6chars" />)
<br>
<c:if test="${status.error}">
<font color="#FF0000">
<spring:message code="error_msg" />
<c:forEach items="${status.errorMessages}" var="error">
<c:out value="${error}"/>
</c:forEach>
</font>
</c:if>
</spring:bind>
</td>
</tr>
<tr>
<td height="23" width="104"><spring:message code="rep_pass_label" />:</td>
<td height="23" width="450">
<spring:bind path="command.password2">
<input type="password"
value="<c:out value='${status.value}'/>"
name="<c:out value='${status.expression}'/>">
<br>
<c:if test="${status.error}">
<font color="#FF0000">
<spring:message code="error_msg" />
<c:forEach items="${status.errorMessages}" var="error">
<c:out value="${error}"/>
</c:forEach>
</font>
</c:if>
</spring:bind>
</td>
</tr>
</table>
<p>
<input type="submit" value="提交" name="B1">
<input type="reset" value="重置" name="B2">
</p>
</form>
</body>
RegisterSuccess.jsp:
<body>
<p align="center">
<c:out value="${logininfo.username}"/>注册成功!
</p>
</body>
showError.jsp:
<body>
<%Exception ex=(Exception)request.getAttribute("Exception"); %>
<h2>Exception:<%ex.getMessage(); %></h2>
<p/>
<%ex.printStackTrace(new java.io.PrintWriter(out)); %>
</body>
模版技术:
XSLT:基于XML的表现层模版技术,移植性强,界面开发难度大,语法复杂,调试难度大,性能比较低下,内存占用较大
Velocity:最为成熟的模版技术,得到广泛的应用。
FreeMarker:表现逻辑和业务逻辑划分严格,模板不允许对Servlet API直接操作,禁止对HttpServletRequest对象直接访问,保证了层次间的清晰。提供了对JSP Tag的良好的支持,在生产效率和学习成本上具有优势。
下面用FreeMarker来改造上面的例子:
Config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- Definition of View Resolver -->
<bean id="viewResolver" class ="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.freemarker.FreeMarkerView</value>
</property>
<property name="cache">
<value>false</value>
</property>
<property name="suffix">
<value>.ftl</value>
</property>
</bean>
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath">
<value>WEB-INF/view/</value>
</property>
</bean>
<!-- Request Mapping -->
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/login.do">LoginAction</prop>
</props>
</property>
</bean>
<!-- Action Definition -->
<bean id="LoginAction" class="com.action.LoginAction">
<property name="commandClass">
<value>com.action.LoginInfo</value>
</property>
<property name="fail_view">
<value>loginfail</value>
</property>
<property name="success_view">
<value>main</value>
</property>
</bean>
</beans>
main.ftl:
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>Current User:${logininfo.username}</h1>
<#list messages as msg>
<#if msg_index%2=0>
*
<#else>
!
</#if>
${msg}<br>
</#list>
</body>
</html>