Spring AOP

在这里插入图片描述

  • 在软件行业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程

  • 作用:在不修改目标代码的前提下,可以通过AOP技术去增强目标类的功能。通过【预编译】和【运行期动态代理】现实程序功能的统一维护的一种技术

  • AOP是一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构

  • AOP最早是由AOP联盟的组织提出的,制定了一套规范。Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范

  • AOP是OOP的延续,是软件开发中心的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型

  • 利用AOP可以对业务代码中【业务逻辑】和【系统逻辑】进行隔离,从而使得【业务逻辑】和【系统逻辑】之间的耦合度降低,提高程序的可用性,同时提高了开发的效率。
    为什么使用AOP

  • 作用:
    AOP采取横向隔离机制,补充了传统纵向继承体系(OOP)无法解决的重复性代码优化(性能监视、事务管理、安全检查、缓存),将业务逻辑和系统处理的代码(关闭连接、事物管理、操作日志记录)解耦。

  • 优势:
    重复性代码被抽取出来之后,维护更加方便

  • 纵向继承体系:
    在这里插入图片描述

  • 横向抽取机制
    在这里插入图片描述
    AOP相关术语介绍
    术语解释

  • Joinpoint(连接点)
    所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法,因为spring只支持方法类型的连接点。

  • Pointcut(切入点)
    所谓切入点指的是我们要对哪些Joinpoint进行拦截的定义

  • Advice(增强/通知)
    所谓通知是指拦截到Joinpoint之后所要做的事请就是通知。通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

  • Introduction(引介)
    引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或者Field

  • target(目标对象)
    代理目标的对象

  • Weaving(织入)
    是指把增强应用到目标对象来创建新的代理对象的过程

  • proxy(代理)
    一个类被AOP织入后,就产生一个代理类

  • Aspect(切面)
    是切入点和通知的结合,以后咱们自己来编写和配置的

  • Advisor(通知器、顾问)
    和ASpect很相似
    在这里插入图片描述
    AOP实现之ASpectJ

  • AspectJ是一个Java实现的AOP框架,它能够对java代码进行AOP编译(一般在编译期间进行),让java代码具有AspectJ的AOP功能(当然需要特殊的编译器)

  • 可以这样说AspectJ是目前实现AOP框架中最成熟,功能最丰富的的语言。更幸运的是,AspectJ与java程序完全兼容,几乎是无缝关联,因此对于有java编程基础的工程师,上手和使用都非常容易。

  • 了解AspectJ应用到java代码的过程(这个过程称为织入),对织入这个概念,可以简单理解为Aspect(切面)应用到目标函数类的过程。

  • 对于织入这个过程,一般分为动态织入和静态织入,动态织入的方式是在运行时动态将要增强的代码织入到目标类中,这样往往是通过动态代理技术完成的,如java JDK的动态代理(Proxy,底层技术通过反射实现)或者CGLIB的动态代理(底层通过继承实现),Spring AOP采用的就是基于运行时增强的代理技术

  • AspectJ采用的就是静态织入的方式。AspectJ主要采用的是编译期织入,在这个期间使用AspectJ的acj编译器(类似javac)把aspect类编译成class类编译成class字节码后,在java目标类编译时织入,即先编译aspect类再编译目标类。
    在这里插入图片描述
    AOP实现之Spring AOP
    实现原理分析

  • Spring AOP是通过动态代理技术实现的

  • 而动态代理是基于反射设计的

  • 动态代理技术实现方式有两种:基于接口的JDK动态代理和基于继承的CGLib动态代理
    在这里插入图片描述
    JDK动态代理
    目标对象必须实现接口

1.使用Proxy类生成代理对象的一些代码如下:
/**
*使用jdk的方式生成代理对象
**/
public class MyProxyUtils{
	public static UserService getProxy(final UserService service){
		//使用Proxy类生成代理对象
		UserService proxy=(UserService)Proxy.newProxyInstance(
			service.getClass().getClassLoader(),
			service.getClass().getInterface(),
			new InvocationHandler(){
				public Object invoke(){
					//代理对象方法执行一次,invoke方法就会执行一次
					public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
						if"save".equals(method.getName()){
							System.out.print("记录日志...");
							//开启事务
						}
						//提交事务
						//让service类的save或者update方法正常执行下去
						return method.invoke(service,args);
					}
				});
				//返回代理对象
				return proxy;
			}
		);
	}
}

CGLib动态代理

  • 目标对象不需要实现接口
  • 底层是通过继承目标对象产生代理子对象(代理子对象中继承了目标对象的方法,并可以对该方法进行增强)
2.编写相关的代码
public static UserService getProxy(){
	//创建CGLib核心类
	Enhancer enhancer=new Enhancer();
	//设置父类
	enhancer.setSuperclass(UserService.class);
	//设置回调函数
	enhancer.setCallback(new MethodInterceptor(){
		@override
		public Object intercept(Object obj,Method method,Object[] args,MethodProxy methodProxy)throws Throwable{
			if("save".equals(method.getName())){
				//记录日志
				System.out.println("记录日志了...");
			}
			return methodProxy.invokeSuper(obj,args);
		}
	});
	//生成代理对象
	UserService proxy=(UserService)enhancer.create();
	return proxy;
}

使用

  • 其使用ProxyFactoryBean创建:
  • 使用aop:advisor定义通知器的方式实现AOP则需要通知类实现Advice接口
  • 增强(通知)的类型有:

-前置通知:org.springframework.aop.MethodBeforeAdvice

-后置通知:org.springframework.aop.AfterReturningAdvice

-环绕通知:org.aopalliance.intercept.MethodIntercept

-异常通知:org.springframework.aop.ThrowAdvice

基于AspectJ的AOP使用
其实就是指Spring+AspectJ整合,不过Spring已经将AspectJ收录到自身框架中了,并且底层织入依然采用的动态织入方式。
添加依赖

<!--基于Aspect的aop依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.0.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>aopallinance</groupId>
    <artifactId>aopallinance</artifactId>
    <version>1.0</version>
</dependency>

编写目标类和目标方法

  • 编写接口和实现类(目标对象)
UserService接口
UserServiceImpl实现类
  • 配置目标类,将目标类交给spring IoC容器管理
<context:component-scan base-package="sourcecode.ioc"/>

使用XML实现
实现步骤

  • 编写通知(增强类,一个普通类)
public class MyAdvice{
	public void log(){
		System.out.println("记录日志...");
	}
}
  • 配置通知,将通知类交给spring IoC容器管理
<!--配置通知、增强-->
<bean name="myAdvice" class="cn.spring.advice.MyAdvice"></bean>

-配置AOP切面 在这里插入图片描述

切入点表达式

  • 切入点表达式的格式:
    execution(【修饰符】返回值类型 包名.类名.方法名(参数))

  • 表达式格式说明:
    execution:必需要
    修饰符:可省略
    返回值类型:必需要,但是可以使用通配符
    包名:
    **多级包之间使用.分割
    **包名可以使用
    代替,多级包名可以使用多个代替
    **如果想省略中间的包名可以使用…
    类名:
    **可以使用
    替代
    **也可以使用DaoImpl
    方法名:
    **也可以使用
    号替代
    *也可以写成add
    参数:
    **参数使用*替代
    **如果有多个参数,可以使用…替代
    通知类型
    通知类型(五种):前置通知、后置通知、最终通知、环绕通知、异常抛出通知。

  • 前置通知:
    *执行时机:目标对象方法之前执行通知
    *配置文件:<aop:before method=“before” pointcut-ref=“myPointcut”/>
    *应用场景:方法开始时可以进行校验

  • 后置通知:
    *执行时机:目标对象方法之后执行通知,有异常则不执行了
    *配置文件:<aop:after-returning method=“afterReturning” pointcut-ref=“myPointcut”/>
    *应用场景:可以修改方法的返回值

  • 最终通知:
    *执行时机:目标对象方法之后执行通知,有没有异常都会执行
    *配置文件:<aop:after method=“after” pointcut-ref=“myPointcut”/>
    *应用场景:例如像释放资源

  • 环绕通知:
    *执行时机:目标对象方法之前和之后都会执行。
    *配置文件:<aop:around method=“around” pointcut-ref=“myPointcut”/>
    *应用场景:事物、统计代码执行时机

  • 异常抛出通知:
    *执行时机:在抛出异常后通知
    *配置文件:<aop:after-throwing method=“afterThrowing” pointcut-ref=“myPointcut”/>
    *应用场景:包装异常

使用注解实现
实现步骤

  • 编写切面类(注意不是通知类,因为该类中可以指定切入点)
    在这里插入图片描述

  • 配置切面类

<context:component-scan base-package="com.spring.aop"/>
  • 开启AOP自动代理
<!--AOP基于注解的配置,开启自动代理-->
<aop:aspectj-autoproxy/>

环绕通知注解配置
@Around

	作用:
		把当前方法看成是环绕通知。属性:
	value:
		用于指定切入点表达式,还可以指定切入点表达式的引用。

在这里插入图片描述
定义通用切入点
使用@PointCut注解在切面类中定义一个通用的切入点,其他通知可以引用该切入点
在这里插入图片描述
纯注解方式

@Configuation
@ComponentScan(basePackages="com.spring")
@EnableAspectJAutoProxy
public class SpringConfiguration{
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值