Spring中的AOP实现方式

本文章列举的 aop 方式共 7 种

  • 底层:JDK,CGLIB
  • 非自动创建代理
    • 面向切面:Advisor
    • 面向切点:PointcutAdvisor
  • 自动创建代理
    • 面向切面:BeanNameAutoProxyCreator
    • 面向切点:DefaultAdvisorAutoProxyCreator
    • 重点推荐:AspectJ:注解方式 + XML方式

AOP概述

  • Aspect Oriented Programing 面向切面编程
  • AOP 采取横向抽取机制,取代传统纵向继承体系重复性代码(AOP可以实现 性能监视、事务管理、安全检查、缓存…)

相关术语

  • Joinpoint(连接点):指被拦截到的点(方法)。在spring中,这些点指的是方法,spring只支持方法类型的连接点
  • Pointcut(切入点):指要对哪些连接点进行拦截的定义,即连接点中真正要操作到的
  • Advice(通知/增强):指拦截到连接点之后要多的事情
    • 前置通知
    • 后置通知
    • 异常通知
    • 最终通知
    • 环绕通知
  • Introduction(引介):一种特殊的通知,在不修改类代码的前提下,引介可以在运行期为类动态地添加一些方法或 Field
  • Target(目标对象):代理的目标对象
  • Weaving(织入):把增强应用到目标对象来创建新的代理对象的过程
    • spring 采用动态代理织入,AspectJ 采用编译期织入类装载期织入
  • Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类
  • Aspect(切面):切入点和通知(引介)的结合

图示
在这里插入图片描述

AOP 底层实现

基于JDK的动态代理

只能对实现了接口的类生成代理

所需继承类:InvocationHandler -> invoke()

代码演示
实现在 save()方法执行前进行权限验证

public class MyJdkProxy implements InvocationHandler{
	private Object object;
	
	public MyJdkProxy(Object object){
		this.object = object;
	}
	
	public Object createProxy(){
		Object proxy = Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),this);
		return proxy;
	}
	
	@Override
	public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
		if("save".equals(method.getName())){
			System.out.println("===权限校验===");
			return method.invoke(Object,args);
		}
		return method.invoke(object,args);
	}
}

部分重点
在这里插入图片描述
测试代理
在这里插入图片描述

使用CGLIB生成代理(spring)

  • 对于不使用接口的业务类,无法使用 JDK 动态代理
  • CGlib 采用非底层字节码技术,可以为一个类创建子类,解决无接口代理问题

代码演示
实现在 save()方法执行前进行权限验证

public class MyCglibProxy implements MethodInterceptor{
	private Object object;
	
	public Object createProxy(){
		//1.创建一个CGLIB的核心类
		Enhancer enhancer = new Enhancer();
		//2.设置父类
		enhancer.setSuperclass(object.getClass());
		//3.设置回调
		enhancer.setCallback(this);
		//4.生成代理
		Object proxy = enhancer.create();
		
		return proxy;
	}

	public Object intercept(Object proxy,Method method,Object[] args,MethodProxy methodProxy)throws Throwable{
		//不是save方法就放行
		if("save".equals(method.getName())){
			System.out.println("===权限校验===";
			return methodProxy.invokeSuper(proxy,args);
		}
		return methodProxy.invokeSuper(proxy,args);
	}
}

测试
在这里插入图片描述

小结

  • 程序中应优先对接口创建代理,便于程序解耦维护
  • 标记为 final 的方法,不能被代理,因为无法进行覆盖
  • JDK 动态代理是针对接口生成子类,接口中方法不能被 final 修饰
  • CGlib 是针对目标类生产子类,因此类或方法不能是 final 的
  • Spring 只支持方法连接点,不提供属性连接点

Spring AOP 增强类型

  • AOP 联盟为通知 Advice 定义了 org.aopalliance.aop.Interface.Advice
  • Spring 按照通知 Advice 在目标类方法的连接点位置,可以分为 5 类
    • 前置通知:MethodBeforeAdvice,在目标方法执行前实施增强
    • 后置通知:AfterReturningAdvice,在目标方法执行后实施增强
    • 环绕通知:MethodInterceptor,在目标方法执行前后实施增强
    • 异常抛出通知:ThrowsAdvice,在方法抛出异常后实施增强
    • 引介通知:IntroductionInterceptor,在目标类中添加一些新的方法和属性(了解)

Spring AOP切面类型

  • Advisor:一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截
  • PointcutAdvisor:代表具有切点的切面,可以指定拦截目标类哪些方法
  • IntroductionAdvisor:代表引介切面,针对引介通知而使用切面(了解)

Advisor 切面案例

ProxyFactoryBean

  • 常用可配置属性
    • target:代理的目标对象
    • proxyInterfaces:代理要实现的接口
      多个接口使用以下格式赋值
<list>
	<value></value>
	...
<list>

所需依赖
在这里插入图片描述
案例代码演示

基础类,基础接口,接口实现类略(无特殊功能)
在这里插入图片描述
编写spring配置文件,声明bean
在这里插入图片描述
在这里插入图片描述
引入spring测试类

在这里插入图片描述
测试
在这里插入图片描述

配置文件其他参数

<property name = "###" value = "###"/>
  • proxyTargetClass:是否对类代理而不是接口,设置为true时,使用 CGlib 代理
  • interceptorNames:需要织入目标的Advice(把增强应用到目标对象来创建新的代理对象的过程)
  • singleton:返回代理是否为单实例,默认为单例
  • optimize:设置为 true 时,强制使用 CGlib

PointcutAdvisor 切点切面

  • 使用普通 Advice 作为切面,将对目标类所有方法进行拦截,不够灵活,在实际开发中常采用带有切点的切面(即针对目标类的某个方法)
  • 常用 PointcutAdvisor 实现类
    • DefaultPointcutAdvisor :最常用的切面类型,他可以通过任意 Pointcut 和 Advice 组合定义切面
    • JdkRegexpMethodPointcut:构造正则表达式切点

案例

环绕通知 且 类不实现接口,针对某个方法进行通知增强

代码演示

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

public class MyArouondAdvice implements MethodInterceptor{
	public Object invoke(MethodInvocation invocation)throws Throwable{
		System.out.println("环绕前增强===");
		
		Object obj = invocation.proceed();

	System.out.println("环绕后增强===");

	return obj;
	}
}

配置文件:
.* save. *:针对 save 方法

name = “patterns” value = " .* save.*, . * delete. *":针对 save ,delete 方法
在这里插入图片描述
测试
在这里插入图片描述

自动创建代理

  • 在前面的案例中,每个代理都是通过 ProxyFactoryBean织入切面代理,在实际开发中,非常多的 Bean 每个都配置 ProxyFactoryBean 开发维护量巨大
  • 解决方案:自动创建代理
    • BeanNameAutoProxyCreator:根据 Bean 名称创建代理
    • DefaultAdvisorAutoProxyCreator:根据 Advisor 本身包含信息创建代理
    • AnnotationAwareAspectJAutoProxyCreator:基于 Bean 中的 Aspectj 注解进行自动代理

BeanNameAutoProxyCreator 案例

在这里插入图片描述
*Dao 是指配置文件里面的 id ,不是实际 java 类中的 Dao

缺点:无法针对某个方法进行增强,是对所有方法
在这里插入图片描述
测试
在这里插入图片描述

DefaultAdvisorAutoProxyCreator 举例

解决了不能针对某一方法进行增强的问题,即是带切点的切面
在这里插入图片描述
针对某个 Dao 的某一个方法的增强
在这里插入图片描述

基于AspectJ的AOP开发(推荐)

  • AspectJ 是一个基于 Java 语言的 AOP 框架
  • Spring 2.0 以后新增了对 AspectJ 切点表达式支持
  • @AspectJ 是 AspectJ 1.5 新增功能,通过 JDK 1.5 注解技术,允许直接在 Bean 类中定义切面
  • 新版本 Spring 框架,建议使用 AspectJ 方式来开发 AOP
  • 使用 AspectJ 需要导入 Spring AOP 和 AspectJ 相关 jar 包
    • spring-aop-4.2.4.RELEASE.jar
    • com.springsource.org.aopalliance-1.0.0.jar
    • spring-aspects-4.2.4.RELEASE.jar
    • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
    • 版本不要求一致

注解方式

需要配置文件引入下列
在这里插入图片描述
pom文件引入spring四个核心:

spring-core; spring-context; spring-beans; spring-expression(用于表达式:#{¥%};aop

除此之外,传统的依赖不可少,因为是依赖它的基础

顺便 Juit 跟 spring-test 也引入
在这里插入图片描述
自建 spring 配置文件
在这里插入图片描述
在这里插入图片描述
在通知中通过 value 属性定义切点

在这里插入图片描述
为目标类定义切面类,注意使用 @Aspect 注解声明切面类
在这里插入图片描述
配置文件
在这里插入图片描述
测试
在这里插入图片描述

切面类修改升级:得到相关信息 JoinPoint

  • 可以在方法中传入 JointPoint 对象,用来获取切点信息
//要增强的代码
@Before("execution(*com.###.UserDao.save(..))")
public void before(JoinPoint joinPoint){
	System.out.println("===前置通知==="+joinPoint);
}

后置通知 @AfteReturing 类似
在这里插入图片描述

环绕通知 @Around
在这里插入图片描述
ProceedingJoinPoint的proceed() 方法 表示执行目标方法,不进行其他操作

如果不调用该方法,表示 目标方法被拦截了,不执行

异常抛出通知 @AfterThrowing
在这里插入图片描述

最终通知 @After
在这里插入图片描述

切点命名

通过 @Pointcut 为切点命名

  • 在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用 @Pointcut 进行定义
  • 切点方法:private void 无参数方法,方法名为切点名
  • 当通知多个切点时,可以使用 || 进行连接

可以理解为 为某一个目标方法定义 id
在这里插入图片描述
将该 id 引用到具体通知类型方法上 value=“id”
在这里插入图片描述

XML 方式

需要引入的依赖于注解方式一样
演示:类有实现接口情况
在这里插入图片描述
测试
在这里插入图片描述
其他类型

后置类型后接收原方法的返回值,记得接收
在这里插入图片描述
在这里插入图片描述
xml文件配置
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值