【问题标题】:How to avoid `IllegalAccessError` when inner classes access protected outer class super当内部类访问受保护的外部类超级时如何避免`IllegalAccessError`
【发布时间】:2018-05-22 15:22:24
【问题描述】:

我在 Spring Controller 中使用内部类。从它的父类超类访问受保护的字段/方法时遇到问题。

研究表明,这是由不同的类加载器在某种程度上造成的,但我对 Spring 的了解还不够,无法确定。

class SuperBaseController {
    protected String aField;

    protected void aMethod() {

    }
}

@Controller
class OuterMyController extends SuperBaseController {

    class Inner {

        public void itsMethod() {
            // java.lang.IllegalAccessError: tried to access method
            aMethod();
        }

        public void getField() {
            // java.lang.IllegalAccessError: tried to access field
            String s = aField;
        }
    }

    void doSomething () {
        // Obviously fine.
        aMethod();
        // Fails in the Inner method call.
        new Inner().itsMethod();

        // Obviously fine.
        String s = aField;
        // Fails in the Inner method call.
        new Inner().getField();
    }
}

是否有任何简单的技术可以避免/解决此问题?最好是那些不涉及制作字段/方法的public

我已经确认外部类的ClassLoader属性与超类的属性不一样。

【问题讨论】:

  • MyController.this.aMethod() 也会发生这种情况吗?
  • 您使用的是哪个编译器(和版本)?
  • @ErwinBolwidt - Java 1.8.0_151(目标 1.7) - Spring 3.0.7
  • 你在谈论类加载器,所以这是由于 Spring 使用一个类加载器加载 MyController 导致的(显然 Spring 将加载该控制器类),并且因为 Inner 在需要时才会加载,它会加载不同的类加载器吗?如果你在@PostConstruct 方法中摆弄Inner(这应该会触发类加载),那么它会起作用吗?
  • 你能发布确切的堆栈跟踪(至少是前几行)吗?该错误应该源于编译器在MyController 类中创建的合成访问器方法。确切的细节将提示原因。

标签: java spring inner-classes superclass illegalaccessexception


【解决方案1】:
class SuperBaseController {
protected String aField;

protected void aMethod() {

    }
}

@Controller
class OuterMyController extends SuperBaseController {


    public void itsMethod() {
        aMethod();
    }

    public void getField() {
        String s = aField;
    }

class Inner {
    public void innerItsMethod() {
        itsMethod();
    }
    public void innerGetField() {
    String s = getField();
    }
}

void doSomething () {
    aMethod();
    new Inner().itsMethod();
    String s = aField;
    new Inner().getField();
    }
}

这行得通吗?

此外,您可能必须定义哪个对象正在运行该方法。 (例如 Inner.getField() 或 SuperBaseController.aMethod())

【讨论】:

    【解决方案2】:

    怎么样:

    class OuterMyController extends SuperBaseController {
        private String getAField() { return super.aField; }
        protected void aMethod() { super.aMethod(); }
        ...
    }
    

    现在内部类使用其外部类的方法,因此没有非法访问,并且外部类的方法可以访问基类的受保护字段。

    【讨论】:

      【解决方案3】:

      我创建了以下类:

      public class BaseController
      {
          protected String field;
          protected void method()
          {
              System.out.println("I'm protected method");
          }
      }
      
      @RestController
      @RequestMapping("/stack")
      public class StackController extends BaseController
      {
          class Inner
          {
              public void methodInvocation()
              {
                  method();
              }
              public void fieldInvocation()
              {
                  field = "Test";
              }
          }
          @RequestMapping(value= {"/invoca"}, method= {RequestMethod.GET})
          public ResponseEntity<String> invocation()
          {
              Inner in = new Inner();
              in.fieldInvocation();
              in.methodInvocation();
              return new ResponseEntity<String>("OK", HttpStatus.OK);
          }
      }
      

      我尝试调用其余服务,但没有问题。现在我将这个配置用于 Spring(基于注解的注解):

      @Configuration
      @EnableTransactionManagement
      @ComponentScan(basePackages = { "it.spring.controller" })
      @PropertySource( value={"classpath:config.properties"}, encoding="UTF-8", ignoreResourceNotFound=false)
      public class DbConfig
      {
      }
      
      
      @Configuration
      @EnableWebMvc
      @Import(DbConfig.class)
      @PropertySource(value = { "classpath:config.properties" }, encoding = "UTF-8", ignoreResourceNotFound = false)
      public class WebMvcConfig extends WebMvcConfigurerAdapter
      {
      }
      

      如你所见,我使用了 @Import 注释,在我的 web.xml 中我使用了这个配置:

      <servlet>
          <servlet-name>SpringDispSvlt</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <init-param>
              <param-name>contextClass</param-name>
              <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
          </init-param>
          <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>it.WebMvcConfig</param-value>
          </init-param>
          <init-param>
              <param-name>dispatchOptionsRequest</param-name>
              <param-value>true</param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>
          <async-supported>true</async-supported>
      </servlet>
      

      通过使用此配置,我在调用内部类中的受保护字段和/或方法时没有问题

      如果您无法使您的配置适应这一配置.. 可以发布您使用的配置吗?

      希望对你有用

      安杰洛

      【讨论】:

        猜你喜欢
        • 2015-11-05
        • 2011-03-19
        • 1970-01-01
        • 1970-01-01
        • 2020-11-02
        • 2013-04-08
        • 1970-01-01
        • 1970-01-01
        • 2016-07-23
        相关资源
        最近更新 更多