AOP详解之一基本概念
什么是AOP AOP 即 Aspect Oriented Programming,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP 是 OOP 的延续,是软件开发中的一个热点,也是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。
利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
说人话:要在我们的功能中加一些功能,而不直接修改修改源代码的前提下,为了降低耦合性,就用AOP 的方式实现。如:日志。
AOP 使用的技术原理主要是jdk的动态代理和cglib修改字节码两种方式。
在AOP中有六个概念: Joinpoint(连接点):在系统运行之前,AOP 的功能模块都需要织入到具体的功能模块中。要进行这种织入过程,我们需要知道在系统的哪些执行点上进行织入过程,这些将要在其之上进行织入操作的系统执行点就称之为 Joinpoint,最常见的 Joinpoint 就是方法调用。
Pointcut(切点):用于指定一组 Joinpoint,代表要在这一组 Joinpoint 中织入我们的逻辑,它定义了相应 Advice 将要发生的地方。通常使用正则表达式来表示。对于上面的例子,Pointcut 就是表示 “所有要加入日志记录的接口” 的一个 “表达式”。例如:“execution(* com.joonwhee.open.demo.service... (..))”。
Advice(通知/增强):Advice 定义了将会织入到 Joinpoint 的具体逻辑,通过 @Before、@After、@Around 来区别在 JointPoint 之前、之后还是环绕执行的代码。
Aspect(切面):Aspect 是对系统中的横切关注点逻辑进行模块化封装的 AOP 概念实体。类似于 Java 中的类声明,在 Aspect 中可以包含多个 Pointcut 以及相关的 Advice 定义。
Weaving(织入):织入指的是将 Advice 连接到 Pointcut 指定的 Joinpoint 处的过程,也称为:将 Advice 织入到 Pointcut 指定的 Joinpoint 处。
Target(目标对象):符合 Pointcut 所指定的条件,被织入 Advice 的对象。
宽泛的说概念很枯燥,读者也不能很好的理解,我们举一个实战中的一个例子。
业务中有个需求,需要在操作一个按钮之前去判断一下,当前按钮是否可以操作。直接修改源代码判断这种方式确实可以,但是耦合性太高了。
我们就采用aop+redis的方式去实现,看一下我们的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Aspect @Component public class OperationAspect { private static String CHECK_FLAG = "CHECK_FLAG:" ; @Pointcut("execution(public * com.ruoyi.project.medicinemanager.operate.controller.OperateController.insert(..))||" + "execution(public * com.ruoyi.project.medicinemanager.operate.controller.OperateController.save(..))" + "||execution(public * com.ruoyi.project.medicinemanager.operate.controller.OperateController.approve(..))") public void OperationController () { } @Before("OperationController()") public void doBefore (JoinPoint joinPoint) throws Throwable { String objectId = SecurityUtilsWrapper.getDeptIdStr(); if (RedisUtil.get(CHECK_FLAG + objectId) != null ) { if (RedisUtil.get(CHECK_FLAG + objectId).equals(1 )) { throw new CustomException ("盘点中不允许操作" ); } } } }
截图中的方法就是Joinpoint(连接点)通俗的说就是我们的aop和我们的业务连接的地方。
切点就是 @Pointcut(“execution)里面的表达式。
@Before就是环绕通知,可以选择在业务执行前还是后去执行。
Aspect(切面)就是我们定义的这个类,里面定义的切点,环绕通知。
Weaving(织入)就是我们判断逻辑的过程,这个也是最抽象的。
Target(目标对象)就是我们的业务逻辑。
如果把aop的过程比喻成切肉,Target就是我们的肉,切的过程就是Weaving,切点就是下刀的地方,Advice(通知/增强)就是在什么时候开始切,Joinpoint就是刀和肉的联系,Aspect(切面)就当我们刀的大脑吧,里面记录着下刀的地方和肉在什么时候切。
经过这个例子应该对aop的概率了然于心了。
Spirng的IOC容器的启动过程是一个大的流程,那么aop就是其中的一个部分,那aop是什么时候在IOC的容器中开始发挥作用的呢?
我们继续看refresh的源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { prepareRefresh() ; ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory() ; prepareBeanFactory(beanFactory ) ; try { postProcessBeanFactory(beanFactory ) ; invokeBeanFactoryPostProcessors(beanFactory ) ; registerBeanPostProcessors(beanFactory ) ; initMessageSource() ; initApplicationEventMulticaster() ; onRefresh() ; registerListeners() ; finishBeanFactoryInitialization(beanFactory ) ; finishRefresh() ; }
毫无疑问aop的注入过程一定是在实例化单例bean的时候注入的。也就是位置8,我们继续深入该方法。
具体过程很多很多…..省略了,我在IOC源码中都有过解析,需要了解的可以移步历史文章。
我们直接步入正题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 @Override protected Object createBean (String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { if (logger.isTraceEnabled()) { logger.trace("Creating instance of bean '" + beanName + "'" ); } RootBeanDefinition mbdToUse = mbd; Class<?> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null ) { mbdToUse = new RootBeanDefinition (mbd); mbdToUse.setBeanClass(resolvedClass); } try { mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException (mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed" , ex); } try { Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null ) { return bean; } } catch (Throwable ex) { throw new BeanCreationException (mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed" , ex); } try { Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'" ); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException ( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation" , ex); } }
AOP就是在这个方法中开始执行的。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
今天暂时先到这里,下篇文章开始深入分析这个方法。
安利时刻: