Spring之AOP

aop( aspect oriented programming ) 面向切面(方面)编程,是对所有对象或者是一类对象编程,核心是( 在增加代码的基础上, 增加新功能 )

首先我们来看一下官方文档所给我们的关于AOP的一些概念性词语的解释:

切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式)或者基于Aspect注解方式来实现。通俗点说就是我们加入的切面类(比如log类),可以这么理解。

连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。通俗的说就是加入切点的那个点

通知(Advice):在切面的某个特定的连接点上执行的动作。其中包括了“around”、“before”和“after”等不同类型的通知(通知的类型将在后面部分进行讨论)。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。

切入点(Pointcut):匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。

引入(Introduction):用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制。

目标对象(Target Object): 被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。

AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

 

通知类型:

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

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

异常通知(After throwing advice):在方法抛出异常退出时执行的通知。

最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。

环绕通知是最常用的通知类型。和AspectJ一样,Spring提供所有类型的通知,我们推荐你使用尽可能简单的通知类型来实现需要的功能。例如,如果你只是需要一个方法的返回值来更新缓存,最好使用后置通知而不是环绕通知,尽管环绕通知也能完成同样的事情。用最合适的通知类型可以使得编程模型变得简单,并且能够避免很多潜在的错误。比如,你不需要在JoinPoint上调用用于环绕通知的proceed()方法,就不会有调用的问题。

 

spring AOP的实现

步骤:

1. 定义接口

2. 编写对象(被代理对象=目标对象)

3. 编写通知(前置通知目标方法调用前调用)

4. 在beans.xml文件配置

4.1 配置 被代理对象=目标对象

4.2 配置通知

4.3 配置代理对象 是 ProxyFactoryBean的对象实例

4.3.1 <!– 代理接口集 –>

4.3.2 织入通知

4.3.3 配置被代理对象

后面还后置通知,环绕通知,异常通知,引入通知

 

下面我们来看一个具体的例子:

代理目标对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.lpq.aop;

public class Test1Service implements TestServiceInter,TestServiceInter2{

    private String name;
   
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void sayHello() {
        // TODO Auto-generated method stub
        System.out.println("Hi "+name);
    }

    @Override
    public void sayBye() {
        // TODO Auto-generated method stub
        System.out.println("Bye "+name);
    }
   
}

代理实现的接口:

1
2
3
4
5
6
7
8
9
10
11
package com.lpq.aop;

public interface TestServiceInter {
    public void sayHello();
}  

package com.lpq.aop;

public interface TestServiceInter2 {
    public void sayBye();
}

前置通知:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.lpq.aop;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class MyMethodBeforeAdvice implements MethodBeforeAdvice{

    /**
     * methos:被调用的方法名字
     * args:给method传递的参数
     * target:目标对象
     */

    @Override
    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("记录日志"+method.getName());
    }
}

后置通知:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.lpq.aop;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class MyAfterReturningAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args,
            Object target) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("关闭资源.......");
    }

}

环绕通知:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.lpq.aop;

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

public class MyMethodInterceptor implements MethodInterceptor{

    @Override
    public Object invoke(MethodInvocation arg0) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("调用前");
        Object obj = arg0.proceed();
        System.out.println("调用后");
        return obj;
    }
   
}

异常通知:

1
2
3
4
5
6
7
8
9
10
11
package com.lpq.aop;

import java.lang.reflect.Method;

import org.springframework.aop.ThrowsAdvice;

public class MyThrowsAdvice implements ThrowsAdvice{
    public void afterThrowing(Method method,Object[] objs,Object target,Exception e){
        System.out.println("发现异常"+e.getMessage());
    }
}

配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
    default-autowire="byName"
    >
   
    <!-- 配置被代理对象 -->
    <bean id="test1Service" class="com.lpq.aop.Test1Service">
        <property name="name" value="刘培庆"></property>
    </bean>
    <!-- 配置前置通知 -->
    <bean id="MyMethodBeforeAdvice" class="com.lpq.aop.MyMethodBeforeAdvice"></bean>
    <!-- 配置后置通知 -->
    <bean id="MyAfterReturningAdvice" class="com.lpq.aop.MyAfterReturningAdvice"></bean>
    <!-- 配置环绕通知 -->
    <bean id="MyMethodInterceptor" class="com.lpq.aop.MyMethodInterceptor"></bean>
    <!-- 配置异常通知 -->
    <bean id="MyThrowsAdvice" class="com.lpq.aop.MyThrowsAdvice"></bean>
    <!-- 定义切入点 -->
    <bean id="myMethodBeforeAdviceFilter" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="advice" ref="MyMethodBeforeAdvice"/>
        <property name="mappedNames">
            <list>
                <value>sayHel*</value>
            </list>
        </property>
    </bean>
    <!-- 配置代理对象 -->
    <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 配置代理接口集 -->
        <property name="proxyInterfaces">
            <list>
                <value>com.lpq.aop.TestServiceInter</value>
                <value>com.lpq.aop.TestServiceInter2</value>
            </list>
        </property>
        <!-- 把通知置入到代理对象 -->
        <property name="interceptorNames">
        <!-- 相当与把MyMethodBeforeAdvice前置通知和代理对象关联起来,也可以把通知看成拦截器,structs2核心拦截器 -->
            <list>
            <!-- 使用了自定义切入点 对方法进行过滤 -->
                <value>myMethodBeforeAdviceFilter</value>
                <value>MyAfterReturningAdvice</value>
                <value>MyMethodInterceptor</value>
                <value>MyThrowsAdvice</value>
            </list>
        </property>
        <!-- 配置被代理对象,可以指定 -->
        <property name="target" ref="test1Service"/>
    </bean>
</beans>

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.lpq.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("com/lpq/aop/beans.xml");
        TestServiceInter inter = (TestServiceInter)ac.getBean("proxyFactoryBean");
        inter.sayHello();
        TestServiceInter2 inter2 = (TestServiceInter2)inter;
        inter2.sayBye();
    }
}

提问? springaop中,当你通过代理对象去实现aop的时候,获取的ProxyFactoryBean是什么类型?

: 返回的是一个代理对象,如果目标对象实现了接口,则spring使用jdk 动态代理技术,如果目标对象没有实现接口,则spring使用CGLIB技术.

 

除非注明,Coder文章均为原创,转载请以链接形式标明本文地址

本文地址:http://www.alonemonkey.com/spring-aop.html

本文链接:http://www.alonemonkey.com/spring-aop.html