天天看点

spring 之 aop

AOP面向切面编程弥补了面向对象编程

连接点(Joinpoint): 在何处加入逻辑功能,对于Spring AOP,Jointpoint指的就是Method

通知(Advice): 在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。

切入点(Pointcut): 匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行

引入(Introduction): 声明额外的方法或者某个类型的字段。 Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象

通知的类型:

前置通知(Before advice): 在某连接点(join point)之前执行的通知

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

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

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

环绕通知(Around Advice): 包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行,环绕通知是最常用的一种通知类型。大部分基于拦截的AOP框架,例如Nanning和JBoss4,都只提供环绕通知。

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

看下面的一个例子:

业务类

public class PersonServiceBean {
	public String save(String name){
		System.out.println("save()方法");
		return "test";
	}
	public void update(String name,int id) {
		System.out.println("update()方法");
	}
}
           

切面类:

@Aspect  //定义一个切面 如果用xml方式则这里的所有注解都不需要
public class MyInteceptor {
	@Pointcut("execution (* com.test.service.impl.PersonServiceBean.*(..))")
	private void anyMethod(){}//定义一个切入点
	
	@Before("anyMethod() && args(name)") //满足切入点的条件且参数只有一个并为String name
	public void before(String name){
		System.out.println("前置通知:"+name);
	}
	
	@AfterReturning(pointcut="anyMethod()",returning="result")//获取返回的值
	public void afterReturn(String result){
		System.out.println("后置通知:"+result);
	}
	
	@After("anyMethod()")
	public void after(){
		System.out.println("最终通知");
	}
	
	@Around("anyMethod()") //环绕通知
	public Object doBasic(ProceedingJoinPoint p) throws Throwable{
		System.out.println("进入方法");//可以写一些逻辑
		Object result=p.proceed();
		System.out.println("退出方法");
		return result;
	}
}
 
           

beans.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:context="http://www.springframework.org/schema/context"
     xmlns:tx="http://www.springframework.org/schema/tx"
     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/context 
     http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/tx
     http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
     http://www.springframework.org/schema/aop 
     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

	<context:annotation-config />  <!--  Activates various annotations to be detected in bean classes -->

	<aop:aspectj-autoproxy/>      <!--Enables the use of the @AspectJ style of Spring AOP -->
	
	<bean id="personService" class="com.test.service.impl.PersonServiceBean"/>
	<bean id="myInteceptor" class="com.test.service.impl.MyInteceptor"/><!-- 定义 的切面也必须交给spring管理 -->
</beans> 
           

调用save()方法,输出以下结果:

进入方法

前置通知:abc

save()方法

后置通知:test

退出方法

最终通知

如果是xml配置,则xml如下配置:

<context:annotation-config />  <!--  Activates various annotations to be detected in bean classes -->

	<aop:aspectj-autoproxy/>      <!--Enables the use of the @AspectJ style of Spring AOP -->
	
	<bean id="personService" class="com.test.service.impl.PersonServiceBean"/>
	<bean id="myInteceptor" class="com.test.service.impl.MyInteceptor"/><!-- 定义 的拦截器也必须交给spring管理 -->
	
	<aop:config>
		<aop:aspect id="asp" ref="myInteceptor">
			<aop:pointcut id="mycut" expression="execution (* com.test.service.impl.PersonServiceBean.*(..))"/>
			<aop:before method="before" pointcut-ref="mycut"/>
		</aop:aspect>
	</aop:config>
           

Aspectj切入点语法定义

例如这个定义 expression="execution(* com.test.spring.. *.*(..))"

整个表达式分为五个部分:

1) execution(): 表达式主体;

2) 第一个*号: 表示返回类型, *号表示所有的类型;

3) 包名: 表示需要拦截的包名, 后面的两个句点表示当前包和当前包的所有子包;

4) 第二个*号: 表示类名, *号表示所有的类;

5) *(..): 最后这个星号表示方法名, *号表示所有的方法, 后面括弧里面表示方法的参数, 两个句点表示任何参数.