通向架構師的道路 ( 第二十二天 ) 萬能框架 spring ( 四 ) 使用 struts2
(點擊
上方公眾號
,可快速關注)
來源:袁鳴凱 ,
blog.csdn.net/lifetragedy/article/details/8192736
一、前言
SSH有了,現在我們要把我們的struts層從原來的1.3替換成struts2.x,引入了struts2.0後我們會發覺我們的代碼和框架的變化還是不小的
二、Struts2的好處
1)在struts2的方法里,一切變數是線程安全的,而原有的struts1不是的;
2)在struts2中如果你聲明了如下這樣的代碼:
privater String studentName=」」;
public void setStudentName(String studentName){
this.studentName = studentName;
}
public String getStudentName(){
return this.studentName;
}
那麼當你對這個studentName進行符值後,不需要再把它用request.setAttribute這樣的形式把值帶到頁面中去了,相當於你可以省去在request中來回的setAttribute{…}getAttribute{…}的操作(有時由於忘記把一個listset 到request中去,經常導致一個頁面就是不顯示列表,對吧?這樣的事可以被極大程度上避免掉)。
3)更豐富且描述簡單的頁面標籤,可以直接支持將一個Object和頁面的<input>進行綁定,如:
我在後台如果有一個StudentVO,這個StudentVO如下描述:
private String studentNo = "";
private String studentName = "";
public String getStudentNo() {
return studentNo;
}
public void setStudentNo(String studentNo) {
this.studentNo = studentNo;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
於是我在前台jsp里可以直接這樣使用我的<input>標籤和我這個VO中的某個欄位進行綁定:
<s:textfield name="studentVO.studentName" size="24" maxlength="25"/>
4)原有在struts1中的formbean徹底消失,去而代之的是使用VO對象,一個strutsaction就是一個普通的類,只是它extendsActionSupport而己。
5)良好的注入機制,連session,request, response都可以注入了,因此你的一個action方法就是一個普通類方法,這樣做的好處是極大化將servlet與我們的action進行解耦合。試想如果是原有的struts1的action方法,我現在要改成swing的actionPerform,你是不是要把原有的action方法包括傳參都要進行重構啊?而現在有了struts2,由於連session,request, response都是被注入的,因此這個struts2的action方法可以直接重用。
Strtus2還有很多好處,這邊不一一列舉了,在struts2的官方文檔和stepby step等書中詳細有說,我們這邊主要以實戰為主,講述struts2怎麼和spring進行整合併且能夠開發我們的應用。
三、整合spring和struts2
我們還是用我們的Maven2。
Struts2變化很大,它是一個幾乎被重寫的框架,而不是一個「增強」的框架,它是繼承自xwrok的框架並且在整個框架中全面使用了filter機制。
對於我們的maven的pom.xml文件來說,這個lib庫的改動還是很大的。
甚至還會出現一些莫名奇妙的錯誤而其原因是因為lib庫的版本不對或者是有衝突,為此筆者整理了一份ssh2的所有需要的jar的mavenpom.xml文件。
雖然,我會在後面把這個xml文件完整列出來但還是希望大家在一開始跟著我能夠一步步走,對pom.xml文件和工程進行排錯,這樣你將對一些常用的框架的lib庫有個比較熟悉的過程。
3.1 延用原有的myssh工程中的pom.xml文件
我們新建一個maven的web工程-myssh2,並將原有的myssh工程中的pom.xml文件拷入工程中。
請確保你使用的jdk版本為version1.6.x。
3.2 去除所有的struts1.3的依賴關係
打開這個pom.xml文件,把下面這段所有的關於struts1.3的依賴包全部去除。
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-core</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-el</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-extras</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-faces</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-mailreader-dao</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-scripting</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-taglib</artifactId>
<version>1.3.10</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts-tiles</artifactId>
<version>1.3.10</version>
</dependency>
3.3 增加struts2的依賴包
我們把原有的struts1.3的依賴包去除後加入struts2的依賴包
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.3.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.1.2</version>
</dependency>
存檔後,此時maveneclipse插件會自動開始編譯和下載相關的jar到你的本地maven的repository中,然後我們會發覺這個pom.xml文件出錯了:
拋一個sun.tool.jar沒有找到的錯誤。
道理很簡單,因為該tool.jar其實已經存在在我們本地安裝的jdk的lib目錄下了,因此我們不需要這個包,但是maven是自動依賴的,你沒有看到它在pom.xml文件中出現不代表這層依賴關係不存在。
因此我們需要做的是exclude這個包。
讓我們在maveneclipse插件中打開這個pom.xml文件,切換到「DependencyHierarchy」視圖,然後找到這個tool.jar文件,點左邊這個list中的tools:1.5.0然後右鍵選「ExcludeMaven Artifact」。
選[ok]按鈕然後存檔。
我們可以看到這個pom.xml文件一切正常了,沒有紅色的「叉叉」了,我們切換到pom.xml視圖,可以看到它其實做了這麼一件事(注意紅色標粗的語句):
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.3.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.1.2</version>
<exclusions>
<exclusion>
<artifactId>tools</artifactId>
<groupId>com.sun</groupId>
</exclusion>
</exclusions>
</dependency>
然後:
1)我們把原先ssh工程中的resources目錄下所有的東西拷到myssh2工程的resources目錄下
2)我們把原先的ssh工程的java文件拷過來;
3)我們把原先的ssh工程的src/main/webapp目錄下的文件也拷貝過來;
4)不要忘了把WEB-INF/web.xml文件和index.jsp文件也拷過來啊!
1)我們把原有的org.sky.ssh.student.form和org.sky.ssh.login.form刪了;
2)我們把原有的service類中的一些需要傳入StudentForm的方法的中的StudentForm改成
org.sky.ssh.vo.StudentVO,其內容如下:
package org.sky.ssh.vo;
import java.io.Serializable;
public class StudentVO implements Serializable {
private String studentNo = "";
private String studentName = "";
public String getStudentNo() {
return studentNo;
}
public void setStudentNo(String studentNo) {
this.studentNo = studentNo;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
}package org.sky.ssh.vo;
import java.io.Serializable;
public class StudentVO implements Serializable {
private String studentNo = "";
private String studentName = "";
public String getStudentNo() {
return studentNo;
}
public void setStudentNo(String studentNo) {
this.studentNo = studentNo;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
}package org.sky.ssh.vo;
import java.io.Serializable;
public class StudentVO implements Serializable {
private String studentNo = "";
private String studentName = "";
public String getStudentNo() {
return studentNo;
}
public void setStudentNo(String studentNo) {
this.studentNo = studentNo;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
}
3)把原有的兩個action文件也刪了吧(刪了就刪了,反正我們要用struts2來重寫)
4)打開web.xml文件,把下面這些內容去掉
<jsp-config>
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
</jsp-config>
5)打開web.xml文件,把.do都改成.action
6)打開web.xml文件,把這些去掉
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml,
/WEB-INF/struts-config/login.xml,
/WEB-INF/struts-config/index.xml
</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>3</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>3</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
7)打開web.xml文件,加入struts2的配置
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
可以看到struts2框架是一個基於filter的框架,確保這個filter在所有工程中你自己定義的filter的最後面,要不然你自己定義的filter會被struts2給filter掉(這點一定要注意)。
最後別忘了把:
<init-param>
<param-name>exclude</param-name>
<param-value>/jsp/login/login.jsp,
/login.action
</param-value>
</init-param>
這邊的原有的/login.do改成/login.action哦。
確保工程編譯沒有任何問題,然後我們按照番外篇《第十九天》中的「四、如何讓Maven構建的工程在eclipse里跑起來」對工程進行設置,使得工程可以在eclipse的tomcat中跑起來。
跑起來後直接拋出一堆的錯,然後我們來看為什麼
就是下面這個狗屁錯。
其原因在於由於使用的struts2。原有的這個cglib:2.1.3,這個包對於spring3和hibernate3還有struts1.3來說沒有任何問題,在遇到struts2時就衝突了,因此我們需要把這個包也給exclude掉
Exclude掉了後沒有cglib包了,AOP類反射沒法玩了,怎麼辦?
簡單:
手工在pom.xml文件中添加一個cglib較新版本的包,如下:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.3.1.ga</version>
<exclusions>
<exclusion>
<artifactId>cglib</artifactId>
<groupId>cglib</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
再運行工程,一切無誤,tomcat啟動時正常,說明我們的框架已經搭好了,接下來我們就要開始改我們的struts的action層了。
四、使用struts2重寫action層
4.1 struts的配置文件
Struts2隻有一個核心的struts.xml文件,它應該放在運行工程的classpath路徑下即WEB-INF/classes目錄下,所以我們把它放在工程的resources目錄下,使得它在工程被編譯時會被自動編譯到工程的WEB-INF/classes目錄下.
其內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<include file="struts-default.xml" />
<package name="login" extends="struts-default">
<global-results>
<result name="error">/jsp/error/syserror.jsp
</result>
</global-results>
<global-exception-mappings>
<exception-mapping result="error" exception="java.lang.Exception" />
</global-exception-mappings>
<action name="login" class="org.sky.ssh.login.action.LoginAction">
<result name="success" type="redirectAction">
<param name="namespace">/</param>
<param name="actionName">indexInit</param>
</result>
</action>
</package>
<package name="index" extends="login">
<action name="indexInit" class="org.sky.ssh.student.action.StudentAction"
method="indexInit">
<result>/index.jsp</result>
</action>
</package>
<package name="studentAdmin" extends="login">
<action name="popAddStudent" class="org.sky.ssh.student.action.StudentAction"
method="popAddStudent">
<result>/jsp/student/studentAdd.jsp</result>
</action>
<action name="addStudent" class="org.sky.ssh.student.action.StudentAction"
method="addStudent">
<result>/jsp/student/studentAdd.jsp</result>
</action>
<action name="addStudent" class="org.sky.ssh.student.action.StudentAction"
method="addStudent">
<result>/jsp/student/studentAdd.jsp</result>
</action>
<action name="delStudent" class="org.sky.ssh.student.action.StudentAction"
method="delStudent">
<result name="success" type="redirectAction">
<param name="namespace">/</param>
<param name="actionName">indexInit</param>
</result>
</action>
</package>
</struts>
4.2 struts2中的類的書寫
struts2中的action就是一個普通的class,它的public方法名就是一個具體的action,這相當於原有struts1中的DispatchAction,只不過它做的更加靈活。
如:我有一個按鈕叫addStudent,如果你在你的action的類中有一個publicString addStudent()方法,那麼你的這個按鈕對應的action名就叫/addStudent.action(神奇吧)。
我們對應著「<action name=」login」class=」org.sky.ssh.login.action.LoginAction」>」來看我們的login.action吧,前面的這個<actionname=」」>後面的東西就是我們的web路徑即相當於「/login.action」。
org.sky.ssh.login.action.LoginAction類
package org.sky.ssh.login.action;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import org.apache.struts2.interceptor.SessionAware;
import org.sky.ssh.service.LoginService;
import org.sky.ssh.util.Constants;
import org.sky.ssh.util.session.UserSessionInfo;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport implements SessionAware, ServletRequestAware, ServletResponseAware {
protected final Log logger = LogFactory.getLog(getClass());
private Map att;
private HttpServletRequest request = null;
private HttpServletResponse response;
@Resource
LoginService loginService;
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public void setServletResponse(HttpServletResponse response) {
this.response = response;
}
public void setSession(Map att) {
this.att = att;
}
public String execute() throws Exception {
String loginId = "";
String loginPwd = "";
HttpSession session = request.getSession();
String loginCode = "100";
try {
loginId = (String) request.getParameter("loginId");
loginPwd = (String) request.getParameter("loginPwd");
if (loginService.login(loginId, loginPwd)) {
UserSessionInfo uinfo = new UserSessionInfo();
uinfo.setLoginId(loginId);
session.setAttribute(Constants.USER_SESSION, uinfo);
} else {
loginCode = "101";
}
request.setAttribute("loginCode", loginCode);
if (!loginCode.equals("100")) {
RequestDispatcher dispatcher = request.getRequestDispatcher("/jsp/login/login.jsp");
dispatcher.forward(request, response);
}
return SUCCESS;
} catch (Exception e) {
logger.error("UserLogin Exception:" + e.getMessage(), e);
throw new Exception("UserLogin Exception:" + e.getMessage(), e);
}
}
}
在查看此類時注意以下幾點地方:
1) 注意request, response, session是怎麼被應用到struts2的action的class中去的;
2) public String execute() throws Exception {…}方法就相當於原有struts1.x中的unspecified方法,是被默認執行的;
3) action方法必須返回一個String類型,默認有SUCCESS和FAIL(注意大小寫),它就對應著你的struts.xml文件中的:<result name=」success」 type=」redirectAction」>。
4.3 struts2中的跳轉
不帶request值的跳轉寫法
<result name="success" type="redirectAction">
<param name="namespace">/</param>
<param name="actionName">indexInit</param>
</result>
注意這個type=」redirectAction」,它只是說明這個跳轉是一個redirect,不管你在上一個action的request中setAttribute了什麼值,當它順利到達下一個action或者是.jsp時,它是帶不出上一個request中的值的。
如果你要從一個action到一個action或者是從一個action到一個jsp並且要把值在request中帶過去該怎麼跳呢?
如下所示:
帶request值的從action跳jsp的寫法
<action name="indexInit" class="org.sky.ssh.student.action.StudentAction" method="indexInit">
<result>/index.jsp</result>
</action>
這邊默認如果action方法返回SUCCESS就會觸發這個跳轉
帶request值的從action跳action的寫法
<action name="delStudent" class="org.sky.ssh.student.action.StudentAction" method="delStudent">
<result name="success" type="redirectAction">
<param name="namespace">/</param>
<param name="actionName">indexInit</param>
</result>
</action>
帶request值的直接從acton類中(不通過struts配置)的跳轉寫法
RequestDispatcher dispatcher = request.getRequestDispatcher("/jsp/login/login.jsp");
dispatcher.forward(request, response);
4.4 /jsp/login/login.jsp
我們打開這個login.jsp,它來自於原有的myssh工程,我們把頭上的這些東西去掉,它們是struts1的標籤:
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
把<form>中的action=後的login.do改成:
action="${pageContext.request.contextPath}/login.action"
4.5 繼續排除maven庫的jar包的錯誤
然後我們啟動tomcat,輸入http://localhost:8080/myssh2 ,在登錄頁面敲入相關的用戶名/密碼後一點登錄,直接看到一堆的前台jsp頁面顯示錯誤和後台錯誤。
其原因就是原有的struts1.x的依賴包中會自動帶入jstl相關的jar包,它們是:
Jstl和standard兩個包,而struts2是不帶這兩個jar包的依賴的,因為struts2有著自己強大的且豐富的tag。因此我們在使用struts2時,要把這個兩個jar包加入到pom.xml文件中去,在pom.xml文件中加入:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.0.6</version>
</dependency>
4.6 使用struts2風格在request中set一個list
在類中聲明一個局部變數且創建一對set{…}get{…}
private List<StudentVO> stdList = new ArrayList<StudentVO>();
public void setStudentVO(StudentVO studentVO) {
this.studentVO = studentVO;
}
public List<StudentVO> getStdList() {
return stdList;
}
此時你一旦在某個public方法中對這個值進行過操作,那麼在這個action跳轉到下一個jsp時,你在jsp的request中會自動取得這個list的值而不需要在原有的struts的action中寫諸如:setAttributer(「stdList」,stdList);這樣的東西了,是不是很優雅?
5. org.sky.ssh.student.action. StudentAction類
package org.sky.ssh.student.action;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import org.apache.struts2.interceptor.SessionAware;
import org.sky.ssh.service.StudentService;
import org.sky.ssh.vo.StudentVO;
import com.opensymphony.xwork2.ActionSupport;
public class StudentAction extends ActionSupport implements SessionAware, ServletRequestAware, ServletResponseAware {
private Map att;
private HttpServletRequest request = null;
private HttpServletResponse response;
private List<StudentVO> stdList = new ArrayList<StudentVO>();
private StudentVO studentVO = new StudentVO();
public StudentVO getStudentVO() {
return studentVO;
}
public void setStudentVO(StudentVO studentVO) {
this.studentVO = studentVO;
}
public List<StudentVO> getStdList() {
return stdList;
}
public void setStdList(List<StudentVO> stdList) {
this.stdList = stdList;
}
@Resource
private StudentService stdService = null;
protected final Log logger = LogFactory.getLog(getClass());
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public void setServletResponse(HttpServletResponse response) {
this.response = response;
}
public void setSession(Map att) {
this.att = att;
}
public String indexInit() throws Exception {
try {
stdList = stdService.getAllStudent();
return SUCCESS;
} catch (Exception e) {
logger.error("Init Index Exception:" + e.getMessage(), e);
throw new Exception("Init Index Exception:" + e.getMessage(), e);
}
}
public String popAddStudent() throws Exception {
return SUCCESS;
}
public String addStudent() throws Exception {
try {
stdService.addStudent(studentVO.getStudentName());
} catch (Exception e) {
logger.error("addStudent error:" + e.getMessage(), e);
throw new Exception("addStudent error:" + e.getMessage(), e);
}
return SUCCESS;
}
public String delStudent() throws Exception {
String[] stdArray = null;
try {
stdArray = request.getParameterValues("selectedStudents");
if (stdArray != null && stdArray.length > 0) {
stdService.delStudent(stdArray);
}
} catch (Exception e) {
logger.error("delStudent error:" + e.getMessage(), e);
throw new Exception("delStudent error:" + e.getMessage(), e);
}
return SUCCESS;
}
}
6. myssh2工程的完整pom.xml
詳見原文。
7. SSH1還是SSH2與Annotation還是Xml配置的問題
這個問題是沒有絕對的優點和缺點的。
有人喜歡說:我就愛用SSH2,因為都是最新的,我也喜歡用全annotation的方式來編程,因為這樣做比較潮流,比較優雅。但是。。。。。。這些都不是真正的justification。
對於在框架選型時,不僅僅是開發者自己喜歡不喜歡的問題,就和有人說:開發者們永遠喜歡推倒重做,永遠喜歡開發新項目而對於修修改改維護類項目感冒是一個道理。
試想,大部分的金融保險客戶,他們的系統都是有一定的年頭了,而且像這樣的企業中的一個IT項目是不可能跟著潮流經常去變化的,因為這些企業中的IT項目或者我們稱作IT資產涉及到數據、機密性、穩定性,一旦這個項目自上線之日起它就一直在穩定的運行了,除非是重大變故一般是不會輕易去改它的架構或者是核心的,一般都是圍繞著己有項目來進行擴展和維護,因此這些企業中的J2EEAPP Server或者是JDK版本都不一定是最新的。
比如說有些銀行到現在還一直在用was6.1,或者是weblogic9.x,更有甚者還在用jdk1.4,如果這時你在接手項目時不去了解企業的現狀況,一拍腦袋說:我們用SSH2吧!結果你的項目做完後連上線都無法上線,到那時就不僅僅是再讓你重構的問題了,呵呵,對吧.
你不要試圖去和客戶解釋說:唉呀你怎麼還在用WAS5.1,你怎麼還在用Tomcat5.5啊,我給你搞個tomcat6.x也行,把你的JDK裝成1.6吧,呵呵,千萬不要這麼做。
客戶可以告訴你,它的伺服器是小型機,上百萬元購買來的,購買小型機時贈送了WAS5.1因此在上面有許多的應用且已經使用了5年之久了,現在你為了說你的框架是STRUTS2而逼著客戶升級JDK和WAS版本,如果萬一有問題了,出錯了,導致了客戶的實時交易延誤而引起的經濟損失,你能支付得起嗎?如果你說因為我們的環境而不能使用你們的框架,那麼對不起,我們公司不會採用你方的架構。呵呵!!
框架的目的在於最大程度上減化一些底層的,重複性的勞動,把對資料庫的訪問,對resource等的訪問從程序員的實際工作中分離出來,使程序員有更多的時間去關注「業務邏輯」——摘自2001版的《EJB2從入門到精通》。所以在實際工作中不能夠為了用框架而用框架。
好比,我用Annotation寫DAO是很方便,很優雅,但是你有沒有想過,當你的SQL如果是經常需要變,或者是需要通過外部動態傳入的時候甚至允許客戶自己構建SQL再傳給我們的DAO的場景下,那麼對於我們來說只需要改改SQL邏輯重新啟動一下伺服器就可以實現的而因為你用了全部基於Annotation的框架,我甚至需要去動我們的代碼,要知道代碼不管你動了多少哪怕你只是加了一個注釋也是需要按照流程來重新測試、重新打包的。因為沒有人敢保證你的改動不引起regressionbug。所以這時把sql或者一些配置寫成xml或者是properties的外部配置形式要比你用annotation來得更靈活,這就是我在第十八天的spring+jdbcTemplate時為什麼喜歡把SQL寫在XML里再通過spring注入到DAO層的原因。因為你的SQL不是一次寫成的,就算是一次寫成,你的工程在將來也會面臨SQL調優這麼一個過程,到時你每改動一次SQL,就要動一次代碼層,而你的改動可能只是把in變成了=或者是把innerjoin改成hashjoin,那麼此時我的SQL如果是配置在外部配置文件中的話我改起來是不是更方便?尤其是一些涉及到大數據量出報表的SQL是經常面臨調優的。
Struts2是基於filter框架的,你可以使用它的filter,你甚至可以不用去使用spring而直接使用struts2或者使用spring的MVC而拋棄struts2,都是沒問題的,沒有什麼所謂「不正統/正統」框架之說。好比我有一個servlet叫LoginFilter,這個filter誕生在8年前,經歷了好幾個項目了已經是非常穩定了,因此當我碰到了struts2的框架時我不是說因為struts2的技術新我就必須全部用struts2來重做我的feature,穩定性重用性在哪裡?我既然手上有一個這麼穩定的歷經了好幾年的一些個組件,雖然它們歷史久遠了些可是我也是照用不誤,原因就在於它穩定實用。
說了這些,主要的目的還是要告訴大家,框架和設計模式是一樣的,它只是在最大程度上解放你的生產力,減少你的重複勞動,避免了不要去重複造輪子。不要為了框架而框架,不要被框架套死。好比剛練武時,一招一式都要照著書本和師傅的樣子去學,但是真正的武功高手是什麼樣的呢?「無招勝有招」,對不對?活用活用,要把框架和模式為你所用而不是做框架的奴隸。
這也是我為什麼強調框架而不僅僅是強調SSX體系的原因,其實在我的SSX框架中還經常可以看到一些古老的jstl,servlet的存在,我的目的就在於充分利用各個技術的優點,把各個技術各個框架的優點集中起來使用這樣才能搭出一本葵花寶典來。
小知識普及 SSH與SSH2這種框架組合的歷史原由
早在2001年時當時的J2EE推崇的是EJB,EJB被稱為J2EE的核心,當時要學J2EE就是Servlet+EJB,在EJB里其實早已經有了AOP與實體映射這些概念了。
EJB有三種形態的BEAN,SessionBean, Entity Bean, MBEAN對吧?其中,EntityBean就是Hibernate,大家看看,嘿嘿,所以技術這個東西所謂的新也是換湯不換藥,在2001時就已經有了Hibernate這種概念了,而且Spring經典的聲明式事務代理也早就有了,就是你的SessionBean如果拋出一個java.runtime.exception,EJB容器就會自動回滾事務,而且我們在聲明EntityBean時就是和Hibernate2.x一樣,寫一個xml文件將欄位表名和類名和屬性名進行一一對應的。
但是有許多人會說ejb2.0是一個失敗之作,它的實體映射隱藏了資料庫底層的操作把對於表的操作轉化成了OOP的操作,這是一個非常好的理念,但是在早期的EJB中連對於如:selectcount(), select max()這樣的操作都沒有,當然在那時如果碰到這樣的處理時有經驗的程序員一般會採用EJB中的BPM即直接使用SessionBean+jdbc去完成的,但是就如我前面所說的,為了使用框架而用框架的事情和人數大有所在,為了在工程中使用一套純正的EJB,純CMP BEAN,很多程序員們就去用ArrayList.size或者是Vector.size來做這些個count,max等操作,甚至在碰到多表連接時對於每個表取一個List然後在Java代碼層去用數據結構來拼裝出一個View來。
EJB2.x的配置也是非常繁瑣的,沒有一個好的現成的工具,一般不包括MBEAN的話僅要使用SessionBean和EntityBean就要配4個xml文件,同時每個EJB的容器如:JBOSS,WEBLOGIC,WAS又彼此間不能通用,在使用不同的J2EE容器時還要為這個容器單獨配一個廠商支持的xml文件。
等等等等。。。。。。這一系列導致了使用EJB的工程變得臃腫複雜,難於調試,並伴有嚴重的性能問題,當時網上罵聲也是一片,EJB一度走入低谷。於是,人們就在想,我能否保留EJB中CMPBean的這種實體映射的特性呢?而且我也只希望使用實體映射,於是Hibernate誕生了.Hibernate就是一個除去了EJB2.x一切特性只保留EntityBean的一種技術。
03年,04年隨著Hibernate的推廣,人們又在想,Hibernate現在有了,EJB原有的聲明式事務也是一個不錯的設計,我可以讓程序員不需要關心他們的資料庫層面的transaction處理而只關心業務層面的transaction,所以原有EJB2.x中的AOP概念又被單獨剝離了出來,這導致了Spring的誕生.
於是在早期的人們採用Spring+Hibernate這樣的架構時,大家其實還是在把這兩者的組合在當作EJB來使用的,這樣的組合其實就是一個輕量級的EJB2.x,一個縮微了的EJB。因為EJB的設計太超前太好了,只是它的一些缺陷一些瓶勁導致程序員們錯用亂用EJB而給EJB造成了不好的口碑,但是因為J2EE的核心就是EJB,因此在04年對於Spring+Hibernate這樣的組合有一句口號叫「Thisis not a J2EE」,因為我們不是EJB但我能做到EJB所有的優點,呵呵。這其實就是一種無招勝有招的典形應用場景,把各自的優點最大化的發揮出來而不拘泥於框架而來論框架.
當然,隨著EJB3的回歸,EJB反過來吸收了Hibernate3與Spring3的一切優點而且它藉助著SUN(現在叫ORACLESUN)的工業標準和強大的技術支持,J2EE終還將回歸EJB。
EJB3前途無量,它不僅僅把SSH全部又整回成了一個EJB還簡化了配置,同時還徹底做到了廠商無關,資料庫無關。如著名的SEAM3框架,SCA編程模形(EJB3的SessionBean中可以調用Webservice,這為EJB滿足SCA編程模型中的引用、導入等概念帶來了極大的便利)都是基於EJB3的,大家有時間我覺得還是可以好好的去關注和學習EJB3技術。
結束今天的教程,下次開始要講在SSX體系中如何來做unit testing以及如何使用Spring來構建一個單獨運行的應用程序如:銀行保險業中的批處理業務的框架的搭建。
系列
通向架構師的道路(第一天)之 Apache 整合 Tomcat
通向架構師的道路(第二天)之 apache tomcat https 應用
通向架構師的道路(第三天)之 apache 性能調優
通向架構師的道路(第四天)之 Tomcat 性能調優
通向架構師的道路(第五天)之 tomcat 集群 – 群貓亂舞
通向架構師的道路(第六天)之漫談基於資料庫的許可權系統的設計
通向架構師的道路(第七天)之漫談使用 ThreadLocal 改進你的層次的劃分
通向架構師的道路(第八天)之 Weblogic 與 Apache 的整合與調優
通向架構師的道路(第九天)之 Weblogic 的集群與配置
通向架構師的道路 ( 第十天 ) 之 Axis2 Web Service ( 一 )
通向架構師的道路 ( 第十一天 ) 之 Axis2 Web Service ( 二 )
通向架構師的道路 ( 第十二天 ) 之 Axis2 Web Service ( 三 )
通向架構師的道路 ( 第十三天 ) Axis2 Web Service 安全初步
通向架構師的道路 ( 第十四天 ) Axis2 Web Service 安全之 rampart
通向架構師的道路 ( 第十五天 ) IBM Websphere 的安裝與優化
通向架構師的道路 ( 第十六天 ) IBM Websphere 與 IBM HttpServer 的集成
通向架構師的道路 ( 第十七天 ) IBM Websphere 集群探秘 – WASND
通向架構師的道路(第十八天)萬能框架 Spring ( 一 )
通向架構師的道路(第二十天)萬能框架spring (二)
通向架構師的道路(第二十一天)萬能框架 Spring ( 三 ) 之 SSH
看完本文有收穫?請轉發分享給更多人
關注「ImportNew」,提升Java技能
TAG:ImportNew |