欢迎来到 IT实训基地-南通科迅教育
咨询电话:0513-81107100
Spring AOP四种实现方式Demo详解与相关知识探究
2017/4/17
南通科迅教育
403
南通Web前端培训哪家好,多少钱_南通Web前端培训_南通Web前端培训

一、前言

在网络上看到一篇博客Spring实现AOP的4种方式,博主写的很通俗易懂,但排版实在抓狂,对于我这么一个对排版、代码格式有强迫症的人来说,实在是不能忍受~~~~(>_<)~~~~。

我亲手实现了一遍,重新整理,加上了一些不易关注到的细节、漏掉的知识,以及自己对AOP的一些理解,写成这篇博客。

 

二、AOP相关概念

(1)AOP是什么?AOP与拦截器的区别?

太抽象的不说,如果你知道Struts2的拦截器,拦截器就是应用的AOP的思想,它用于拦截Action以进行一些预处理或结果处理。而Spring的AOP是一种更通用的模式,可以拦截Spring管理的Bean,功能更强大,适用范围也更广,它是通过动态代理与反射机制实现的。(更详细的解释可参看博客http://blog.csdn.net/zhangliangzi/article/details/51648032)

(2)使用AOP需要的一些概念。

1.通知(Advice)

通知定义了在切入点代码执行时间点附近需要做的工作。

Spring支持五种类型的通知:

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)

通知定义了切面要发生的“故事”,连接点定义了“故事”发生的时机,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,Spring中允许我们方便的用正则表达式来指定。

4.切面(Aspect)

通知、连接点、切入点共同组成了切面:时间、地点和要发生的“故事”。

5.引入(Introduction)

引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)。

6.目标(Target)

即被通知的对象,如果没有AOP,那么通知的逻辑就要写在目标对象中,有了AOP之后它可以只关注自己要做的事,解耦合!

7.代理(proxy)

应用通知的对象,详细内容参见设计模式里面的动态代理模式。

8.织入(Weaving)

把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:

(1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器;

(2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码;

(3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术。

 

三、使用AOP的几种方式

1.经典的基于代理的AOP

2.@AspectJ注解驱动的切面

3.纯POJO切面

4.注入式AspectJ切面

 

四、Demo详解

在讲Demo之前,先把项目结构贴一下,我用的的一般的Java Project+Maven进行测试,用Web Project的小区别一会会说到。有一点很重要,jar依赖必须导入正确,我在测试过程中,很多bug都是因为依赖问题引起的,这里也贴一下。

包结构:

\

pom.xml:

 

?
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
    <modelversion>4.0.0</modelversion>
 
    <groupid>com.springAOP</groupid>
    springAOP</artifactid>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>springAOP</name>
    <url>http://maven.apache.org</url>
 
    <properties>
        <project.build.sourceencoding>UTF-8</project.build.sourceencoding>
        <org.springframework.version>3.0.5.RELEASE</org.springframework.version>
    </properties>
 
 
    <dependencies>
 
        <dependency>
            <groupid>junit</groupid>
            junit</artifactid>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
 
        <!-- Spring -->
        <dependency>
            <groupid>org.springframework</groupid>
            spring-context</artifactid>
            <version>${org.springframework.version}</version>
        </dependency>
 
        <!-- Spring AOP + AspectJ -->
        <dependency>
            <groupid>org.springframework</groupid>
            spring-aop</artifactid>
            <version>${org.springframework.version}</version>
        </dependency>
         
        <dependency>
            <groupid>org.aspectj</groupid>
            aspectjrt</artifactid>
            <version>1.8.9</version>
        </dependency>
         
        <dependency>
            <groupid>org.aspectj</groupid>
            aspectjweaver</artifactid>
            <version>1.8.9</version>
        </dependency>
     
    </dependencies>
</project>


下面开始正式的讲解:

 

1、经典的基于代理的AOP实现,以一个睡觉的例子实现。

(1)可睡觉的接口,任何可以睡觉的人或机器都可以实现它。

 

?
1
2
3
publicinterfaceSleepable {
    publicvoidsleep();
}

(2)接口实现类,“Me”可以睡觉,“Me”就实现可以睡觉的接口。

 

 

?
1
2
3
4
5
publicclassMeimplementsSleepable{
    publicvoidsleep() {
        System.out.println("\n睡觉!不休息哪里有力气学习!\n");
    }
}

3)Me关注于睡觉的逻辑,但是睡觉需要其他功能辅助,比如睡前脱衣服,起床脱衣服,这里开始就需要AOP替“Me”完成!解耦!首先需要一个SleepHelper类。因为一个是切入点前执行、一个是切入点之后执行,所以实现对应接口。

 

 

?
1
2
3
4
5
6
7
8
9
10
11
publicclassSleepHelperimplementsMethodBeforeAdvice, AfterReturningAdvice {
 
    publicvoidbefore(Method arg0, Object[] arg1, Object arg2)throwsThrowable {
        System.out.println("睡觉前要脱衣服!");
    }
 
    publicvoidafterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3)throwsThrowable {
        System.out.println("起床后要穿衣服!");
    }
 
}

(4)最关键的来了,Spring核心配置文件application.xml配置AOP。

 

 

?
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
<!--?xml version="1.0"encoding="UTF-8"?-->
<beans span=""style="white-space:pre"xmlns="http://www.springframework.org/schema/beans"> 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 ">
    
   <!-- 定义被代理者 -->
   <beanclass="com.springAOP.bean.Me"id="me"></bean>
    
   <!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->
   <beanclass="com.springAOP.bean.SleepHelper"id="sleepHelper"></bean>
    
   <!-- 定义切入点位置 -->
   <beanclass="org.springframework.aop.support.JdkRegexpMethodPointcut"id="sleepPointcut">
        <property name="pattern"value=".*sleep"></property>
   </bean>
    
   <!-- 使切入点与通知相关联,完成切面配置 -->
   <beanclass="org.springframework.aop.support.DefaultPointcutAdvisor"id="sleepHelperAdvisor">
        <property name="advice"ref="sleepHelper"></property>      
        <property name="pointcut"ref="sleepPointcut"></property>
   </bean>
    
   <!-- 设置代理 -->
   <beanclass="org.springframework.aop.framework.ProxyFactoryBean"id="proxy">
        <!-- 代理的对象,有睡觉能力 -->
        <property name="target"ref="me"></property>
        <!-- 使用切面 -->
        <property name="interceptorNames"value="sleepHelperAdvisor"></property>
        <!-- 代理接口,睡觉接口 -->
        <property name="proxyInterfaces"value="com.springAOP.bean.Sleepable"></property>
   </bean>
     
</beans>

其中:

 

是Spring的配置标签,beans里面几个重要的属性:

xmlns:

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

xmlns:xsi:

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

xmlns:aop:

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

xmlns:tx:

Spring中与事务相关的配置内容。

(5)测试类,Test,其中,通过AOP代理的方式执行Me的sleep()方法,会把执行前、执行后的操作执行,实现了AOP的效果!

 

?
1
2
3
4
5
6
7
8
9
10
publicclassTest {
    publicstaticvoidmain(String[] args){
        @SuppressWarnings("resource")
        //如果是web项目,则使用注释的代码加载配置文件,这里是一般的Java项目,所以使用下面的方式
        //ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");
        ApplicationContext appCtx =newFileSystemXmlApplicationContext("application.xml");
        Sleepable me = (Sleepable)appCtx.getBean("proxy");
        me.sleep();
    }
}

执行结果:

 

\
(6)通过org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator简化配置。

将配置文件中设置代理的代码去掉,加上:

 

?
1
<beanclass="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>

然后,在Test中,直接获取me对象,执行sleep方法,就可以实现同样的功能!

 

通过自动匹配,切面会自动匹配符合切入点的bean,会被自动代理,实现功能!
 

2、更简单的方式,通过AspectJ提供的注解实现AOP。

(1)同样的例子,修改后的SleepHelper:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Aspect
publicclassSleepHelper{
 
    publicSleepHelper(){
         
    }
     
    @Pointcut("execution(* *.sleep())")
    publicvoidsleeppoint(){}
     
    @Before("sleeppoint()")
    publicvoidbeforeSleep(){
        System.out.println("睡觉前要脱衣服!");
    }
     
    @AfterReturning("sleeppoint()")
    publicvoidafterSleep(){
        System.out.println("睡醒了要穿衣服!");
    }
     
}

 

(2)在方法中,可以加上JoinPoint参数以进行相关操作,如:

 

?
1
2
3
4
5
6
7
8
//当抛出异常时被调用
    publicvoiddoThrowing(JoinPoint point, Throwable ex)
    {
        System.out.println("doThrowing::method "
                + point.getTarget().getClass().getName() +"."
                + point.getSignature().getName() +" throw exception");
        System.out.println(ex.getMessage());
    }

 

(3)然后修改配置为:

 

?
1
2
3
4
5
     
<!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->
<beanclass="com.springAOP.bean.SleepHelper"id="sleepHelper"></bean>
<!-- 定义被代理者 -->
<beanclass="com.springAOP.bean.Me"id="me"></bean></aop:aspectj-autoproxy>


(4)最后测试,一样的结果!

?
1
2
3
4
5
6
7
8
9
10
publicclassTest {
    publicstaticvoidmain(String[] args){
        @SuppressWarnings("resource")
        //如果是web项目,则使用注释的代码加载配置文件,这里是一般的Java项目,所以使用下面的方式
        //ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");
        ApplicationContext appCtx =newFileSystemXmlApplicationContext("application.xml");
        Sleepable me = (Sleepable)appCtx.getBean("me");
        me.sleep();
    }
}


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

 

(1)修改后的SleepHelper类,很正常的类,所以这种方式的优点就是在代码中不体现任何AOP相关配置,纯粹使用xml配置。

 

?
1
2
3
4
5
6
7
8
9
10
11
publicclassSleepHelper{
 
    publicvoidbeforeSleep(){
        System.out.println("睡觉前要脱衣服!");
    }
     
    publicvoidafterSleep(){
        System.out.println("睡醒了要穿衣服!");
    }
    
}

(2)配置文件:

 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--?xml version="1.0"encoding="UTF-8"?-->
    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 ">
 
    <!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->
    <beanclass="com.springAOP.bean.SleepHelper"id="sleepHelper"></bean>
    <!-- 定义被代理者 -->
    <beanclass="com.springAOP.bean.Me"id="me"></bean>
 
     
         
             
             
        </aop:after></aop:before></aop:aspect>
    </aop:config>
 
</beans>

 

(3)配置的另一种写法

 

?
1
2
3
4
5
6
7
     
         
         
                 
    </aop:after></aop:before></aop:pointcut></aop:aspect>
</aop:config>
77
关闭
先学习,后交费申请表
每期5位名额
在线咨询
免费电话
QQ联系
先学习,后交费
TOP
您好,您想咨询哪门课程呢?
关于我们
机构简介
官方资讯
地理位置
联系我们
0513-91107100
周一至周六     8:30-21:00
微信扫我送教程
手机端访问
南通科迅教育信息咨询有限公司     苏ICP备15009282号     联系地址:江苏省南通市人民中路23-6号新亚大厦三楼             法律顾问:江苏瑞慈律师事务所     Copyright 2008-