SpringJava配置、@Autowire vs.Constructor注入、@Transactional和CGLIB

SpringJava配置、@Autowire vs.Constructor注入、@Transactional和CGLIB,spring,autowired,transactional,proxy-classes,cglib,Spring,Autowired,Transactional,Proxy Classes,Cglib,我们已经成功地使用了@Autowired和基于Java的Spring配置,但现在,我们正在失去控制。每个人都开始在任何地方添加自动连接的依赖项,产生循环和奇怪的bug 因此,我们正在考虑使用构造函数注入和Spring配置的自动布线 旧的: 新的: 这非常有效(它驱使人们拆分bean,而不是创建具有10+构造函数参数的怪物) 但是当Bean有一个用@Transactional注释的方法时,它失败了,因为CGLIB随后尝试创建一个代理,但该代理失败了,因为它找不到无参数构造函数 这个问题的解决方案是

我们已经成功地使用了
@Autowired
和基于Java的Spring配置,但现在,我们正在失去控制。每个人都开始在任何地方添加自动连接的依赖项,产生循环和奇怪的bug

因此,我们正在考虑使用构造函数注入和Spring配置的自动布线

旧的:

新的:

这非常有效(它驱使人们拆分bean,而不是创建具有10+构造函数参数的怪物)

但是当
Bean
有一个用
@Transactional
注释的方法时,它失败了,因为CGLIB随后尝试创建一个代理,但该代理失败了,因为它找不到无参数构造函数


这个问题的解决方案是什么?

您有几种可能的解决方案

  • 介绍你的类的接口
  • 将Spring版本升级到至少4.0
  • 添加受保护的
    无参数构造函数
  • 介绍接口 在为类引入接口时,可以放弃使用CgLib。然后,Spring将能够使用JDK动态代理来处理接口。它围绕一个已经存在的bean实例创建一个代理,该代理实现它所包装的类的所有接口。这样,类是否具有无参数构造函数就无关紧要了

    升级到Spring4 在Spring4.0中,添加了支持,以允许代理缺少无参数构造函数的类(请参阅)。为了能够升级您的Spring版本并添加到您的类路径中,Spring4自带了自己的重新打包的cglib版本,因此不再需要它了

    需要注意的一点是,如果在构造函数中执行null检查或init逻辑,则应该有一个没有逻辑的构造函数,如果cglib创建实例,则可能会失败。我怀疑它会将null传递给所有构造函数参数(或原语的一些默认值)

    添加了受保护的
    无参数构造函数
    
    Cglib需要能够创建一个用于包装实际类的实例。在类中有一个受保护的构造函数,这样cglib就可以调用它了。

    我只需重构
    @Transactional
    类,以提供带有业务逻辑的接口,并注入这些业务接口,使用类拥有的相同方法创建接口,比如说
    void saveSomething(Something a)
    ,并将该接口注入客户端bean,而不是注入类实例。原始类(注册为Springbean)将实现该接口,但客户端将通过接口引用它。请注意,在方法的实现上保留
    @Transactional
    ,不要将其移动到接口。当使用接口时,会导致使用JDK动态代理,该代理可以由任意bean创建。您可能还想尝试升级spring版本(到4.0或4.1),因为我记得这方面的一些修复应该允许cglib在这种情况下工作。然而,我仍然喜欢界面,但这只是我。还有一个关于构造函数注入的说明,它也可以与
    @Autowired
    一起使用,只需使用
    @Autowired
    而不是字段来注释构造函数。同样对于cglib来说,有一个受保护的构造函数就足够了,它只是需要它来进行子类化。CGLIB使用继承来生成代理类,但是在创建子类实例时不能绕过调用“super()”构造函数方法。一些语言缓解了这个问题,例如在GoogleDart中,类自动定义接口。另一个问题是注释不是,因此接口上的
    @Transactional
    并不总是起作用。所以这些问题只是Java/JVM的问题。但如果“任何东西”都是java接口,那么Spring框架将根本不使用cglib,而是使用标准。还要记住,cglib代理类继承了所有字段,但它们从未被使用,因为代理方法调用将把工作委托给代理后面的原始bean(您必须注意,所有公共方法都不是final),因此java接口的方法更简洁。re最后一点:如果代理中的字段都为空,那么CGLIB不会失败,因为代理将始终委托给真实的bean?是的,这就是代理的全部要点。它拦截方法调用,执行拦截器,最后在实际bean上调用实际方法。如果构造函数中有逻辑(null检查、init逻辑等),它可能会失败。我认为objeness允许绕过构造函数代码,但我不确定Spring是否使用这种行为。如果对象在类路径上,Spring将使用它。阅读答案中链接的问题。
    class Bean {
       @Autowired Foo foo;
    }
    
    @Configuration
    @Import( FooCfg.class )
    class BeanCfg {
       @Bean public Bean bean() { return new Bean(); }
    }
    
    class Bean {
       public Bean(Foo foo) {...}
    }
    
    @Configuration
    class BeanCfg {
       @Autowired FooCfg fooCfg;
       @Bean public Bean bean() { return new Bean(fooCfg.foo()); }
    }