spring6源码阅读(五):spring-core解析

回调类型:用于为不同类插入具体的相同代码

生成动态子类以启用方法拦截。此类最初是作为 JDK 1.3 中包含的标准动态代理支持的替代品,但除了实现接口之外,它还允许代理扩展具体基类。动态生成的子类覆盖了超类的非最终方法,并具有回调到用户定义的拦截器实现的钩子。
原始的、最通用的回调类型是 MethodInterceptor,用 AOP 术语来说,它启用了“周围通知”——也就是说,您可以在调用“super”方法之前和之后调用自定义代码。此外,您可以在调用 super 方法之前修改参数,或者根本不调用它。
尽管通用性足以满足任何拦截需求,但它 MethodInterceptor 往往有点矫枉过正。为了简单性和性能,还可以使用其他专用回调类型,例如 LazyLoader 。通常,每个增强类将使用单个回调,但您可以使用 CallbackFilter.
此类最常见的用途体现在静态帮助程序方法中。对于高级需求,例如自定义要使用的 , ClassLoader 您应该创建一个新的 Enhancer实例。CGLIB 中的其他类也遵循类似的模式。
所有增强对象都实现该 Factory 接口,除非 setUseFactory 用于显式禁用此功能。该 Factory 接口提供了一个 API 来更改现有对象的回调,以及创建相同类型的新实例的更快、更简单的方法。
有关 的 java.lang.reflect.Proxy几乎直接替代品,请参阅 Proxy 课程。

以上为Enhancer的方法注释,值得注意的是所谓回调类型,他是指org.springframework.cglib.proxy.Callback的抽象子类,也是生成子类默认实现父类接口Factory中对回调逻辑的语义化接口定义。

回调类型测试

test01

  public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(User.class);

        Callback[] callbacks = new Callback[] {
                new MyMethodInterceptor(),
                new MyLazyLoader()
        };

        enhancer.setCallbackFilter(method -> {
            return 1;
        });

        enhancer.setCallbacks(callbacks);
        User user=(User)enhancer.create();
        user.hello();
    }
输出
你好张三

在test01测试中,我尝试使用了LazyLoaderMethodInterceptor两种回调类型进行创建代理,但是他报错了,提示我要使用CallbackFilter

/**
将生成 Enhancer 的子类的方法映射到特定回调。为每个方法选择的回调类型会影响子类中为该方法生成的字节码,并且在类的生命周期内无法更改。
注意: CallbackFilter 实现应该是轻量级的,因为 cglib 可能会使对象保持 CallbackFilter 活动状态以启用生成类的缓存。更喜欢使用类来 static 实现 CallbackFilter.
 */
public interface CallbackFilter {
    /**
将方法映射到回调。
形参:
method – 截获的方法
返回值:
用于方法的回调数组(如 Enhancer.setCallbacks) 指定的索引,
     */
    int accept(Method method);

显然,cglib默认仅能对一个方法执行一个回调类型,

test02

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(User.class);
        enhancer.setCallback(new MyMethodInterceptor());

        User user=(User)enhancer.create();
        user.hello();

    }
public class MyMethodInterceptor implements MethodInterceptor {

    private static final MyLazyLoader MY_LAZY_LOADER = new MyLazyLoader();

    private volatile boolean initialized;

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        ensureInitialized(obj);
        System.out.println("Before method: " + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("After method: " + method.getName());
        return result;
    }

    private void ensureInitialized(Object obj) throws Exception {
        if (initialized) {
            return;
        }
        synchronized (this) {
            if (!initialized) {
                User loaded = (User) MY_LAZY_LOADER.loadObject();
                User target = (User) obj;
                target.name=loaded.getName();
                initialized = true;
            }
        }
    }
}

ps:这里为什么保存loaded 直接调用呢,是因为proxy.invokeSuper执行他会报错

Exception in thread "main" java.lang.ClassCastException: class org.example.test02.User cannot be cast to class org.example.test02.User$$EnhancerByCGLIB$$ebd2185e

原因是: MethodProxy想要接收的obj,他代表增强后的示例,而不是原对象的实例.

特别提醒: MethodProxy为什么需要增强后的实例?这就是提到MethodProxy和Method的区别了,前者是cglib,后者jdk的反射,在功能结果上他俩没有区别,但是在调用过程中反射需要突破jdk当中的访问级别等规则,而MethodProxy相当于字节码调用,越过了那些损耗

重新梳理一下回调类型

LazyLoader: 控制被代理对象的创建,只有被代理方法第一次调用后才会走内部的模板方法创建一个被代理对象
MethodInterceptor: 大致看了一下obj的来源,总之应当不是每次走代理方法都要创建,方法链路很长,一直在向上套。

总结

剩余的回调类型没有再继续测试,不过他们都是及其语义化的方法。

spring-aop的回调类型测试

依赖导入

为了避免springboot二次配置的污染,我选择仅仅用spring的依赖,但是我并没有找到spring-all这个模块,那么该如何使用呢?

答案是查看依赖关系

spring-aop.gralde中的依赖配置
dependencies {
	api(project(":spring-beans"))
	api(project(":spring-core"))
	optional("org.apache.commons:commons-pool2")
	optional("org.aspectj:aspectjweaver")
	optional("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
	testFixturesImplementation(testFixtures(project(":spring-beans")))
	testFixturesImplementation(testFixtures(project(":spring-core")))
	testFixturesImplementation("com.google.code.findbugs:jsr305")
	testImplementation(project(":spring-core-test"))
	testImplementation(testFixtures(project(":spring-beans")))
	testImplementation(testFixtures(project(":spring-core")))
	testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
}

最终结果就是导入以下包,


  
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-framework-bom</artifactId>
                <version>6.1.15</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>
    </dependencies>

context是为了方便以注解形式运行,里面包含了对core、aop等模版的依赖,aspectjweaver是一个aop壳子,官方解释如下

@AspectJ是指一种将切面声明为使用注解标注的常规Java类的方式。@AspectJ风格是由AspectJ项目作为AspectJ 5版本的一部分引入的。Spring会像AspectJ 5一样解释相同的注解,并使用AspectJ提供的库进行切入点解析和匹配。不过,AOP运行时仍然是纯Spring AOP,并不依赖AspectJ编译器或织入器。

测试结果

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB
@ComponentScan("org.example")
public class Main {

    @Bean
    public User user() {
        User user = new User();
        user.setName("SpringUser");
        return user;
    }

    @Bean
    public UserProxy userProxy() {
        return new UserProxy();
    }

    public static void main(String[] args) {
        try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class)) {
            User user = context.getBean(User.class);
            user.show();


            Factory factory =(Factory)  user;
            Callback[]  callbacks=  ((Factory) user).getCallbacks();
            for (Callback callback:callbacks){
                System.out.println(callback);
            }
        }
    }
}
UserProxy around before
My name is SpringUser
UserProxy around after
org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor@238d68ff
org.springframework.aop.framework.CglibAopProxy$StaticUnadvisedInterceptor@4b86805d
org.springframework.aop.framework.CglibAopProxy$SerializableNoOp@5852c06f
org.springframework.aop.framework.CglibAopProxy$StaticDispatcher@4149c063
org.springframework.aop.framework.CglibAopProxy$AdvisedDispatcher@9cb8225
org.springframework.aop.framework.CglibAopProxy$EqualsInterceptor@76b07f29
org.springframework.aop.framework.CglibAopProxy$HashCodeInterceptor@38af9828

解析
1,回调类型使用:由于此处我使用的aroun,所以最终回调类型一定是MethodInterceptor,也就只能是DynamicAdvisedInterceptor或者StaticUnadvisedInterceptor,断点+注释双重认证,show使用回调类型是DynamicAdvisedInterceptor

2,回调类型使用,

  • 1,有些方法不需要回调,例如getter和setter,这是 NoOp等回调类型的意义
posted @ 2025-09-29 16:07  wenzhuo4657  阅读(3)  评论(0)    收藏  举报