Java 使用Springbean的惰性对象生成器

Java 使用Springbean的惰性对象生成器,java,spring,spring-aop,cglib,Java,Spring,Spring Aop,Cglib,我正在考虑一个想法,即使用@Configuration类能够做到的类似方法,即它们可以通过调用@Bean方法懒洋洋地创建Bean,并在已经调用的情况下返回现有对象。这是通过使用CGLib代理实现的 一件特别有趣的事情是,它甚至在对自身调用方法时也能工作: @Configuration class Config { @Bean ClassA beanA() { return new ClassA(beanB()); } @Bean ClassB beanB

我正在考虑一个想法,即使用
@Configuration
类能够做到的类似方法,即它们可以通过调用
@Bean
方法懒洋洋地创建Bean,并在已经调用的情况下返回现有对象。这是通过使用CGLib代理实现的

一件特别有趣的事情是,它甚至在对自身调用方法时也能工作:

@Configuration
class Config {
    @Bean ClassA beanA() {
        return new ClassA(beanB());
    }

    @Bean ClassB beanB() {
        return new ClassB();
    }
}
现在,在我的用例中,不涉及Spring配置,我想通过调用builderbean的方法(如果尚未调用该方法将创建对象),并返回已调用的现有对象,来使用这种能力惰性地创建任意对象图(不应该是springbean)。我还想利用在同一实例上自调用方法的能力。到目前为止,我没能做到这一点

我如何创建和增强Springbean(作为CGLib代理)以便它们能够自调用方法,类似于
@Configuration
类,但使用我自己的自定义建议来处理惰性和缓存


编辑:更多详细信息

最终的结果应该类似于上面的配置示例,但它将是一个普通的Spring单例bean:

@Component
@MyBuilder // or some other custom annotation
class MyObjectGraphBuilder {
    @Builder ClassA objectA() {
        return new ClassA(objectB());
    }

    @Builder ClassB objectB() {
        return new ClassB();
    }
}
添加了只调用原始方法一次的功能,并为任何后续调用缓存结果(,尤其包括自调用)。上面只是一个例子,可能有许多这样的构建器bean,它们之间可能存在交叉依赖关系,因此非常复杂

方法调用结果缓存很简单(可以由AOP完成),但我想要的是自调用功能,Spring通常不支持自调用功能,除非它是
@Configuration


我认为Spring是通过使用自己的CGlib代理增强
@Configuration
bean类来实现的。然而,它涉及到大量的复制和定制(例如ConfigurationClassEnhancer、ConfigurationClassPostProcessor等),到目前为止,我还没有幸真正使用我的定制后处理器和增强器(代码太长,但它基本上是上述类的副本,并编写了我的定制方法拦截器)。因此,我试图找出是否还有其他方法。

关于AOP和自调用的简单答案是:不能使用Spring AOP,必须使用完整的AspectJ。好消息是,解决方案不需要任何代理。Spring手册介绍了如何使用。不用担心,如果配置正确,您可以将AspectJ与通过SpringAOP实现的其他方面一起使用。此外,如果您不喜欢LTW,还可以通过AspectJ Maven插件使用编译时编织

下面是纯Java+AspectJ(不涉及Spring)中的一个小缓存示例,用于演示:

生成器注释:

@Configuration
class Config {
    @Bean ClassA beanA() {
        return new ClassA(beanB());
    }

    @Bean ClassB beanB() {
        return new ClassB();
    }
}
package de.scrum\u master.app;
导入静态java.lang.annotation.ElementType.METHOD;
导入静态java.lang.annotation.RetentionPolicy.RUNTIME;
导入java.lang.annotation.Retention;
导入java.lang.annotation.Target;
@保留(运行时)
@目标(方法)
public@interface Builder{}
示例类:

@Configuration
class Config {
    @Bean ClassA beanA() {
        return new ClassA(beanB());
    }

    @Bean ClassB beanB() {
        return new ClassB();
    }
}
package de.scrum\u master.app;
公共B类{
@凌驾
公共字符串toString(){
返回“ClassB@”+hashCode();
}
}
package de.scrum\u master.app;
甲级公共课{
私有类B对象B;
公共类A(类B对象B){
this.objectB=objectB;
}
@凌驾
公共字符串toString(){
返回“ClassA@”+hashCode()+”(“+objectB+”);
}
}
带注释工厂方法的驱动程序应用程序:

@Configuration
class Config {
    @Bean ClassA beanA() {
        return new ClassA(beanB());
    }

    @Bean ClassB beanB() {
        return new ClassB();
    }
}
package de.scrum\u master.app;
公共类MyObjectGraphBuilder{
@建筑商
ClassA objectA(){
返回新的ClassA(objectB());
}
@建筑商
类B对象B(){
返回新的ClassB();
}
公共静态void main(字符串[]args){
MyObjectGraphBuilder=新的MyObjectGraphBuilder();
System.out.println(builder.objectB());
System.out.println(builder.objectA());
System.out.println(builder.objectB());
System.out.println(builder.objectA());
System.out.println(builder.objectB());
System.out.println(builder.objectA());
}
}
无缓存方面的控制台日志:

@Configuration
class Config {
    @Bean ClassA beanA() {
        return new ClassA(beanB());
    }

    @Bean ClassB beanB() {
        return new ClassB();
    }
}
ClassB@1829164700
ClassA@2018699554(ClassB@1311053135)
ClassB@118352462
ClassA@1550089733(ClassB@865113938)
ClassB@1442407170
ClassA@1028566121(ClassB@1118140819)
到目前为止,这是可以预测的。这是正常的行为,根本没有缓存

缓存方面:

@Configuration
class Config {
    @Bean ClassA beanA() {
        return new ClassA(beanB());
    }

    @Bean ClassB beanB() {
        return new ClassB();
    }
}
这方面其实很简单。没有线程安全性,无法创建同一类或任何类似类的多个命名bean,但我想您可以从这里开始,原理保持不变

package de.scrum_master.app;

import java.util.HashMap;
import java.util.Map;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

@Aspect
public class BuilderCacheAspect {
  private Map<Class<?>, Object> cachedObjects = new HashMap<>();

  @Around("@annotation(de.scrum_master.app.Builder) && execution(* *(..))")
  public Object findOrCreateObject(ProceedingJoinPoint thisJoinPoint) throws Throwable {
    //System.out.println(thisJoinPoint);
    Class<?> returnType = ((MethodSignature) thisJoinPoint.getSignature()).getReturnType();
    Object cachedObject = cachedObjects.get(returnType);
    if (cachedObject == null) {
      cachedObject = thisJoinPoint.proceed();
      cachedObjects.put(returnType, cachedObject);
    }
    return cachedObject;
  }
}

塔达!这是我们的简单对象缓存。享受。

我认为您尚未收到任何反馈的原因是问题有点不清楚。e、 有一次你谈到创建“任意对象图(不应该是SpringBeans)”,但后来你问“我如何创建和增强SpringBeans”。那你现在想要什么?到目前为止你试过什么?我看不到您的应用程序和/或方面代码。你不期望有人从头开始为你创造一切,是吗?我当然不期望。我同意它可能不是用最好的方式写的,让我重新措辞一下。自从19个小时前你说你会用,我就没有见过你重新措辞过任何东西。那么?;-)对不起,我不在电脑旁,在电话上写不方便。我补充了更多细节。但是为了回答您的问题:创建的对象图不应该包含SpringBean。但是对象图应该由一个(或多个)具有自调用和结果缓存功能的Springbean创建。把这些SpringBean想象成你在crea应用程序中使用的智能工厂