2

Spring中是否有现成的解决方案来代理一个类,以便在调用该类的方法时创建(新实例)和初始化(调用setter)?

我发现并尝试使用 org.springframework.aop.target.LazyInitTargetSource。

也许我做错了什么,但在以下场景中,我的类实例被创建了两次。从上下文中检索 bean 时一次,然后在调用方法时再一次:

我的代理类:

public class NewClass {

    private Integer i;

    public NewClass() {
        System.out.println("NewClass()");
    }

    public void setI(Integer i) {
        System.out.println("setI(): " + i);
        this.i = i;
    }

    public Integer add() {
        return i + 1;
    }
}

弹簧配置:

<bean id="newClassTarget" class="com.mycompany.spring.NewClass" lazy-init="true">
    <property name="i" value="1"/>
</bean>

<bean id="newClass" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource">
        <bean class="org.springframework.aop.target.LazyInitTargetSource">
            <property name="targetBeanName">
                <idref local="newClassTarget"/>
            </property>
        </bean>
    </property>
</bean>

运行代码:

public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("lazy.xml");
    System.out.println("Context initialized");
    System.out.println("before getting bean");
    NewClass newClass = (NewClass) applicationContext.getBean("newClass");
    System.out.println("after getting bean");
    System.out.println("calling add()...");
    System.out.println(newClass.add());
}

产生:

Context initialized
before getting bean
NewClass()
after getting bean
calling add()...
NewClass()
setI(): 1
2

因此,当从 Spring 上下文中获取 bean 和调用 add() 方法时,会调用 NewClass 构造函数。我不觉得这很好,我搞砸了什么?

无论如何,第一个调用来自 Cglib2AopProxy.getProxy() 中的 enhancer.create()。似乎代理在请求 bean 时创建代理类的实例,而不是在第一个方法调用发生时。那不是我想要的。

我可以创建自己的 java.lang.reflect.Proxy 作为我的 NewClass 的持有者,并在第一次调用方法时在处理程序的 invoke() 中创建 NewClass 的实例。不过,我会先玩调用二传手。

Spring 中是否有任何现成的解决方案可以用来实现:

Context initialized
before getting bean
after getting bean
calling add()...
NewClass()
setI(): 1
2

?

4

3 回答 3

2

我能够复制您的行为 - 我的解决方法是简单地为您的 NewClass 定义一个接口,这样创建 Java 动态代理而不是 CGLIB 增强代理。然后它按预期工作 - 在被调用的方法处实例化 NewClass。

public interface NewInterface {
    public void setI(Integer i);
    public Integer add() ;
}

public class NewClass implements NewInterface{

    private Integer i;

    public NewClass() {
        System.out.println("NewClass()");
    }

    public void setI(Integer i) {
        System.out.println("setI(): " + i);
        this.i = i;
    }

    public Integer add() {
        return i + 1;
    }
}
于 2012-06-19T12:42:26.283 回答
0

您可以尝试在@PostConstruct方法中初始化您的类。我想Spring只会为你的类的实际实例调用这个方法,而不是代理。

更新

如果您启用了注释配置,那么您可以试试这个:

public class NewClass {

    private Integer i;

    public NewClass() {
        System.out.println("NewClass()");
    }

    @PostConstruct
    public void init() {
        System.out.println("Initialization");
    }

    public void setI(Integer i) {
        System.out.println("setI(): " + i);
        this.i = i;
    }

    public Integer add() {
        return i + 1;
    }
}

检查"Initialization"打印到控制台的次数。

一些链接:
不带注释的初始化方法:http
://www.mkyong.com/spring/spring-init-method-and-destroy-method-example/ 带注释 的初始化方法: http ://www.mkyong.com/spring /spring-postconstruct-and-predestroy-example/

于 2012-06-19T08:57:07.560 回答
0

感谢所有的评论。我想出了一个快速(不完美)的解决方案来创建和初始化一个类,直到它的方法被调用。我希望它不会太复杂。我不喜欢这个解决方案的一点是委派 setter 调用的问题。我让工厂和类实现了相同的“可设置”接口。也许有人会有更好的主意。

类及其代理工厂的通用接口:

public interface ProxiedClassSettable {

    void setI(Integer i);
}

代理类接口(扩展“setter”接口):

public interface ProxiedClass extends ProxiedClassSettable {

   Integer add();

}

代理类:

public class ProxiedClassImpl implements ProxiedClass {

    private Integer i;

    public ProxiedClassImpl() {
        System.out.println("ProxiedClassImpl()");
    }

    public Integer add() {
        return i + 1;
    }

    public void setI(Integer i) {
        System.out.println("setI(): " + i);
        this.i = i;
    }
}

代理工厂(我让它实现了 'setter' 接口以显示与 ProxiedClass 的耦合):

public class ProxiedClassFactory implements FactoryBean, ProxiedClassSettable {

    private Integer i;

    public Object getObject() throws Exception {
        return Proxy.newProxyInstance(ProxiedClass.class.getClassLoader(),
                new Class[]{ProxiedClass.class},
                new InvocationHandler() {

                    private ProxiedClass proxiedClass;

                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (proxiedClass == null) {
                            proxiedClass = new ProxiedClassImpl();
                            //not a very nice way to initialize the proxied class
                            proxiedClass.setI(i);
                        }
                        return method.invoke(proxiedClass, args);
                    }
                });
    }

    public Class getObjectType() {
        return ProxiedClass.class;
    }

    public boolean isSingleton() {
        return true;
    }

    public void setI(Integer i) {
        this.i = i;;
    }
}

春天 XML:

<bean id="proxiedClass" class="com.mycompany.spring.proxy.ProxiedClassFactory">
    <property name="i" value="1"/>
</bean>

跑步:

public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("proxiedClass.xml");
    System.out.println("Context initialized");
    System.out.println("before getting bean");
    ProxiedClass newClass = (ProxiedClass) applicationContext.getBean("proxiedClass");
    System.out.println("after getting bean");
    System.out.println("calling add()...");
    System.out.println(newClass.add());
}

产生我想要的——首先创建类,它的方法被称为:

Context initialized
before getting bean
after getting bean
calling add()...
ProxiedClassImpl()
setI(): 1
2
于 2012-06-19T13:31:45.543 回答