spring框架aop_使用Spring框架和AOP进行动态路由

spring框架aop

本文的总体思路是展示业务交易如何动态触发业务事件以进行子系统处理。 本文显示的示例有效地使用了Spring框架2.0和Spring AOP来将业务服务与子系统处理功能分离。 现在,让我们详细了解业务需求。

业务需求

客户注册系统(CRS)需要在完成在线注册后向其客户发送通知,并将其地址数据传输到发票系统,以便生成用于支付费用的发票。

技术设计

让我们将上述业务需求分解为技术设计。 在此示例中,我们将定义一个自定义业务事件以指示客户注册过程。

事件可以被视为在特定时间间隔内发生的事件。 在这种情况下,这就是客户注册过程。 通常,单个事件可能包含一个或多个事件发生时需要执行的操作。 根据业务需求,我们确定了两个操作,例如:

  1. 发送邮件通知给客户。
  2. 将客户地址数据传输到发票系统。

现在,我们将设计事件数据结构来保存存储在事件数据库表中的信息。 标识了以下事件属性。

  • 事件标识符:1
  • 活动说明:客户注册活动
  • 动作代码:MT

事件标识符是数据库中映射的主键。 事件描述定义了有关事件的描述。 最后一个是动作代码,表示事件发生时需要执行的不同动作。 动作代码在动作代码参考表中定义。

针对上述事件识别的动作代码为M和T,而M代表向客户发送邮件通知,T代表将客户地址数据发送至发票系统。

示例:Event.java

/**
*Event.java - The event domain object
*@author - Vigil Bose
*/
public class Event implements Serializable {

private Integer eventId;
private String eventDesc;
private String eventActionCodes;
private static final long serialVersionUID = 1L;

/** The cached hash code value for this instance. Settting to 0 triggers
re-calculation. */
private int hashValue = 0;

/**
*@return the eventActionCodes
*/
public String getEventActionCodes(){
return eventActionCodes;
}
/**
* @param eventActionCodes the eventActionCodes to set
*/
public void setEventActionCodes(String eventActionCodes) {
this.eventActionCodes = eventActionCodes;
}
/**
* @return the eventDesc
*/
public String getEventDesc() {
return eventDesc;
}
/**
* @param eventDesc the eventDesc to set
*/
public void setEventDesc(String eventDesc) {
this.eventDesc = eventDesc;
}
/**
* Return the simple primary key value that identifies this object.
* @return the eventId
*/
public Integer getEventId() {
return eventId;
}
/**
* Set the simple primary key value that identifies this object.
* @param eventId the eventId to set
*/
public void setEventId(Integer eventId) {
this.hashValue = 0;
this.eventId = eventId;
}
/**
*Implementation of the equals comparison on the basis of equality
*of the primary key values.
* @param rhs
* @return boolean
*/
public boolean equals(Object rhs){
if (rhs == null)
return false;
if (! (rhs instanceof Event))
return false;
Event that = (Event) rhs;
if (this.getEventId() == null || that.getEventId() == null)
return false;
return (this.getEventId().equals(that.getEventId()));
}

/**
* Implementation of the hashCode method conforming to the Bloch pattern with
* the exception of array properties (these are very unlikely primary key types).
* @return int
*/
public int hashCode(){
if (this.hashValue == 0){
int result = 17;
int eventIdValue = this.getEventId() == null ? 0 :
this.getEventId().hashCode();
result = result * 37 + eventIdValue;
this.hashValue = result;
}
return this.hashValue;
}
}

现在,我们已经设计了事件域对象来表示客户注册事件。 现在,我们继续设计Web层和业务服务层之间的API合同。 设计约束之一是域模型更改不应破坏各层之间的API约定。 为了满足此设计约束,确定了两个数据包装器类。 AbstractData和UserData。 AbstractData本质上是抽象的,定义了某些行为。 UserData是AbstractData的子类,它提供其他行为。 例如,如果您有一个应用程序框架,则抽象类可以提供默认服务,例如事件和消息处理。

在此示例中,AbstractData负责收集各种业务事件。 AbstractData的子类是UserData,它用于保存我们的主域对象User对象。

用户域对象由标识用户所需的不同属性组成,例如userId,firstName,lastName和加密的密码。 它还由具有各种地址属性的地址域对象组成,例如地址行1,地址行2,城市,州等。

示例:AbstractData.java

/**
*AbstractData.java - A template pattern like class that implments
*the event collection behavior. This class is used by all data
*transfer wrapper objects between UI Layer and Server side Layer
*@author - Vigil Bose
*/
public abstract class AbstractData{

/**
*Stores all the events identified during the transaction
*Processing.
*/
private Set eventSet = Collections.synchronizedSet(new HashSet());

/**
* @return Returns the eventSet.
*/
public Set getEventSet() {
return eventSet;
}

/**
*@param event - An instance of a business event resulted from a particular
*business transaction
*/
public void addToEventSet(Event event) {
this.eventSet.add(event);
}

}

在AbstractData中声明集合的原因是为了避免在给定的时间点在集合中重复相同的事件。 让我们看看UserData的样子。 UserData包含实际的User域对象。 因此,对User域对象的任何更改都限于此包装类,并且不会破坏客户端与业务服务层之间的接口协定。

示例:UserData.java

/**
*UserData.java - A concrete POJO data wrapper whose responsibility is to
*holds the main domain object reference and used between client and business
*service layers.
*@author - Vigil Bose
*/
public class UserData extends AbstractData{

private User user;

/**
* @return The user domain object instance
*/
public Users getUsers(){
return this.users;
}
/**
*@param The user instance to set.
*/
public void setUser(User user){
this.user = user;
}

}

让我们看一下业务服务接口。 在此示例中,我们将在IRegistrationService接口中定义一个名为doRegister()的API合同,以完成用户注册业务流程。 该API本质上是事务性的,因为它在多个数据库表中插入记录。 客户端层和业务层通过此接口进行交互。

示例:IRegistrationService.java

/**
*IRegistrationService.java - A classic example of EJB's business
*methods interface pattern that exposes all the Registration
*related business methods that can be implemented by both the
*enterprise session bean as well as the Pojo service. Moreover
*this pattern allows us to switch to POJO implementation later
*if it makes sense to do so.
*@author - Vigil Bose
*/
public interface IRegistrationService{

/**
*API to complete the registration business process
*@param userData - The data wrapper object
*/
public void doRegister(AbstractData userData);

}

为简单起见,在此示例中,我们仅使用POJO(普通Java对象)服务。 商业服务实现实施商业逻辑以完成客户注册过程。 在这种情况下,此服务实现的唯一职责是将调用委派给数据访问层,以在适当的数据库表中记录客户注册交易的状态。

示例:RegistrationServiceImpl.java

/**
*The primary business method implementation of Customer Registration Service.
*This is a POJO. It does not depend on any Spring APIs. It's usable outside a
*Spring container, and can be instantiated using new in a JUnit test. However,
*we can still apply declarative transaction management to it using Spring AOP.
*@author - Vigil Bose
*/
public class RegistrationServiceImpl implements IRegistrationService{

private IRegistrationServiceDao registrationServiceDao;

/**
* A setter method of dependency injection
* @param registrationServiceDao - The registrationServiceDao to set.
*/
public setRegistrationServiceDao(IRegistrationServiceDao
registrationServiceDao){
this.registrationServiceDao = registrationServiceDao;
}
/**
* API to register the user
* @param user - The user domain object
*/
public void doRegister(AbstractData userData){
this.registrationServiceDao.completeRegistration(userData.getUser());
}
}

实际使用DAO模式

数据访问对象(DAO)是集成层设计模式,如《核心J2EE设计模式》一书中所述。 它将持久性存储访问和操作代码封装到一个单独的层中。 本文上下文中的持久性存储是RDBMS。

这种模式在业务逻辑层和持久性存储层之间引入了一个抽象层。 业务对象通过数据访问对象访问RDBMS(数据源)。 这个抽象层简化了应用程序代码并引入了灵活性。 理想情况下,对数据源进行的更改(例如切换数据库供应商或类型)将仅需要更改数据访问对象,并且对业务对象的影响应最小。 在该示例中,我们使用Hibernate实现数据访问策略。

DAO设计模式提供的灵活性主要归因于对象设计的最佳实践:“ 程序到接口” 。 该原则指出,具体对象必须实现调用程序中使用的接口,而不是具体对象本身。 因此,您可以轻松替换其他实现,而对客户端代码的影响很小。

按照上述原则,我们将定义一个具有completeRegistration()行为的Registration Service DAO接口-IRegistrationServiceDao.java。 业务组件将通过此接口与DAO交互。

示例:IRegistrationServiceDao.java

/**
*A POJO data access object interface for the CRS services business layer.
*The API's defined in this interface are all transactional APIs within the
*business services layer
*@author - Vigil Bose
*/
public interface IRegistrationServiceDao{
/**
* Data Access API to create the new user in the system
* @param user - The composite user domain object
*/
public void completeRegistration(User user) throws DataAccessException;
}

定义了数据访问接口后,现在我必须提供IRegistrationServiceDao的具体实现,即RegistrationServiceDaoImpl。

此示例中显示的实现是特定于Hibernate的。 此处使用的模式是一种策略,可以用任何对象关系映射产品或JDBC代替。 此类的职责是在数据库表中记录客户注册交易的状态。

示例:RegistrationServiceDaoImpl.java

/**
*The Registration Services Data Access Strategy implementation
*using Hibernate persistence mechanism that support various
*registration related business transactions.
*@author - Vigil Bose
*/
public class RegistrationServiceDaoImpl extends HibernateDaoSupport
implements IRegistrationServiceDao{

/**
* Data Access API to create the new user in the system
* @param users - The composite users domain object
*/
public void completeRegistration(Users users) throws DataAccessException {

getHibernateTemplate().save(users);
}

}

在任何应用程序中,访问只读数据都很重要。 让我们看一个简单的Java接口ILookUpServiceDao的示例,该接口在客户注册系统中使用,它公开finder和getter方法来访问只读数据。

示例:ILookUpServiceDao.java

/**
*A POJO data access object interface that exposes the lookup API's in Customer
*Registration System.
*The API's defined in this interface can be used with or without any other
*transactional APIs within the business services layer
*@author - Vigil Bose
*/
public interface ILookUpServiceDao{
/**
* Data Access API to find the event instance based on its primary key
* @param eventId - The event tables primary key identifier
*/
public Event findEventById(Integer eventId) throws DataAccessException;

}

下面显示的示例是Hibernate的策略实现。 ILookUpServiceDao接口中定义的API在具体的类LookUpServiceDaoImpl中实现。 为简单起见,示例中仅显示了一种API实现。

示例:LookUpServiceDaoImpl.java

/**
*A POJO data access implementation that implements the lookup API's in Customer
*Registration System.
*The API's defined in this interface can be used with any other
*transactional APIs within the business services layer
*@author - Vigil Bose
*/
public classe LookUpServiceDaoImpl extends HibernateDaoSupport
implements ILookUpServiceDao {
/**
* Data Access API to find the event instance based on its primary key
* @param eventId - The event tables primary key identifier
* @return an instance of Event domain object
* @throws DataAccessException
*/
public Event findEventById(Integer eventId) throws DataAccessException{
return (Event)getHibernateTemplate().get(Event.class, eventId);
}

}

Spring框架提供的HibernateDaoSupport类是一种模板模式实现,它针对Hibernate Session抽象了Hibernate相关的API和资源管理。

通过数据访问实现,我们满足了业务需求的一个方面,即客户注册过程。 现在,我们将说明需求的第二方面,即设计过程中确定的子系统功能。 要求是这样的,当注册完成时,系统应该向客户发送邮件通知,并将客户地址数据传输到发票系统以生成发票。 我们将通过著名的命令模式实现子系统处理。

命令模式

命令模式用于提供执行不同命令的通用接口。 任何实现命令接口的类都可以在execute()方法中提供任务的特定实现。

在设计过程中,我们确定了同一接口的两个不同命令实现。 就业务服务层而言,子系统功能是一个关注点。 因此,借助于面向方面的编程技术(AOP),我将这种关注分离与主要的业务服务层实现(RegistrationServiceImpl)分离了。

有兴趣了解有关AOP概念的更多信息,请单击此处 。 您也可以向下滚动以查看有关AOP的一些介绍。 现在,让我们设计命令界面。

示例:ICommand.java

/**
*ICommand.java - The famous command interface that exposes the execute API.
*@author - Vigil Bose
*/
public interface ICommand{

/**
*The Command design pattern encapsulates the concept of the
*command into an object. The issuer holds a reference to the
*command object rather than to the recipient.The issuer sends
*the command to the command object by executing a specific
*method on it. The command object is then responsible for
*dispatching the command to a specific recipient to get the
*job done.
*@param data - The data transfer object
*/
public void execute(Object data);

}

典型的命令实现提供了一种打包计算(接收器和一组动作)并将其作为第一类对象传递的方法。 该命令对象调用命令请求的接收者的方法来实际处理该请求。 通常,找到一种命令实现来自行处理特定任务,而无需将请求委托给接收者。 在这种情况下,MailingCommandImpl通过调用EmailService发出邮件通知来实现邮件发送任务。 为简单起见,示例中未显示EmailService实现。 毕竟,其目的是提供有关如何借助AOP和Spring 2.0将业务事件路由到子系统处理器的见解。

示例:MailingCommandImpl.java

/**
*MailingCommandImpl.java - A command implementation that implements
*the task of sending mail notification to the customer who completed
*the registration process.
*@author - Vigil Bose
*/
public class MailingCommandImpl implements ICommand{

private IEmailService emailService;

/**
*A setter method of dependency injection
*@param emailService - The emailService instance to set.
*/
public void setEmailService(IEmailService emailService){
this.emailService = emailService;
}
/**
*API execute is used to execute the mailing tasks implemented
*@param args - An instance of AbstractData used in business service layer
*/
public void execute(Object args){

//get the reference of user object
User user = (User)args;

//get the reference of address object via its parent object User.
Address address = user.getAddress()

//Invoke the EmailService API here to send out the notifications....

}
}

现在,我将设计第二个命令实现,它将帮助实现将客户地址数据传输到发票应用程序的业务需求。 在此特定实现中,我们可以选择任何选择的协议(例如,Web服务,HTTP上的消息传递或XML等),以将客户信息发送到发票应用程序,前提是该发票应用程序能够使用上述任何协议进行应用程序集成。 为简单起见,下面给出的示例使用JMS消息传递。 示例中未显示JMS消息传递的内部。

示例:SendCustomerInfoCommandImpl.java

/**
*SendCustomerInfoCommandImpl.java - A command implementation that implements
*the task of transmiting the customer's address data to the invoice system.
*@author - Vigil Bose
*/
public class SendCustomerInfoCommandImpl implements ICommand{

private IMessagingService messagingService;

/**
* A setter method of dependency injection
*/
public void setMessagingService(IMessagingService messagingService){
this.messagingService = messagingService;
}

/**
*API execute is used to execute the messaging task implemented.
*@param args - An instance of AbstractData used in the business service layer
*/
public void execute(Object args){

User user = (User)args;

//Invoke the appropriate messagingService API
//to send the customer information here....
}
}

AOP的基本概念

AOP又称面向方面的编程,旨在帮助程序员分离关注点,尤其是跨领域的关注点。 过程,包,类和方法都可以帮助程序员将关注点封装到单个实体中。 但是,有些担忧使这些形式的封装无效。 我们称它们为横切关注点,因为它们跨越了程序中的许多模块。 可能是一些分散或纠结的代码,使它难以理解和维护。 当一个关注点(例如在这种情况下,它是事件路由)分散在多个模块(例如,类和方法)上时,它就散布了。 这意味着要更改事件调度,可能需要修改所有受影响的模块。

由于各种新的关注点与基本功能(有时称为业务逻辑关注点)交织在一起,因此代码失去了优雅和简单性。 事务,消息传递,安全性和日志记录都是跨领域关注的例证。

AOP试图通过允许程序员在称为方面的独立模块中表达横切关注点来解决此问题。 方面可以包含建议(连接到程序中指定点的代码)和类型间声明(添加到其他类的结构成员)。 例如,安全模块可以包括在访问银行帐户之前执行安全检查的建议。 切入点定义了可以访问银行帐户的时间(连接点),通知正文中的代码定义了如何实施安全检查。 这样,支票和地方都可以保留在一个地方。 此外,良好的切入点可以预期以后程序会发生变化,因此,如果另一个开发人员创建了一种访问银行帐户的新方法,则该建议将在执行时应用于新方法。 领先的AOP实现是AspectJ,AspectWorkz,Spring AOP等。

Spring AOP是用纯Java实现的。 不需要特殊的编译过程。 AspectJ需要特殊的编译过程。 Spring AOP不需要控制类加载器的层次结构,因此适合在J2EE Web容器或应用程序服务器中使用。 Spring 2.0提供了与AspectJ的更紧密集成。

事件路由

为了满足我们的业务需求,我确定了两个AOP组件。 它们是RegistrationBeforeAdvice和RegistrationAfterAdvice。 请参考Spring参考文档以了解有关各种AOP建议和其他概念的更多信息。

识别两个AOP组件的基本原理是支持事件路由并最大程度地减少代码中的交叉依赖性。 RegistrationBeforeAdvice的责任仅限于识别和收集适当的业务事件。 可以在Spring应用程序上下文文件中配置Spring AOP的事前通知实现,以拦截业务服务接口API以注入自定义行为,以标识适当的业务事件并将其添加到事件集合中。

在这种情况下,RegistrationBeforeAdvice截获业务服务接口IRegistrationService的doRegister(AbstractData数据)API。 该建议可以访问服务接口API的输入参数,即AbstractData。 此时,较早在AbstractData层中实现的事件收集机制变得很方便。 RegistrationBeforeAdvice标识正确的业务事件并将其添加到事件集合中。

在Spring应用程序上下文中配置的eventMap是全局事件映射。 eventKeys使用适当的业务服务接口API名称映射到事件标识符。 这使我们能够将在业务服务接口中定义的新业务API无缝映射到全局事件映射配置中的事件ID,而无需在RegistrationBeforeAdvice AOP组件中进行任何代码更改。 但是,这种方法有一个警告。 当程序员在全局事件映射中将错误的方法名称配置为eventId时出错时,在编译期间不会很明显。 但是一个简单的Junit测试将有助于找出这个错误的名称。

现在,将另一个业务API名称(例如doUpdate())映射到另一个事件ID 2变得非常容易。 我们要做的唯一更改是在接口中定义了新的API之后,在spring应用程序上下文文件的事件映射中添加一个映射。 请参阅下面的配置示例片段。

<!-- creates a java.util.Map instance with values loaded from
the supplied 'sourceMap'.Global Event Map. The values are mapped
in event table. The keys are matched with the business API name-->
<util:map id="eventMap">
<entry key="doRegister">
<value type="java.lang.Integer">1</value></entry>
<entry key="doUpdate">
<value type="java.lang.Integer">2</value></entry>
</util:map>

在某些情况下,单个业务流程可能会导致多个事件。 只需对RegistrationAfterAdvice中的代码和事件映射配置进行一些修改,仍然可以执行此操作。 在这种情况下,我们需要考虑每个业务交易的事件列表。 为简单起见,本文中的示例仅限于每个业务事务仅显示单个事件。

请参考下面的代码示例以查看操作前的建议。

示例:RegistrationBeforeAdvice.java

/**
*RegistrationBeforeAdvice.java - This advise acts before the doRegister() API in
*the business service interface executes and sets the appropriate event in the
*eventSet collection implemented in the AbstractData layer. The event is Customer
*Registered Event. This advice inserts custom behavior in IRegistrationService API
*doRegister() before it is executed and identifies the correct business event add
*it to the event collection
*@author - Vigil Bose
*/
public class RegistrationBeforeAdvice implements MethodBeforeAdvice{

private Map eventMap;
private ILookUpServiceDao lookUpServiceDao;

/**
* A setter method of dependency injection
* @param Map - The eventMap instance to set.
*/
public void setEventMap(Map eventMap){
this.eventMap = eventMap;
}
/**
* A setter method of dependency injection
* @param lookUpServiceDao - The lookUpServiceDao instance to set.
*/
public void setLookUpServiceDao(ILookUpServiceDao lookUpServiceDao){
this.lookUpServiceDao = lookUpServiceDao;
}
/**
*Before advice can insert custom behavior before the join point
*executes, but cannot change the return value.If a before advice
*throws an exception, this will abort further execution of the
*interceptor chain. The exception will propagate back up the
*interceptor chain. If it is unchecked, or on the signature of the
*invoked method, it will be passed directly to the client; otherwise
*it will be wrapped in an unchecked exception by the AOP proxy.
*@param method - method being invoked
*@param args - arguments to the method
*@param target - target of the method invocation. May be null
*@throws Throwable - if this object wishes to abort the call.
*Any exception thrown will be returned to the caller if it's allowed
*by the method signature. Otherwise the exception will be wrapped
*as a runtime exception
*@see org.springframework.aop.MethodBeforeAdvice#before(java.lang.reflect.Method
*java.lang.Object[], java.lang.Object)
*/
public void before(Method method, Object[] args, Object target) throws Throwable {

AbstractData data = (AbstractData)args[0];

Set keySet = this.eventMap.keySet();
Integer eventId;

Event event = null;
String eventKey = null;

//Iterate through the key set and extract event identifier and
//retrieve the event from the database and add it to the event
//collection.

for (Iterator iter = keySet.iterator(); iter.hasNext();) {
eventKey = (String) iter.next();

//Check whether the eventKey is matched with the business
//service interface API name. If it does, extract the eventId
//and retrieve the event instance from the datastore and add it
//to the event collection.

if (eventKey.equalsIgnoreCase(method.getName()){
eventId = (Integer)eventMap.get(eventKey);

event = this.lookupService.findEventById(Integer eventId);
data.addToEventSet(event);
}
}
}
}

在此示例中,应考虑的设计约束条件之一是由“前建议”或“后建议”组件引起的异常不应影响在线业务交易。 在线客户不应因事件路由错误而受到惩罚。 为简单起见,在此示例中,我没有显示异常处理应如何工作。

RegistrationAfterAdvice的职责是遍历事件收集和操作代码并启动路由过程。 在本示例中使用的动作代码是M和T。spring应用程序上下文文件中为每个动作代码映射了命令。 然后,它遍历与事件(客户注册事件)关联的每个操作代码,并检索映射的命令对象实例。 一旦获得命令对象引用,就会通过将客户数据传递到每个命令实现以适当执行任务来自动进行路由。

示例:RegistrationAfterAdvice.java

/**
*RegistrationAfterAdvice.java - This advise acts after when the doRegister()
*API of the business service implementation is executed. This advise will
*actually delegate the event actions associated with the Customer Registered
*Event to the appropriate command. This advice inserts custom behavior to
*IRegistrationService interface API's after the API is executed.
*@author - Vigil Bose
*/
public class RegistrationAfterAdvice implements AfterReturningAdvice {
/**
*After returning advice is invoked only on normal method return,
*not if an exception is thrown. Such advice can see the return
*value, but cannot change it.
*
*@param returnValue - the value returned by the method, if any
*@param method - method being invoked
*@param args - arguments to the method
*@param target - target of the method invocation. May be null
*@throws Throwable - if this object wishes to abort the call.
*Any exception thrown will be returned to the caller if it's allowed
*by the method signature. Otherwise the exception will be wrapped as a runtime
*exception
*@see org.springframework.aop.AfterReturningAdvice#afterReturning
*(java.lang.Object, java.lang.reflect.Method, java.lang.Object[],
*java.lang.Object)
*/
public void afterReturning(Object returnValue, Method method, Object[] args,
Object target) throws Throwable {

AbstractData data = (AbstractData)args[0];
User userInfo = (User)data.getUser();
Set eventSet = data.eventSet();

Set keySet = this.commandMap.keySet();
Event event = null;
String actionCodes = null;
String actionCode = null;

//Iterate through the event set
for (Iterator iter = eventSet.iterator(); iter.hasNext();) {

event = (Event) iter.next();
actionCodes = event.getEventActionCodes();

//Loop through the action codes and extract each command mapped to
//the action code in spring application context file.

for (int i=0; i < actionCodes.length();i++){
actionCode = new Character(eventActionCodes.charAt(i)).toString();
  command = (ICommand)commandMap.get(actionCode);
if (command != null){
command.execute(userInfo);
}
}
}
}
}

在上面显示的示例中,我本可以在RegistrationAfterAdvice本身中实现事件收集和事件路由机制。 但是为了使事件收集和事件路由的职责分开,我决定使用两个AOP建议组件来处理子系统路由功能。

放在一起

现在,将所有内容连接到Spring应用程序上下文xml文件中。 在下面的示例中,Hibernate实现用作数据访问层的对象关系映射。 与多个资源提供者(例如数据库和JMS)打交道时,建议使用JtaTransaction策略。 为简单起见,仅显示本地交易策略。

示例:applicationContext.xml

<?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"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jee="http://www.springframework.org/schema/jee"
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
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">

<bean id="registrationService" class="RegistrationServiceImpl">
<property name="registrationServiceDao" ref="registrationServiceDao"/>
</beans>

<bean id="registrationServiceDao" class="RegistrationServiceDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</beans>

<bean id="lookUpServiceDao" class="LookUpServiceDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</beans>

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"/>
<property> name="dataSource" ref="dataSource"/>
<!-- Use the following property jtaTransactionManager when dealing
with CMT transactions -->
<!-- <property name="jtaTransactionManager" ref="jtaTransactionManager"/>-->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.connection.pool_size">3</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.cache.use_structured_entries">true</prop>
<prop key="hibernate.max_fetch_depth">3</prop>
<prop key="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.region_prefix">node1</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>Users.hbm.xml</value>
<value>Address.hbm.xml</value>
</list>
</property>
</bean>

<bean>id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>

<!-- <bean id="jtaTransactionManager"
class="org.springframework.jndi.JndiObjectFactoryBean"> -->
<!--The JNDI name of TransactionManager published in OC4J Container in 10g-->
<!-- <property name="jndiName"
value="java:comp/pm/TransactionManager"/>
</bean> -->

<!-- Transaction manager that delegates to JTA for ultimate coordinate of transactions -->
<!-- <bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager"/>-->

<aop:config>
<!--Format: execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)-->

<!--The pointcut expression here is the execution of any public method
defined by the IRegistrationService interface-->

<aop:pointcut id="registrationServicePointcut"
expression="execution(public * *..IRegistrationService.*(..))"/>
<!--
Here: applying the advice named "registrationBeforeAdvice" to all methods on classes named RegistrationServiceImpl.
-->
<aop:advisor pointcut-ref="registrationServicePointcut"
advice-ref="registrationBeforeAdvice" order="1"/>

<!--
This definition creates auto-proxy infrastructure based on the given
pointcut, expressed in AspectJ pointcut language. Here: applying the
advice named "registrationServiceTransactionAdvice" to all methods
on classes named RegistrationServiceImpl.-->
<aop:advisor pointcut-ref="registrationServicePointcut"
advice-ref="registrationServiceTransactionAdvice" order="2"/>

<!--
This definition creates auto-proxy infrastructure based on the given
pointcut,expressed in AspectJ pointcut language. Here: applying the
advice named "registrationAfterAdvice" to all methods on
classes named RegistrationServiceImpl.
-->
<aop:advisor pointcut-ref="registrationServicePointcut"
advice-ref="registrationAfterAdvice" order="3"/>

</aop:config>
<!--
Transaction advice definition, based on method name patterns.
Defaults to PROPAGATION_REQUIRED for all methods whose name starts with
"do". By default, the transaction is rolled back for runtime
exceptions including DataAccessException.
-->
<tx:advice id="registrationServiceTransactionAdvice"
transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="do*"/>
</tx:attributes>
</tx:advice>

<bean id="registrationBeforeAdvice" class="RegistraionBeforeAdvice">
<property name="order" value="1"/>
<property name="eventMap" ref="eventMap"/>
<property name="lookUpServiceDao" ref="lookUpServiceDao"/>
</bean>

<bean id="registrationAfterAdvice"
class="RegistrationAfterAdvice">
<property name="order" value="3"/>
<property name="commandMap">
<map>
<entry key="M"><ref bean="mailingCommand"/></entry>
<entry key="T"><ref bean="sendCustomerInfoCommand"/> </entry>
</map>
</property>
</beans>

<bean id="mailingCommand" class="MailingCommandImpl">
<property name="emailService" ref="emailService"/>
</beans>

<bean id="sendCustomerInfoCommand" class="SendCustomerInfoCommandImpl">
<property name="messagingService" ref="messagingService"/>
</beans>

<bean id="messagingService" class="MessagingService">
<property name="jmsTemplate" ref="jmsTemplate"/>
</beans>

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="defaultQueueConnectionFactory" />
<property name="defaultDestination" ref="defaultQueueDestination" />
</bean>

<!-- JNDI Lookup configuration for event queue connection factory
used in messaging -->
<jee:jndi-lookup id="defaultQueueConnectionFactory" jndi-name="EVENT_QUEUE"/>
<!-- JNDI Lookup configuration for event queue destination used in messaging -->
<jee:jndi-lookup id="defaultQueueDestination" jndi-name="EVENTQueue"/>

<!-- JNDI Lookup configuration for DataSource in J2EE environments -->
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/userDS"/>

<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="localhost"/>
</bean>

<bean id="emailService" class="EmailServicesImpl">
<property name="mailSender" ref="mailSender"/>
 </bean>

<!-- creates a java.util.Map instance with values loaded from
the supplied 'sourceMap'.Global Event Map. The values are mapped
in event table. The keys are matched with the business API name-->
<util:map id="eventMap">
<entry key="doRegister">
<value type="java.lang.Integer">1</value></entry>
</util:map>
</beans>

AOP或面向方面的编程是编程中的一个相对较新的概念。 这项技术是对面向对象技术的补充,在面向对象技术薄弱的地方提供了更多功能和关注点分离。

结论

关注点分离是开发面向服务的体系结构的关键原则。 它需要分别在体系结构和实施级别上应用。 在本文中,我们演示了如何使用Spring框架的依赖注入原理和面向方面的编程功能来分离跨领域关注点。 正如您在示例代码中所看到的那样,使用这种方法使我们能够最小化处理服务的每个方面的代码中的交叉依赖性。

参考资料

Spring参考文档: http : //www.springframework.org/docs/reference/


免责声明

请查看所提供的代码,而不是经过认证的生产准备。 如果您正在使用代码,并且有任何问题,请告诉我,我将对其进行更新。

翻译自: https://www.infoq.com/articles/dynamic-routing-using-spring/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

spring框架aop

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值