同在一個產業鏈園區的XX廠因為5台Window2003服務器收到了律師函並且被迫下了12萬$的采購單,雖然100萬對XXX廠來數不是大數目,但是總有種被打劫的感覺。
在企業ERP應用中服務層一般都是做成遠程調用的,具體Windows平台的技術有WebService,WCF,Remoting等,這里展示的是服務端采用linux 平台下采用Hessian組件實現RPC.
服務端:
Web服務器:JBoss,tomcat (weblogic挺美但是不免費啊)
數據庫:mysql(一般erp都用oracle做數據庫,當然那個啥費用也是不含糊地)
客戶端:
逆天的XP(sp3) 加.net4.0 (NND,這個組合量你也收不了多少錢把!)
VS2010? 我們用180試用版或者那啥notepad
Hessian+spring配置
1.web.xml

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>HessianTest</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:conf/ht-core.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>hessionRpc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:conf/ht-rpc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>hessionRpc</servlet-name> <url-pattern>/rpc/*</url-pattern> </servlet-mapping> <servlet> <description></description> <display-name>TestSpring</display-name> <servlet-name>TestSpring</servlet-name> <servlet-class>f.studio.web.servlet.TestSpring</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestSpring</servlet-name> <url-pattern>/TestSpring</url-pattern> </servlet-mapping> <filter> <display-name>HessianCtxFilter</display-name> <filter-name>HessianCtxFilter</filter-name> <filter-class>f.studio.web.filter.HessianCtxFilter</filter-class> </filter> <filter-mapping> <filter-name>HessianCtxFilter</filter-name> <url-pattern>/rpc/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>
2.Service,DAO等spring配置

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean name="studentServiceImpl" class="f.studio.service.impl.StudentServiceImpl" scope="prototype" /> </beans>
3.Hessian導出層spring配置,因為一個項目里可能使用strut2,Servlet,cxf等服務提供層,但是他們需要共用Service,DAO等
參考:http://jinnianshilongnian.iteye.com/blog/1602617

<?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: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-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"> <bean name="/studentServiceRpc" class="org.springframework.remoting.caucho.HessianServiceExporter"> <property name="service" ref="studentServiceImpl" /> <property name="serviceInterface" value="f.studio.service.StudentService" /> </bean> </beans>
用戶登陸狀態問題
Hessian的C#實現可以自己保存cookie,並且是全局的(應用程序范圍)
服務端嘗試使用Hessian提供的ServiceContext獲取對Session的引用但是結果總為null,所以寫個filter 來自己維護用戶登陸Session供Service實現類使用

package f.studio.web.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import f.studio.util.HessianCtxContainer; /** * Servlet Filter implementation class HessianCtxFilter */ public class HessianCtxFilter implements Filter { public static final String LOGIN_SESSION_KEY="LOGIN_USER_KEY"; /** * Default constructor. */ public HessianCtxFilter() { // TODO Auto-generated constructor stub } /** * @see Filter#destroy() */ public void destroy() { // TODO Auto-generated method stub } /** * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpSession session = ((HttpServletRequest) request).getSession(true); System.out.println("sessionId:" + session.getId()); try { Object user = session.getAttribute(LOGIN_SESSION_KEY); HessianCtxContainer.setAttribute(LOGIN_SESSION_KEY, user); System.out.println("sessionUser:" + user); chain.doFilter(request, response); } finally { //移除掉ThreadLocal中Map中的對象防止益出 //session具備自動移動除功能 //不要在環境下(cxf,strut2等)使用HessionCtxContainer,避免線程重用時造成混亂 Object user = HessianCtxContainer.remove(LOGIN_SESSION_KEY); session.setAttribute(LOGIN_SESSION_KEY, user); } } /** * @see Filter#init(FilterConfig) */ public void init(FilterConfig fConfig) throws ServletException { // TODO Auto-generated method stub } }
服務實現類:

package f.studio.service.impl; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.omg.PortableInterceptor.USER_EXCEPTION; import com.caucho.hessian.io.Hessian2Output; import com.caucho.services.server.ServiceContext; import f.studio.domain.Klass; import f.studio.domain.QueryStudentInfo; import f.studio.domain.StudentInfo; import f.studio.service.StudentService; import f.studio.util.HessianCtxContainer; import f.studio.web.filter.HessianCtxFilter; public class StudentServiceImpl implements StudentService { public List<StudentInfo> query(QueryStudentInfo q) { CheckLogin(); ServletRequest request= ServiceContext.getContextRequest(); System.out.println(ServiceContext.getServiceName()); //HttpSession session= request.getSession(true); //session.setAttribute("User", new Date()); System.out.println("ServiceImpHashCode:" + this.hashCode()); System.out.println(q); //if(1==1)throw new RuntimeException("運行錯誤信息啊"); List<StudentInfo> list=new ArrayList<StudentInfo>(); Klass klass=new Klass(); klass.setId(9999); klass.setName("張老師"); klass.setAddTime(new Date()); for(int i=0;i<10;i++){ StudentInfo s=new StudentInfo(); //===父類 s.setRecId(88888); s.setCreateDate(new Date()); //== s.setId(i); s.setName("張思念" + i); s.setSex(i % 5 ==0); //===添加兩個元素== s.getKs().add(klass); s.getKs().add(klass); list.add(s); } return list; } public String hello(String name) { return "Hi " +name; } public void Login(String username, String password) { if("Admin".equals(username) && "123".equals(password)){ HessianCtxContainer.setAttribute(HessianCtxFilter.LOGIN_SESSION_KEY , username); return; } throw new RuntimeException("錯誤的用戶名或密碼!"); } public static void CheckLogin(){ if(HessianCtxContainer.getAttribute(HessianCtxFilter.LOGIN_SESSION_KEY)==null){ throw new RuntimeException("未登錄或登錄超時!"); } } }
.net客戶端,需要添加對hessianCsharp.dll的引用
調用一次Login后,再執行其他調用

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using hessiancsharp.client; namespace HessianTest { using f.studio.domain; using System.Threading; public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { try { CHessianProxyFactory factory = new CHessianProxyFactory("userName", "password"); string url = "http://localhost/HessianTest/rpc/studentServiceRpc";//修改為你的server端地址 StudentService test = (StudentService)factory.Create(typeof(StudentService), url); string result = test.hello("大白鯊"); var q = new QueryStudentInfo() { BTime = DateTime.Now, Name = "哈哈", Id = 1888, Sex = false }; q.Data = new byte[] { 5, 4, 3, 2, 1 }; q.CreateDate = DateTime.Now; q.RecId = 99999; var list = test.query(q); foreach (var it in list) { Console.WriteLine(it); } Console.WriteLine(result); } catch (Exception ex) { Console.WriteLine(ex.Message); } } private void button5_Click(object sender, EventArgs e) { try { CHessianProxyFactory factory = new CHessianProxyFactory("userName", "password"); string url = "http://localhost/HessianTest/rpc/studentServiceRpc";//修改為你的server端地址 StudentService test = (StudentService)factory.Create(typeof(StudentService), url); test.Login("Admin", "123"); Console.WriteLine("登陸成功"); } catch (Exception ex) { Console.WriteLine(ex.Message); } } } public interface StudentService { string hello(string name); List<StudentInfo> query(QueryStudentInfo q); void Login(String usename, String password); } } namespace f.studio.domain { public class BaseInfo { private DateTime? createDate; public DateTime? CreateDate { get { return createDate; } set { createDate = value; } } private long? recId; public long? RecId { get { return recId; } set { recId = value; } } } /// <summary> /// 上傳時用需要保持命名空間與服務器一致 /// </summary> public class QueryStudentInfo :BaseInfo { private int id; private String name; private DateTime? btime; private Byte[] data; private bool sex; public int Id { get { return id; } set { id = value; } } public DateTime? BTime { get { return btime; } set { btime = value; } } public string Name { get { return name; } set { name = value; } } public Byte[] Data { get { return data; } set { data = value; } } public bool Sex { get { return sex; } set { sex = value; } } } /// <summary> /// 不能使用public int Id{get;set;} /// private 字段名稱,大小寫需要跟服務端定義一致 /// [Serializable]標記貌似不是必須的 /// </summary> public class Klass :BaseInfo { private int id; private String name; private DateTime? addTime; public DateTime? AddTime { get { return addTime; } set { addTime = value; } } public string Name { get { return name; } set { name = value; } } public int Id { get { return id; } set { id = value; } } } public class StudentInfo :BaseInfo { private string name; private bool? sex; private long id; private byte[] fileData; public byte[] FileData { get { return fileData; } set { fileData = value; } } private List<Klass> ks; public List<Klass> Ks { get { return ks; } set { ks = value; } } public string Name { get { return name; } set { name = value; } } public long Id { get { return id; } set { id = value; } } public bool? Sex { get { return sex; } set { sex = value; } } public override string ToString() { return string.Format("Id:{0},Name:{1},Sex:{2},RecId:{3},CreateDate:{4}", Id, Name, Sex,RecId,CreateDate); } } }