-2

多年来,我一直在使用 Tomcat 6.0.26-6.0.35 和 JSF 2 Mojarra,直到 2.1.2 的各种版本,我已经使用了几个月。我有几个请求范围和会话范围的 bean,代码如下:

private @Resource(name="jdbc/cLabs", mappedName="jdbc/cLabs") DataSource cLabs;

它已正确注入到我使用过的每个版本的 Tomcat 6 中。我也有其他类型的@Resource也不起作用,所以不仅仅是DataSource resources.我尝试切换到 Tomcat 7.0.27,突然这些结构都不再起作用了。资源未注入。我也有其他类型的@Resource也不起作用,所以它不仅仅是DataSource资源。然而,在每种情况下,命名的资源确实存在,并且可以通过例如

new InitialContext().lookup("java:comp/env/jdbc/cLabs");

[它们由 context.xml 中的元素定义]

这当然是皇家 PITA,因为我在一两年前花了一些时间将后者替换为前者。我必须用 Tomcat 7 编织一些其他魔法咒语才能让它再次工作吗?

请注意,资源正确地注入到 Servlet 中,因此它并没有被完全破坏。Tomcat 和 JSF 之间的一些交互。

4

4 回答 4

3

我的猜测是某些原因导致 org.apache.catalina.core.DefaultInstanceManager 忽略注释,因为面需要 @PostConstruct 方法与 @Resource 字段分开处理。我创建了一个适用于使用@Resource 注释的字段的解决方法。

将以下内容添加到 web.xml:

<context-param>
    <param-name>com.sun.faces.injectionProvider</param-name>
    <param-value>com.example.faces.Tomcat7InjectionProvider</param-value>
</context-param>

并将该类添加到您的源中:

package com.example.faces;

import java.lang.reflect.Field;

import javax.annotation.Resource;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;

import org.apache.catalina.util.Introspection;

import com.sun.faces.spi.InjectionProviderException;
import com.sun.faces.vendor.WebContainerInjectionProvider;

public class Tomcat7InjectionProvider extends WebContainerInjectionProvider {

    public Tomcat7InjectionProvider(ServletContext servletContext) {
    }

    @Override
    public void inject(Object managedBean) throws InjectionProviderException {
        if (managedBean != null) {
            // see org.apache.catalina.core.DefaultInstanceManager
            Field[] fields = Introspection.getDeclaredFields(managedBean.getClass());
            for (Field field : fields) {
                // field may be private
                field.setAccessible(true);
                if (field.isAnnotationPresent(Resource.class)) {
                    Resource annotation = null;
                    try {
                        annotation = field.getAnnotation(Resource.class);
                        Context ctx = new InitialContext();
                        Object resource = ctx.lookup("java:comp/env/" + annotation.name());
                        field.set(managedBean, resource);
                    } catch (Exception e) {
                        throw new InjectionProviderException("cannot find resource " + annotation.name(), e);
                    }
                }
            }
        }
    }
}
于 2014-02-24T03:52:30.063 回答
2

回答我自己的问题,@JeffE 答案的改进版本。基本问题是:

  1. ATomcat6InjectionProvider由 JSF 2.0 提供,但在某些时候被删除。
  2. 正如 JeffE 指出的那样,默认WebContainerInjectionProvider不处理 @Resource 注释。

您可以在没有上下文条目的情况下克服此问题web.xml,如下所示:

  1. 创建一个名为的文件META-INF/services/com.sun.faces.spi.injectionprovider并向其中添加以下行:

    com.sun.faces.vendor.Tomcat7InjectionProvider:org.apache.catalina.core.DefaultInstanceManager
    

    这一行的意思是,如果部署中存在第二个类,则将第一个类用作注入提供程序。上面的第二个类是 Tomcat 7 的一部分。

  2. 编译以下类。

此版本包含对 JeffE 版本的许多改进。具体来说:

  • 它根据@Resource@ResourcesJavadoc的要求处理超类
  • 它在类级别处理@Resource和注释@Resources
  • 它根据Javadoc@Resource的要求处理带有 注释的方法@Resource
  • 它按照Javadoc的要求正确处理 s 的空属性或缺失name属性@Resource@Resource
  • 它恢复了Field的原始访问权限
  • 它不依赖于 Tomcat 类。

如果更改包名称,请调整上面的包名称。

package com.sun.faces.vendor;

import com.sun.faces.spi.DiscoverableInjectionProvider;
import com.sun.faces.spi.InjectionProviderException;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;

/**
 * @author Jeff E
 * @author Esmond Pitt Improvements named above.
 * 
 * @see javax.annotation.Resource
 *
 * @see <a href="http://stackoverflow.com/a/21978577/207421">This StackOverflow
 * answer, although what org.apache.catalina.util.Introspection may be and where
 * it lives remains a mystery.</a>
 */
public class Tomcat7InjectionProvider
    extends DiscoverableInjectionProvider
{
    private Logger logger = Logger.getLogger(this.getClass().getName());
    private ServletContext  servletContext;

    private WebContainerInjectionProvider   delegate = new WebContainerInjectionProvider();

    public Tomcat7InjectionProvider(ServletContext servletContext)
    {
        logger.config("constructed");
        this.servletContext = servletContext;
    }

    @Override
    public void inject(Object managedBean) throws InjectionProviderException
    {
        logger.log(Level.CONFIG, "managedBean={0}", new Object[]{managedBean.getClass().getName()});
        Class<?> clazz = managedBean.getClass();
        do
        {
            List<Resource>  classResources = new LinkedList<>();
            // Process class-level @Resources and @Resource
            if (clazz.isAnnotationPresent(Resources.class))
            {
                Resources annotation = clazz.getAnnotation(Resources.class);
                for (Resource resource : annotation.value())
                {
                    classResources.add(resource);
                }
            }
            if (clazz.isAnnotationPresent(Resource.class))
            {
                Resource    annotation = clazz.getAnnotation(Resource.class);
                classResources.add(annotation);
            }
            for (Resource annotation : classResources)
            {
                String  name = annotation.name();
                // Make sure the resource exists.
                try
                {
                    Context ctx = new InitialContext();
                    Object resource = ctx.lookup("java:comp/env/" + name);
                }
                catch (NamingException exc)
                {
                    throw new InjectionProviderException("checking class resource " + annotation.name()+" of "+clazz.getName(), exc);
                }
            }
            // Process fields with @Resource
            // see org.apache.catalina.core.DefaultInstanceManager
//            Field[] fields = Introspection.getDeclaredFields(managedBean.getClass());
            Field[] fields = managedBean.getClass().getDeclaredFields();
            for (Field field : fields)
            {
                if (field.isAnnotationPresent(Resource.class))
                {
                    Resource annotation = field.getAnnotation(Resource.class);
                    String name = annotation.name();
                    logger.log(Level.CONFIG, "injecting @Resource(name=\"{2}\") into {0}.{1}", new Object[]
                        {
                            managedBean.getClass().getName(), field.getName(), name
                        });
                    try
                    {
                        Context ctx = new InitialContext();
                        Object resource;
                        if (name != null && name.length() > 0)
                        {
                            resource = ctx.lookup("java:comp/env/" + name);
                        }
                        else
                        {
                            resource = ctx.lookup(clazz.getName() + "/" + field.getName());
                        }
                        // field may be private
                        boolean accessibility = field.isAccessible();
                        try
                        {
                            field.setAccessible(true);
                            field.set(managedBean, resource);
                        }
                        finally
                        {
                            field.setAccessible(accessibility);
                        }
                    }
                    catch (NamingException | IllegalAccessException exc)
                    {
                        throw new InjectionProviderException("injecting resource " + annotation.name()+" into "+clazz.getName()+"."+field.getName(), exc);
                    }
                }
            }
            // Process methods with @Resource
            for (Method method : clazz.getDeclaredMethods())
            {
                if (method.isAnnotationPresent(Resource.class)
                && method.getName().startsWith("set")
                && method.getName().length() > 3
                && method.getReturnType() == void.class
                && method.getParameterTypes().length == 1)
                {
                    // It's a setter with @Resource
                    Resource annotation = method.getAnnotation(Resource.class);
                    String name = annotation.name();
                    logger.log(Level.CONFIG, "injecting @Resource(name=\"{2}\") via {0}.{1}", new Object[]
                        {
                            managedBean.getClass().getName(), method.getName(), name
                        });
                    try
                    {
                        Context ctx = new InitialContext();
                        Object resource;
                        if (name != null && name.length() > 0)
                        {
                            resource = ctx.lookup("java:comp/env/" + name);
                        }
                        else
                        {
                            name = method.getName().substring(3);
                            name = name.substring(0,1).toLowerCase()+name.substring(1);
                            resource = ctx.lookup(clazz.getName() + "/" + name);
                        }
                        // method may be private
                        boolean accessibility = method.isAccessible();
                        try
                        {
                            method.setAccessible(true);
                            method.invoke(managedBean, resource);
                        }
                        finally
                        {
                            method.setAccessible(accessibility);
                        }
                    }
                    catch (NamingException | IllegalAccessException | InvocationTargetException exc)
                    {
                        throw new InjectionProviderException("injecting resource " + annotation.name()+" via "+clazz.getName()+"."+method.getName(), exc);
                    }
                }
            }
        } while ((clazz = clazz.getSuperclass()) != Object.class);
    }

    @Override
    public void invokePostConstruct(Object managedBean) throws InjectionProviderException
    {
        logger.log(Level.CONFIG, "managedBean={0}", new Object[]{managedBean});
        delegate.invokePostConstruct(managedBean);
    }

    @Override
    public void invokePreDestroy(Object managedBean) throws InjectionProviderException
    {
        logger.log(Level.CONFIG, "managedBean={0}", new Object[]{managedBean});
        delegate.invokePreDestroy(managedBean);
    }
}

E&OE

于 2014-11-18T03:34:32.707 回答
1

另一种可能性,但我认为不太可能;您是metadata-complete="true"在 web.xml 还是 web-fragment.xml 文件中使用。

已定义的 metadata-complete:metadata-complete 属性定义此模块的此部署描述符和其他相关部署描述符(例如,Web 服务描述符)是否完整,或者是否应检查此模块可用并与此应用程序一起打包的类文件用于指定部署信息的注释。如果 metadata-complete 设置为“true”,则部署工具必须忽略任何指定部署信息的注释,这些注释可能存在于应用程序的类文件中。如果未指定 metadata-complete 或设置为“false”,则部署工具必须检查应用程序的类文件中的注释,如规范所指定的。

示例 web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app metadata-complete="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>ResourceTest</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
 </welcome-file-list>
</web-app>
于 2013-06-28T20:42:57.140 回答
-1

抱歉,由于代表,我无法发表评论,否则我会要求对您看到的错误/等进行更多说明。话虽如此,我已经使用 java 完整版“1.6.0_51-b11-457”对 Tomcat 7.0.27 和 Tomcat 7.0.41 进行了测试,并且能够使用 @Resource。

上下文.xml

<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <Resource name="jdbc/Sample" auth="Container"
      type="javax.sql.DataSource" username="nbuser" password="nbuser"
      driverClassName="org.apache.derby.jdbc.ClientDriver" 
      url="jdbc:derby://localhost/Sample"
      maxActive="8" maxIdle="4"/>
</Context>

资源测试.java

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

@WebServlet("/ResourceTest")
public class ResourceTest extends HttpServlet {
  private static final long serialVersionUID = 1L;
  @Resource(name="jdbc/Sample")
  private DataSource ds;


public ResourceTest() {
    // TODO Auto-generated constructor stub
}


protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
    PrintWriter out = response.getWriter();
    out.println("<body>");


    try {
        //Context initCtx = new InitialContext();
        //Context envCtx = (Context) initCtx.lookup("java:comp/env");
        //DataSource ds = (DataSource) envCtx.lookup("jdbc/Sample");
        Connection conn = ds.getConnection();
        Statement s = conn.createStatement();
        s.execute("Select * From \"NBUSER\".\"Friends\"");
        ResultSet rs = s.getResultSet();
        while (rs.next()) {
            out.println(rs.getString("NAME") + " is my friend.");
        }
        conn.close();
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    out.println("</body>");

}

/**
 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
 *      response)
 */
protected void doPost(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
}

}
于 2013-06-27T16:50:33.040 回答