Java核心知识体系:AOP原理和切面应用

【JavaEE】Spring AOP (面向切面)详解 阅读详情

1 概述

我们所说的Aop(即面向切面编程),即面向接口,也面向方法,在基于IOC的基础上实现。

Aop最大的特点是对指定的方法进行拦截并增强,这种增强的方式不需要业务代码进行调整,无需侵入到业务代码中,使业务与非业务处理逻辑分离。

以Spring举例,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。

aop的实现主要包括了两个部分:

  • 匹配符合条件的方法(Pointcut)
  • 对匹配的方法增强(JDK代理、cglib代理)
    spring针对xml配置和配置自动代理的Advisor有很大的处理差别,在IOC中主要是基于XML配置分析的,在AOP的源码解读中,则主要从自动代理的方式解析,分析完注解的方式,再分析基于xml的方式。

2 案例分析

下面是spring aop的用法 也是用于源码分析的案例

切面类:TracesRecordAdvisor

@Aspect
@Component
public class TracesRecordAdvisor {
    
    @Pointcut("execution(* spring.action.expend.aop.services.*.*(..))")
    public void expression() {
    }

    @Before("expression()")
    public void beforePrint()
    {
        System.out.println("进入服务,在服务执行之前,记录日志....");
    }

    @AfterReturning("expression()")
    public void afterPrint()
    {
        System.out.println("退出服务,在服务执行结束之后,记录日志.....");
    }
}

xml配置: aop的注解启用只需要在xml中配置这段代码即可,这个是作为入口

<aop:aspectj-autoproxy/>

服务类:PayServiceImpl 使用jdk代理 所以要有一个接口

@Service
public class PayServiceImpl implements PayService {
    public void payMoneyMenthod() {
        System.out.println("正在执行付款...");
    }
}

测试方法:

@Test
    public void springAopTestService() {
        ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-aop.xml");
        PayService payService= (PayService) applicationContext.getBean("payServiceImpl");
        payService.payMoneyMenthod();

执行结果:

进入服务,在服务执行之前,记录日志....
正在执行付款...
退出服务,在服务执行结束之后,记录日志.....

 从上面的执行结果看,payMoneyMenthod 方法的确是被增强了。

3 BeanFactoryPostProcessor

读spring源码的时候,可以首先看下BeanFactoryPostProcessor和BeanPostProcess,这两个接口都是在spring通过配置文件或者xml获取bean声明,生成BeanDefinition后,允许我们再对生成的BeanDefinition,进行入口包装和增强。

我们看看BeanFactoryPostProcessor的定义

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}

 

方法postProcessBeanFactory的参数为ConfigurableListableBeanFactory,我们之前讨论过beanFactory用来获取bean的,而ConfigurableListableBeanFactory继承接口SingletonBeanRegistry和BeanFactroy,所以可以访问到已经生成过的BeanDefinitions集合,如果某个类实现该接口,spring会注册这个类,然后执行这个类的postProcessBeanFactory方法,以便我们对BeanDefinition进行扩展。

接下来的代码表示Spring是如何注册BeanFactoryPostProcessor并执行postProcessBeanFactory的。

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            prepareRefresh();
            //核心方法1
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            prepareBeanFactory(beanFactory);
            try {
                postProcessBeanFactory(beanFactory);
                //核心方法2 执行BeanFactoryPostProcessor
                invokeBeanFactoryPostProcessors(beanFactory);
                //核心方法 3 注册BeanPostProcessor
                registerBeanPostProcessors(beanFactory);
                // Initialize message source for this context.
                initMessageSource();
                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();
                // Initialize other special beans in specific context subclasses.
                onRefresh();
                // Check for listener beans and register them.
                registerListeners();
                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);
                // Last step: publish corresponding event.
                finishRefresh();
            }
            catch (BeansException ex) {
                 ............
                throw ex;
            }
            finally {
                 ............
                resetCommonCaches();
            }
        }
    }

核心方法1obtainFreshBeanFactory就是前两篇所说的生成BeanDefinition的入口,invokeBeanFactoryPostProcessors核心方法2就是执行BeanFactoryPostProcessor接口的方法。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}

通过方法getBeanFactoryPostProcessors获取注册BeanFactoryPostProcessor,然后来看看如何添加一个处理器

@Override
public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) {
   this.beanFactoryPostProcessors.add(beanFactoryPostProcessor);
}

 对于方法invokeBeanFactoryPostProcessors不再往下看了,里面的方法大致先对BeanFactoryPostProcessor进行排序,排序的标准是是否实现了PriorityOrdered,然后根据设置的order大小指定执行顺序,生成一个排序集合和一个普通的集合,最后执行invokeBeanFactoryPostProcessors

private static void invokeBeanFactoryPostProcessors(
  Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
  for (BeanFactoryPostProcessor postProcessor : postProcessors) {
    //执行到自定义的BeanFactoryPostProcessor
 postProcessor.postProcessBeanFactory(beanFactory);
   }
}

这个方法就会循环先前注册的BeanFactoryPostProcessor集合,然后执行postProcessBeanFactory。

4 BeanPostProcess 解读

与BeanFactoryPostProcessor相比,BeanPostProcess就重要得多了,因为Spring的注解、AOP等都是通过这个接口的方法拦截执行的,它贯穿了Bean创建过程的整个生命周期,在IOC阶段,Spring只注册BeanPostProcess,执行则放到了Bean的实例化创建阶段。

首先看下BeanPostProcessor的接口定义

public interface BeanPostProcessor {
        //在bean创建 属性赋值之后  Aware接口执行之后执行
        Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
        //在init-method afterPropertiesSet 执行之后执行
        Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
    }

在bean的声明周期中,下面的序列是bean创建后要执行的接口和方法顺序:

  • 实例化(autowireConstructor或者instantiateBean)
  • 属性初始化(populateBean)
  • Aware接口(如果你的 bean 有进行实现)
  • BeanPostProcess.postProcessBeforeInitialization
  • PostConstructInitializingBean.afterPropertiesSet
  • BeanPostProcess.postProcessAfterInitialization

其中通过注解引入依赖的方式就是在AutowiredAnnotationBeanPostProcessor这个类中实现的,而接下来要分析的Spring Aop也是从这里开始的,这个类叫AnnotationAwareAspectJAutoProxyCreator,

5 NameSpaceHanlder 解读

在Spring中,任何的技术都是在IOC的基础上进行的,Aop也不例外,程序会首先读取xml配置文件,然后对读取到的标签先查找命名空间,然后找对应的NameSpaceHandler,最终调用parse方法解析标签。

aop标签的解析,使用纯注解的方式aop:aspectj-autoproxy和使用aop:config的配置解析不太一样,具体表现在生成PointCut和生成Before、After、Around等切面类时,使用aop:config的方式会为这些注解生成一个BeanDefinition,而这个BeanDefinition的构造函数是由3个BeanDefinition组成,表明这个类是合成类,即synthetic这个属性为true。然后跟解析普通的bean一样,生成这些实例对象,后面的过程就跟是用纯注解的方式相同了,接下来的分析是基于纯注解分析的,也就是解析从解析aop:aspectj-autoproxy这个标签开始。

前面的xml文件的标签解析是通过parseDefaultElement方法解析默认的 标签的,而我们在配置文件里面配置了启动自动代理的方式 <aop:aspectj-autoproxy/> ,当Spring读取到这个标签,则会走parseCustomElement(root)这个方法了,这个方法的源码不再解析,主要完成的功能如下:

  • 获取element的nameSpaceUri,根据nameSpaceUri找到NameSpaceHanlder
  • 调用NameSpaceHan
Java切面编程(AOP)深度解析:原理、应用与最佳实践 阅读详情

相关推荐

java spring核心技术AOP面向切面编程图文并茂包含例子demo

在哪里切入 where:就是权限校验等非业务操作在哪些业务代码中执行什么时候切入 when :就是业务代码执行前还是执行后切入后做什么事 what :比如做权限校验,日志记录等等。

qubes的专栏 1190

Fluent温度超限调试:从能量方程到网格优化的实战指南

本文深入解析Fluent中温度超限问题的诊断与解决方法,从能量方程机制到网格优化策略,提供实战指南。通过分析温度与焓的换算关系,探讨典型场景如能量方程发散、局部网格质量差等,并给出五步调试法及高级命令应用,帮助工程师有效解决温度计算异常问题。

weixin_29045585的博客 531

15. GD32E230定时器实战:基于TIMER5的1秒LED闪烁中断配置详解

本文详细讲解了如何在GD32E230微控制器上配置TIMER5定时器,实现精准的1秒LED闪烁中断。内容涵盖从时钟开启、参数计算、中断设置到服务函数编写的完整流程,重点解析了预分频值与周期值的配置方法,帮助开发者掌握硬件定时器的核心应用。

weixin_29092787的博客 136

用VSCode远程开发Jetson小车:告别Jupyter,在本地IDE高效调试Rosmaster Python程序

本文详细介绍了如何利用VSCode的Remote-SSH扩展在本地IDE中高效开发Jetson小车的Rosmaster Python程序。通过配置远程开发环境、调试技巧和项目管理方法,开发者可以告别Jupyter Notebook的碎片化编码,实现完整的机器人控制程序开发,显著提升工作效率。

weixin_30652491的博客 477

手把手教你排查libstdc++.so.6版本问题:从GLIBCXX缺失到完美解决

本文深入解析Linux系统中常见的GLIBCXX版本缺失问题,以`ImportError: /lib/x86_64-linux-gnu/libstdc++.so.6: version 'GLIBCXX_3.4.29' not found`为例,从动态链接原理入手,提供使用`strings`和`find`命令的诊断方法,并详细对比了四种解决方案的优缺点。文章重点推荐通过设置`LD_LIBRARY_PATH`或更新系统库的安全策略,并最终建议使用Docker容器化技术实现环境依赖的根治与统一管理。

j7k8l的博客 301

实验十一 ——————二层交换基础

实验内容实验网络拓扑图如上所示SW1以及SW2都创建VLAN10和VLAN20,按照上图所示,将接口添加到特定的VLAN;测试PC之间的连通性,要求相同VLAN内的PC能够相互通信要求设置两台交换机名称以及进行特权模式的密码,并允许通过Telnet方式管理交换机实验目的了解二层交换机工作原理掌握交换机基本配置命令理解VLAN的概念,掌握VLAN的配置理解trunk的概念,掌握trunk的配置实验步...

Fly_鹏程万里 1489

【硬件】LT3763中文手册

1)精确控制输入和输出电流;2) 3000:1真彩色PWM™调光;3) ±1.5%电压调节精度;4) ±6%电流调节精度;5)6V至60 V输入电压范围;6)宽输出范围最高可达55V;7)<2µA关断电流;8)用于负载电流热控制的控制引脚;9)输入和输出电流监控和限制;10)开路、短路和C/10故障检测;11)适用于LED应用的PWM驱动器输出;12)热增强型28引脚FE封装。

qq_39200110的博客 491

2025高教社数学建模国赛A题 - 烟幕干扰弹的投放策略(完整参考论文)

摘要:本研究基于模拟遗传退火算法优化无人机投放烟幕弹策略,建立三维运动学模型分析遮蔽效果。针对不同战术场景(单机单弹至多机多弹),构建以遮蔽时间最大化为目标的非线性优化模型。通过改进遗传算法求解最优参数组合,结果显示在给定条件下单枚烟幕弹对M1导弹的有效遮蔽时长可达1.4秒。研究为无人机协同干扰提供了高效解决方案,验证了智能算法在复杂战术决策中的适用性。关键词:烟幕干扰;无人机投弹;运动学建模;时序优化;遗传算法。

ProminentWu的博客 545

[STC32G144K246入门第六步]DAC驱动

本文摘要介绍了STC32G144微控制器内置的12位DAC模块特性与应用。该芯片集成双通道独立DAC,需通过内部运放缓冲后从P0.7/P5.0引脚输出,支持音频处理、波形生成等应用场景。重点阐述了DAC的R-2R梯形网络工作原理、关键性能指标(12位分辨率、转换速度等)及编程配置方法,包括频率设置、寄存器操作和运放引脚配置。文中还提供了32点正弦波表示例和初始化代码,强调参考电压稳定性对输出精度的重要影响。

sinat_58149788的博客 142

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

上一篇: Java线程池创建方式和应用场景
下一篇: ChaosBlade Java 场景性能优化,那些你不知道的事
java_lujj
博客等级 码龄4年 18粉丝 152原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值