Spring是目前最流行的JavaEE Framework,但是使用Spring的Spring-WS开发WebService却十分繁琐。XFire是一个简化WebService开发的开源项目,通过Spring和XFire的结合可以大大简化基于Spring Framework的应用中的WebService开发。
Spring和XFire可以通过多种方式结合,下文介绍的是笔者常用的一种简单而实用的方法。所用的Spring版本为2.0,XFire版本为1.2.6。
1 配置XFire Servlet
在web.xml中加入如下配置:
<servlet>
<servlet-name>XFireServlet</servlet-name>
<servlet-class>
org.codehaus.xfire.spring.XFireSpringServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/servlet/XFireServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
2 配置Spring的监听器,同基于spring的Web项目一样Spring的监听器是必不可少的。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:org/codehaus/xfire/spring/xfire.xml,
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
以下是完整的web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:org/codehaus/xfire/spring/xfire.xml,
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>XFireServlet</servlet-name>
<servlet-class>
org.codehaus.xfire.spring.XFireSpringServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/servlet/XFireServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
</web-app>
3 定义接口及实现服务
定义接口,这个接口中定义要通过WebService暴露的方法
package org.ccsoft;
publicinterface HelloWS {
public String sayHello(String sb);
}
实现服务
package org.ccsoft;
publicclass HelloWSImp implements HelloWS {
public String sayHello(String sb) {
// TODO Auto-generated method stub
return"Hello "+sb;
}
}
4 配置服务
将上文中实现的服务,加入到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-2.0.xsd">
<bean id="helloWS" class="org.ccsoft.HelloWSImp"/>
<bean name="helloService" class="org.codehaus.xfire.spring.ServiceBean">
<property name="serviceBean" ref="helloWS"/>
<property name="serviceClass" value="org.ccsoft.HelloWS"/>
<property name="inHandlers">
<list>
<ref bean="addressingHandler"/>
</list>
</property>
</bean>
<bean id="addressingHandler" class="org.codehaus.xfire.addressing.AddressingInHandler"/>
</beans>
好了现在你可以通过http://127.0.0.1:8080/XFireWS1/services/HelloWS?wsdl来验证是否部署成功了。
—————————————————————————————————————
蔡 超
SCEA , SCBCD , MCSD
IBM Certified Specialist RUP
IBM Certified Solution Designer OOA&D UML v2
北京天融信软件架构师
SUN,Microsoft培训中心特邀高端教师
常年提供架构咨询服务
chaocai2001@yahoo.com.cn , 010-82776427
public class HibernateTestBean implements SessionBean {
SessionContext sessionContext;
SessionFactory sf;
public void setSessionContext(SessionContext sessionContext) {
this.sessionContext = sessionContext;
try {
InitialContext ctx = new InitialContext();
sf=(SessionFactory) ctx.lookup("java:/hibernate/SessionFactory");
} catch (NamingException ex) {
ex.printStackTrace();
}
}
public void tran(){
tran1();
tran2();
}
public void tran1() {
Session session=sf.getCurrentSession();
Message msg=new Message();
msg.setCreateTime(new Date());
msg.setDetail("trans1");
session.save(msg);
System.out.println("Session:"+session.hashCode());
session.flush();
session.close();
}
public void tran2() {
Session session=sf.getCurrentSession();
Message msg=new Message();
msg.setCreateTime(new Date());
msg.setDetail("trans2");
session.save(msg);
System.out.println("Session:"+session.hashCode());
// throw new RuntimeException("wrong");
}
……
}
注:EJB采用CMT,各方法的事务属性是required
客户端调用tran以上代码可以正确运行吗?
如果把tran1中的sf.getCurrentSession();改为sf.openSession()可以正确运行吗?
辨析:
1 上述代码是不能正确运行的,运行tran2时会抛出异常,告诉你session is closed.
其实这是应为getCurrentSession()会使用环境已有的Session,同时注意getCurrentSession()要在事务的环境中使用。
这是也许你一定会问,那么什么时候关闭Session呢?答案是事务完成的时候(提交或是回滚)。
2 如果上述代码tran1中的sf.getCurrentSession();改为sf.openSession()代码将可以正确运行。这是因为openSession()每次都会返回一个新的Session。而在tran2种的sf.getCurrentSession()并不会使用tran1中的session,而是会使用当前事务环境中的默认的session.
也许你会问如果tran2种的调用抛出RuntimeException,tran1所作的操作还可以回滚吗?
答案是仍然可以回滚的.
蔡超
北京天融信 软件架构师
SCEA,SCBCD,MCSD
IBM Certified Specialist RUP v2003
IBM Certified Solution Designer OOA&D UMLv2
蔡 超
SCEA,SCBCD,MCSD,IBM RUP Specilist
北京天融信软件架构师
SUN,Microsoft培训中心特邀高端教师
常年提供架构咨询服务
chaocai2001@yahoo.com.cn ,010-82776427
问题:
在spring+hibernate的常见架构中,常会应为hibernate的延迟加载遇到一些麻烦。如Hibernate的引入使用脱管领域对象直接取代了DTO,然而前台组织显示时常会应为脱管领域对象的一些关系域未被加载而抛出异常(其实延迟加载是优化系统性能的一种有效方式)。
为了使显示层正常工作,我们就必须在业务层显式的加载这些表现层会用到的延迟加载的关系域对象。而这样的工作不仅需要额外的代码,并且这些代码往往也与所进行的业务逻辑无关。并且表现层的变化较多这样一来如果上述代码进入业务层就会导致业务层跟随表现层的变化。
解决方案:
在“Spring构建应用系统的最佳架构与模式实践(1)”中将逻辑层分为了Façade和ApplicationService两层。
Façade的方法组织是针对客户端请求的,所以如果我们把业务逻辑封装在ApplicationService对象中,而把脱管对象产生(包括根据表现层初始化延迟加载对象,关闭Session)。这样便可以有效的防止表现层逻辑混入业务逻辑中。
蔡 超
SCEA,SCBCD,MCSD,IBM RUP Specilist
北京天融信软件架构师
SUN,Microsoft培训中心特邀高端教师
常年提供架构咨询服务
chaocai2001@yahoo.com.cn ,010-82776427
引言
在使用Spring构建应用时和采用EJB构建应用一样同样也存在不少常用模式和最佳实践,当然很多Core J2EE Pattern仍然是我们构建spring应用中的优秀模式,不过有的也不再适用了(如:IoC的应用使得Service Locator不再适用,Hibernate取代Entity Bean使得DTO不再适用等)。
下文是一些在以Spring为核心的架构中的常见模式和架构最佳实践。
图表 1 Spring应用常见架构模式列表
DAO模式
虽然采用了诸如Hibernate这样的O/R Mapping工具或是iBates这样的SQL Mapping工具但是采用DAO模式仍有相当好处。
在实际应用中我们常会遇到如下问题:
1 性能问题:由于性能问题我们可能要改变数据访问方式,如采用JDBC替换Hibernate这时,这时如果采用了DAO模式,数据访问被有效的封装和业务逻辑完全分离,易于实现替换。
2 移植问题:需要支持多种数据库,DAO模式仍然是一种稳妥的选择。虽然诸如Hibernate及iBates也提供很好的数据库无关性,但如果使用某些数据库的特殊功能时,就会出现问题。
当然,对于是否采用DAO模式也不可一概而论,应为他毕竟会增加应用的开发复杂性。个人认为很关键的一个判定条件是业务逻辑是否会和持久化逻辑混合。
Application Service模式
封装一定的业务逻辑和功能,提高复用性,即防止Façade和业务对象的臃肿。注意在此可以应用Command模式及Strategy模式提供系统可维护性和可扩展性。
这个模式很重要,但常常被大家忽略,这时应为我们常会把逻辑放在Façade中,其实这是很错误的。例如:我们可能会提供多种不同Façade以适应不同的访问方式,这样就会出现大量重复的业务逻辑代码。
Façade模式
为客户端提供访问业务组件的统一模式。便于实现对不同访问方式的支持(如:远程或本地)。特别在远程调用时通过暴露粗粒度的接口,提高系统的性能。
同时,可以根据客户的不同,通过不同Façade来控制客户的操作。
目前,很多人都认为
spring
不论在那个方面都会比
SLSB
有更高的效率,真的是这样吗?
spring
中的
POJO
的生命周期可以是
Singlton
或每请求创建(或是
2.0
支持的
session
及
application,request
等范围),
SLSB
是通过实例池经心管理的。如果
spring POJO
不采用
singlton
的形式那么就需要承受创建和销毁
POJO
的消耗,当然
SLSB
的出池和入池同样会有同步的消耗,由于现在的虚拟机对象的创建和消耗速度大幅提高所以不一定比
SLSB
获取的速度慢,但是如果每个对象构建的资源消耗很大如总是需要构建或初始化复杂对象,那么
SLSB
的速度显然有优势。如果
spring
采用
singleton
模式,那么其中如果需要同步,则虽然省去了创建和销毁的消耗,但是大量的同步会使性能的杀手。并且在多
CPU
的服务器上没有同步的多线程并行效果更好(可以在不同的处理器上单独运行)
1 MDB可以通过@Timeout或实现java.ejb.TimedObject
来实现定时回调
2 EJB3
规范要求在部署
EJB
时必须绑定到各业务接口的全限定名上,最终可有
JNDI
查找
如:
ctx.lookup(TaxRate.class.getName());
3 @Remove
表示
SFSB
中的删除方法,如果存在
@PreDestory
则在其后执行
4
会话
Bean
不实现
SessionBean
接口,
MDB
不必实现
MessageDerivenBean
,只要通过
@Annotation
标明或在部署文件中说明。
5 EJBContext
中加入了
lookup
方法,用于查找和
Bean
绑带的
JNDI
名
蔡
超
SCEA
,
SCBCD
,
MCSD
北京天融信软件架构师
SUN,Microsoft
培训中心特邀高端教师
常年提供架构咨询服务
chaocai2001@yahoo.com.cn
,
010-82776427
很多设计模式的书中都用这样的语言来描述
”
桥模式
”(GOF 95) –
“把抽象与实现分开”(
”Prefactoring”, Ken Pugh,2006
)
,
这样的描述实在有些让人很难体会该模式的精髓。其实在我看来桥模式就是一种面向对象技术中“极度分割”
思想的体现。
下面看一个常见的说明桥模式的例子:
1
一个可以以不同方式输出日志的工具类(输出到文件或控制台):
2
如果此时我们要求可以以多种不同格式来输出日志(如:
XML
和普通文本格式),为了适应这一需求我调整一下类的设计
设计一:
从以上类图可以看出这一设计会产生大量的类,分析其原因可以发现导致这一问题产生的是根类涉及了太多方面,如果这些方面都同时进行扩展就会形成复杂的继承
(
层次较深
)
。如果我们把这些可扩展的不同方面进行分割,就会得到如下设计
以上设计正是传说中的“桥模式”。
经过以上分析我们可看到桥模式本质就是“极度分割”思想的一种体现。
蔡超
(
SCEA
,
MCSD
,
IBM RUP Specialist
)
Spring Reference
中介绍如何在采用
@AspectJ
方式在剖面中如何获取
target
和
JoinPoint
并给出了示例,但并没有给出采用
XML
配置方式时介绍及示例,下面附上一个简单的小例子供大家参考。
package aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* @author Administrator
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class LogAdvice1 {
public void log(JoinPoint jp,MathImp imp){
System.out.println("log:"+imp+" "+jp.toLongString());
}
}
/*
* Created on 2006-11-1
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package aop;
/**
* @author Administrator
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class MathImp /*implements Math*/{
/* (non-Javadoc)
* @see aop.Math#add(int, int)
*/
public void add(int op1, int op2) {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see aop.Math#addtest(int, int)
*/
public void addtest(int op1, int op2) {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see aop.Math#sub(int, int)
*/
public void sub(int op1, int op2) {
// TODO Auto-generated method stub
}
}
配置:
<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"
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">
<!-- this is the object that will be proxied by Spring's AOP infrastructure -->
<bean id="mathImp" class="aop.MathImp"/>
<!-- this is the actual advice itself -->
<bean id="logger" class="aop.LogAdvice1"/>
<aop:config>
<aop:aspect ref="logger">
<aop:pointcut id="addLog"
expression="execution(* aop.MathImp.*(..)) and target(imp) and JoinPoint(jp)" />
<aop:before pointcut-ref="addLog"
method="log" arg-names="jp,imp" />
</aop:aspect>
</aop:config>
</beans>
测试
package aop;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
/**
* @author Administrator
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class Test {
public static void main(String[] args) {
AbstractApplicationContext context=new FileSystemXmlApplicationContext("aop2.xml");
//Math math=(Math) context.getBean("math");
MathImp math=(MathImp) context.getBean("mathImp");
math.add(1,2);
math.addtest(3,4);
math.sub(5,6);
}
}
摘要: 蔡超
北京天融信,软件架构师
SUN certified Enterprise Architect
Microsoft certified Solution Developer
...
阅读全文