SSM框架-Spring 面向切面编程
Spring 面向切面编程
OOP
允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting
)代码,在OOP
设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP
(面向切面编程)技术则恰好解决了这些不足,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为 “Aspect
”,即切面。所谓“切面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
1.使用前后置通知统计所有方法的执行时间
AOP基本概念
Aspect
(切面): 切面由切点 (Pointcut
) 和增强/通知 (Advice) 组成,它既包括了横切逻辑的定义、也包括了连接点的定义;Joint point
(连接点):能够被拦截的地方:Spring AOP
是基于动态代理的,所以是方法拦截的。每个成员方法都可以称之为连接点;Pointcut
(切点):具体定位的连接点,上面也说了,每个方法都可以称之为连接点,我们具体定位到某一个方法就成为切点;Advice
(通知/增强):表示添加到切点的一段逻辑代码,并定位连接点的方位信息。简单来说就定义了是干什么的,具体是在哪干;Spring AOP
提供了5种Advice
类型给我们,分别是:前置(Before
)、后置(After
)、返回(AfterReturning
)、异常(AfterThrowing
)、环绕(Around
);Target
(目标对象):织入Advice
的目标对象;Weaving
(织入):将增强/通知添加到目标类的具体连接点上的过程。
切点完整表达式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
参数 | 参数说明 |
---|---|
modifiers-pattern | 修饰符 可选 public private protected |
declaring-type-pattern | 方法的声明类型 |
name-patterm | 方法名称类型,例 set* 则表示以set开头的所有的方法名称 |
param-pattern | 参数匹配:(..) 表示任意多个参数,每个参数任意多个类型,(*,String) 表示两个参数,第一个是任意类型,第二个是String |
throws-pattern | 异常的匹配模式 |
ret-type-pattern | 返回类型 必选 * 代表任意类型 |
五种通知
前置通知:在连接点前面执行,前置通知不会影响连接点的执行;
后置通知:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容;
异常返回通知:在连接点抛出异常后执行;
正常返回通知:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行;
环绕通知:最为强大的通知,它能够让你编写的逻辑将被通知的目标方法完全包裹起来。实际上就像在一个通知方法中同时编写前置通知和后置通知。定义通知的时候在通知方法中添加了入参ProceedingJoinPoint
,这个参数是必须写的。因为需要在通知中使用ProceedingJoinPoint.proceed()
调用目标方法。
任务代码
1 |
|
2.使用环绕通知统计所有带参方法的执行时间
环绕通知和前后通知的区别
目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知是不能决定的,他们只是在方法的调用前后执行通知而已,即目标方法肯定是要执行的;
环绕通知可以控制返回对象,即你可以返回一个与目标对象完全不同的返回值,虽然这很危险,但是你却可以办到。而后置方法是无法办到的,因为他是在目标方法返回值后调用。
任务代码
1 |
|
第二关有点问题,我看评论区,大家都不知道怎么让Pointcut
只锁定输出业务管理一的,网上也没找到答案,所以要在命令行里面打开BlogService
类把业务功能2的输出语句注释掉。
3.AOP实现原理-JDK动态代理
代理模式(Proxy)
代理模式就是给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用,在不改变目标对象方法的情况下对方法进行增强。
AOP实现的两种方式
切面类中的方法会根据相应的策略对目标对象进行增强,Spring AOP
使用JDK
动态代理或CGLIB
为给定目标对象创建代理。如果要代理的目标对象实现了至少一个接口,则AOP
默认使用JDK
动态代理,否则使用CGLIB
代理。
JDK动态代理步骤
Proxy.newInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- 第一个参数
ClassLoader loader
:类加载器,一般写当前类; - 第二个参数
Class<?>[] interfaces
:代理类所需要实现的接口; - 第三个参数
InvocationHandler h
:处理类,一般写匿名类。
步骤
- 定义一个
java.lang.reflect.InvocationHandler
接口的实现类,重写invoke
方法; - 将
InvocationHandler
对象作为参数传入java.lang.reflect.Proxy
的newProxyInstance
方法中; - 通过调用
java.lang.reflect.Proxy
的newProxyInstance
方法获得动态代理对象; - 通过代理对象调用目标方法。
任务代码
1 |
|
4.AOP实现原理-CgLib动态代理
CGLIB
代理模拟AOP
实现主要是采用字节码增强框架cglib
,在运行时创建目标类的子类,从而对目标类进行增强。
CGLIB动态代理步骤
- 定义一个
org.springframework.cglib.proxy.MethodInterceptor
接口的实现类,重写intercept
方法; - 获取
org.springframework.cglib.proxy.Enhancer
类的对象; - 分别调用
Enhancer
对象的setSuperclass
和setCallback
方法,使用create
方法获取代理对象; - 通过代理对象调用目标方法。
任务代码
1 |
|
实训地址:头歌编程