Могут ли аннотации Java помочь мне в этом? - PullRequest
3 голосов
/ 22 сентября 2009

Мне интересно, есть ли способ указать, что метод вызывается до метода класса. Я знаю, что подобное должно быть возможным, так как JUnit имеет before (), то, что я хочу сделать, похоже.

Вот конкретный пример того, что я хотел бы сделать

class A {

 public void init(int a) {
  System.out.println(a);
 }

 @magic(arg=1)
 public void foo() { 
   //
 }

 public static void main() {
   A a = new A();
   a.foo();
 }
}

//Output: 1

Обычно я хочу, чтобы аннотация сообщала компилятору или jvm-вызову init () перед foo ()

Ответы [ 7 ]

8 голосов
/ 22 сентября 2009

Если у вас есть interface A, вы можете обернуть экземпляры этого интерфейса с помощью Proxy и внутри invoke метода его InvocationHandler вы можете проверить, является ли метод комментируется и выполняет некоторые действия в зависимости от этого:

class Initalizer implements InvocationHandler {
    private A delegate;
    Initializer(A delegate) {
        this.delegate = delegate;
    }

    public Object invoke(Object proxy, Method method, Object[] args) {
        if (method.isAnnotationPresent(magic.class)) {
            magic annotation = method.getAnnotation(magic.class);
            delegate.init(magic.arg);
        }
        method.invoke(delegate, args);
    }
} 
A realA = ...;
A obj = Proxy.newProxyInstance(A.class.getClassLoader(), new Class[] {A.class}, new Initializer(realA));

Или вы можете попробовать использовать "до" совета AspectJ. Это будет что-то вроде следующего:

@Aspect
public class Initializer {
    @Before("@annotation(your.package.magic) && target(obj) && @annotation(annotation)")
    private void initialize(A obj, magic annotation) {             
         a.init(annotation.arg);
    }
}

Я не уверен, что фрагменты работают, они просто иллюстрируют идею.

3 голосов
/ 22 сентября 2009

Почему ты это делаешь? Вы пытаетесь избежать использования конструктора со многими аргументами (используя сеттеры, затем вызываете init), или вы избегаете иметь много конструкторов, которые имеют одинаковые аргументы? В этом случае вы можете использовать шаблон компоновщика.

public class Foo {
int a, b, c, d, e;
Foo(int a, int b, int c, int d, int e) { this.a=a; /*etc*/ }
}

public class FooBuilder {
int a,b,c,d,e;
FooBuilder A(int a) { this.a=a; return this;}
FooBuilder B(int b) { this.b=b; return this;}
//etc
Foo create(){ return new Foo(a,b,c,d,e);
}

Если это не сработает, я бы посоветовал изучить АОП. Я бы пометил методы, которые уже должны вызывать init (), уже с аннотацией [возможно, @requires ('init') или тому подобное) и заставил бы вашу AOP-инфраструктуру вставлять правильный код. Будьте внимательны, если у нескольких инициаторов нет побочных эффектов или вы правильно синхронизируете состояние has_init_been_called.

2 голосов
/ 22 сентября 2009

АОП делает это с так называемыми pointcut AspectJ может иметь то, что вам нужно.

Проще говоря, вы бы добавили перед советом метод foo (), который вызвал бы init ()

2 голосов
/ 22 сентября 2009

Просто вызовите Init () в начале foo ()?

1 голос
/ 22 сентября 2009

Нет прямого способа сделать это на языке Java. То, что вы видите в JUnit - это среда, принимающая решение о том, как запускать методы, сначала вызывая методы, аннотированные @Before. Очень легко найти аннотированные методы и запустить их, но это является обязанностью вызывающей стороны.

Проблема, которую вы представляете, слишком проста, чтобы знать правильный путь к ее решению. AspectJ действительно решает эту проблему, манипулируя байтовым кодом (по сути, вызывая метод init (), когда вызывается foo (), изменяя байт-код, чтобы это произошло), но я не могу представить это как хак для решения проблемы.

Если вы можете представить интерфейс или объект-оболочку этому классу, вы можете сделать это таким образом. Но я бы посоветовал вам опубликовать некрасивый хак, который первым поставил вас в эту ситуацию, в отдельном вопросе, а затем опубликуйте, как ваше текущее хакерское решение требует, чтобы вызовы методов были перехвачены и почему это так, и если есть лучшие обходные пути. Таким образом, мы сможем помочь лучше удовлетворить основную потребность.

0 голосов
/ 22 сентября 2009

Я предполагаю, что проблема здесь в следующем:

  1. У вас есть конструктор, который может частично построить объект, но не может полностью построить его из-за способа, которым класс должен быть построен. (Я не могу придумать пример от руки.)
  2. Итак, вам нужен init() метод, который завершит строительство.
  3. Итак, вы хотите иметь какую-то гарантию, что init() будет вызываться сразу после конструктора.

Я предлагаю использовать фабричный объект или метод. Самый простой способ - сделать конструктор частным, добавить метод construct() с параметрами конструктора или что-то в этом роде, а затем метод construct() создать объект, вызвать init() и вернуть его.

0 голосов
/ 22 сентября 2009

Посмотрите на AspectJ .Это поможет вам сделать то, что вы просите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...