【spring框架】XML配置AOP

1 前言

        在 AspectJ动态代理实现AOP 中,笔者介绍了使用 AspectJ 动态代理实现 AOP,本文将介绍使用 xml 文件配置AOP。

        在 xml 文件中,可以使用 aop:config 标签配置 AOP,常用的属性如下:

  • aop:aspect:配置切面
  • aop:pointcut:配置切入点
  • aop:before:前置通知,作用于方法执行之前
  • aop:after:后置通知,作用于方法的finally语句块,即不管方法有没有异常都会执行,通常用于关闭资源
  • aop:after-returning:返回通知,作用于方法执行之后
  • aop:after-throwing:异常通知,作用于方法抛出异常时
  • aop:around:环绕通知

        注意:要想让 IOC 容器管理切面,还需要给切面和被代理类标注 @Component,其用法见\to通过注解配置bean

        需要导入的包如下:

        其中,com 开头的包为 AspectJ 动态代理核心包,下载如下:

        com.springsource.net.sf.cglib-2.2.0com.springsource.org.aopalliance-1.0.0com.springsource.org.aspectj.weaver-1.7.2.RELEASE

2 案例

2.1 前置通知、后置通知、返回通知、异常通知

        Comp.java

package com.compute;
 
public interface Comp {
	public int add(int a,int b);
	public int div(int a,int b);
}

        CompImp.java

package com.compute;
 
import org.springframework.stereotype.Component;
 
@Component
public class CompImp implements Comp{
 
	@Override
	public int add(int a, int b) {
		return a+b;
	}
 
	@Override
	public int div(int a, int b) {
		return a/b;
	}
}

        注意:CompImp 类前加 @Component 注解,是为了将 bean 交给 IOC 容器管理 

        Logger.java

package com.compute;

import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Component;

@Component
public class Logger {
	
	public void beforeMethod(JoinPoint joinPoint) {
		Object[] args=joinPoint.getArgs();
		String methodName=joinPoint.getSignature().getName();
		System.out.println("前置通知:method:"+methodName+", args:"+Arrays.toString(args));
	}
	
	public void afterMethod() {
		System.out.println("后置通知");
	}
	
	public void afterReturning(JoinPoint joinPoint,Object result) {
		String methodName=joinPoint.getSignature().getName();
		System.out.println("返回通知:method:"+methodName+", result:"+result);
	}
	
	public void afterThrowing(Exception e) {
		System.out.println("异常通知:"+e);
	}
}

         注意:Logger 类前加 @Component 注解,是为了将 bean 交给 IOC 容器管理 

        comp.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:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
	<context:component-scan base-package="com.compute"></context:component-scan>
	<aop:config>
		<aop:aspect ref="logger">
			<aop:before method="beforeMethod" pointcut="execution(* com.compute.*.*(..))"/>
			<aop:after method="afterMethod" pointcut="execution(* com.compute.*.*(..))"/>
			<aop:after-returning method="afterReturning" pointcut="execution(* com.compute.*.*(..))" returning="result"/>
			<aop:after-throwing method="afterThrowing" pointcut="execution(* com.compute.*.*(..))" throwing="e"/>
		</aop:aspect>
	</aop:config>
</beans>

        注意:需要导入 context 和 aop 命名空间;context:component-scan 用于扫描组件,即对标有 @Compnent 的类生成 bean 交给 IOC 容器管理;aop:config 用于配置 AOP;method 指定通知调用的方法;pointcut 指定切入点,* com.compute.*.*(..) 表示该通知作用于 com.compute 包下的所有类的所有方法。如果只需给 CompImp 类的 add 方法添加通知,如下:

<aop:before method="beforeMethod" pointcut="execution(public int com.compute.CompImp.add(int, int))"/>

        Test.java

package com.compute;

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

public class Test {
	public static void main(String[] args) {
		ApplicationContext ac=new ClassPathXmlApplicationContext("comp.xml");
		Comp comp=ac.getBean("compImp",Comp.class);
		System.out.println(comp.getClass().getName()); //打印代理类的类名
		int result=comp.add(1,2);
		System.out.println(result);
		result=comp.div(2,0);
		System.out.println(result);
	}
}

        运行结果:

com.sun.proxy.$Proxy7
前置通知:method:add, args:[1, 2]
后置通知
返回通知:method:add, result:3
3
前置通知:method:div, args:[2, 0]
后置通知
异常通知:java.lang.ArithmeticException: / by zero

        拓展延伸

        当有很多通知作用于同一切入点时,可以通过 aop:pointcut 指定公共切入点,如下:

        comp.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:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
	<context:component-scan base-package="com.compute"></context:component-scan>
	<aop:config>
		<aop:aspect ref="logger">
			<aop:pointcut expression="execution(* com.compute.*.*(..))" id="pcut"/>
			<aop:before method="beforeMethod" pointcut-ref="pcut"/>
			<aop:after method="afterMethod" pointcut-ref="pcut"/>
			<aop:after-returning method="afterReturning" pointcut-ref="pcut" returning="result"/>
			<aop:after-throwing method="afterThrowing" pointcut-ref="pcut" throwing="e"/>
		</aop:aspect>
	</aop:config>
</beans>

2.2 环绕通知

        本节仅介绍 Logger.java 和 comp.xml,其他类见2.1节

        Logger.java

package com.compute;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

@Component
public class Logger {
	
	public Object aroundMethod(ProceedingJoinPoint joinPoint) {
		Object result=null;
		try {
			System.out.println("前置通知");
			result=joinPoint.proceed(); //执行方法
			System.out.println("返回通知");
			return result;
		} catch (Throwable e) {
			System.out.println("异常通知");
			e.printStackTrace();
		}finally {
			System.out.println("后置通知");
		}
		return -1;
	}
}

        comp.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:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
	<context:component-scan base-package="com.compute"></context:component-scan>
	<aop:config>
		<aop:aspect ref="logger">
			<aop:around method="aroundMethod" pointcut="execution(* com.compute.*.*(..))"/>
		</aop:aspect>
	</aop:config>
</beans>

        运行结果:

com.sun.proxy.$Proxy7
前置通知
返回通知
后置通知
3
前置通知
异常通知
java.lang.ArithmeticException: / by zero
	at com.compute.CompImp.div(CompImp.java:15)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
	at com.compute.Logger.aroundMethod(Logger.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:68)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
	at com.sun.proxy.$Proxy7.div(Unknown Source)
	at com.compute.Test.main(Test.java:13)
后置通知
-1

2.3 切面优先级

        当有多个切面时,可以通过 aop:aspect 标签的 order 属性定义切面的优先级。

本节仅介绍LoggerA.java、LoggerB.java、comp.xml 以及 Test.java,Comp.java、CompImp.java见2.1节。

        LoggerA.java

package com.compute;

import org.springframework.stereotype.Component;

@Component
public class LoggerA {
	
	public void beforeMethod() {
		System.out.println("前置通知:LoggerA");
	}
}

        LoggerB.java

package com.compute;

import org.springframework.stereotype.Component;

@Component
public class LoggerB {
	
	public void beforeMethod() {
		System.out.println("前置通知:LoggerA");
	}
}

        comp.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:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
	<context:component-scan base-package="com.compute"></context:component-scan>
	<aop:config>
		<aop:aspect ref="loggerA" order="2">
			<aop:before method="beforeMethod" pointcut="execution(* com.compute.*.*(..))"/>
		</aop:aspect>
		<aop:aspect ref="loggerB" order="1">
			<aop:before method="beforeMethod" pointcut="execution(* com.compute.*.*(..))"/>
		</aop:aspect>
	</aop:config>
</beans>

        Test.java

package com.compute;

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

public class Test {
	public static void main(String[] args) {
		ApplicationContext ac=new ClassPathXmlApplicationContext("comp.xml");
		Comp comp=ac.getBean("compImp",Comp.class);
		int result=comp.add(1,2);
		System.out.println(result);
	}
}

        运行结果:

前置通知:LoggerB
前置通知:LoggerA
3

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

little_fat_sheep

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

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

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

打赏作者

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

抵扣说明:

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

余额充值