Spring与AOP系列二:通知(Advice)

本文详细解析了AOP(面向切面编程)的通知机制,包括前置通知、后置通知、环绕通知和异常通知的实现原理与应用。通过具体示例展示了如何在Spring框架下配置这些通知,以及它们在目标方法执行前后的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通知(Advice),切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。常用通知有:前置通知、后置通知、环绕通知、异常处理通知。

1.通知的用法步骤

  1. 定义目标类
  2. 定义通知类
  3. 注册目标类
  4. 注册通知切面
  5. 注册代理工厂Bean类对象ProxyFactoryBean
  6. 客户端访问动态代理对象
  7. 在容器中的整体配置

2.通知详解

(1)前置通知MethodBeforeAdvice

定义前置通知,需要实现MethodBeforeAdvice接口。该接口中有一个方法before(),会在目标方法执行之前执行。

前置通知的特点:

  • 在目标方法执行之前先执行。
  • 不改变目标方法的执行流程,前置通知代码不能阻止目标方法执行。
  • 不改变目标方法执行的结果。

目标类:

package com.lmm.aop01;

// 目标类
public class SomeServiceImpl implements ISomeService {

	@Override
	public void doFirst() {
		System.out.println("执行doFirst()方法");
	}

	@Override
	public void doSecond() {
		System.out.println("执行doSecond()方法");
	}
}

定义前置通知类:

package com.lmm.aop01;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

// 前置通知
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {

	// 当前方法在目标方法执行之前执行
	// method:目标方法
	// args:目标方法的参数列表
	// target:目标对象
	@Override
	public void before(Method method, Object[] args, Object target)
			throws Throwable {
		// 对于目标方法的增强代码就应该写在这里
		System.out.println("执行前置通知方法");
	}

}

配置文件配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 注册目标对象 -->
    <bean id="someService" class="com.lmm.aop01.SomeServiceImpl"/>
    
    <!-- 注册切面:通知 -->
    <bean id="myAdvice" class="com.lmm.aop01.MyMethodBeforeAdvice"/>
    
    <!-- 生成代理对象 -->
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<!-- <property name="targetName" value="someService"/> 用的是对象名-->
    	<!-- 指定目标对象  用的是对象-->
    	<property name="target" ref="someService"/>
    	<!-- 指定切面 -->
    	<property name="interceptorNames" value="myAdvice"/>
    </bean>

</beans>

测试类:

package com.lmm.aop01;

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

public class MyTest {

	@Test
	public void test01() {
		// 创建容器对象,加载Spring配置文件
		String resource = "com/lmm/aop01/applicationContext.xml";
		ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
		ISomeService service = (ISomeService) ac.getBean("serviceProxy");
		service.doFirst();
		System.out.println("==================");
		service.doSecond();
	}

}

(2)后置通知AfterReturningAdvice

定义后置通知,需要实现接口AfterReturningAdvice。该接口中有一个方法afterReturning(),会在目标方法执行之后执行。后置通知的特点:

  • 在目标方法执行之后执行。
  • 不改变目标方法的执行流程,后置通知代码不能阻止目标方法执行。
  • 不改变目标方法执行的结果。
  • 后置通知可以获取到目标方法的返回结果,但无法改变目标方法的结果。

实现方法和前置通知类似,不在赘述!不过比前值方法多了一个返回值记录一下:

package com.lmm.aop02;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

// 后置通知:可以获取到目标方法的返回结果,但无法改变目标方法的结果
public class MyAfterReturningAdvice implements AfterReturningAdvice {

	// 在目标方法执行之后执行
	// returnValue:目标方法的返回值
	@Override
	public void afterReturning(Object returnValue, Method method,
			Object[] args, Object target) throws Throwable {
		System.out.println("执行后置通知方法  returnValue = " + returnValue);
		if (returnValue != null) {
			returnValue = ((String) returnValue).toUpperCase();
			System.out.println("修改过的结果  returnValue = " + returnValue);
		}
	}

}

 

(3)环绕通知MethodInterceptor

定义环绕通知,需要实现MethodInterceptor接口。环绕通知,也叫方法拦截器,可以在目标方法调用之前及之后做处理,可以改变目标方法的返回值,也可以改变程序执行流程

注意, org.aopalliance.intercept.MethodInterceptor才是需要的包。(jar包不是Spring框架的,而是AOP联盟的)

package com.lmm.aop03;

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

// 环绕通知:可以修改目标方法的返回结果
public class MyMethodInterceptor implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println("执行环绕通知:目标方法执行之前");
		// 执行目标方法
		Object result = invocation.proceed();
		System.out.println("执行环绕通知:目标方法执行之后");
		if(result != null) {
			//可以改变目标方法的返回值
			result = ((String)result).toUpperCase();
		}
		return result;
	}

}

(4)异常通知ThrowsAdvice

定义异常通知,需要实现ThrowsAdvice接口。该接口的主要作用是,在目标方法抛出异常后,根据异常的不同做出相应的处理。当该接口处理完异常后,会简单地将异常再次抛出给目标方法。

不过,这个接口较为特殊,从形式上看,该接口中没有必须要实现的方法。但,这个接口却确实有必须要实现的方法afterThrowing()。这个方法重载了四种形式。由于使用时,一般只使用其中一种,若要都定义到接口中,则势必要使程序员在使用时必须要实现这四个方法。这是很麻烦的。所以就将该接口定义为了标识接口(没有方法的接口)。

测试程序

(5)通知的其它用法

(1)给目标方法织入多个切面

若要给目标方法织入多个切面,则需要在配置代理对象的切面属性时,设定为list。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 注册目标对象 -->
    <bean id="someService" class="com.lmm.aop06.SomeServiceImpl"/>
    
    <!-- 注册切面:通知 -->
    <bean id="myBeforeAdvice" class="com.lmm.aop06.MyMethodBeforeAdvice"/>
    <bean id="myAfterAdvice" class="com.lmm.aop06.MyAfterReturningAdvice"/>
    
    <!-- 生成代理对象 -->
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<property name="target" ref="someService"/>
    	<property name="interceptorNames" value="myBeforeAdvice,myAfterAdvice"/>
    	<!-- <property name="interceptorNames">
    		<array>
    			<value>myBeforeAdvice</value>
    			<value>myAfterAdvice</value>
    		</array>
    	</property> -->
    </bean>

</beans>

(2)无接口的CGLIB代理生成

前面调用的都是jdk的Proxy代理,若不存在接口,则ProxyFactoryBean会自动采用CGLIB方式生成动态代理。

(有接口自动选择jdk的Proxy代理,无接口自动选择CGLIB方式生成动态代理)

(3)有接口的CGLIB代理生成- proxyTargetClass属性

  • 若存在接口,但又需要使用CGLIB生成代理对象,此时,只需要在配置文件中增加一个proxyTargetClass属性设置,用于指定强制使用CGLIB代理机制。
  • 也可指定optimize(优化)的值为true,强制使用CGLIB代理机制。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 注册目标对象 -->
    <bean id="someService" class="com.lmm.aop08.SomeServiceImpl"/>
    
    <!-- 注册切面:通知 -->
    <bean id="myAdvice" class="com.lmm.aop08.MyAfterReturningAdvice"/>
    
    <!-- 生成代理对象 -->
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<property name="target" ref="someService"/>
    	<property name="interceptorNames" value="myAdvice"/>
    	<!-- 也可指定optimize(优化)的值为true,强制使用CGLIB代理机制。 -->
    	<property name="optimize" value="true"/>
    	<!-- 在配置文件中增加一个proxyTargetClass属性设置,用于指定强制使用CGLIB代理机制 -->
    	<!-- <property name="proxyTargetClass" value="true"/> -->
    </bean>

</beans>

参考:北京动力节点视频课

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值