@Resource 注解在 Tomcat 10.0.10 中不起作用

Posted

技术标签:

【中文标题】@Resource 注解在 Tomcat 10.0.10 中不起作用【英文标题】:@Resource annotation doesn't work in Tomact 10.0.10 【发布时间】:2021-11-13 21:35:47 【问题描述】:

最近我尝试了 Tomcat 10.0.10,当尝试将连接池作为 JNDI 资源注入时,发现 @Resource 注释不起作用。

然后我尝试通过创建InitialContext 以编程方式获取它,并且它起作用了。最初我认为它仅适用于 java:comp/env/jdbc,所以我尝试使用如下所示的简单 bean,并尝试使用 @Resource 注释注入它,但它不再起作用。当我尝试通过创建InitialContext 以编程方式获取它时,它可以工作。然后我检查 @PostConstruct@PreDestroy 注释是否有效,发现它们也不起作用。

package lk.ijse.test.tomcatdbcp;
    
public class Something 

<?xml version="1.0" encoding="UTF-8"?>
<Context>

    <Resource name="bean/Something" auth="Container"
              type="lk.ijse.test.tomcatdbcp.Something"
              factory="org.apache.naming.factory.BeanFactory"
              />

</Context>
<?xml version="1.0" encoding="UTF-8"?>
<web-app metadata-complete="false" xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">

    <resource-env-ref>
        <resource-env-ref-name>bean/Something</resource-env-ref-name>
        <resource-env-ref-type>lk.ijse.test.tomcatdbcp.Something</resource-env-ref-type>
    </resource-env-ref>
</web-app>
package lk.ijse.test.tomcatdbcp;

import java.io.*;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;

import javax.naming.InitialContext;
import javax.naming.NamingException;

@WebServlet(name = "helloServlet", value = "/hello", loadOnStartup = 1)
public class HelloServlet extends HttpServlet 
    private String message;

    @Resource(name= "java:comp/env/bean/Something")
    private Something something;

    @PostConstruct
    public void doSomething()
        System.out.println("Does it work?");
    

    public void init() 
        message = "Hello World!";
        try 
            InitialContext ctx = new InitialContext();
            Something lookup = (Something) ctx.lookup("java:comp/env/bean/Something");
            System.out.println(lookup);
            System.out.println(something); // null
         catch (NamingException e) 
            e.printStackTrace();
        
    

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException 
        response.setContentType("text/html");

        // Hello
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>" + message + "</h1>");
        out.println("</body></html>");
    

    public void destroy() 
    

为了重现同样的问题,我在这里创建了一个示例 repo:https://github.com/sura-boy-playground/play-with-tomcat10 (完整的代码可以在那里找到)

起初,我使用了javax.annotation.Resource 注解,所以我认为这是因为javax.*jakarta.* 命名空间发生了变化。然后我用jakarta.annotation.Resource试了一下,结果还是一样。

我用 Tomcat 9.0.41 加上 javax.* 命名空间尝试了相同的应用程序,它运行良好。

我需要在 Tomcat 10.0.10 上做任何额外的事情来启用这些注释吗?我挖掘了 Tomcat 10 文档,但我找不到任何与我的问题。

我发现之前在 Tomcat 7 中也有类似的情况,但我现在不喜欢这种解决方法。 Tomcat @Resource annotations API annotation stops working in Tomcat 7

【问题讨论】:

【参考方案1】:

您应该将jakarta.annotation 依赖项的范围声明为provided

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.0.0</version>
    <scope>provided</scope>
</dependency>

如果您有两个jakarta.annotation.Resource 副本(一个在common 类加载器中,一个在应用程序的类加载器中),这两个类是不同的。 InstanceManager 将查找使用 common 类加载器的 @Resource 副本注释的字段,而 something 字段将使用您的 web 应用程序的 @Resource 副本进行注释。

虽然将jakarta.annotation 添加到与provided 不同的范围肯定是一个错误,但它可能是一个常见错误,并且Tomcat 有一个故障安全功能,它会忽略与您的应用程序一起分发的某些类(参见source code) .由于jakarta.annotation.* 类不在其中,您可以提交(愿望清单)错误报告。

备注:如果您使用 Java 11 或更高版本,您将在 Tomcat 9.0 中遇到同样的问题。在 Java 11 之前,javax.annotation.* 类包含在 JRE 中。 Servlet 容器需要先查看 bootstrap/JRE 类加载器 查看 webapp 类加载器(覆盖 javax.* 类违反了 Java 的许可证),因此 Tomcat 永远不会找到类的附加副本。

【讨论】:

太棒了,非常感谢。在您发布答案之前,我正在调试 DefaultInstanceManager 类,我为 method.isAnnotationPresent(annotation) 设置的手表表达式一直给我错误的 @PostConstruct 而在 servlet 中它一直返回 true,我想知道发生了什么直到你想出并用两个类加载器清除这个想法。再次非常感谢您的澄清。你拯救了我的一天!

以上是关于@Resource 注解在 Tomcat 10.0.10 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

@Resource注解完成自动装配

@Resource与@Autowired注解的区别

@Autowired和@Resource注解的区别?

Spring中@Autowired注解@Resource注解的区别

Autowried注解和Resource注解的区别

Spring 注解 @Resource和@Autowired