- 记住:AOP的底层就是动态代理
1.什么是AOP
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
2.AOP在Spring中的作用
提供声明式事务;允许用户自定义切面
以下名词需要了解下
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能
3.使用Spring实现AOP
【重点】使用AOP织入,需要导入一个依赖包!
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
- 定义好要实现的业务,即service层
原来我们在这里的时候都是去代理模式编写一个动态代理类
现在我们使用spring来实现代理模式
【需求】还是在不改变真实业务代码的情况下,在执行方法前后打印日志信息
1.方式1:使用spring的API接口
-
创建两个日志类BeforeLog和AfterLog
-
BeforeLog实现接口MethodBeforeAdvice(方法执行之前)
package com.thhh.Log; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class BeforeLog implements MethodBeforeAdvice { //method:要执行的方法 //args:方法的参数 //target:目标对象,就是这个method要依附于哪个对象执行 //这个方法和动态代理的invoke()一样,调用的参数也是不需要我们传入的 //在我们调用指定的method方法之前(MethodBeforeAdvice)就会来自动的执行这个方法,这和我们的代理模式在代理类中调用核心方法的前戏是一样的 public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+"的"+method.getName()+"被方法执行了"); } }
-
AfterLog实现接口AfterReturningAdvice(在方法返回了返回值之后)
package com.thhh.Log; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class AfterLog implements AfterReturningAdvice { /** * 在方法执行了返回值动作之后执行 * @param returnValue:这个方法返回的返回值 * @param method:执行的这个方法 * @param args:方法的参数 * @param target:这个方法依赖于哪个对象执行 * @throws Throwable */ public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+"的"+method.getName()+"被方法执行了,返回值 = "+returnValue); } }
-
配置spring配置文件
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--注册bean--> <bean id="UserService" class="com.thhh.service.UserServiceImpl"/> <bean id="beforeLog" class="com.thhh.Log.BeforeLog"/> <bean id="afterLog" class="com.thhh.Log.AfterLog"/> <!--配置AOP--> <!--1、导入AOP约束--> <aop:config> <!--2、设置pointcut切入点,这里就是在配置代理模式中前戏、核心方法、收尾工作的执行顺序--> <!-- expression表达式,这是固定的格式,即expression="execution(要执行的位置)" execution()里填写的就是执行的位置,里面填写的格式为:*(返回值的类型) *(包名) *(子包名) *(类名) *(方法名) (..)任意参数类型,*代表通配 --> <aop:pointcut id="pointcut" expression="execution(* com.thhh.service.UserServiceImpl.*(..) )"/> <!--执行环绕(advisor)增强,就是在执行上面指定的切入点的前后增加执行一个什么方法,就叫执行环绕增强,名字唬人--> <!--方法具体是执行核心方法前执行还是执行核心方法后执行,是由方法继承的接口决定的,所以我们在配置执行的方法的时候只需要指定哪个切入点引用哪个 bean/对象 即可--> <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans>
注意:execution(* com.thhh.service.UserServiceImpl.*(…) ),这个表达式要深入到具体的类上在". * (…)",不能只是到了包就停止了,否则会报错
-
测试
import com.thhh.service.UserService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService) context.getBean("UserService"); userService.add(); System.out.println("==============="); userService.delete(); System.out.println("==============="); userService.update(); System.out.println("==============="); userService.query(); } }