beanpostprocessor使用场景_Spring因动态代理使用不注意导致的诡异现象

49f43db206b4dd6adc946e9c67b4773c.gif关注“Java艺术”一起来充电吧!

笔者在新的定时任务项目中,限定一个类只能写一个Job,类似于写脚本,一个Job一个脚本。对于简单的任务我们并不约定一定要有Service层,但在Job中我们可能需要将某些数据库操作放到事务中执行,为让注解事务生效,我们不能直接使用this调用事务方法。

调用本类事务方法有两种方式可以让注解事务生效:

  • 一是通过在类中注入自己,也就是循环依赖注入;

  • 二是在需要时再从bean工厂中获取bean

场景描述

假设现有类A,在类AmethodA方法中,先从Springbean工厂获取到类A的实例,再调用类AmethodB方法,这样做的目的是使事务生效。代码如下:

@Component
public class ProxyObjFieldNpe {

@Value("${field_value}")
private String fieldValue;

public void methodA() {
if (fieldValue == null) {
System.out.println("methodA NPE...");
}
// 从bean工厂取,使AOP生效
ProxyObjFieldNpe thisRef = OnionXxlJobApplicationContent.getBean(ProxyObjFieldNpe.class);
// ......调用某些事务方法
thisRef.methodB();
}

private void methodB() {
if (fieldValue == null) {
System.out.println("methodB NPE...");
}
}

}

外部调用methodA方法:proxyObjFieldNpe.methodA();

结果输出的是:"methodB NPE..."

为什么methodA方法获取到fieldValue字段的值不为空,而methodB方法获取到的fieldValue却为空呢?这就是笔者遇到的问题。细心的朋友,你有没有看出原因呢?

实际项目中调试的结果截图如下:

2da70f4aa888ae51ec09b221cd7362d7.png

图中AutoCloseTimeoutOrderJob实例的字段都为空,这些字段都是声明自动注入的MapperService,不可能为空。但从调试结果我们可以看出,从bean工厂获取到的是AutoCloseTimeoutOrderJob的代理对象,并非AutoCloseTimeoutOrderJob

ProxyObjFieldNpe的例子中,我们从bean工厂获取到的也是ProxyObjFieldNpe的代理对象,该代理对象继承ProxyObjFieldNpe。因此,与上面截图一样,代理对象的字段都是NULL

外部调用ProxyObjFieldNpemethodA方法调用的是代理类的methodA方法,那为什么methodA方法拿到字段的值非空,而methodB方法拿到的是空值呢?

因为methodB方法被声明为private了,代理类没法重写该方法。因此thisRef.methodB();实际调用的是代理类父类的methodB方法。methodB方法中获取fieldValue获取的是代理类对象的,这就是methodA方法获取到fieldValue字段的值不为NULL,而methodB方法获取到fieldValue字段的值为NULL的原因。

“methodB方法中获取fieldValue获取的是代理类对象的 ",这句我们稍后从字节码层面理解。

问题:

  • 为什么methidB方法的访问标志是private,代理对象是ProxyObjFieldNpe的子类,却能调用其父类的methidB方法?

  • 为什么代理对象的字段为NULL?

为什么代理对象能调用父类的private方法?

因为调用访问标志为privatemethodB方法是在ProxyObjFieldNpe类的methodA方法中调用的,而不是在代理类的methodA方法中调用的,内部调用当然有访问权限。

代理类继承ProxyObjFieldNpe,外部调用代理类的methodA方法时,最终经过方法拦截器调用代理类父类的methodA方法,因此methodA方法中调用methodB方法实际上是在父类中调用的。

ProxyObjFieldNpemethodA方法编译后生成的字节码如下(部分):

   15: ldc           #6    // class com/wujiuye/test/ProxyObjFieldNpe
17: invokestatic #7 // Method com/wujiuye/test/OnionXxlJobApplicationContent.getBean:(Ljava/lang/Class;)Ljava/lang/Object;
20: checkcast #6 // class com/wujiuye/test/ProxyObjFieldNpe
23: astore_1
24: aload_1
25: invokespecial #8 // Method com/wujiuye/test/ProxyObjFieldNpe.methodB:()V

偏移量为151720三条指令是:从bean工厂获取代理bean,并使用checkcast指令将代理对象类型强制转为父类类型。

偏移量为2425两条字节码实现调用methodB方法,非静态方法的第一个隐式参数为this引用,此处传的是代理类对象的引用,因此在methodB方法中,使用this(代理对象的引用)获取到的字段都是空的。

为什么代理对象的字段为NULL?

如果熟悉Spring Bean生命周期,那么就不难理解。

bean的创建过程如下:

  • 1、反射创建bean;

  • 2、为bean注入属性;

  • 3、调用*Aware接口的方法;

  • 4、调用BeanPostProcessorpostProcessBeforeInitialization方法;

  • 5、调用初始化方法,afterPropertiesSet或自定义的初始化方法;

  • 6、调用BeanPostProcessorpostProcessAfterInitialization方法;

代理对象是在上述步骤的第六步创建的,即调用某个BeanPostProcessorpostProcessAfterInitialization方法之后,返回代理对象,如果是单例对象,则会将该对象保存到bean工厂(容器)中。也就是说,bean工厂中存储的是代理对象。

下面两张图是我在项目中调试Spring代码的截图。(图中的小红点下方有个问号,这是条件断点,只有满足条件时才会停在断点处。条件的设置可右击小红点,在弹出框中输出条件,条件的编写与在代码中添加一个if语句是一样的。)

18a717fa5736715aa86effab8276c87d.png

在调用BeanPostProcessorpostProcessAfterInitialization方法之前, bean还是原生的bean

8fff131529c0bf0275c10c6f80e432be.png

在调用BeanPostProcessorpostProcessAfterInitialization方法之后,bean已经变成代理对象了。

因此,使用cglib生成的代理对象  (继承方式),在父类中,通过代理对象调用父类私有方法不会报错,但字段都是空的。

d55130d586f73b52b8489eb908a28a6b.png00932d5f96ed033cceb0ad18b833a70d.png公众号:Java艺术扫码关注最新动态
Spring Bean生命周期全解析:从核心原理到面试实战 在Java企业级开发中,控制反转(IoC)和依赖注入(DI)是Spring框架的核心设计思想,它们通过管理对象的创建与依赖关系,实现了组件间的解耦。其底层原理依赖于Bean的生命周期管理,涵盖了从定义、实例化、属性填充到初始化的完整过程。理解这一机制的技术价值在于,开发者可以精准地在同阶段插入自定义逻辑,实现功能增强与问题调试,例如通过BeanPostProcessor进行AOP代理或性能监控。在实际应用场景中,这仅是构建高可维护性应用的基础,更是解决循环依赖、事务管理等复杂问题的关键。本文聚焦于Spr 阅读详情

相关推荐

Spring框架核心机制:IoC、DI与AOP深度解析

控制反转(IoC)和依赖注入(DI)是现代Java框架的核心设计模式,通过将对象创建和依赖管理的控制权交给容器,实现了组件间的松耦合。面向切面编程(AOP)则通过动态代理技术,将横切关注点与业务逻辑分离。Spring框架作为Java企业开发的事实标准,其IoC容器和AOP实现构成了框架的基石。理解Bean的生命周期管理、依赖注入方式选择以及切面编程原理,能够帮助开发者构建更灵活、可维护的系统架构。在实际项目中,合理应用这些机制可以显著提升开发效率,特别是在微服务架构和云原生应用场景下。

weixin_34409357的博客 619

Spring经典扩展接口应用:BeanPostProcessor

BeanPostProcessor 接口在 Spring 框架中的实际应用场景有很多,以下是一些举例:属性注入+AOP 预处理+定制初始化逻辑+数据校验+资源回收

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 6万+

Spring Boot AOP代理机制深度解析:JDK与CGLIB实战对比与避坑指南

面向切面编程(AOP)是一种将横切关注点(如日志、事务、安全)与核心业务逻辑分离的编程范式,其核心原理是通过代理模式在运行时动态增强目标对象。在Java生态中,动态代理主要有两种实现方式:基于接口的JDK动态代理和基于继承的CGLIB字节码生成。理解这两种代理机制的技术原理,对于实现模块解耦、提升代码可维护性具有重要价值。它们广泛应用于事务管理、缓存、日志记录和性能监控等场景。本文聚焦于Spring Boot框架中AOP的默认实现,深入探讨其代理机制,特别是CGLIB代理与JDK动态代理在底层实现、性能表现

weixin_34314962的博客 406

为什么“this.fieldName“拿到的是NULL? 动态代理使用注意事项

条件的设置可右击小红点,在弹出框中输出条件,条件的编写与在代码中添一个。有两种方式可以让注解事务生效,一是通过在类中注入自己,也就是循环依赖注入,二是在需要时再从。生成的代理对象 (继承方式),在父类中,通过代理对象调用父类私有方法会报错,但字段都是空的。这些,我也就看看书,没有去深入学习过,甚至现在都会用,因为工作中没用到,只是先了解一下。方法之后,返回代理对象,如果是单例对象,则会将该对象保存到。获取的是代理类对象的 ",这句我们稍后从字节码层面理解。引用,此处传的是代理类对象的引用,因此在。

吴就业 976

Spring BeanPostProcessor

介绍BeanPostProcessor的bean逻辑增强功能,以及利用该特性实现一个插件管理器。

小确幸的博客 1617

BeanPostProcessor使用场景

一、BeanPostProcessor 在bean实例化前后可进行的操作。可以通过自定义的bean继承该类,通过注解或xml配置bean,这样在bean被初始化的时候可以调用。 二、使用场景 1、处理自定义注解。bean可以添我们自定义的注解,自定义的注解处理方式在该类中实现,如通过注解识别一组或几组bean,在后续的业务处理中根据组bean进行逻辑。 2、打印日志,将每个bean的初始化情况打印出来;打印初始化时间等。 ...

SJZYLC的专栏 4958

深入解析Spring Bean生命周期:从核心原理到实战应用

在Java企业级开发中,控制反转(IoC)和依赖注入(DI)是Spring框架的核心设计思想,它们通过容器管理对象的创建与依赖关系,实现了组件间的解耦。其底层实现原理依赖于Bean生命周期的精细管理,包括实例化、属性赋值、初始化及销毁等关键阶段。这一机制的技术价值在于提供了强大的扩展点,如BeanPostProcessor、Aware接口等,允许开发者在Bean生命周期的关键时刻进行定制化干预,从而支持AOP、事务管理等高级特性。在实际应用场景中,理解生命周期有助于解决循环依赖、资源优雅释放、启动任务执行等

weixin_34279246的博客 667

Spring Bean生命周期详解:12个关键节点与扩展点实战

Spring框架中Bean生命周期是IoC容器的核心机制,涉及实例化、依赖注入、初始化和销毁等关键阶段。通过BeanPostProcessor等扩展点,开发者可以干预Bean的创建过程,实现AOP代理、性能监控等高级功能。理解完整的生命周期路线图(包含12个关键节点)能有效解决循环依赖、资源管理等典型问题。在微服务架构下,合理运用@PostConstruct、@PreDestroy等标准注解,结合SmartInitializingSingleton等特殊接口,可以优化启动性能并确保资源安全释放。本文通过多租

weixin_30797199的博客 466

Spring Bean生命周期全解析:从核心原理到实战避坑指南

在Java企业级开发中,控制反转(IoC)和依赖注入(DI)是构建松耦合、可测试应用的核心设计模式。Spring框架通过其IoC容器实现了这一机制,其核心在于对Bean生命周期的精细管理。Bean生命周期描述了对象从定义、实例化、属性装配、初始化到销毁的完整过程,理解这一过程是掌握Spring框架工作原理的基础。其技术价值在于,它仅是框架实现依赖注入和面向切面编程(AOP)的基石,还通过BeanPostProcessor等扩展点提供了强大的可定制能力,使得开发者能够干预容器的行为。在实际应用场景中,深入理

weixin_30919571的博客 435

深入解析Spring Bean生命周期:从原理到实战应用

依赖注入(DI)和控制反转(IoC)是现代Java企业级应用开发的核心设计模式,它们通过将对象的创建与依赖管理交由容器负责,实现了组件间的松耦合。其底层原理基于Bean的生命周期管理,涵盖了从Bean定义、实例化、属性填充到初始化的完整流程。理解这一机制的技术价值在于,它仅是框架高效运作的基础,更是开发者解决复杂工程问题的关键,例如处理循环依赖、实现自定义初始化逻辑以及优化应用启动性能。在实际应用场景中,掌握Spring Bean生命周期有助于精准定位因初始化顺序导致的空指针异常,并能够通过BeanP

weixin_30493401的博客 433

Spring事务与AOP源码深度解析:从原理到实践,手写实现核心机制

在Java企业级开发中,Spring框架的事务管理和面向切面编程(AOP)是构建高内聚、低耦合应用的核心技术。事务管理通过声明式事务抽象,确保了复杂业务场景下的数据一致性,其底层依赖于动态代理和连接绑定等机制。AOP作为面向对象编程的有力补充,通过切面、通知和代理模式,将日志、安全等横切关注点与核心业务逻辑解耦,极大地提升了代码的可维护性。理解其原理,仅能帮助开发者精准定位诸如事务传播行为不生效、AOP切面执行顺序错乱等常见问题,更能为应对高级技术面试和进行框架级定制开发奠定坚实基础。本文以Spring

weixin_30698297的博客 352

Spring中的BeanPostProcessor作用和应用场景

Spring IOC(Inversion of Control,控制反转)后置处理器在Spring框架中扮演着重要的角色,它们允许开发者在Bean的创建和初始化过程中插入自定义的逻辑。以下是Spring IOC后置处理器的主要作用:BeanPostProcessor接口是Spring框架提供的一个扩展点,允许开发者在Bean初始化之前和之后执行自定义的逻辑。实现这个接口的类可以对IOC容器中的每一个Bean实例进行后处理。在Bean初始化之前进行后处理:在Bean初始化之后进行后处理:属性检查和修改:动态代

oopxiajun博客专栏 1041

com.alibaba.fastjson.JSONException: syntax error, expect [, actual {, pos 0, fieldName null

1,单个java对象转为成JSONArray时报错,自己对转换的理解错误! 2,单个java对象(student)只能转为JSON对象,java集合对象(list)转化的JSON对象才可以转换成JSONArray List<Student> list = new ArrayList<>(); Student student1 = new Student ("提莫",12); Student student2 = new Stude

u011220266的博客 2万+

SpringBeanPostProcessor的八个实现应用点

BeanPostProcessor及其子接口有很多扩展点,帮助我们在bean创建过程中去拓展和灵活创建。总结了在创建过程中四个接口共八个方法作用。

endwas 1407

spring——事务动态代理造成属性为null

    近日遇到一个很诡异的问题,kotlin的一段老代码莫名其妙报空指针,而且只有整个springboot项目起起来才会报错,如果写单元测试时单独注入几个类,是会报错的。最终锁定原因是因为在原本的service类中,新增的一些方法我上了@Transcational进行事务处理,这就导致生成service类的对象时会为他生成一个基于cglib创建的代理子类。而这个子类中所有的属性都是空指针: 但是为什么创建出来的对象会属性都为空呢,毕竟这这些属性都在父类的构造方法里,继承父类构建的子类也需要重写父类的

qq_30095631的博客 3207

Spring 知识面面通 之 BeanPostProcessor 后置处理器应用详解

  BeanPostProcessor   ① BeanPostProcessor是BeanFactory初始化Bean钩子,可用于对Bean进行自定义定制修改。   ② ApplicationContext可以自动检测到其中注册的BeanPostProcessor类型的Bean,并在随后应用到创建的任意的Bean。   ③ 普通Bean工厂允许编程注册方式注册后置处理器,应用于通过工厂创建的所有Bean。   ④ 填充Bean属性等操作通常实现postProcessBeforeInitialization

键盘上流淌的日子 624

深入探索Spring后置处理器:解析作用与实际应用场景

BeanDefinitionRegistryPostProcessor BeanFactoryPostProcessor InstantiationAwareBeanPostProcessor BeanPostProcessorspring生命周期中常见的4个后置处理器,但是对于其作用和执行顺序很多人还是非常清楚,这里给大家讲解一下其作用以及执行顺序,并带上实操。

Apple_Boy的博客 2881
上一篇: python学生成绩管理系统实验报告心得_python简单学生成绩管理
下一篇: pythonsuper_用__init __()方法理解Python super()
weixin_39650745
博客等级 码龄9年 35粉丝 155原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值