Spring 面向方面编程 AOP

Spring AOP(面向方面编程)框架,用于在模块化方面的横切关注点。简单得说,它只是一个拦截器,用来拦截一些过程。例如,当一个方法执行,Spring AOP 可以劫持此执行方法,在方法执行之前或之后添加额外的功能。

在Spring AOP中,有 5 种类型通知(advices)的支持:

  • 前置通知(Before advice):在目标方法执行之前执行,前置通知不会影响目标方法的执行,除非此处抛出异常。
  • 正常返回通知(After returning advice):在目标方法执行完成后执行,如果目标方法抛出异常,则不会执行。目标方法执行后,该方法返回一个结果。
  • 异常返回通知(After throwing advice):在目标方法抛出异常后执行。
  • 返回通知(After advice):在目标方法执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。
  • 环绕通知(Around advice):环绕通知围绕在目标方法前后,即一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。

1. 前置通知

它会在方法执行之前执行。创建一个实现 MethodBeforeAdvice 接口的类。

public class ArticleServiceMethodBeforeAdvice implements MethodBeforeAdvice {

	public void before(Method method, Object[] args, Object target)	throws Throwable {
		System.out.println("Before " + method.getName() + " of " + target.getClass().getSimpleName() + " class.");
	}
}

在 bean 配置文件(Spring-Bean.xml)配置代理对象。

<bean id="articleDao" class="com.angelia.spring.dao.ArticleDaoImpl">
	<property name="dataSource" ref="dataSource" />
</bean>
<bean id="articleService" class="com.angelia.spring.service.ArticleServiceImpl"	scope="prototype">
	<property name="articleDao">
		<ref bean="articleDao" />
	</property>
</bean>

<bean id="articleServiceBeforeAdvice" class="com.angelia.spring.dao.aop.ArticleServiceMethodBeforeAdvice" />
<bean id="articleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="articleService" />
	<property name="interceptorNames">
		<list>
			<value>articleServiceBeforeAdvice</value>
		</list>
	</property>
</bean>

   ‘target’ – 定义你想拦截的bean。‘interceptorNames’ – 定义要应用这个代理/目标对象的类(通知)。

 2. 正常返回通知

该方法返回一个结果之后它将执行。创建一个实现AfterReturningAdvice接口的类。

public class ArticleServiceAfterReturningAdvice implements AfterReturningAdvice {
	
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		System.out.println("After return result of the " + method.getName() + " of " + target.getClass().getSimpleName() + " class.");
	}
}

bean配置文件

<bean id="articleServiceAfterAdvice" class="com.angelia.spring.dao.aop.ArticleServiceAfterReturningAdvice" />
<bean id="articleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="articleService" />
	<property name="interceptorNames">
		<list>
			<value>articleServiceBeforeAdvice</value>
			<value>articleServiceAfterAdvice</value>
		</list>
	</property>
</bean>

3. 异常返回通知

它将在执行方法抛出一个异常后。创建一个实现ThrowsAdvice接口的类,并创建一个afterThrowing方法拦截抛出:IllegalArgumentException异常

public class ArticleServiceThrowsAdvice implements ThrowsAdvice {
	
	public void afterThrowing(IllegalArgumentException e) throws Throwable {
		System.out.println("ArticleServiceThrowsAdvice : Throw exception in ArticleServiceImpl!");
	}
}

 bean配置文件

<bean id="articleServiceThrowsAdvice" class="com.angelia.spring.dao.aop.ArticleServiceThrowsAdvice" />
<bean id="articleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="articleService" />
	<property name="interceptorNames">
		<list>
			<value>articleServiceBeforeAdvice</value>
			<value>articleServiceAfterAdvice</value>
			<value>articleServiceThrowsAdvice</value>
		</list>
	</property>
</bean>

4. 返回通知

该方法执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。创建一个实现AfterAdvice接口的类。

public class ArticleServiceAfterAdvice implements AfterAdvice {
	public void after(Method method, Object[] args, Object target)	throws Throwable {
		System.out.println("After " + method.getName() + " of " + target.getClass().getSimpleName() + " class.");
	}
}

bean配置

<bean id="articleServiceAfterAdvice" class="com.angelia.spring.dao.aop.ArticleServiceAfterAdvice" />
<bean id="articleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="articleService" />
	<property name="interceptorNames">
		<list>
			<value>articleServiceAfterAdvice</value>
		</list>
	</property>
</bean>

5. 环绕通知

环绕通知结合了上面的三个通知,在方法执行过程中执行。创建一个实现了MethodInterceptor接口的类。

public class ArticleServiceAroundMethod implements MethodInterceptor {

	public Object invoke(MethodInvocation invocation) throws Throwable {
		
		// MethodBeforeAdvice
		System.out.println("ArticleServiceMethodBeforeAdvice...");
		try {
			// 执行原来的方法
			Object result = invocation.proceed();
			// AfterReturningAdvice
			System.out.println("ArticleServiceAfterReturningAdvice...");

			return result;

		} catch (IllegalArgumentException e) {
			// ThrowsAdvice
			System.out.println("ArticleServiceThrowsAdvice...");
			throw e;
		}
	}
}

必须调用“invocation.proceed();” 继续在原来的方法执行,否则原来的方法将不会执行。

bean配置文件

<bean id="articleServiceAroundMethod" class="com.angelia.spring.dao.aop.ArticleServiceAroundMethod" />
<bean id="articleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="articleService" />
	<property name="interceptorNames">
		<list>
			<!-- <value>articleServiceBeforeAdvice</value>
			<value>articleServiceAfterAdvice</value>
			<value>articleServiceThrowsAdvice</value> -->
			<value>articleServiceAroundMethod</value>
		</list>
	</property>
</bean>

在每一个 ArticleService 方法执行后,将运行 ArticleServiceAroundMethod 的 invoke() 方法。

6. Spring AOP 切入点 (Pointcut,Advisor)

上面的Spring AOP通知的例子,一个类的整个方法被自动拦截。但在大多数情况下,可能只需要一种方式来拦截部分方法,这就是为什么引入'切入点'的原因。它允许你根据method的名字去拦截指定的method。另外,一个Pointcut必须结合一个Advisor来使用。

在Spring AOP中,有3个常用的概念,Advices、Pointcut、Advisor。

  • Advices:表示一个method执行前或执行后的动作。
  • Pointcut:表示根据method的名字或者正则表达式去拦截一个method。
  • Advisor:Advice和Pointcut组成的独立的单元,并且能够传给proxy factory 对象。

前面的例子,ArticleServiceImpl中全部的method方法全部被拦截了,下边将利用Pointcuts只拦截queryArticleById()。我们可以用名字匹配法和正则表达式匹配法去匹配要拦截的method。

切入点 - 名称匹配

创建一个NameMatchMethodPointcut的bean,将你想拦截的方法的名字queryArticleById注入到属性mappedName。创建一个DefaultPointcutAdvisor的advisor bean,将pointcut和advice关联起来。

<bean id="articleServiceBeforeAdvice" class="com.angelia.spring.dao.aop.ArticleServiceMethodBeforeAdvice" />

<bean id="articlePointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">
	<property name="mappedName" value="queryArticleById" />
</bean>
<bean id="articleAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
	<property name="pointcut" ref="articlePointcut" />
	<property name="advice" ref="articleServiceBeforeAdvice" />
</bean>

<bean id="articleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="articleService" />
	<property name="interceptorNames">
		<list>
			<!-- <value>articleServiceBeforeAdvice</value> -->
			<value>articleAdvisor</value>			
		</list>
	</property>
</bean>

以上配置中pointcut和advisor可以合并在一起配置,即不用单独配置articlePointcut和articleAdvisor,只要配置articleAdvisor时class选择NameMatchMethodPointcutAdvisor如下:

<bean id="articleAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
	<property name="mappedName" value="queryArticleById" />
        <property name="advice" ref="articleServiceBeforeAdvice" />
</bean>

实际上这种做法将 method 名字与具体的advice捆绑在一起,有悖于 Spring 松耦合理念,如果将 method 名字单独配置成pointcut(切入点),advice和pointcut的结合会更灵活,使一个pointcut可以和多个advice结合。

切入点 - 正则表达式

可以用正则表达式匹配需要拦截的method。

<bean id="articleAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <property name="patterns">
        <list>
            <value>.*Article.*</value>
        </list>
    </property>
    <property name="advice" ref="articleServiceBeforeAdvice" />
</bean>

现在,你可以拦截名字中包含Article字符的method了。

7. Spring自动创建代理

前面的 Spring AOP例子 – advice, pointcut 和 advisor, 必须手动创建一个代理bean(ProxyFactryBean),对每个Bean需要AOP支持。这显然不是一种有效的方式。例如,如果想在客户模块,所有的Servic类实现SQL日志支持的AOP功能,那么必须手动创建很多代理工厂bean,因此在 bean配置文件可能会泛滥代理类。幸运的是,Spring有两个自动代理创建者来自动创建代理bean。

BeanNameAutoProxyCreator

自动代理机制,只需要创建一个的 BeanNameAutoProxyCreator,并包含所有你的bean(通过bean的名字,或正则表达式名)和“advisor” 作为一个单位。

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
	<property name="beanNames">
		<list>
			<value>*Service</value>
		</list>
	</property>
	<property name="interceptorNames">
		<list>
			<value>articleAdvisor</value>
		</list>
	</property>
</bean>

现在,可以通过“articleService”的原始名称获取bean, 如果知道这个bean已经代理。

DefaultAdvisorAutoProxyCreator

DefaultAdvisorAutoProxyCreator 是非常强大的,如果有 bean 相关连,Spring会自动创建一个代理。

<bean id="articleServiceBeforeAdvice" class="com.angelia.spring.dao.aop.ArticleServiceMethodBeforeAdvice" />
<bean id="articleAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
	<property name="mappedName" value="queryArticleById" />
	<property name="advice" ref="articleServiceBeforeAdvice" />
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AngeliaZheng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值