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

spring框架aop_使用Spring框架和AOP进行动态路由相关推荐

  1. 若依前后端分离框架去掉首页 登录后跳转至动态路由的第一个路由

    若依前后端分离框架去掉首页 登录后跳转至动态路由的第一个路由 若依框架VUE前端界面,登录后默认跳转至动态路由第一路由(第一个子菜单) 一.登录后跳转第一路由界面 二.设置路由的首页路径,方便后续的获 ...

  2. java 路由框架_使用Spring框架和AOP实现动态路由

    本文的大体思路是展示了一次业务交易如何动态地为子系统处理过程触发业务事件.本文所示的例子使用Spring框架和Spring AOP有效地解耦业务服务和子系统处理功能.现在让我们仔细看看业务需求. 业务 ...

  3. spring的aop_关于Spring AOP的灵魂十问

    荒腔走板 今天没有这个环节... 什么是AOP? AOP全称是Aspect Oriented Programming,翻译过来是"面向切面"编程.在Java语言里,一切皆对象,所以 ...

  4. spring cloud gateway 之动态路由

    前面分别对 Spring Cloud Zuul 与 Spring Cloud Gateway 进行了简单的说明,它门是API网关,API网关负责服务请求路由.组合及协议转换,客户端的所有请求都首先经过 ...

  5. Spring Cloud Alibaba 集成 Gateway 实现动态路由功能

    文章目录 1 摘要 2 核心 Maven 依赖 3 名词释义 4 Gateway 动态路由原理 5 数据库表 6 核心代码 6.1 配置信息 6.2 路由实体类 6.3 本地路由数据库持久层(DAO/ ...

  6. 【SSM框架系列】Spring 的 AOP(面向切面编程)

    什么是 AOP AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP 是 OOP ...

  7. Spring框架学习笔记(三)(AOP,事务管理)

    Spring框架学习笔记(三) 九.AOP 9.1 AOP的注解配置 (1) 新建计算器核心功能(模拟:不能在改动核心代码) (2) 建立一个普通的Java类写增强代码(面向切面编程),使用Sprin ...

  8. Spring框架,IOC,DI,AOP,单例多例,懒加载

    文章目录 1.Spring 2.IOC 2.1 什么是IOC 2.2 xml配置文件管理对象 2.3 全注解的方式管理对象 3.Spring创建对象-工厂模式(必会内容) 4.单例与多例 4.1@Sc ...

  9. spring 测试demo乱码_spring框架的入门学习:AOP和面向切面的事务

    使用注解配置spring,需要以下几个步骤: 需要导入一个包: 步骤1:需要为主配置文件引入新的命名空间(约束),和之前介绍的一样,导入新的约束: 然后在application.xml的Design中 ...

最新文章

  1. 2、JDBC连接数据库
  2. SAP ABAP收货或者货物移动(MIGO,MB11,MB1A)在保存时候的增强点
  3. 第11章:项目风险管理—章节重点
  4. Percona5.6自身已支持杀死慢SQL
  5. activemq网络桥接_ActiveMQ –经纪人网络解释
  6. mysql dede arctiny_如何用织梦SQL命令行工具操作数据库及常用sql语句整理
  7. 记:从百度空间搬家到博客园--写博客要写的舒服
  8. 如何用yolov5训练自己的图片
  9. 软件开发过程中最重要的是人?还是领导者?
  10. xjoi9235区间翻转
  11. iOS程序员必读之热门书单
  12. 系统学习机器学习之组合多分类器
  13. 一套Java架构开发的电商系统要多少钱
  14. 前端入门-HTML篇
  15. 企立方-拼多多采集注意的点有哪些
  16. 网络编程 正则表达式
  17. 第十三天 06-文本编辑器VI的使用修改网卡等
  18. element 日期选择器el-date-picker 月份/日期范围控制
  19. Squeeze-and-Attention Networks for Semantic Segmentation解读
  20. Axure RP 8 闪退问题解决方案

热门文章

  1. APP接入友盟统计,不上报数据问题
  2. 【题解】【循环】幂级数求和
  3. Mybatis-plus的自动填充功能
  4. python实现一元线性回归预测电影票房收入
  5. 为什么要进行数据标准化?
  6. 《互联网周刊》封面报道:P2P——新媒体革命
  7. 电动车AMT换挡规律研究——换挡点计算
  8. InputStream读JSON数据时乱码
  9. java 农历算法_中国农历算法java实现
  10. 桌面上的文件夹怎么保存到计算机硬盘里,电脑文件怎么保存到桌面