什么是Aop?
Aspect Oriented Programming 面向切面编程
通过预编译的方式和运行期动态代理实现程序功能统一维护的一种技术
是OOP的延续,也是Spring第二个核心内容
可以利用AOP对业务逻辑的各个部分进行隔离
降低之间的耦合,提高代码可重用性,和开发效率
Aop 在Spring的作用
提供声明式事务,允许用户自定义切面
横切关注点,和业务本身无关但需要关注的部分
切面:ASPECT
横切的关注点,被模块化的特殊实例,即一个类而已
通知:ADIVCE
切面需要完成的工作,即 实例的增强方法
目标:TARGET
被通知的对象,就是要代理的实例
代理:PROXY
向目标应用通知之后创建的对象
切入点:POINTCUT
切面通知执行的具体地点,分前切后切,
就是被代理对象前执行,和被代理对象后执行
连接点:JOINTPOINT
与切入点匹配的执行点
Spring切面的支持需要导入AspectJweaver
Maven坐标
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency>
演示案例:
使用SpringAPI接口
我们声明一个Service接口
就是普通四个业务抽象
public interface UserService { void addUser(); void getUser(); void updateUser(); void deleteUser(); }
然后实现
public class UserServiceImpl implements UserService{ public void addUser() { System.out.println("增加用户的功能"); } public void getUser() { System.out.println("获取用户的功能"); } public void updateUser() { System.out.println("修改用户的功能"); } public void deleteUser() { System.out.println("删除用户的功能"); } }
我们加入前后日志功能,但是不要修改Service
前置日志功能类
//前置日志 public class BeforeLog implements MethodBeforeAdvice { /** * * @param method 要执行目标对象的方法 * @param objects 方法的参数 * @param o 目标对象 * @throws Throwable */ public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("被执行的目标对象是:" + o.getClass().getName()); System.out.println("被执行的目标方法是:" + method.getName()); } }
后置日志结果类
public class AfterLog implements AfterReturningAdvice { /** * * @param o 返回切面插入后的增强对象 * @param method 要执行目标对象的方法 * @param objects 方法的参数列表 * @param o1 目标对象 * @throws Throwable */ public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("执行了方法:" + method.getName()+" 返回结果是 " + o); } }
还是使用XML配置操作
注册Service
<!-- 注册 --> <bean id="userServiceImpl" class="cn.dai.service.UserServiceImpl" /> <bean id="beforeLog" class="cn.dai.log.BeforeLog"/> <bean id="afterLog" class="cn.dai.log.AfterLog"/>
配置我们的切面
<!-- Aop配置 需要aop约束支持 --> <aop:config> <!-- 切入点:在哪里执行 --> <!-- expression 表达式 execution 要执行的位置 要执行的位置 * * * * cn.dai.service.UserServiceImpl这个类的所有方法,不一样的参数 --> <aop:pointcut id="pc" expression="execution(* cn.dai.service.UserServiceImpl.*(..))" /> <!-- 环绕执行 把我们的这个log类 切入到指定的切入点执行 --> <aop:advisor advice-ref="beforeLog" pointcut-ref="pc" /> <aop:advisor advice-ref="afterLog" pointcut-ref="pc" /> </aop:config>
关于切入点的详细声明:https://blog.csdn.net/corbin_zhang/article/details/80576809
开始测试
注意,我们注册的是UserService实现类,但是实际切入的是Service接口
所以需要多态,是接口的引用,指向的是Impl的实现,注意GETBEAN方法的第二参数,是接口
public class AnnotationTest { @Test public void a2(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); UserService userService = applicationContext.getBean("userServiceImpl", UserService.class); userService.getUser(); userService.addUser(); userService.deleteUser(); userService.updateUser(); } }
结果
使用自定义类实现AOP
写一个自定义的切入类
public class PointCutLog { public void beforeExe(){ System.out.println("前切入!!!!"); } public void afterExe(){ System.out.println("后切入!!!"); } }
Bean的配置
<bean id="pointCutLog" class="cn.dai.log.PointCutLog"/>
切面配置
<aop:config> <!-- 自定义切面引用的切面类--> <aop:aspect ref="pointCutLog"> <!-- 切入点 --> <aop:pointcut id="point" expression="execution(* cn.dai.service.UserServiceImpl.*(..))" /> <!-- 通知:执行的方法 --> <aop:before method="beforeExe" pointcut-ref="point"/> <aop:after method="afterExe" pointcut-ref="point"/> </aop:aspect> </aop:config>
测试
使用注解实现AOP
切面的配置不需要XML,我们在自定义的类上面打上注解
@Aspect public class PointCutLog { @Before("execution(* cn.dai.service.UserServiceImpl.*(..))") public void beforeExe(){ System.out.println("前切入!!!!"); } @After("execution(* cn.dai.service.UserServiceImpl.*(..))") public void afterExe(){ System.out.println("后切入!!!"); } }
配置注解切面的支持
<!-- 开启注解支持--> <aop:aspectj-autoproxy />
切面类可以使用Bean注册,也可以使用注解@Component
测试
环绕的切入
@Around("execution(* cn.dai.service.UserServiceImpl.*(..))") public void allCut(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕前... "); // 获得签名? 类的信息 System.out.println(joinPoint.getSignature()); Object proceed = joinPoint.proceed(); System.out.println(proceed); System.out.println("环绕后..."); }
测试
切面实现方式的补充:
<!-- 开启注解支持--> <!-- proxy-target-class="false" 代理实现的方式 JDK 默认的 --> <!-- proxy-target-class="true" 代理实现的方式 cglib --> <aop:aspectj-autoproxy proxy-target-class="false"/>