Can Java Annotations help me with this?

前端 未结 7 562
日久生厌
日久生厌 2021-01-13 14:52

I\'m wondering if there is a way to specify that a method gets called in advance of a class method. I know something like this should be posssible, since JUnit has before(),

相关标签:
7条回答
  • 2021-01-13 15:06

    Just call Init() at the start of foo()?

    0 讨论(0)
  • 2021-01-13 15:06

    Have a look at AspectJ. It will help you do what you are asking.

    0 讨论(0)
  • 2021-01-13 15:10

    Why are you doing this? Are you attempting to avoid having a constructor with many arguments (using setters then calling init) or are you avoiding having many constructors that all have similar arguments? If this is the case, you can use a builder pattern.

    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);
    }
    

    If this doesn't work, I'd suggest looking into AOP. I'd mark the methods that must have init() called already with an annotation [perhaps @requires('init') or the like] and make you AOP framework insert the proper code. Be careful that multiple init's either don't have side effects or that you do proper synchronization on your has_init_been_called state.

    0 讨论(0)
  • 2021-01-13 15:20

    There is no direct way to do this in the java language. What you are seeing in JUnit is the framework making a decision about how to run the methods by calling the methods annotated with @Before first. It is very easy to find annotated methods and run them, but that is the responsibility of the caller.

    The problem you present is too simple to know the right way to a solution. AspectJ does address this need by manipulating the byte code (essentially calling the init() method when foo() is called by changing the bytecode to make that happen), but I can't imagine introducing that as a hack around a problem.

    If you can present an interface or a wrapper object to this class, you could do it that way. But I would suggest you post the ugly hack that got you into this situation in the first place in a separate question, and then post how your current hack solution requires that method calls be intercepted and why that is the case, and if there are better workarounds. That way we can help address the underlying need better.

    0 讨论(0)
  • 2021-01-13 15:25

    If you have interface A you can wrap instances of this interface with Proxy and inside invoke method of its InvocationHandler you are free to check whether method is annotated and perform some actions depending on that:

    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));
    

    Or you can try using "before" advice of AspectJ. It will be something like the next:

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

    I'm not sure that snippets are working, they just illustrate idea.

    0 讨论(0)
  • 2021-01-13 15:25

    I assume that the problem here is as follows:

    1. You have a constructor that can partially build the object, but can't completely build it because of the way the class must be constructed. (I can't think of an example offhand.)
    2. So you need an init() method that will finish construction.
    3. So you want to have some kind of guarantee that init() will be called right after the constructor.

    My suggestion is to use a factory object or method. The simplest way is to make the constructor private, add a construct() method with the parameters of the constructor or something of that sort, and then have the construct() method both create the object and call init(), then return it.

    0 讨论(0)
提交回复
热议问题