文档章节

Spring学习笔记(13)----------aop拦截器

Simon丶Ma
 Simon丶Ma
发布于 2016/04/14 14:56
字数 5324
阅读 6
收藏 0
点赞 1
评论 0

原理

AOP(Aspect Oriented Programming),也就是面向方面编程的技术。AOP基于IoC基础,是对OOP的有益补充。

  AOP将应用系统分为两部分,核心业务逻辑(Core business concerns)及横向的通用逻辑,也就是所谓的方面Crosscutting enterprise concerns,例如,所有大中型应用都要涉及到的持久化管理(Persistent)、事务管理(Transaction Management)、安全管理(Security)、日志管理(Logging)和调试管理(Debugging)等。

  AOP正在成为软件开发的下一个光环。使用AOP,你可以将处理aspect的代码注入主程序,通常主程序的主要目的并不在于处理这些aspect。AOP可以防止代码混乱。

  Spring framework是很有前途的AOP技术。作为一种非侵略性的、轻型的AOP framework,你无需使用预编译器或其他的元标签,便可以在Java程序中使用它。这意味着开发团队里只需一人要对付AOP framework,其他人还是像往常一样编程。

  AOP概念

  让我们从定义一些重要的AOP概念开始。

  — 方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的Advisor或拦截器实现。

  — 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

  — 通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。

  — 切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。

  — 引入(Introduction):添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现IsModified接口,来简化缓存。

  — 目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。

  — AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。

  — 编织(Weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

  各种通知类型包括:

  —  Around通知:包围一个连接点的通知,如方法调用。这是最强大的通知。Aroud通知在方法调用前后完成自定义的行为,它们负责选择继续执行连接点或通过返回它们自己的返回值或抛出异常来短路执行。

  —  Before通知:在一个连接点之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。

  —  Throws通知:在方法抛出异常时执行的通知。Spring提供强制类型的Throws通知,因此你可以书写代码捕获感兴趣的异常(和它的子类),不需要从Throwable或Exception强制类型转换。

  —  After returning通知:在连接点正常完成后执行的通知,例如,一个方法正常返回,没有抛出异常。

  Around通知是最通用的通知类型。大部分基于拦截的AOP框架(如Nanning和Jboss 4)只提供Around通知。

  如同AspectJ,Spring提供所有类型的通知,我们推荐你使用最为合适的通知类型来实现需要的行为。例如,如果只是需要用一个方法的返回值来更新缓存,你最好实现一个after returning通知,而不是around通知,虽然around通知也能完成同样的事情。使用最合适的通知类型使编程模型变得简单,并能减少潜在错误。例如,你不需要调用在around通知中所需使用的MethodInvocation的proceed()方法,因此就调用失败。

  切入点的概念是AOP的关键,它使AOP区别于其他使用拦截的技术。切入点使通知独立于OO的层次选定目标。例如,提供声明式事务管理的around通知可以被应用到跨越多个对象的一组方法上。 因此切入点构成了AOP的结构要素。

 拦截器(也称拦截机)

    拦截机 (Interceptor), 是 AOP (Aspect-OrientedProgramming) 的另一种叫法。AOP本身是一门语言,只不过我们使用的是基于JAVA的集成到Spring 中的 SpringAOP。同样,我们将通过我们的例子来理解陌生的概念。

   接口类

Java代码
package com.test.TestSpring3;  

  

public interface UserService // 被拦截的接口  

...{  

    public void printUser(String user);  

}

 

<span style="font-size:14px;">package com.test.TestSpring3;

public interface UserService // 被拦截的接口
...{
    public void printUser(String user);
}
</span>

 实现类

Java代码
package com.test.TestSpring3;  

  

public class UserServiceImp implements UserService // 实现UserService接口  

...{  

    public void printUser(String user) ...{  

        System.out.println("printUser user:" + user);// 显示user  

    }  

}

 

<span style="font-size:14px;">package com.test.TestSpring3;

public class UserServiceImp implements UserService // 实现UserService接口
...{
    public void printUser(String user) ...{
        System.out.println("printUser user:" + user);// 显示user
    }
}

</span>

 AOP拦截器

Java代码
package com.test.TestSpring3;  

  

import org.aopalliance.intercept.MethodInterceptor;  

import org.aopalliance.intercept.MethodInvocation;  

  

public class UserInterceptor implements MethodInterceptor  

// AOP方法拦截器  

...{  

  

    public Object invoke(MethodInvocation arg0) throws Throwable ...{  

          try ...{  

  

            if (arg0.getMethod().getName().equals("printUser"))  

            // 拦截方法是否是UserService接口的printUser方法  

            ...{  

                Object[] args = arg0.getArguments();// 被拦截的参数  

                System.out.println("user:" + args[0]);  

                arg0.getArguments()[0] = "hello!";// 修改被拦截的参数  

  

            }  

  

            System.out.println(arg0.getMethod().getName() + "---!");  

            return arg0.proceed();// 运行UserService接口的printUser方法  

  

        } catch (Exception e) ...{  

            throw e;  

        }  

    }  

}

 

 

<span style="font-size:14px;">package com.test.TestSpring3;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class UserInterceptor implements MethodInterceptor
// AOP方法拦截器
...{

    public Object invoke(MethodInvocation arg0) throws Throwable ...{

        try ...{

            if (arg0.getMethod().getName().equals("printUser"))
            // 拦截方法是否是UserService接口的printUser方法
            ...{
                Object[] args = arg0.getArguments();// 被拦截的参数
                System.out.println("user:" + args[0]);
                arg0.getArguments()[0] = "hello!";// 修改被拦截的参数

            }

            System.out.println(arg0.getMethod().getName() + "---!");
            return arg0.proceed();// 运行UserService接口的printUser方法

        } catch (Exception e) ...{
            throw e;
        }
    }
}
</span><span style="font-size:14px;">
</span>

 测试类

Java代码
package com.test.TestSpring3;  

import org.springframework.beans.factory.BeanFactory;    

import org.springframework.beans.factory.xml.XmlBeanFactory;  

import org.springframework.context.ApplicationContext;  

import org.springframework.context.support.ClassPathXmlApplicationContext;  

import org.springframework.context.support.FileSystemXmlApplicationContext;  

import org.springframework.core.io.ClassPathResource;  

import org.springframework.core.io.Resource;  

import org.springframework.web.context.support.WebApplicationContextUtils;  

  

public class TestInterceptor ...{  

  

    public static void main(String[] args) ...{  

        ApplicationContext ctx = new FileSystemXmlApplicationContext(  

                "classpath:applicationContext.xml");  

//        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");      

          

        UserService us = (UserService) ctx.getBean("userService");  

        us.printUser("shawn");  

      }  
}

 

<span style="font-size:14px;">package com.test.TestSpring3;

import org.springframework.beans.factory.BeanFactory;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.context.support.WebApplicationContextUtils;

public class TestInterceptor ...{

    public static void main(String[] args) ...{
        ApplicationContext ctx = new FileSystemXmlApplicationContext(
                "classpath:applicationContext.xml");
//        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");    
        
        UserService us = (UserService) ctx.getBean("userService");
        us.printUser("shawn");

    }
}</span><span style="font-size:14px;">
</span>

 配置文件 

Xml代码
<?xml version="1.0" encoding="UTF-8"?>  

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">  

<beans>  

    <bean id="userServiceImp"  

        class="com.test.TestSpring3.UserServiceImp" />  

  

    <bean id="userInterceptor" class="com.test.TestSpring3.UserInterceptor" />  

  

    <bean id="userService"  

        class="org.springframework.aop.framework.ProxyFactoryBean">  

      <!-- 代理接口 -->  

        <property name="proxyInterfaces">  

            <value>com.test.TestSpring3.UserService</value>  

        </property>  

       <!-- 目标实现类 -->  

        <property name="target">  

            <ref local="userServiceImp" />   

      </property>  

        <!-- 拦截器 -->  

        <property name="interceptorNames">  

            <list>  

                <value>userInterceptor</value>  

            </list>  

        </property>  

    </bean>  

</beans>

 

 

 输出:
  user:shawn
   printUser---!
  printUser user:hello! 

 

结论:调用方法的时候 传入的值被拦截修改了.

拦截器中的事务管理(事务拦截机)

 如果不采用拦截机的机制时,在使用JDBC进行数据库访问时,存在两种情况:

  • 自动提交        这是JDBC驱动默认的模式,每次数据库操作(CRUD)成功完成后,都作为一个单独的事务自动提交,如果未成功完成,即抛出了 SQLException 的话,仅最近的一个操作将回滚。
  • 非自动提交    这是想更好的控制事务时需要程序地方式进行控制:
    • 在进行该事务单元的任何操作之前 setAutoCommit(false)
    • 在成功完成事务单元后 commit()
    • 在异常发生后 rollback()

    自动提交模式是不被推荐的,因为每个操作都将产生一个事务点,这对于大的应用来说性能将受到影响;再有,对于常见的业务逻辑,这种模式显得无能为力。比如:
    转帐,从A帐户取出100元,将其存入B帐户;如果在这两个操作之间发生了错误,那么用户A将损失了100元,而本来应该给帐户B的,却因为失败给了银行。
    所以,建议在所有的应用中,如果使用 JDBC 都将不得不采用非自动提交模式(你们要能发现了在我们的 JDBC 那个例子中,我们采用的就是自动提交模式,我们是为了把精力放在JDBC上,而不是事务处理上),即我们不得不在每个方法中:

    Java代码
    try {      
    
     // 在获得连接后,立即通过调用 setAutoCommit(false) 将事务处理置为非自动提交模式  // Prepare Query to fetch the user Information         
    
         pst = conn.prepareStatement(findByName);                   
    
           // ...            conn.commit();         
    
     }  catch(Exception ex) {         
    
         conn.rollback();        
    
          throw ex;         
    
     }finally {     
    
             try {      
    
               // Close Result Set and Statement    
    
              if (rset != null) rset.close();                 
    
             if (pst != null) pst.close();                          
    
        } catch (Exception ex) {                 
    
           throw new Exception("SQL Error while closing objects = " + ex.toString());              
    
    }    
    
    }


     

     

    <span style="font-size:14px;">try {    
     // 在获得连接后,立即通过调用 setAutoCommit(false) 将事务处理置为非自动提交模式  // Prepare Query to fetch the user Information       
         pst = conn.prepareStatement(findByName);                 
           // ...            conn.commit();       
     }  catch(Exception ex) {       
         conn.rollback();      
          throw ex;       
     }finally {   
             try {    
               // Close Result Set and Statement  
              if (rset != null) rset.close();               
             if (pst != null) pst.close();                        
        } catch (Exception ex) {               
           ex.printStackTrace();                
           throw new Exception("SQL Error while closing objects = " + ex.toString());            
    }  
    }
    </span>

     这样代码在AOP的倡导者看来是“肮脏”的代码。他们认为,所有的与事务有关的方法都应当可以集中配置(见声明性事务控制),并自动拦截,程序应当关心他们的主要任务,即商业逻辑,而不应和事务处理的代码搅和在一起。

    我先看看 Spring 是怎么做到拦截的:

    Spring 内置支持的事务处理拦截机

    这里因为要用到JpetStore项目中的代码,我们将 applicationContext.xml 全部内容列出:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <!--
      - Application context definition for JPetStore's business layer.
      - Contains bean references to the transaction manager and to the DAOs in
      - dataAccessContext-local/jta.xml (see web.xml's "contextConfigLocation").
    
        Jpetstore 的应用上下文定义,包含事务管理和引用了在 dataAccessContext-local/jta.xml(具体使用了哪个要看 web.xml 中的 'contextConfigLocation' 的配置)中注册的DAO
    -->
    <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">
        
        
        <!-- ========================= GENERAL DEFINITIONS ========================= -->
    
        <!-- Configurer that replaces ${...} placeholders with values from properties files 
             占位符的值将从列出的属性文件中抽取出来
         -->
        <!-- (in this case, mail and JDBC related properties) -->
        <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>WEB-INF/mail.properties</value>
                    <value>WEB-INF/jdbc.properties</value>
                </list>
            </property>
        </bean>
        
        <!-- MailSender used by EmailAdvice 
             指定用于发送邮件的 javamail 实现者,这里使用了 spring 自带的实现。此 bean 将被 emailAdvice 使用
         -->
        <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
            <property name="host" value="${mail.host}"/>
        </bean>
        
        
        
        <!-- ========================= BUSINESS OBJECT DEFINITIONS ======================== -->
    
        <!-- 不需要,因为被 SpringMVC 的实现使用 Generic validator for Account objects, to be used for example by the Spring web tier -->
        <bean id="accountValidator" class="org.springframework.samples.jpetstore.domain.logic.AccountValidator"/>
        
        <!-- 不需要,因为被 SpringMVC 的实现使用 Generic validator for Order objects, to be used for example by the Spring web tier -->
        <bean id="orderValidator" class="org.springframework.samples.jpetstore.domain.logic.OrderValidator"/>
        
        <!--
            主要的商业逻辑对象,即我们所说的门面对象
    注入了所有的DAO,这些DAO是引用了 dataAccessContext-xxx.xml 中定义的DAO
    门面对象中的所有方法的事务控制将通过下面的 aop:config 来加以控制 - JPetStore primary business object (default implementation). - Transaction advice gets applied through the AOP configuration below. --> <bean id="petStore" class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl"> <property name="accountDao" ref="accountDao"/> <property name="categoryDao" ref="categoryDao"/> <property name="productDao" ref="productDao"/> <property name="itemDao" ref="itemDao"/> <property name="orderDao" ref="orderDao"/> </bean> <!-- ========================= ASPECT CONFIGURATION ======================== --> <!-- AOP配置,用来控制哪些方法将需要进行事务处理,采用了AspectJ 的语法 --> <aop:config> <!-- This definition creates auto-proxy infrastructure based on the given pointcut, expressed in AspectJ pointcut language. Here: applying the advice named "txAdvice" to all methods on classes named PetStoreImpl. --> <!-- 指出在 PetStoreFacade 的所有方法都将采用 txAdvice(在紧接着的元素中定义了)事务方针,注意,我们这里虽然指定的是接口 PetStoreFacace, 但其暗示着其所有的实现类也将
            同样具有这种性质,因为本身就是实现类的方法在执行的,接口是没有方法体的。 --> <aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))" advice-ref="txAdvice"/> <!-- This definition creates auto-proxy infrastructure based on the given pointcut, expressed in AspectJ pointcut language. Here: applying the advice named "emailAdvice" to insertOrder(Order) method of PetStoreImpl --> <!-- 当执行 PetStoreFacade.insertOrder方法,该方法最后一个参数为Order类型时(其实我们的例子中只有一个 insertOrder 方法,但这告诉了我们,当我们的接口或类中有重载了的方法,
            并且各个重载的方法可能使用不同的拦截机机制时,我们可以通过方法的参数加以指定),将执行emailAdvice(在最后定义的那个元素)--> <aop:advisor pointcut="execution(* *..PetStoreFacade.insertOrder(*..Order))" advice-ref="emailAdvice"/> </aop:config> <!--       事务方针声明,用于控制采用什么样的事务策略 Transaction advice definition, based on method name patterns. Defaults to PROPAGATION_REQUIRED for all methods whose name starts with "insert" or "update", and to PROPAGATION_REQUIRED with read-only hint for all other methods. --> <tx:advice id="txAdvice"> <tx:attributes> <tx:method name="insert*"/> <tx:method name="update*"/> <tx:method name="*" read-only="true"/> </tx:attributes> </tx:advice> <!-- 拦截机,用于在适当的时机(通过AOP配置,如上面)在方法执行成功后发送邮件 AOP advice used to send confirmation email after order has been submitted --> <!-- --> <bean id="emailAdvice" class="org.springframework.samples.jpetstore.domain.logic.SendOrderConfirmationEmailAdvice"> <property name="mailSender" ref="mailSender"/> </bean> <!-- ========================= 忽略 REMOTE EXPORTER DEFINITIONS ======================== --> </beans>

     

     

    这个配置比想象的要简单的多:

    Xml代码

    <aop:config>         

     <!-- This definition creates auto-proxy infrastructure based on the given pointcut, expressed in AspectJ pointcut language.   

    Here: applying the advice named        "txAdvice" to all methods on classes named PetStoreImpl. 指出在 PetStoreFacade   

    的所有方法都将采用 txAdvice(在紧接着的元素中定义了)事务方针,注意,我们这里虽然指定的是接口 PetStoreFacace,          

     但其暗示着其所有的实现类也将同样具有这种性质,因为本身就是实现类的方法在执行的,接口是没有方法体的。    -->     

           <aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))" advice-ref="txAdvice"/>                 

     <!-- 其它拦截机-->    

          </aop:config>  

     

    <span style="font-size:14px;"><aop:config>       
     <!-- This definition creates auto-proxy infrastructure based on the given pointcut, expressed in AspectJ pointcut language. 
    Here: applying the advice named        "txAdvice" to all methods on classes named PetStoreImpl. 指出在 PetStoreFacade 
    的所有方法都将采用 txAdvice(在紧接着的元素中定义了)事务方针,注意,我们这里虽然指定的是接口 PetStoreFacace,        
     但其暗示着其所有的实现类也将同样具有这种性质,因为本身就是实现类的方法在执行的,接口是没有方法体的。    -->   
           <aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))" advice-ref="txAdvice"/>               
     <!-- 其它拦截机-->  
    </aop:config>
    </span>

    1. 所有的拦截机配置都放在 <aop:config> 配置元素中.
    2. 下面还是需要理解一下几个有关AOP的专用名词,不过,是挺抽象的,最好能会意出其的用意

    • pointcut 切入点,比如:updateAccount 方法需要进行事务管理,则这个切入点就是“执行方法体”(execution)。Spring 所有支持的切入点类型在都在 Spring reference: 6.2.3.1. Supported Pointcut Designators 中列出了。
    • advice    要对这个切入点进行什么操作,比如事务控制
    • advisor   Spring 特有的概念,将上两个概念合到一个概念中来,即一个 advisor 包含了一个切入点及对这个切入点所实施的操作。

    因为 方法执行切入点 execution 为最常见的切入点类型,我们着重介绍一下,execution 的完全形式为:

    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

    这是一个正则表达式,其中由 '?' 结尾的部分是可选的。翻译过来就是:

    执行(方法访问修饰符? 方法返回类型 声明类型? 方法名(方法参数类型) 抛出异常?)

    所有的这些都是用来定义执行切入点,即那些方法应该被侯选为切入点:

    • 方法访问修饰符   即 public, private 等等
    • 方法返回类型       即方法返回的类型,如 void, String 等等
    • 声明类型                1.5的语法,现在可以先忽略它
    • 方法名                    方法的名字
    • 方法参数类型       方法的参数类型
    • 抛出异常                方法声明的抛出的异常



    例如,所有dao代码被定义在包 com.xyz.dao 及子包 com.xyz.dao.hibernate, 或者其它,如果还有的话,子包中, 里面定义的是提供DAO功能的接口或类,那么表达式:

    execution(* com.xyz.dao..*.*(..))

    表示切入点为:执行定义在包 com.xyz.dao 及其子包(因为 .. 所致) 中的任何方法

    详细情况可以参见 Spring refernce: 6.2.3.4. Examples


    因此这个表达式为执行定义在类 PetStoreFacade 及其实现类中的所有方法,采取的动作定义在 txAdvice 中. 关于该 advice 的定义,(见声明性事务控制)一节

    <aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))" advice-ref="txAdvice"/> 

     

    Spring 自定拦截机

    来为了进行事务控制,我们只需简单地配置几下,所有的工作都由 Spring 来做。这样固然很好,但有时我们需要有我们特有的控制逻辑。因为Spring 不可能包含所有人需要的所有拦截机。所以它提供了通过程序的方式加以定制的方式。我们的项目中就有这么一个拦截机,在用户确认付款后,将定单信息通过 email 的方式发送给注册用户的邮箱中。

    <aop:config> ... <!-- 当执行 PetStoreFacade.insertOrder方法,该方法最后一个参数为Order类型时(其实我们的例子中只有一个 insertOrder 方法,但这告诉了我们,当我们的接口或类中有重载了的方法,
            并且各个重载的方法可能使用不同的拦截机机制时,我们可以通过方法的参数加以指定),将执行emailAdvice(在最后定义的那个元素)
    --> <aop:advisor pointcut="execution(* *..PetStoreFacade.insertOrder(*..Order))" advice-ref="emailAdvice"/> </aop:config>

    红色的注释已经说的很清楚这个 Advisor 了,它的切入点(pointcut) 为 PetStoreFacade 的 void insertOrder(Order order) 方法,采取的动作为引用的 emailAdvice, 下面我们就来看看 emailAdvice:

     <bean id="emailAdvice" class="org.springframework.samples.jpetstore.domain.logic.SendOrderConfirmationEmailAdvice"> <property name="mailSender" ref="mailSender"/> </bean>

    它给了这个 advice 的实现类为 logic 包中 SendOrderConfirmationEmailAdvice, 该Bean 引用了我们前面定义的邮件发送器(一个 Spring 内置的邮件发送器).

    下面看看这个实现类:

    public class SendOrderConfirmationEmailAdvice implements AfterReturningAdvice, InitializingBean { // user jes on localhost private static final String DEFAULT_MAIL_FROM = "test@pprun.org"; private static final String DEFAULT_SUBJECT = "Thank you for your order!"; private final Log logger = LogFactory.getLog(getClass()); private MailSender mailSender; private String mailFrom = DEFAULT_MAIL_FROM; private String subject = DEFAULT_SUBJECT;  public void setMailSender(MailSender mailSender) {
    this.mailSender = mailSender;
    } public void setMailFrom(String mailFrom) { this.mailFrom = mailFrom; } public void setSubject(String subject) { this.subject = subject; } public void throws Exception { if (this.mailSender == null) { throw new IllegalStateException("mailSender is required"); } } /**
    *
    * @param returnValue 被拦截的方法的返回值
    * @param m 被拦截的方法的所有信息(Method类封装了这些信息)
    * @param args 被拦截的方法的所有参数组成的数组
    * @param target 目标对象,对于方法执行来说,即是方法所在的类的实例(与 this 同,批当前对象)
    * @throws java.lang.Throwable
    */ public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {
    // 我们被拦截的方法为 void insertOrder(Order order),方法只有一个参数,所以可知数据的第1个元素即是被传进的 order 对象 // 得到了order 对象,就可以将 order 对应的帐户名及帐单号发送到邮件中,以便确认无误。 Order order = (Order) args[0]; Account account = ((PetStoreFacade) target).getAccount(order.getUser().getUsername()); // don't do anything if email address is not set if (account.getEmail() == null || account.getEmail().length() == 0) { return; } StringBuffer text = new StringBuffer(); text.append("Dear ").append(account.getFirstname()). append(' ').append(account.getLastname()); text.append(", thank your for your order from JPetStore. " + "Please note that your order number is "); text.append(order.getId()); SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setTo(account.getEmail()); mailMessage.setFrom(this.mailFrom); mailMessage.setSubject(this.subject); mailMessage.setText(text.toString()); try { this.mailSender.send(mailMessage); } catch (MailException ex) { // just log it and go on logger.warn("An exception occured when trying to send email", ex); } } }

     

    1. 色的内容即为反向注入的 mailSender 属性

    2. 色的内容为 Spring Bean 的一个通用的接口 InitializingBean ,实现类需要实现该接口定义的方法 afterPropertiesSet() ,该方法中一般是在Bean 被初始化后并设置了所有的 setter 注入后调用的。所以这里是保证邮件发送器配置正确。因为如果没有配置正确,下面的工作是无法进行的,所以与其等那时抛出异常,还不如早早地在部署时就告知(通过抛出 IllegalStateException 来提示)

    3. 绿色的内容为这个 Advise 的核心,即在切入点被切入后将采用的动作。因为 Advise 也同样有多种类型,比如我们这里的“方法正常返回”,“方法执行前”,“方法执行后”,“环绕在方法执行前后”,“方法抛出异常时”等等(详情参见 Spring Reference: 6.2.4. Declaring advice)。但是我们的逻辑为在用户确认定单并且执行成功(所谓的成功是指将这一定单插入到了表 Order 中了)后,将发送一确认信。所以”方法正常返回“完全符合我们的要求。
    接口AfterReturningAdvice即是 Spring中表示
    ”方法正常返回“ 这一语义的 Advice, 所以我们实现这个接口及其必须的方法afterReturning.
    方法代码的工作其实并不重要,只要我们理解这些“魔法”一样的技术后,实现代码是很简单的。值得提及的是这个方法的参数,这些参数是封装了切入点的所有信息,请见上面的注释。在我们的实现中只使用了被拦截方法的参数,在复杂的 Advice 实现中可能会用到切入点所有信息。

     

    本文转自http://javacrazyer.iteye.com/blog/794035

    © 著作权归作者所有

    共有 人打赏支持
    Simon丶Ma
    粉丝 4
    博文 134
    码字总数 299850
    作品 0
    深圳
    程序员
    Spring AOP 日志拦截器的事务管理

    如果要在方法执行前或后或抛出异常后加上一个自己的拦截器,或者一个环绕拦截器,在拦截器中执行一些操作,比如执行一些数据库操作,记录一些信 息,这些操作通过调用一个服务类的方法来执行...

    哲别0 ⋅ 05/18 ⋅ 0

    Spring AOP与拦截器的区别

    作用层面 拦截器只对action负责,作用层面一般位于Controller层 Spring AOP主要是拦截对Spring管理的Bean的访问,一般作用与Service层 2、联系与区别 联系: Spring AOP和拦截器一样,都是A...

    niithub ⋅ 05/06 ⋅ 0

    3.5 ProxyFactoryBean实现AOP的过程总结

    回顾下通过ProxyFactoryBean实现AOP的整个过程: 首先需要对目标对象以及拦截器进行正确配置,以便AopProxy代理对象顺利产生;这些配置可以通过配置ProxyFactoryBean的属性来完成,或者通过编...

    edwardGe ⋅ 06/04 ⋅ 0

    青橘子的服务端 - 拦截器+注解实现登陆验证

    思路 拦截器拦截所有配置好的路径,取出注解对象判断是否需要进行登陆判断。 其实也可以用AOP来实现。 代码 测试Controller 注解类 配置类 拦截器 参考资料 SpringBoot使用自定义注解实现权限...

    花有信 ⋅ 2017/11/27 ⋅ 0

    SSM整合,个人总结和step

    我最近才整合完SSM。 前段时间,分开学习了这三个框架,都是看某黄埔军校的视频,整合的部分没看视频,想自己做。 但是有一些部分自己没有能完成,主要是如何从spring容器里取出Application...

    wzlhlhhh ⋅ 05/11 ⋅ 0

    springmvc+mybatis学习笔记(汇总)

    springmvc+mybatis学习笔记(汇总) 标签 : springmvc mybaits [TOC] 笔记分为两大部分:mybatis和springmvc mybatis springmvc 笔记内容主要是mybatis和springmvc的一些基本概念和使用方法,...

    brianway ⋅ 2016/03/30 ⋅ 2

    3.4 Spring AOP拦截器调用的实现

    3.4.1 设计原理 在Spring AOP通过JDK的Proxy方式或者CGLIB方式生成代理对象的时候,相关的拦截器已经配置到代理对象中去了,拦截器在代理对象中起作用是通过对这些方法的回调来完成的。 如果...

    edwardGe ⋅ 06/04 ⋅ 0

    Spring Security 3 (一) 为什么要使用Spring Security

    Spring Security 3 (一) 为什么要使用Spring Security 大多数项目应该都有权限一类的控制,目的在于让用户不能去访问不该访问的内容。在没有接触SpringSecurity 3(简称SS3)之前,都是通过各种...

    sgyyz ⋅ 2014/02/12 ⋅ 0

    详述 Spring MVC 框架中拦截器 Interceptor 的使用方法

    1 前言   昨天新接了一个需要,“拦截 XXX,然后 OOO”,好吧,说白了就是要用拦截器干点事(实现一个具体的功能)。之前,也在网络上搜了很多关于的文章,但感觉内容都大同小异,而且知识...

    architect刘源源 ⋅ 05/04 ⋅ 0

    SpringMVC开发 知识点速查

    SpringMVC入门 什么是SpringMVC 实现MVC设计模式的框架 SpringMVC核心组件 DispatcherServlet 前置控制器,调度 Handler 处理器,完成具体业务逻辑 HandlerMapping 将请求映射到Handler,映射...

    linxinzhe ⋅ 05/19 ⋅ 0

    没有更多内容

    加载失败,请刷新页面

    加载更多

    下一页

    RabbitMQ学习以及与Spring的集成(三)

    本文介绍RabbitMQ与Spring的简单集成以及消息的发送和接收。 在RabbitMQ的Spring配置文件中,首先需要增加命名空间。 xmlns:rabbit="http://www.springframework.org/schema/rabbit" 其次是模...

    onedotdot ⋅ 23分钟前 ⋅ 0

    JAVA实现仿微信红包分配规则

    最近过年发红包拜年成为一种新的潮流,作为程序猿对算法的好奇远远要大于对红包的好奇,这里介绍一种自己想到的一种随机红包分配策略,还请大家多多指教。 算法介绍 一、红包金额限制 对于微...

    小致dad ⋅ 35分钟前 ⋅ 0

    Python 数电表格格式化 xlutils xlwt xlrd的使用

    需要安装 xlutils xlwt xlrd 格式化前 格式化后 代码 先copy读取的表格,然后按照一定的规则修改,将昵称中的学号提取出来替换昵称即可 from xlrd import open_workbookfrom xlutils.copy ...

    阿豪boy ⋅ 今天 ⋅ 0

    面试题:使用rand5()生成rand7()

    前言 读研究生这3 年,思维与本科相比变化挺大的,这几年除了看论文、设计方案,更重要的是学会注重先思考、再实现,感觉更加成熟吧,不再像个小P孩,人年轻时总会心高气傲。有1 道面试题:给...

    初雪之音 ⋅ 今天 ⋅ 0

    Docker Toolbox Looks like something went wrong

    Docker Toolbox 重新安装后提示错误:Looks like something went wrong in step ´Checking if machine default exists´ 控制面板-->程序与应用-->启用或关闭windows功能:找到Hyper-V,如果处......

    随你疯 ⋅ 今天 ⋅ 0

    Guacamole 远程桌面

    本文将Apache的guacamole服务的部署和应用,http://guacamole.apache.org/doc/gug/ 该链接下有全部相关知识的英文文档,如果水平ok,可以去这里仔细查看。 一、简介 Apache Guacamole 是无客...

    千里明月 ⋅ 今天 ⋅ 0

    nagios 安装

    Nagios简介:监控网络并排除网络故障的工具:nagios,Ntop,OpenVAS,OCS,OSSIM等开源监控工具。 可以实现对网络上的服务器进行全面的监控,包括服务(apache、mysql、ntp、ftp、disk、qmail和h...

    寰宇01 ⋅ 今天 ⋅ 0

    AngularDart注意事项

    默认情况下创建Dart项目应出现以下列表: 有时会因为不知明的原因导致列表项缺失: 此时可以通过以下步骤解决: 1.创建项目涉及到的包:stagehand 2.执行pub global activate stagehand或pub...

    scooplol ⋅ 今天 ⋅ 0

    Java Web如何操作Cookie的添加修改和删除

    创建Cookie对象 Cookie cookie = new Cookie("id", "1"); 修改Cookie值 cookie.setValue("2"); 设置Cookie有效期和删除Cookie cookie.setMaxAge(24*60*60); // Cookie有效时间 co......

    二营长意大利炮 ⋅ 今天 ⋅ 0

    【每天一个JQuery特效】淡入淡出显示或隐藏窗口

    我是JQuery新手爱好者,有时间就练练代码,防止手生,争取每天一个JQuery练习,在这个博客记录下学习的笔记。 本特效主要采用fadeIn()和fadeOut()方法显示淡入淡出的显示效果显示或隐藏元...

    Rhymo-Wu ⋅ 今天 ⋅ 0

    没有更多内容

    加载失败,请刷新页面

    加载更多

    下一页

    返回顶部
    顶部