【问题标题】:Can't get annotations from classes loaded by custom classloader in tomcat无法从tomcat中自定义类加载器加载的类中获取注释
【发布时间】:2018-04-01 21:30:11
【问题描述】:

给定班级org.popper.example.pages.Login

@Page(name="Login")
public interface Login {
}

导出到c:\pos\example.jar 和下面的servlet

public class PopperServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {
        URLClassLoader ucl = new URLClassLoader(new URL[] {new File("c:/pos/example.jar").toURI().toURL()});
        System.out.println(Arrays.asList(ucl.loadClass("org.popper.example.pages.Login").getAnnotations()));
    }

    public PopperServlet() throws MalformedURLException, ClassNotFoundException {
        URLClassLoader ucl = new URLClassLoader(new URL[] {new File("c:/pos/example.jar").toURI().toURL()});
        System.out.println(Arrays.asList(ucl.loadClass("org.popper.example.pages.Login").getAnnotations()));
   }
}

以main方式运行代码显示预期结果

[@org.popper.fw.annotations.Page(name=Login)]

在tomcat中作为servlet运行代码找不到注解

[]

谁能告诉我为什么?

【问题讨论】:

  • 和往常一样:看classloader-hierarchy new URLClassLoader(new URL[] {new File("c:/pos/example.jar").toURI().toURL()} , PopperServlet.class.getClassloader());成功了。但令人惊讶的是没有找到注释而是一个cnfe

标签: java tomcat annotations classloader


【解决方案1】:

和往常一样:注意类加载器层次结构!

new URLClassLoader(new URL[] {new File("c:/pos/example.jar").toURI().toURL()}, PopperServlet.class.getClassloader());

成功了。但令人惊讶的是没有找到注释而是ClassNotFoundExceptionNoClassDefError,这就是我在加载类时找不到注释时所期望的......

【讨论】:

  • 我正在创建一个 Maven 插件,遇到了类似的问题。感谢分享。我希望我能更多地投票给这个答案,因为很明显,没有很多开发人员会弄乱类加载器。
  • 当 tomcat webcontext 类加载器不处理类注释时,我遇到了同样的问题。这解决了它!
【解决方案2】:

是的,你肯定找不到你的注释,你必须注释你的注释才能在运行时保持活动状态,添加:

@Retention(RetentionPolicy.RUNTIME)

Retention Java doc : 指明注解类型的注解要保留多长时间。如果注释类型声明中没有保留注释,则保留策略默认为 RetentionPolicy.CLASS。

更多详情:here

【讨论】:

    【解决方案3】:

    我也遇到了同样的问题。我通过我的自定义类加载器解决了它,也许你可以尝试一下。

    类加载器代码:

    import java.util.HashMap;
    import java.util.Map;
    /**
     * Load class from byte[] which is compiled in memory.
     *
     * @author David
     */
    class CustomClassLoader extends ClassLoader {
    
        // class name to class bytes:
        private Map<String, byte[]> classBytes = new HashMap<String, byte[]>();
    
        public CustomClassLoader(Map<String, byte[]> classBytes) {
            super(CustomClassLoader.class.getClassLoader());
            Thread.currentThread().setContextClassLoader(this);
            this.classBytes.putAll(classBytes);
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] buf = classBytes.get(name);
            if (buf == null) {
                return super.findClass(name);
            }
            classBytes.remove(name);
            return defineClass(name, buf, 0, buf.length);
        }
    
    }
    

    客户代码:

    byte[] code = this.createEntity(logicalTable, keyCount, relationMap);
    Map<String, byte[]> results = Maps.newHashMap();
    results.put(this.entityPackage + "." + logicalTable.getTableName(), code);
    CustomClassLoader classLoader = new CustomClassLoader(results);
    Class<?> clazz = classLoader.findClass(this.entityPackage + "." + logicalTable.getTableName());
    

    【讨论】:

    • 我认为没有必要调用super.findClass(name),因为loadClass(name)已经在默认实现中委托给了父级。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多