Java Spring的bean具有私有构造函数

51
在Spring中,一个bean的类是否可以没有公共构造函数,只有一个私有构造函数呢? 当创建bean时,这个私有构造函数会被调用吗?

8
试一试然后发布结果怎么样? - dm3
1
很困难 - 我没有所有的源代码。 - user710818
6个回答

85

是的,Spring可以调用私有构造函数。如果它找到了具有正确参数的构造函数,无论其可见性如何,它都将使用反射来设置其构造函数为可访问。


你的意思是如果一个bean有类似于private bean() {...}这样的东西,Spring可以调用它吗?那怎么可能呢,这违背了“private”的整个目的。 - Ashkan Aryan
1
抱歉,我可能错了,它可能不仅仅是无参构造函数。我只是根据自己项目中的观察来说的。我不能说我曾经在Spring文档中看到过它。但这是负责实例化的类的javadoc。http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/BeanUtils.html#instantiateClass(java.lang.reflect.Constructor,%20java.lang.Object...) - KevinS
4
可以使用反射实现。特别是,Spring的BeanUtils类使用ReflectionUtils类使构造函数可访问。请查看Constructor.setAccessible()的javadoc。 - KevinS
我认为是的,可以通过反射机制使用私有构造函数。可能Spring库已经做到了。 - user710818
请注意,如果您有一个非Spring-aware的第三方bean实现,您将不得不使用XML来配置它(包括设置需要传递给构造函数的值)。但这并不应该是个问题。 - Donal Fellows
显示剩余2条评论

3

您可以始终使用工厂方法创建bean,而不是依赖于默认构造函数,引用自IoC容器:使用实例工厂方法进行实例化

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="com.foo.DefaultServiceLocator">
  <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="exampleBean"
      factory-bean="serviceLocator"
      factory-method="createInstance"/>

这样做的好处是您可以为bean使用非默认构造函数,同时将工厂方法bean的依赖关系注入。


有趣,但我找不到工厂。也许还有其他方法? - user710818
@Matthew:他似乎正在将Spring适配到一些丑陋的第三方代码中。这肯定会很混乱。 - Donal Fellows
1
@Donal,不好意思,我没听懂他在评论里说的话。 - Matthew Farwell
请问这个 XML 配置的 Java 配置等价代码是什么? - Mishal Harish

3

是的,私有构造函数可以被Spring调用。

考虑下面的代码:

Bean定义文件:

<bean id="message" class="com.aa.testp.Message">
        <constructor-arg index="0" value="Hi Nice"/>
    </bean>

Bean类:

package com.aa.testp;

public class Message {

    private String message;

    private Message(String msg) {
       // You may add your log or print statements to check execution or invocation
        message = msg;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public void display() {
        System.out.println(" Hi " + message);
    }

}

上述代码运行正常。因此,Spring调用了私有构造函数。

有人知道如何使用Spring Java配置(而不是基于xml)来完成这个吗? - lastmannorth

1

是的!Spring可以访问私有构造函数。它会在内部像下面的代码一样工作。

 try {
    Class clazz = Class.forName("A"); // A - Fully qualified class name
    Constructor constructor[] = clazz.getDeclaredConstructors();
    constructor[0].setAccessible(true);
    A a = (A) constructor[0].newInstance();
 }
 catch (Exception e) {
        e.printStackTrace();
 }

0
通常在这样的bean中,您会有一个静态工厂方法,您可以指定该方法以便Spring获取该bean的实例。请参见3.3.1.3 这里。这是Spring推荐的方式,而不是违反可见性限制。

很奇怪,没有静态方法,也没有公共构造函数,但是当我进行调试时,私有构造函数被sun.reflect.NativeConstructorAccessorImpl调用了! - user710818
我想你错了。没有特定的代码 - 我只在调试器中看到标准的sun/spring方法。 - user710818
1
@Ashkan:我怀疑你得到负一票是因为你说错了。Spring可以窥视幕后(除非有严格的安全管理器并且Spring代码没有标记为可信)。 - Donal Fellows
1
@Ashkan:违反可见性限制是非常糟糕的编程风格。不要这样做!如果你这样做了,一定会感到非常难受。(Spring 这样做是因为它想将 bean 的定义与其配置分离,但它们通常仍然密切相关。这个问题恰好是关于例外情况的,即它们之间没有紧密联系的情况...) - Donal Fellows
@Donal Fellows:确切地说,这就是我最初的回复。尽管我和Matthew一再询问,但我仍然无法理解他为什么要这样做,他想要实现什么目标。 - Ashkan Aryan
显示剩余2条评论

0

Spring框架不会将私有构造函数作为Bean范围调用。如果这样做,将会抛出以下错误。

导致此问题的常见原因包括使用final类或非可见类。嵌套异常为

java.lang.IllegalArgumentException: 类中没有可见的构造函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接