文档章节

Spring AOP原理及简单应用

飓风2000
 飓风2000
发布于 2014/06/10 07:46
字数 2565
阅读 247
收藏 11

AOP 应用:
1. 监控系统重要API的调用事件,用来监控系统的性能。
2.Authentication 权限
3. Caching 缓存
4. Context passing 内容传递
5. Error handling 错误处理
6. Lazy loading 懒加载
7. Debugging  调试
8. logging, tracing, profiling and monitoring 记录跟踪 优化 校准
9. Performance optimization 性能优化
10. Persistence  持久化
11. Resource pooling 资源池
12. Synchronization 同步
13. Transactions 事务

......

相信只要使用过Spring框架的,大家对于AOP都不陌生,尤其提起它就能立刻随口说出,一般用在日志处理、异常处理、权限验证等方面。但刚开始接触难免会有各种各样的疑惑,今天抽时间,按照之前的理解整理了一份关于Spring AOP的简单教程,希望能够帮助大家尽快的了解它的实现过程及原理。首先来明确几个概念:

  • JointPoint

系统在运行之前,AOP的功能模块需要织入到OOP的功能模块中。要进行这种织入过程,需要知道在系统的哪些功能点上进行织入操作,这些将要在其上进行织入操作的系统功能点就称为JointPoint。如某方法调用的时候或者处理异常的时候,在Spring AOP中,一个连接点总是表示一个方法的执行。常见的几种类型的JoinPoint:

Ø 方法调用:当某个方法被调用的时候所处的程序执行点;

Ø 方法执行:该类型表示的是某个方法内部执行开始时的点,应该与方法调用相区分;

Ø 构造方法调用:程序执行过程中对某个对象调用其构造方法进行初始化时的点;

Ø 构造方法执行:它与构造方法调用关系如同方法调用与方法执行间的关系;

Ø 字段设置:对象的某个属性通过setter方法被设置或直接被设置的执行点;

Ø 字段获取:某个对象相应属性被访问的执行点;

Ø 异常处理执行:某些类型异常抛出后,对应的异常处理逻辑执行点;

Ø 类初始化:类中某些静态类型或静态块的初始化时的执行点。

  • Pointcut

Pointcut代表的是JoinPoint的表述方式。在将横切逻辑织入当前系统的过程中,虽然知道需要在哪些功能点上织入AOP的功能模块,但需要一种表达方法。Pointcut和一个切入点表达式关联,并在满足这个切入点的Joinpoint上运行。目前通常使用的Pointcut方式有以下几种:

Ø 直接指定Joinpoint所在的方法名称;

Ø 正则表达式,Spring的AOP支持该种方式;

Ø 使用特定的Pointcut表述语言,Spring 2.0后支持该方式。

  • Advice

Advice是单一横切关注点逻辑的载体,它代表将会织入到JoinPoint的横切逻辑。在切面的某个特定的连接点上执行的逻辑。根据它在Joinpoint位置执行时机的差异或完成功能的不同,可分为以下几种形式:

Ø Before Advice:在Joinpoint指定位置之前执行的Advice类型,可以采用它来做一些系统的初始化工作,如设置系统初始值,获取必要系统资源等。

Ø After Advice:在相应连接点之后执行的Advice类型,它还可以细分为以下三种:

² After Returning Advice:只有当前Joinpoint处执行流程正常完成后,它才会执行;

² After throwing Advice:在当前Joinpoint执行过程中抛出异常的情况下会执行;

² After Advice:该类型的Advice不管JoinPoint处执行流程是正常还是抛出异常都会执行。

Ø Around Advice:对附加其上的Joinpoint进行包裹,可以在joinpoint之前和之后都指定相应的逻辑,甚至中断或忽略joinpoint处原来程序流程的执行。

  •  Aspect

它是对系统中横切关注点逻辑进行模块化封装的AOP概念实体,它可以包含多个Pointcut以及相关的Advice定义。

  •  织入器

经过织入过程后,以Aspect模块化的横切关注点才会集成到oop的现存系统中,而完成织入过程实体称为织入器。Spring中使用一组类来完成最终的织入操作,ProxyFactory类是Spring AOP最通用的织入器。

  •  目标对象

符合Pointcut所指定的条件,将在织入过程中被织入横切逻辑的对象,称之为目标对象。

单看上述的概念,可能会觉得有点眼花缭乱,其实通过一个简单的AOP的实例即可以帮助我们很快的了解其内部的机制。其实对于方法拦截有不同的实现方式,常用的即有直接采用Spring提供的各种Advice进行拦截,另一种则是采用MethodInterceptor方式进行拦截。

Spring提供的Advice拦截方式

定义一个逻辑接口IBusinessLogic:

package com.wow.asc.aop;

public interface IBusinessLogic {

    public void foo();

    public void bar() throws BusinessLogicException;
    
    public long time();
}

其中有一个BusinessLogicException异常,它用于后面对于ThrowsAdvice进行检验的实例,在此定义为:

package com.wow.asc.aop;

public class BusinessLogicException extends Exception {

}

对于该业务逻辑的实现BusinessLogic,如下所示:

package com.wow.asc.aop;

public class BusinessLogic implements IBusinessLogic {
    @Override
    public void foo() {
        System.out.println("Inside BusinessLogic.foo()");
    }
    @Override
    public void bar() throws BusinessLogicException {
        System.out.println("Inside BusinessLogic.bar()");
        throw new BusinessLogicException();
    }
     /* 
     * 返回该方法执行的时间
     */
    @Override
    public long time() {
        System.out.println("Inside BusinessLogic.time()");
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000000; i++);
        long endTime = System.currentTimeMillis();
        
        return (endTime - startTime);
    }
}

在完成上述业务逻辑编码后,接下来将进行更多的横切插入点的设计,如在方法执行前或返回时、抛出异常时进行各种处理。对于Advice的写法如下所示:

package com.wow.asc.aop;

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
/*
* 表示一个在方法执行前进行拦截的一个Advice
 */
public class TracingBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("execute before (by " + method.getDeclaringClass().getName() + "." + method.getName() + ")");
    }
}

package com.wow.asc.aop;

import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
/*
 * 表示一个在方法返回时进行拦截的Advice
*/
public class TracingAfterAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(method.getDeclaringClass().getName() + "." + method.getName() + "spend time: " + returnValue);
        System.out.println("execute after (by " + method.getDeclaringClass().getName() + "." + method.getName() + ")");
    }
}

package com.wow.asc.aop;

import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
/*
 * 表示一个异常抛出时进行拦截的Advice
*/
public class TracingThrowsAdvice implements ThrowsAdvice {
    
    public void afterThrowing(Method method, Object[] args, Object target, Throwable subclass) {
         System.out.println( "Logging that a " + subclass + "Exception was thrown.");
      }
}

在设计完上述的代码及逻辑后,即可以通过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:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
        http://www.springframework.org/schema/context http://localhost:8080/schema/www.springframework.org/schema/context/spring-context-2.5.xsd">
	<bean id="businessLogic" class="com.wow.asc.aop.BusinessLogic" />
	<bean id="businessLogicBean" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="proxyInterfaces">
			<value>com.wow.asc.aop.IBusinessLogic</value>
		</property>
		<property name="target">
			<ref local="businessLogic"/> 
		</property>
		<property name="interceptorNames">
			<list>
				<value>theTracingBeforeAdvisor</value>
            	    <value>theTracingAfterAdvisor</value>
            	    <value>theTracingThrowsAdvisor</value>
			</list>
		</property>
	</bean>
	
	<bean id="theTracingBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="advice">
         	<ref local="theTracingBeforeAdvice"/>
      	</property>
		<property name="pattern">
			<value>.*</value>
		</property>
	</bean>
	<bean id="theTracingAfterAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="advice">
			<ref local="theTracingAfterAdvice"/>
		</property>
		<property name="pattern">
			<value>.*time.*</value>
		</property>
	</bean>
	<bean id="theTracingThrowsAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="advice">
			<ref local="theTracingThrowsAdvice"/>
		</property>
		<property name="pattern">
			<value>.*bar.*</value>
		</property>
	</bean>
	<bean id="theTracingBeforeAdvice" class="com.wow.asc.aop.TracingBeforeAdvice"/>
	<bean id="theTracingAfterAdvice" class="com.wow.asc.aop.TracingAfterAdvice"/>
	<bean id="theTracingThrowsAdvice" class="com.wow.asc.aop.TracingThrowsAdvice"/>
</beans>

         通过上述的配置,我们可以看出我们将IBusinessLogic做为代理接口,同时它的真正的目标类是BusinesssLogic。同时会对所有进入方法之前采用TracingBeforeAdvice进行拦截,进行方法前的预处理;对time方法采用TracingAfterAdvice进行拦截,进行方法返回后的处理;对于bar则采用TracingThrowsAdvice进行拦截,当方法返回BusinessLogicException时进行相应的处理。

         在配置完上述类的依赖关系及需要拦截的方法后,即可以编写客户端程序来调用,查看它的运行机制。客户端调用代码:

package com.wow.asc.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.wow.asc.aop.BusinessLogicException;
import com.wow.asc.aop.IBusinessLogic;

public class AOPTest {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        IBusinessLogic ibl = (IBusinessLogic)ac.getBean("businessLogicBean");
        ibl.foo();
        try {
            ibl.bar();
        } catch (BusinessLogicException e) {
            System.out.println("Caught BusinessLogicException");
        }
        ibl.time();
    }
}

        通过运行结果来详细的了解下,看是否真正的如上所述,会在方法前、后及异常抛出时能够拦截并进行相应处理。结果如下:

1、execute before (by com.wow.asc.aop.IBusinessLogic.foo)
2、Inside BusinessLogic.foo()
3、execute before (by com.wow.asc.aop.IBusinessLogic.bar)
4、Inside BusinessLogic.bar()
5、Logging that a com.wow.asc.aop.BusinessLogicExceptionException was thrown.
6、Caught BusinessLogicException
7、execute before (by com.wow.asc.aop.IBusinessLogic.time)
8、Inside BusinessLogic.time()
9、com.wow.asc.aop.IBusinessLogic.timespend time: 46
10、execute after (by com.wow.asc.aop.IBusinessLogic.time)

        其实通过1、3、7行可以非常清晰的了解到,每个方法在执行前都被TracingBeforeAdvice拦截到,并执行了预处理。5、6行表示当调用的是bar方法时,会被TracingThrowsAdvice拦截,当有异常抛出时,会执行相应的处理;8、9、10行则表示当调用的是time方法,返回时会被TracingAfterAdvice拦截,对其返回值进行处理。

MethodInterceptor拦截方式

        采用该种方式进行拦截,需要实现一个继承自MethodInterceptor的类,并将该类注册至spring Context中,具体如下:

package com.wow.asc.aop;

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

public class MyInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) {
        Object result = null;
        StringBuffer info = new StringBuffer();
        info.append("intercept the method: ");
        info.append(invocation.getMethod().getDeclaringClass().
getName());
        info.append(".");
        info.append(invocation.getMethod().getName());
        System.out.println("start " + info.toString());
        try {
           result = invocation.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println("end " + info.toString());
        }
        return result;
    }
}

对于类的装配,其实和上面的非常类似,示例:

<bean id="testBean" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="proxyInterfaces">
			<value>com.wow.asc.aop.IBusinessLogic</value>
		</property>
		<property name="target">
			<ref local="businessLogic"/> 
		</property>
		<property name="interceptorNames">
			<list>
				<value>myInterceptor</value>
			</list>
		</property>
</bean>
<bean id="myInterceptor" class="com.wow.asc.aop.MyInterceptor"/>

        再通过客户端进行调用,可得到运行结果,从结果来分析可以看出它在方法执行的前、后均添加了相应的日志。

start intercept the method: com.wow.asc.aop.IBusinessLogic.foo
Inside BusinessLogic.foo()
end intercept the method: com.wow.asc.aop.IBusinessLogic.foo
start intercept the method: com.wow.asc.aop.IBusinessLogic.time
Inside BusinessLogic.time()
end intercept the method: com.wow.asc.aop.IBusinessLogic.time

至此,采用两种不同方式实现的AOP就结束了,希望大家能够体会到其中的奥妙。

参考AOP资料:

http://www.zabada.com/technology/aop-example.html

http://onjava.com/pub/a/onjava/2004/07/14/springaop.html?page=1

http://dnizna.iteye.com/blog/1157663

本文转载自:http://blog.csdn.net/zhaozheng7758/article/details/7866202

共有 人打赏支持
飓风2000
粉丝 36
博文 324
码字总数 135576
作品 0
浦东
高级程序员
私信 提问
那些年,我们一起追的Spring

学无止境,但仍需及时总结。 自去年开始写作以来,写了一些关于Spring的文章,今天将它们汇总起来,一方面方便大家阅读,另一方面,也是一次小的复盘总结。 IOC 首先是Spring的IOC,也就是控...

SexyCode
2018/08/14
0
0
深入 Spring Boot : 快速集成 Dubbo + Hystrix

背景 Hystrix 旨在通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以...

小致dad
2018/07/02
0
0
主持与主编一本适合大学生适用的开源技术(JavaEE)规划思路(一)

Java 技术已经成为互联网与企业应用的主流软件技术。 各种Java Web 和 JavaEE框架技术涌现出来,其中最为會E 对于熟悉SSH 开发人员来说,其实Simple 就是提供一个更为轻量级的企业开发平台,...

simplesns
2012/02/16
353
0
spring中的AOP与IOC

在J2EE的整个发展历程中,现在正是一个非常时刻。从很多方面来说,J2EE都是一个伟大的成功:它成功地在从前没有标准的地方建立了标准;大大提升了企业级软件的开放程度,并且得到了整个行业和...

Sandy_wu
2013/06/08
0
0
Spring AOP就是这么简单啦

前言 只有光头才能变强 上一篇已经讲解了Spring IOC知识点一网打尽!,这篇主要是讲解Spring的AOP模块~ 之前我已经写过一篇关于AOP的文章了,那篇把比较重要的知识点都讲解过了一篇啦:Sprin...

Java3y
2018/05/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

tac 与cat

tac从后往前看文件,结合grep使用

writeademo
29分钟前
2
0
表单中readonly和dsabled的区别

这两种写法都会使显示出来的文本框不能输入文字, 但disabled会使文本框变灰,而且通过通过表单提交时,获取不到文本框中的value值(如果有的话), 而readonly只是使文本框不能输入,外观没...

少年已不再年少
50分钟前
2
0
SpringBoot上传图片操作

首先有个上传文件的工具类 /** * 文件上传 * @param file * @param filePath * @param fileName * @throws Exception */public static void uploadFile(byte[] file, String ...

_liucui_
今天
6
0
DrawerLayout

public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener,OnFragmentInteractionListener{ public NavigationView navView; ......

安卓工程师王恒
今天
1
0
python精简笔记

python精简笔记-字符串基本用法 字符串常见用法: * encode() # 编码成bytes类型 * find() # 查找子串 * index() # 获取下标 * replace() # 替换子串 * len(string) # 返回字符串长度,...

平头哥-Enjoystudy
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部