2015-03-17 8 views
3

У меня есть своеобразное требование, когда мне нужно убедиться, что только конкретный метод из одного класса разрешен для вызова общедоступного (нестатического) метода из второго класса. Наследование не может быть использовано.Java - Как ограничить вызов метода по определенному методу

Одним из вариантов является использование StackTrace следующим образом:

ClassA.java

package org.rnd.stack; 


public class ClassA { 
    public void methodA() throws IllegalAccessException { 
     Exception fake = new Exception("FAKE-IGNORE"); 
     StackTraceElement[] stack = fake.getStackTrace(); 
     StackTraceElement st = stack[1]; 
     if ("org.rnd.stack.ClassB".equals(st.getClassName()) 
       && "methodB".equals(st.getMethodName())) { 
      System.out.println("You are allowed to call"); 
     } else { 
      throw new IllegalAccessException("You are not allowed to call"); 
     } 
    } 
} 

ClassB.java

package org.rnd.stack; 

public class ClassB { 
    public void methodB() throws IllegalAccessException { 
     new ClassA().methodA(); 
    } 

    public void illegalMethod() throws IllegalAccessException { 
     new ClassA().methodA(); 
    } 

    public static void main(String[] args) { 
     try { 
      new ClassB().methodB(); 
     } catch (IllegalAccessException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 
} 

Теперь выше решение работает хорошо, но из-за для контроля качества при проверке кода мне нужно придумать другое (или, вернее) лучшее решение. Есть ли лучший способ достичь этого?

+8

Это очень странное требование. Зачем тебе это? –

+1

Точно ... обычно для странных требований, подобных этому, вы должны думать об изменении своей логики, чтобы полностью избежать ситуации. – Neilos

+0

Изменение общедоступного метода для ссылки на объект и проверка того, что тип ссылки объекта с использованием 'instanceof' может быть менее хрупким. Else напишите некоторые специальные модульные тесты. Но предложение @Neilos более разумно. – Bathsheba

ответ

1

Способ улучшить ваш метод состоит в том, что вам не нужно создавать исключение для получения stacktrace, вы можете использовать методы потоков.

StackTraceElement[] stack = Thread.currentThread().getStackTrace(); 

Возможно, вы захотите использовать класс вместо почерка пакета. Например:

if (ClassB.class.getName().equals(st.getClassName()) 
       && "methodB".equals(st.getMethodName())) { 
    System.out.println("You are allowed to call"); 
} else { 
    throw new IllegalAccessException("You are not allowed to call"); 
} 

Кроме того, я не знаю, как вы можете сделать это лучше без изменения логики или с использованием наследования.

+0

Почему downvote? Как я могу исправить это, если вы не скажете мне? – PhoneixS

7

Правильная вещь - пересмотреть свое требование. Метод, который может быть вызван только некоторыми другими кодами кода, несовместим с public. Общая рекомендация - использовать private-private для предотвращения внешних вызовов и принять, что любой код в пакете может вызвать метод, но не будет, потому что вы или ваша команда его проверяете.

Видимость метода в конечном счете не является безопасным решением для предотвращения выполнения; у кого-то есть ваши .class-файлы и возможность выполнять их на машине, они могут делать что угодно. Вы не должны тратить слишком много времени, пытаясь заблокировать вызовы методов. Вместо этого четко документируйте намерение метода (например, «Вспомогательная функция для methodB(), пожалуйста, не используйте в другом месте.») и доверяйте людям, развивающимся с вами, знаете, что они делают. Вы даже можете дать методу четкое имя, например dangerousMethodBForInternalUseOnly(), если вы действительно хотите победить людей над головой.

Вы также можете быть заинтересованы в dependency-injection, который представляет собой шаблон дизайна, который использует систему типов для защиты (не предотвращения) людей от выполнения опасного кода. Вот несколько переговоров о Guice, популярной рамки DI, которая идет более подробно о концепции:


Все, что сказал, как ученый упражнение - вот один из вариантов ограничения обращения метода к фиксированному числу кодировок - полагаться на общий секрет. Добавьте поле Object secret к вашему заблокированному методу и вызовите сбой метода, если прошедший secret не соответствует жестко заданному значению (private static final Object SECRET = new Object()). Затем вы можете использовать другие механизмы, чтобы делиться секретом только с кодами, которые вы разрешаете (например, иметь статический инициализатор в заблокированном классе, публиковать его в классах, которым вы явно доверяете).

Очевидно, что это все еще может быть обработано вредоносным разработчиком, и это довольно грубо, но это обеспечило бы какое-то поведение блокировки, предполагая, что вы можете доверять своему заблокированному классу, который не будет изменен без вашего ведома.

1
  1. Pass звонящие в качестве аргумента и проверить, если абонент instanceof требуется класс - многопоточное решение, не может обойти по reflecion.
  2. Get стека потока свалка и проверить верхний вход - странный, тяжелый, но возможно
  3. Создание прокси - но это будет overheaded вариант решения 1.
1

Вы можете быть в состоянии удовлетворить это требование, используя класс Class способ getEnclosingMethod(). Это, как он работает (docs here):

Если этот объект класса представляет собой локальный или анонимный класс в методе, возвращает объект, представляющий метода немедленно заключающий метод базового класса.

  1. Сигнатура methodA() должен быть изменен, чтобы принять Class объект в качестве параметра.

    public void methodA(Class c) { } 
    
  2. Правовой метод из ClassB должен создать анонимный объект класса, и передать его класс в качестве аргумента methodA().

    public void methodB() throws IllegalAccessException, NoSuchMethodException { 
        new ClassA().methodA(new Object(){}.getClass()); 
    } 
    
  3. Затем methodA() следует проверить, если метод класса вшита действительно methodB() от ClassB.

    public void methodA(Class c) throws IllegalAccessException, NoSuchMethodException { 
        if (c.getEnclosingMethod().equals(ClassB.class.getMethod("methodB"))) { 
         System.out.println("You are allowed to call"); 
        } else { 
         throw new IllegalAccessException("You are not allowed to call"); 
        } 
    } 
    

Недостатки:

  • Вы должны создавать новый объект каждый раз, когда вы называете methodB(). Это может стать дорогостоящим в зависимости от того, сколько раз вы это делаете. Вместо этого, вы можете создать локальный класс внутри methodB() поэтому нет не создание объекта над головой:

    public void methodB() throws IllegalAccessException, NoSuchMethodException { 
        class Local {}; 
        new ClassA().methodA(Local.class); 
    } 
    
  • Вам нужно обрабатывать NoSuchMethodException и изменить код, если methodB() изменения имени;

  • Кто-то, у кого есть доступ к коду, все еще может изменить methodB(), чтобы вернуть класс анонимного объекта другому методу и использовать его для вызова methodA(). Так что это не идеальное решение, но может быть достаточно для вашего использования.

 Смежные вопросы

  • Нет связанных вопросов^_^