'Bean does not have a public constructor that does not take parameters' error despite clearly having one?

谁都会走 提交于 2019-12-02 07:56:10

问题


I have an EmailService EJB that has a very simple 'send_email' method. I'm receving the error in the title despite clearly having a public constructor that does not take parameters. Below is the exact error and the class code. This is very confusing.

Error:

[ERROR ] CNTR5007E: The websphere.jaxrs.service.EmailService bean class for the WebApiConsole#WebApiConsole.war#EmailService bean does not have a public constructor that does not take parameters.

See here for error details (not much to see): http://pic.dhe.ibm.com/infocenter/wxdinfo/v6r1/index.jsp?topic=%2Fcom.ibm.websphere.messages.doc%2Fcom.ibm.ejs.container.container.html

Code:

package websphere.jaxrs.service;

import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;

import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;

import websphere.jaxrs.helpers.ContextFinder;

    /**
     * This class provides an interface for emailing messages. It uses server environment variables to configure SMTP server and port and authentication.
     * 
     * @author me
     *
     */
    @Stateless
    @LocalBean
    public class EmailService {

        @EJB ContextFinder ctf;

        private static final String EMAIL_PASSWORD_JNDI_NAME = "EMAIL_PASSWORD";
        private static final String EMAIL_USERNAME_JNDI_NAME = "EMAIL_USERNAME";
        private static final String SMTP_SERVER_JNDI_NAME = "SMTP_SERVER";
        private static final String SMTP_PORT_JNDI_NAME = "SMTP_PORT";

        private String username;
        private String password;
        private String server;
        private Integer port;


        public EmailService() { 
            username = (String) ctf.lookup(EMAIL_USERNAME_JNDI_NAME);
            password = (String) ctf.lookup(EMAIL_PASSWORD_JNDI_NAME);
            server = (String) ctf.lookup(SMTP_SERVER_JNDI_NAME);
            port = (Integer) ctf.lookup(SMTP_PORT_JNDI_NAME);
        }

        /**
         * Sends an email to a specific user.
         * 
         * @param sendTo
         * @param subject
         * @param message
         */
        public void sendMail(String sendTo, String subject, String message) {

            try {
                Email email = new SimpleEmail();
                email.setHostName(server);
                email.setSmtpPort(port);
                email.setAuthentication(username, password);
                email.setSSLOnConnect(true);
                email.setFrom(username);
                email.setSubject(subject);
                email.setMsg(message);
                email.addTo(sendTo);
                email.send();
            } catch (EmailException e) {
                System.err.println("Failed to email");
                e.printStackTrace();
            }

        }

    }

I'm inclined to think this is a bug. I might be wrong but but everything is pretty self-contained in the class above (no other configurations that I know of required) and I keep getting this error. I tried re-building the project.

[EDIT] I did some experimentation and very specifically, removing sendMail() causes it to be error free.


回答1:


Why you don't remove initialization from constructor into a @PostConstruct method?

@Stateless
public class EmailService {
    @EJB ContextFinder ctf;

    private static final String EMAIL_PASSWORD_JNDI_NAME = "EMAIL_PASSWORD";
    private static final String EMAIL_USERNAME_JNDI_NAME = "EMAIL_USERNAME";
    private static final String SMTP_SERVER_JNDI_NAME = "SMTP_SERVER";
    private static final String SMTP_PORT_JNDI_NAME = "SMTP_PORT";

    private String username;
    private String password;
    private String server;
    private Integer port;

    @PostConstruct
    public void init() { 
        username = (String) ctf.lookup(EMAIL_USERNAME_JNDI_NAME);
        password = (String) ctf.lookup(EMAIL_PASSWORD_JNDI_NAME);
        server = (String) ctf.lookup(SMTP_SERVER_JNDI_NAME);
        port = (Integer) ctf.lookup(SMTP_PORT_JNDI_NAME);
    }

    public void sendMail(String sendTo, String subject, String message) {
        // send mail
    }

}



回答2:


The injection has not yet happened.

In short:

  1. container calls the constructor
  2. then the injected fields get injected
  3. then the postconstruct stuff happens

Thus extract the lookups, as suggested, into a postconstruct method




回答3:


I would prefer to annotate your constructor with @Inject instead:

@Stateless
@LocalBean
public class EmailService {

    ....
    @Deprecated
    public EmailService(){}       

    @Inject 
    public EmailService(ContextFinder ctf) { 
        username = (String) ctf.lookup(EMAIL_USERNAME_JNDI_NAME);
        password = (String) ctf.lookup(EMAIL_PASSWORD_JNDI_NAME);
        server = (String) ctf.lookup(SMTP_SERVER_JNDI_NAME);
        port = (Integer) ctf.lookup(SMTP_PORT_JNDI_NAME);
    }

...
}

>> Using @PostConstruct is not exactly the same as you had. init() method will not work outside the container like in JUnit test.

You still need to provide the default not parametrized constructor (part of the The EJB 3.1 spec, section 4.9.2 Bean Classes).




回答4:


Just in case you are arriving here several years late as I did, it also turns out this error message may be misleading. I got the error,

[ERROR   ] CNTR4006E: The EVENT_MANAGER_BEAN enterprise bean in the MySpecialEJB.jar module of the some-ear-DEVELOPMENT-SNAPSHOT application failed to start. Exception: com.ibm.ejs.container.EJBConfigurationException: EJB class com.notarealcompany.event.EventManagerEJB must have a public constructor that takes no parameters : some-ear-DEVELOPMENT-SNAPSHOT#MySpecialEJB.jar#EVENT_MANAGER_BEAN
    at com.ibm.ws.ejbcontainer.jitdeploy.EJBUtils.validateEjbClass(EJBUtils.java:346)
    at [internal classes]
Caused by: java.lang.NoClassDefFoundError: commonj/work/Work
    at java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
    at java.lang.Class.getConstructor0(Class.java:3075)
    at java.lang.Class.getConstructor(Class.java:1825)
    at com.ibm.ws.ejbcontainer.jitdeploy.EJBUtils.validateEjbClass(EJBUtils.java:337)
    ... 1 more

I don't think the error message is trying to say that such a method does not exist. Rather it's saying that the loader couldn't find one that does exist that can be successfully loaded. The actual error (caused by) is the NoClassDefFoundError on the class commonj.work.Work. It turns out that there is also a warning earlier in the log:

[WARNING ] CWNEN0047W: Resource annotations on the fields of the com.notarealcompany.app.ApplicationEJB class will be ignored. The annotations could not be obtained because of the exception : java.lang.NoClassDefFoundError: Lcommonj/work/WorkManager;

So the Application EJB didn't get its resources injected either because commonj.work.WorkManager could not be found. Why that's a warning and not an error is a good question. I'm sure the app will do just fine without those resources, right? Basically, the jar with the APIs for commonj.work just didn't get included with the deployment. In our case, that's because the old application server provided it whereas the new application server did not. A little adjustment to the POM file and we got past that issue. Very misleading error message in my opinion.



来源:https://stackoverflow.com/questions/24868561/bean-does-not-have-a-public-constructor-that-does-not-take-parameters-error-de

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!