Spring AOP全面详解(超级详细)(spring aop简单理解)

  本篇文章为你整理了Spring AOP全面详解(超级详细)(spring aop简单理解)的详细内容,包含有spring aop实例讲解 spring aop简单理解 什么是 spring 的 aop,它的功能是什么? spring aop @annotation Spring AOP全面详解(超级详细),希望能帮助你了解 Spring AOP全面详解(超级详细)。

  如果说 IOC 是 Spring 的核心,那么面向切面编程AOP就是 Spring 另外一个最为重要的核心@mikechen

  AOP的定义

  AOP (Aspect Orient Programming),直译过来就是 面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。

  面向切面编程,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术,如下图所示:
AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。

  

  AOP的作用

  AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

  主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

  简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。

  

  AOP的应用场景

  比如典型的AOP的应用场景:
 

  
AOP可以拦截指定的方法,并且对方法增强,比如:事务、日志、权限、性能监测等增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离。

  

  Spring AOP的术语

  在深入学习SpringAOP 之前,让我们先对AOP的几个基本术语有个大致的概念。

  AOP核心概念

  Spring AOP 通知分类

  Spring AOP 织入时期

  

  Spring AOP三种使用方式

  AOP编程其实是很简单的事情,纵观AOP编程,程序员只需要参与三个部分:

  1、定义普通业务组件

  2、定义切入点,一个切入点可能横切多个业务组件

  3、定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作

  所以进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法。

  方式1:使用Spring自带的AOP

  

public class LogAdvice implements MethodBeforeAdvice, AfterReturningAdvice,MethodInterceptor {

 

   @Override

   public void before(Method method, Object[] objects, Object target) throws Throwable {

   //前置通知

   @Override

   public void afterReturning(Object result, Method method, Object[] objects, Object target) throws Throwable {

   //后置通知

   @Override

   public Object invoke(MethodInvocation methodInvocation) throws Throwable {

   //环绕通知

   //目标方法之前执行

   methodInvocation.proceed(); //目标方法

   //目标方法之后执行

   return resultVal;

  }

 

  

  配置通知时需实现org.springframework.aop包下的一些接口

  前置通知:MethodBeforeAdvice

  后置通知:AfterReturningAdvice

  环绕通知:MethodInterceptor

  异常通知:ThrowsAdvice

  创建被代理对象

  

 bean id="orderServiceBean" / 

 

   bean id="userServiceBean" /

 

  

  通知(Advice)

  

 bean id="logAdviceBean" / 

 

   bean id="performanceAdviceBean" /

 

  

  切入点(Pointcut):通过正则表达式描述指定切入点(某些 指定方法)

  

 bean id="createMethodPointcutBean" 

 

   !--注入正则表达式:描述那些方法为切入点--

   property name="pattern" value=".*creat.*"/

   /bean

 

  

  Advisor(高级通知) = Advice(通知) + Pointcut(切入点)

  

 bean id="performanceAdvisorBean" 

 

   !--注入切入点--

   property name="pointcut" ref="createMethodPointcutBean"/

   !--注入通知--

   property name="advice" ref="performanceAdviceBean"/

   /bean

 

  

  创建自动代理

  

 bean 

 

   !--Bean名称规则(数组):指定那些bean创建自动代理--

   property name="beanNames"

   list

   value *ServiceBean /value

   value *TaskBean /value

   /list

   /property

   !--通知列表:需要执行那些通知--

   property name="interceptorNames"

   list

   value logAdviceBean /value

   value performanceAdvisorBean /value

   /list

   /property

   /bean

 

  

  方式2:使用Aspectj实现切面(普通POJO的实现方式)

  导入Aspectj相关依赖

  

 !--aop依赖1:aspectjrt -- 

 

   dependency

   groupId org.aspectj /groupId

   artifactId aspectjrt /artifactId

   version 1.9.5 /version

   /dependency

   !--aop依赖2: aspectjweaver --

   dependency

   groupId org.aspectj /groupId

   artifactId aspectjweaver /artifactId

   version 1.9.5 /version

   /dependency

 

  

  通知方法名随便起,没有限制

  

public class LogAspectj {

 

   //前置通知

   public void beforeAdvice(JoinPoint joinPoint){

   System.out.println("========== 【Aspectj前置通知】 ==========");

   //后置通知:方法正常执行后,有返回值,执行该后置通知:如果该方法执行出现异常,则不执行该后置通知

   public void afterReturningAdvice(JoinPoint joinPoint,Object returnVal){

   System.out.println("========== 【Aspectj后置通知】 ==========");

   public void afterAdvice(JoinPoint joinPoint){

   System.out.println("========== 【Aspectj后置通知】 ==========");

   //环绕通知

   public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {

   System.out.println("##########【环绕通知中的前置通知】##########");

   Object returnVale = joinPoint.proceed();

   System.out.println("##########【环绕通知中的后置通知】##########");

   return returnVale;

   * 异常通知:方法出现异常时,执行该通知

   public void throwAdvice(JoinPoint joinPoint, Exception ex){

   System.out.println("出现异常:" + ex.getMessage());

  }

 

  

  使用Aspectj实现切面,使用Spring AOP进行配置

  

 !--业务组件bean-- 

 

   bean id="userServiceBean" /

   !--日志Aspect切面--

   bean id="logAspectjBean" /

   !--使用Aspectj实现切面,使用Spring AOP进行配置--

   aop:config

   !--配置切面--

   !--注入切面bean--

   aop:aspect ref="logAspectjBean"

   !--定义Pointcut:通过expression表达式,来查找 特定的方法(pointcut)--

   aop:pointcut id="pointcut"

   expression="execution(* com.apesource.service.impl.*.create*(..))"/

   !--配置"前置通知"--

   !--在pointcut切入点(serviceMethodPointcut)查找到 的方法执行"前",

   来执行当前logAspectBean的doBefore--

   aop:before method="beforeAdvice" pointcut-ref="pointcut"/

   !--配置“后置通知”--

   !--returning属性:配置当前方法中用来接收返回值的参数名--

   aop:after-returning returning="returnVal"

   method="afterReturningAdvice" pointcut-ref="pointcut"/

   aop:after method="afterAdvice" pointcut-ref="pointcut"/

   !--配置"环绕通知"--

   aop:around method="aroundAdvice" pointcut-ref="pointcut"/

   !--配置“异常通知”--

   !--throwing属性:配置当前方法中用来接收当前异常的参数名--

   aop:after-throwing throwing="ex" method="throwAdvice" pointcut-ref="pointcut"/

   /aop:aspect

   /aop:config

 

  

  方式3:使用Aspectj实现切面(基于注解的实现方式)

  

//声明当前类为Aspect切面,并交给Spring容器管理

 

  @Component

  @Aspect

  public class LogAnnotationAspectj {

   private final static String EXPRESSION =

   "execution(* com.apesource.service.impl.*.create*(..))";

   //前置通知

   @Before(EXPRESSION)

   public void beforeAdvice(JoinPoint joinPoint){

   System.out.println("========== 【Aspectj前置通知】 ==========");

  
//后置通知:方法正常执行后,有返回值,执行该后置通知:如果该方法执行出现异常,则不执行该后置通知

   @AfterReturning(value = EXPRESSION,returning = "returnVal")

   public void afterReturningAdvice(JoinPoint joinPoint,Object returnVal){

   System.out.println("========== 【Aspectj后置通知】 ==========");

   //后置通知

   @After(EXPRESSION)

   public void afterAdvice(JoinPoint joinPoint){

   System.out.println("========== 【Aspectj后置通知】 ==========");

   //环绕通知

   @Around(EXPRESSION)

   public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {

   System.out.println("##########【环绕通知中的前置通知】##########");

   Object returnVale = joinPoint.proceed();

   System.out.println("##########【环绕通知中的后置通知】##########");

   return returnVale;

   // 异常通知:方法出现异常时,执行该通知

   @AfterThrowing(value = EXPRESSION,throwing = "ex")

   public void throwAdvice(JoinPoint joinPoint, Exception ex){

   System.out.println("********** 【Aspectj异常通知】执行开始 **********");

   System.out.println("出现异常:" + ex.getMessage());

   System.out.println("********** 【Aspectj异常通知】执行结束 **********");

   !-- 自动扫描器 --

   context:component-scan base-package="com.apesource"/

   !--配置Aspectj的自动代理--

   aop:aspectj-autoproxy/

  

 

  

  Spring AOP的实现原理

  Spring的AOP实现原理其实很简单,就是通过动态代理实现的。

  Spring AOP 采用了两种混合的实现方式:JDK 动态代理和 CGLib 动态代理。

  JDK动态代理:Spring AOP的首选方法。 每当目标对象实现一个接口时,就会使用JDK动态代理。目标对象必须实现接口

  CGLIB代理:如果目标对象没有实现接口,则可以使用CGLIB代理。

  JDK动态代理

  Spring默认使用JDK的动态代理实现AOP,类如果实现了接口,Spring就会使用这种方式实现动态代理。

  JDK实现动态代理需要两个组件,首先第一个就是InvocationHandler接口。

  我们在使用JDK的动态代理时,需要编写一个类,去实现这个接口,然后重写invoke方法,这个方法其实就是我们提供的代理方法。

  如下源码所示:

  

/**

 

   * 动态代理

   * @author mikechen

  public class JdkProxySubject implements InvocationHandler {

   private Subject subject;

   public JdkProxySubject(Subject subject) {

   this.subject = subject;

   @Override

   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

   System.out.println("before 前置通知");

   Object result = null;

   try {

   result = method.invoke(subject, args);

   }catch (Exception ex) {

   System.out.println("ex: " + ex.getMessage());

   throw ex;

   }finally {

   System.out.println("after 后置通知");

   return result;

  }

 

  

  然后JDK动态代理需要使用的第二个组件就是Proxy这个类,我们可以通过这个类的newProxyInstance方法,返回一个代理对象。

  生成的代理类实现了原来那个类的所有接口,并对接口的方法进行了代理,我们通过代理对象调用这些方法时,底层将通过反射,调用我们实现的invoke方法。

  

public class Main { public static void main(String[] args) { 

 

   //获取InvocationHandler对象 在构造方法中注入目标对象

   InvocationHandler handler = new JdkProxySubject(new RealSubject());

   //获取代理类对象

   Subject proxySubject = (Subject)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, handler);

   //调用目标方法

   proxySubject.request(); proxySubject.response();

  }

 

  

  运行结果:

  

before 前置通知

 

  执行目标对象的request方法......

  after 后置通知

  before 前置通知

  执行目标对象的response方法......

  after 后置通知

 

  

  JDK动态代理优缺

  优点

  JDK动态代理是JDK原生的,不需要任何依赖即可使用;

  通过反射机制生成代理类的速度要比CGLib操作字节码生成代理类的速度更快;

  缺点

  如果要使用JDK动态代理,被代理的类必须实现了接口,否则无法代理;

  JDK动态代理无法为没有在接口中定义的方法实现代理,假设我们有一个实现了接口的类,我们为它的一个不属于接口中的方法配置了切面,Spring仍然会使用JDK的动态代理,但是由于配置了切面的方法不属于接口,为这个方法配置的切面将不会被织入。

  JDK动态代理执行代理方法时,需要通过反射机制进行回调,此时方法执行的效率比较低;

  

  CGLib代理

  CGLIB组成结构

  Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截,如下图所示Cglib与Spring等应用的关系:

  最底层的是字节码Bytecode,字节码是Java为了保证“一次编译、到处运行”而产生的一种虚拟指令格式,例如iload_0、iconst_1、if_icmpne、dup等

  位于字节码之上的是ASM,这是一种直接操作字节码的框架,应用ASM需要对Java字节码、Class结构比较熟悉

  位于ASM之上的是CGLIB、Groovy、BeanShell,后两种并不是Java体系中的内容而是脚本语言,它们通过ASM框架生成字节码变相执行Java代码,这说明在JVM中执行程序并不一定非要写Java代码----只要你能生成Java字节码,JVM并不关心字节码的来源,当然通过Java代码生成的JVM字节码是通过编译器直接生成的,算是最“正统”的JVM字节码

  位于CGLIB、Groovy、BeanShell之上的就是Hibernate、Spring AOP这些框架了,这一层大家都比较熟悉

  最上层的是Applications,即具体应用,一般都是一个Web项目或者本地跑一个程序

  所以,Cglib的实现是在字节码的基础上的,并且使用了开源的ASM读取字节码,对类实现增强功能的。

  

  以上

  陈睿mikechen,10年+大厂架构经验,《BAT架构技术500期》系列文章作者,分享十余年BAT架构经验以及面试心得!

  阅读mikechen的互联网架构更多技术文章合集

  Java并发JVMMySQLSpringRedis分布式高并发架构师

  以上就是Spring AOP全面详解(超级详细)(spring aop简单理解)的详细内容,想要了解更多 Spring AOP全面详解(超级详细)的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: