首先。AOP 面向切面编程。

就是说通过配置将业务逻辑和系统的服务分离。目的是让业务逻辑之关系业务的处理而不再去处理其他事情。其中切面一般都是哪些可以为多个类提供服务的模块,将其封装起来称为切面。减少系统的重复代码和低模块之间的耦合度。一般用于权限验证、日志、事务等。

拦截器也是应用了AOP的思想,将拦截(请求)Action以进行一些预处理或者结果处理。Spring的AOP可以拦截Spring管理的bean。

AOP主要是通过动态代理和反射机制实现的。

Spring代理实际上是对JDK代理和CGLIB代理做了一层封装,并且引入了AOP概念:Aspect、advice、joinpoint等等,同时引入了AspectJ中的一些注解@pointCut,@after,@before等等.Spring Aop严格的来说都是动态代理,所以实际上Spring代理和Aspectj的关系并不大.

基础概念

  1. 通知(Advice)
    定义了切入点代码执行时间点附近需要做的工作。

切入点:
@Before org.apringframework.aop.MethodBeforeAdvice
@After-returning(返回后) org.springframework.aop.AfterReturningAdvice
@After-throwing(抛出后) org.springframework.aop.ThrowsAdvice
@Arround周围 org.aopaliance.intercept.MethodInterceptor
@Introduction引入 org.springframework.aop.IntroductionInterceptor
2. 连接点(Joinpoint)
程序能够应用通知的一个时机。这些时机就是连接点。比如方法调用时、异常抛出时、方法返回后等。
3. 切入点(Pointcut)
通知定义了切面要发生的故事,连接点定义了故事要发生的时机,而切入点定义了故事发生的地点,例如某个类or方法的名称。Spring中允许我们用正则表达式来指定。

  1. 切面(Aspect)
    Advice Joinpoint Pointcut共同组成了切面:要发生的故事、时间、地点
  2. 引入(Introduction)
    引入允许我们向现有的类添加新的方法和属性。
    Spring中的方法注入的功能。
  3. 目标(Target)
    就是被通知的对象。
    如果没有AOP,通知的逻辑就要写在目标对象中。有了AOP之后他可以只关注自己要做的事情,解耦。
  4. 代理(Proxy)
    应用通知的对象。
  5. 织入(Weaving)
    把切面应用到目标对象来创建新的代理对象的过程。
    织入一般发生在以下几个时机:
    编译时:AspectJ的织入编译器
    类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码。
    运行时 :切面在运行的某一个时刻被织入,SpringAOP就是运行时织入切面的。(JDK的动态代理技术)

使用AOP的几种方式:

  1. 经典的基于代理的AOP
  2. @AspectJ注解驱动的切面
  3. 纯POJO切面
  4. 注入式AspectJ切面

举一个例子:
Me类是实现了Sleepable的接口,重写了其中的sleep方法。

基于代理的AOP
  1. 由于Me完成了sleep的逻辑,但是其他睡觉需要的功能 ,例如起床穿衣服,睡前脱衣服都可以由AOP代替“Me”执行。实现解耦。
    那么我们就可以通过SleepHelper类
public class SleepHelper implements MethodBeforeAdvice, AfterReturningAdvice {  
  
    public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {  
        System.out.println("睡觉前要脱衣服!");  
    }  
  
    public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {  
        System.out.println("起床后要穿衣服!");  
    }  
  
}
  1. Spring配置文件 application.xml配置AOP
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
<span style="white-space:pre">    </span>xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
<span style="white-space:pre">    </span>xmlns:aop="http://www.springframework.org/schema/aop"  
<span style="white-space:pre">    </span>xsi:schemaLocation="http://www.springframework.org/schema/beans  
<span style="white-space:pre">    </span>http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
<span style="white-space:pre">    </span>http://www.springframework.org/schema/aop  
<span style="white-space:pre">    </span>http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">  
     
   <!-- 定义被代理者 -->  
   <bean id="me" class="com.springAOP.bean.Me"></bean>  
     
   <!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->  
   <bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>  
     
   <!-- 定义切入点位置 -->  
   <bean id="sleepPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">  
        <property name="pattern" value=".*sleep"></property>  
   </bean>  
     
   <!-- 使切入点与通知相关联,完成切面配置 -->  
   <bean id="sleepHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">  
        <property name="advice" ref="sleepHelper"></property>         
        <property name="pointcut" ref="sleepPointcut"></property>  
   </bean>  
     
   <!-- 设置代理 -->  
   <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">  
        <!-- 代理的对象,有睡觉能力 -->  
        <property name="target" ref="me"></property>  
        <!-- 使用切面 -->  
        <property name="interceptorNames" value="sleepHelperAdvisor"></property>  
        <!-- 代理接口,睡觉接口 -->  
        <property name="proxyInterfaces" value="com.springAOP.bean.Sleepable"></property>   
   </bean>  
      
</beans>

其中配置标签中的几个重要属性

xmlns:

是默认的xml文档解析格式,即spring的beans。地址是http://www.springframework.org/schema/beans;通过设置这个属性,所有在beans里面声明的属性,可以直接通过<>来使用,比如<bean>等等。一个XML文件,只能声明一个默认的语义解析的规范。例如上面的xml中就只有beans一个是默认的,其他的都需要通过特定的标签来使用,比如aop,它自己有很多的属性,如果要使用,前面就必须加上aop:xxx才可以。类似的,如果默认的xmlns配置的是aop相关的语义解析规范,那么在xml中就可以直接写config这种标签了。

xmlns:xsi:

是xml需要遵守的规范,通过URL可以看到,是w3的统一规范,后面通过xsi:schemaLocation来定位所有的解析文件。

xmlns:aop:

这个是重点,是我们这里需要使用到的一些语义规范,与面向切面AOP相关。

xmlns:tx:

Spring中与事务相关的配置内容。
  1. 测试:通过AOP代理的方式执行Me的sleep()方法,会把执行前、执行后的操作执行,实现了AOP的效果!
public class Test {  
    public static void main(String[] args){  
        @SuppressWarnings("resource")  
        //如果是web项目,则使用注释的代码加载配置文件,这里是一般的Java项目,所以使用下面的方式  
        //ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");  
        ApplicationContext appCtx = new FileSystemXmlApplicationContext("application.xml");  
        Sleepable me = (Sleepable)appCtx.getBean("proxy");  
        me.sleep();  
    }  
}

然后我们其实可以通org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator简化配置。

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

在Test中就可以直接获取me对象,执行sleep方法。自动匹配,切面会自动匹配符合切入点的bean,会被自动代理,实现功能。

AspectJ提供的注解实现AOP

AspactJ框架常用注解:
@PonitCut // 声明切入点的注解
@Before // 声明前置通知注解
@After // 声明后置通知注解(原始方法执行正常或者非正常执行都会进入)
@AfterReturing //声明后置通知注解(原始方法必须正常执行)
@AfterThrowing // 声明异常通知
@Around // 环绕通知注解

  1. 修改SleepHelper类
@Aspect  
public class SleepHelper{  
  
    public SleepHelper(){  
          
    }  
      
    @Pointcut("execution(* *.sleep())")  
    public void sleeppoint(){}  
      
    @Before("sleeppoint()")  
    public void beforeSleep(){  
        System.out.println("睡觉前要脱衣服!");  
    }  
      
    @AfterReturning("sleeppoint()")  
    public void afterSleep(){  
        System.out.println("睡醒了要穿衣服!");  
    }  
      
}
  1. 在方法中,可以加上JoinPoint参数以进行相关操作,如:
1. //当抛出异常时被调用
 public void doThrowing(JoinPoint point, Throwable ex)
 {
 System.out.println(“doThrowing::method "
 + point.getTarget().getClass().getName() + “.”
 + point.getSignature().getName() + " throw exception”);
 System.out.println(ex.getMessage());
 }
  1. 然后修改配置为:
<aop:aspectj-autoproxy />  
<!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->  
<bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>  
<!-- 定义被代理者 -->  
<bean id="me" class="com.springAOP.bean.Me"></bean>
  1. 测试
4. public class Test {
 public static void main(String[] args){
 @SuppressWarnings(“resource”)
 //如果是web项目,则使用注释的代码加载配置文件,这里是一般的Java项目,所以使用下面的方式
 //ApplicationContext appCtx = new ClassPathXmlApplicationContext(“application.xml”);
 ApplicationContext appCtx = new FileSystemXmlApplicationContext(“application.xml”);
 Sleepable me = (Sleepable)appCtx.getBean(“me”);
 me.sleep();
 }
 }

Pojo切面

使用Spring来定义纯粹的POJO切面
(名字很绕口,其实就是纯粹通过aop:fonfig标签配置,也是一种比较简单的方式)。

  1. 修改SleepHelper类,所以这种方式的优点就是在代码中不体现任何AOP相关配置,纯粹使用xml配置
    public class SleepHelper{
public void beforeSleep(){  
     System.out.println("睡觉前要脱衣服!");  
 }  
   
 public void afterSleep(){  
     System.out.println("睡醒了要穿衣服!");  
 }

}

  1. 配置文件
<?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"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    http://www.springframework.org/schema/aop  
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">  
  
    <!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->  
    <bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>  
    <!-- 定义被代理者 -->  
    <bean id="me" class="com.springAOP.bean.Me"></bean>  
  
    <aop:config>  
        <aop:aspect ref="sleepHelper">  
            <aop:before method="beforeSleep" pointcut="execution(* *.sleep(..))" />  
            <aop:after method="afterSleep" pointcut="execution(* *.sleep(..))" />  
        </aop:aspect>  
    </aop:config>  
  
</beans>
  1. 另一种配置写法
<aop:config>  
    <aop:aspect ref="sleepHelper">  
           <aop:pointcut id="sleepHelpers" expression="execution(* *.sleep(..))" />  
           <aop:before pointcut-ref="sleepHelpers" method="beforeSleep" />  
           <aop:after pointcut-ref="sleepHelpers" method="afterSleep" />            
       </aop:aspect>  
</aop:config>