springAop之xml

xml方式AOP快速入门

2、准备目标类、准备增强类,并配置给Spring管理
在这里插入图片描述
3、配置切点表达式(哪些方法被增强)
4、配置织入(切点被哪些通知方法增强,是前置增强还是后置增强)

<aop:config>
	<!--配置切点表达式,对哪些方法进行增强-->
	<aop:pointcut id="myPointcut" expression="execution(void com.itheima.service.impl.UserServiceImpl.show1())"/>
	<!--切面=切点+通知-->
	<aop:aspect ref="myAdvice">
		<!--指定前置通知方法是beforeAdvice-->
		<aop:before method="beforeAdvice" pointcut-ref="myPointcut"/>
		<!--指定后置通知方法是afterAdvice-->
		<aop:after-returning method="afterAdvice" pointcut-ref="myPointcut"/>
	</aop:aspect>
</aop:config>

写法二

<aop:config>
	<!--配置切点表达式,对哪些方法进行增强-->
	<aop:pointcut id="myPointcut" expression="execution(void com.itheima.service.impl.UserServiceImpl.show1())"/>
	<!--切面=切点+通知-->
	<aop:aspect ref="myAdvice">
		<!--指定前置通知方法是beforeAdvice-->
		<aop:before method="beforeAdvice" pointcut-ref="myPointcut"/>
		<!--指定后置通知方法是afterAdvice-->
		<aop:after-returning method="afterAdvice" pointcut="execution(void com.itheima.service.impl.UserServiceImpl.show1())"/>
	</aop:aspect>
</aop:config>

xml方式AOP配置详解
切点表达式是配置要对哪些连接点(哪些类的哪些方法)进行通知的增强,语法如下:
execution([访问修饰符]返回值类型 包名.类名.方法名(参数))
其中,
⚫ 访问修饰符可以省略不写;
⚫ 返回值类型、某一级包名、类名、方法名 可以使用 * 表示任意;
⚫ 包名与类名之间使用单点 . 表示该包下的类,使用双点 … 表示该包及其子包下的类;
⚫ 参数列表可以使用两个点 … 表示任意参数
xml方式AOP配置详解
切点表达式举几个例子方便理解

//表示访问修饰符为public、无返回值、在com.itheima.aop包下的TargetImpl类的无参方法show
execution(public void com.itheima.aop.TargetImpl.show())
//表述com.itheima.aop包下的TargetImpl类的任意方法
execution(* com.itheima.aop.TargetImpl.*(..))
//表示com.itheima.aop包下的任意类的任意方法
execution(* com.itheima.aop.*.*(..))
//表示com.itheima.aop包及其子包下的任意类的任意方法
execution(* com.itheima.aop..*.*(..))
//表示任意包中的任意类的任意方法
execution(* *..*.*(..)

AspectJ的通知由以下五种类型

通知名称     配置方式       			执行时机
前置通知 < aop:before >          目标方法执行之前执行
后置通知 < aop:after-returning > 目标方法执行之后执行,目标方法异常时,不在执行
环绕通知 < aop:around > 			目标方法执行前后执行,目标方法异常时,环绕后方法不在执行
异常通知 < aop:after-throwing >  目标方法抛出异常时执行
最终通知 < aop:after > 			不管目标方法是否有异常,最终都会执行	

环绕通知

public void around(ProceedingJoinPoint joinPoint) throws Throwable {
	//环绕前
	System.out.println("环绕前通知");
	//目标方法
	joinPoint.proceed();
	///环绕后
	System.out.println("环绕后通知");
}
<aop:around method="around" pointcut-ref="myPointcut"/>

异常通知,当目标方法抛出异常时,异常通知方法执行,且后置通知和环绕后通知不在执

public void afterThrowing(){
	System.out.println("目标方法抛出异常了,后置通知和环绕后通知不在执行");
}
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/>

最终通知,类似异常捕获中的finally,不管目标方法有没有异常,最终都会执行的通知

public void after(){
	System.out.println("不管目标方法有无异常,我都会执行");
}
<aop:after method="after" pointcut-ref="myPointcut"/>

通知方法在被调用时,Spring可以为其传递一些必要的参数

参数类型 			 作用
JoinPoint 			 连接点对象,任何通知都可使用,可以获得当前目标对象、目标方法参数等信息
ProceedingJoinPoint  JoinPoint子类对象,主要是在环绕通知中执行proceed(),进而执行目标方法
Throwable 			 异常对象,使用在异常通知中,需要在配置文件中指出异常对象名称

JoinPoint 对象

public void 通知方法名称(JoinPoint joinPoint){
	//获得目标方法的参数
	System.out.println(joinPoint.getArgs());
	//获得目标对象
	System.out.println(joinPoint.getTarget());
	//获得精确的切点表达式信息
	System.out.println(joinPoint.getStaticPart());
}

ProceedingJoinPoint对象

public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
	System.out.println(joinPoint.getArgs());//获得目标方法的参数
	System.out.println(joinPoint.getTarget());//获得目标对象
	System.out.println(joinPoint.getStaticPart());//获得精确的切点表达式信息
	Object result = joinPoint.proceed();//执行目标方法
	return result;//返回目标方法返回值
}

Throwable对象

public void afterThrowing(JoinPoint joinPoint,Throwable th){
//获得异常信息
System.out.println("异常对象是:"+th+"异常信息是:"+th.getMessage());
}
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="th"/>

基于注解配置的AOP

注解方式AOP配置,该方式需要通知类实现Advice的子功能接口

public interface Advice {
}

在这里插入图片描述
例如:通知类实现了前置通知和后置通知接口

public class Advices implements MethodBeforeAdvice, AfterReturningAdvice {
	public void before(Method method, Object[] objects, Object o) throws Throwable {
		System.out.println("This is before Advice ...");
	}
	public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
		System.out.println("This is afterReturn Advice ...");
	}
}

切面使用advisor标签配置

<aop:config>
	<!-- 将通知和切点进行结合 -->
	<aop:advisor advice-ref="advices" pointcut="execution(void com.itheima.aop.TargetImpl.show())"/>
</aop:config>

通知类实现了方法拦截器接口

public class MyMethodInterceptor implements MethodInterceptor {
	@Override
	public Object invoke(MethodInvocation methodInvocation) throws Throwable {
		System.out.println("前置逻辑功能...");
		//执行目标方法
		Object invoke = methodInvocation.getMethod().invoke(methodInvocation.getThis(),methodInvocation.getArguments());
		System.out.println("后置逻辑功能...");
		return invoke;
	}
}

切面使用advisor标签配置

<aop:config>
	<!-- 将通知和切点进行结合 -->
	<aop:advisor advice-ref=“myMethodInterceptor" pointcut="execution(void com.itheima.aop.TargetImpl.show())"/>
</aop:config>

使用aspect和advisor配置区别如下:
1)配置语法不同:

<!-- 使用advisor配置 -->
<aop:config>
	<!-- advice-ref:通知Bean的id -->
	<aop:advisor advice-ref="advices" pointcut="execution(void com.itheima.aop.TargetImpl.show())"/>
</aop:config>

<!-- 使用aspect配置 -->
<aop:config>
	<!-- ref:通知Bean的id -->
	<aop:aspect ref="advices">
		<aop:before method="before" pointcut="execution(void com.itheima.aop.TargetImpl.show())"/>
	</aop:aspect>
</aop:config

2)通知类的定义要求不同,advisor 需要的通知类需要实现Advice的子功能接口:

public class Advices implements MethodBeforeAdvice {
	public void before(Method method, Object[] objects, Object o) throws Throwable {
		System.out.println("This is before Advice ...");
	}
	public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
		System.out.println("This is afterReturn Advice ...");
	}
}

aspect 不需要通知类实现任何接口,在配置的时候指定哪些方法属于哪种通知类型即可,更加灵活方便:

public class Advices {
	public void before() {
		System.out.println("This is before Advice ...");
	}
	public void afterReturning() {
		System.out.println("This is afterReturn Advice ...");
	}
}

3)可配置的切面数量不同:
一个advisor只能配置一个固定通知和一个切点表达式;
一个aspect可以配置多个通知和多个切点表达式任意组合,粒度更细。
4)使用场景不同:
如果通知类型多、允许随意搭配情况下可以使用aspect进行配置;
如果通知类型单一、且通知类中通知方法一次性都会使用到的情况下可以使用advisor进行配置;
在通知类型已经固定,不用人为指定通知类型时,可以使用advisor进行配置,例如后面要学习的Spring事务控制的配置;
所以实际开发中我们使用的更多的是aspect,所以接下来详解aspect

基于xml配置的AOP详解

通过xml方式配置AOP时,我们引入了AOP的命名空间,根据讲解的,要去找spring-aop包下的META-INF,在去
找spring.handlers文件
在这里插入图片描述
xml方式AOP原理剖析
最终加载的是 AopNamespaceHandler,该Handler的init方法中注册了config标签对应的解析器

this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());

以ConfigBeanDefinitionParser作为入口进行源码剖析,最终会注册一个AspectJAwareAdvisorAutoProxyCreator
进入到Spring容器中,那该类作用是什么呢?看一下集成体系图
在这里插入图片描述
AspectJAwareAdvisorAutoProxyCreator 的上上级父类AbstractAutoProxyCreator中的
postProcessAfterInitialization方法

//参数bean:为目标对象
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	if (bean != null) {
		Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
			//如果需要被增强,则wrapIfNecessary方法最终返回的就是一个Proxy对象
			return this.wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}

通过断点方式观察,当bean是匹配切点表达式时,this.wrapIfNecessary(bean, beanName, cacheKey)返回的是
一个JDKDynamicAopProxy
在这里插入图片描述
可以在深入一点,对wrapIfNecessary在剖析一下,看看是不是我们熟知的通过JDK的
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) 的方式创建的代理
对象呢?经过如下一系列源码跟踪

==> this.wrapIfNecessary(bean, beanName, cacheKey)
==> Object proxy = this.createProxy(参数省略)
==> proxyFactory.getProxy(classLoader)
==> this.createAopProxy().getProxy(classLoader)
==> getProxy()是一个接口方法,实现类有两个,如下截图
==> Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this

在这里插入图片描述
动态代理的实现的选择,在调用getProxy() 方法时,我们可选用的 AopProxy接口有两个实现类,如上图,这两种
都是动态生成代理对象的方式,一种就是基于JDK的,一种是基于Cglib的
在这里插入图片描述在这里插入图片描述
JDK的动态代理代码,之前已经写过了,下面看一下Cglib基于超类的动态代理

Target target = new Target();//目标对象
Advices advices = new Advices();//通知对象
Enhancer enhancer = new Enhancer();//增强器对象
enhancer.setSuperclass(Target.class);//增强器设置父类
//增强器设置回调
enhancer.setCallback((MethodInterceptor )(o, method, objects, methodProxy) -> {
	advices.before();
	Object result = method.invoke(target, objects);
	advices.afterReturning();
	return result;
});
//创建代理对象
Target targetProxy = (Target) enhancer.create();
//测试
String result = targetProxy.show("haohao");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOPSpring框架中的一个重要模块,它通过动态代理实现了面向切面编程的思想。下面是Spring AOPXML配置详解: 1. 配置命名空间和约束 在使用Spring AOP之前,需要在XML配置文件中声明Spring AOP的命名空间和约束,如下所示: ```xml <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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> ``` 2. 配置切面 在Spring AOP中,切面由切点和通知组成。切点定义了在哪些方法上进行拦截,通知定义了在拦截时要执行的逻辑。下面是一个切面的配置示例: ```xml <bean id="myAspect" class="com.example.MyAspect"> <property name="transactionManager" ref="txManager"/> </bean> <aop:config> <aop:aspect ref="myAspect"> <aop:pointcut id="serviceMethods" expression="execution(* com.example.Service.*(..))"/> <aop:before pointcut-ref="serviceMethods" method="beginTransaction"/> <aop:after-returning pointcut-ref="serviceMethods" method="commitTransaction"/> <aop:after-throwing pointcut-ref="serviceMethods" method="rollbackTransaction"/> </aop:aspect> </aop:config> ``` 上面的例子中,声明了一个名为“myAspect”的切面,并定义了一个名为“serviceMethods”的切点,拦截com.example.Service包中所有方法的执行。在拦截时,分别执行了beginTransaction()、commitTransaction()和rollbackTransaction()方法。 3. 配置通知 通知是切面中的一个组成部分,它定义了在什么时候执行切面的逻辑。Spring AOP支持五种类型的通知:前置通知、后置通知、返回通知、异常通知和环绕通知。下面是通知的配置示例: ```xml <aop:before pointcut="execution(* com.example.Service.*(..))" method="beforeAdvice"/> <aop:after-returning pointcut="execution(* com.example.Service.*(..))" method="afterReturningAdvice"/> <aop:after-throwing pointcut="execution(* com.example.Service.*(..))" method="afterThrowingAdvice"/> <aop:around pointcut="execution(* com.example.Service.*(..))" method="aroundAdvice"/> ``` 上面的例子中,分别配置了前置通知、返回通知、异常通知和环绕通知,它们都拦截com.example.Service包中的所有方法。在拦截时,分别执行了beforeAdvice()、afterReturningAdvice()、afterThrowingAdvice()和aroundAdvice()方法。 4. 配置引入 引入是Spring AOP中的一个特殊功能,它允许将额外的方法和属性添加到现有的类中,而不需要修改原始类的代码。下面是引入的配置示例: ```xml <aop:config> <aop:aspect ref="myAspect"> <aop:declare-parents types-matching="com.example.Service+" implement-interface="com.example.Transactional" default-impl="com.example.TransactionalImpl"/> </aop:aspect> </aop:config> ``` 上面的例子中,声明了一个名为“myAspect”的切面,并引入了接口com.example.Transactional,将其实现类指定为com.example.TransactionalImpl。这样,在运行时,com.example.Service类就自动实现了com.example.Transactional接口。 以上就是Spring AOPXML配置详解。在实际开发中,可以根据业务需求和实际情况选择合适的配置方式来实现切面编程。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值