我需要能够标记方法,以便在多次调用它们时抛出RuntimeException。
我正在尝试强制执行一些单一的赋值语义,并且我的类的参数数量太大而无法放入单个构造函数中,我需要能够使这些类JAXB
也知道,所以对象需要是可变的,但我想强制执行单一赋值语义。
我很确定我可以用Aspects做到这一点,但我真的希望能够使用我自己的Annotations处理器。
我知道如何使用Python中的装饰器来做到这一点。
如何编写一个Annotation处理器,它可以在运行时拦截对带注释方法的调用,而不仅仅是在编译时?
我认为我正在使用Dynamic Proxies拦截方法调用,我只需要弄清楚如何将它们与我的Annotation处理器集成。
动态代理要求您使用界面,这是繁琐的方式,我现在有CGLib MethodInterceptor工作,对截获和装饰的内容要求更少,但代价是添加了依赖性。
答案 0 :(得分:3)
不,没有什么可以随时使用的。 AspectJ似乎是让它以更一般的方式工作的唯一方法。正如JB Nizet所指出的那样 - 注释应该有一个解析器来解析它。
但是,我会建议一个更好,更简单的解决方案 - Builder模式。它看起来像什么:
FooBuilder
(它也可能是一个静态的内部类),它是可变的,每个字段都有一个setter和getter FooBuilder
有一个build()
方法,可返回Foo
Foo
有一个只占FooBuilder
的构造函数,并在那里分配每个字段。那样:
Foo
是不可变的,这是您的最终目标易于使用。您只需设置所需的字段。类似的东西:
Foo foo = new Foo.FooBuilder().setBar(..).setBaz(..).build();
这样构建器可以识别JAXB。例如:
FooBuilder builder = (FooBuilder) unmarshaller.unmarshal(stream);
Foo foo = builder.build();
JAXB对象需要是可变的,并且您的需求是一个不可变对象。因此,构建器可以很方便地构建它。
答案 1 :(得分:2)
这个问题与问题Applying CGLib Proxy from a Annotation Processor有些相似。
如果您希望能够在注释处理器中更改原始源代码的行为,请查看http://projectlombok.org/如何实现此目的。 IMO唯一的缺点是lombok依赖于com.sun。*类。
由于我自己需要这种东西,我想知道是否有人知道更好的方法来实现这一点,仍然使用注释处理器。
答案 2 :(得分:1)
您可以使用@XmlAccessorType(XmlAccessType.FIELD)
将JAXB配置为使用字段(实例变量)访问。这将允许您使用set方法执行所需操作:
您还可以使用JAXB的XmlAdapter
机制来支持不可变对象:
答案 3 :(得分:0)
您可以使用。而不是使用注释。
assert count++ != 0;
每种方法需要一个计数器。
答案 4 :(得分:0)
我有类似的要求。长话短说,当您在Spring中注入组件时,像A依赖于B以及B依赖于A的循环依赖关系情况就很好,但是您需要将这些组件作为字段或setter注入。构造函数注入会导致堆栈溢出。因此,我不得不为这些组件引入方法init()
,与构造函数不同的是,它们可能被错误多次调用。不用说样板代码,例如:
private volatile boolean wasInit = false;
public void init() {
if (wasInit) {
throw new IllegalStateException("Method has already been called");
}
wasInit = true;
logger.fine("ENTRY");
...
}
开始无处不在。由于这几乎不是应用程序的关键点,因此我决定引入一种优雅的线程安全的单线解决方案,该方法倾向于简洁而不是速度:
public class Guard {
private static final Map<String, Object> callersByMethods = new ConcurrentHashMap<String, Object>();
public static void requireCalledOnce(Object source) {
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
String fullClassName = stackTrace[1].getClassName();
String methodName = stackTrace[1].getMethodName();
int lineNumber = stackTrace[1].getLineNumber();
int hashCode = source.hashCode();
// Builds a key using full class name, method name and line number
String key = new StringBuilder().append(fullClassName).append(' ').append(methodName).append(' ').append(lineNumber).toString();
System.out.println(key);
if (callersByMethods.put(key, source) != null) {
throw new IllegalStateException(String.format("%s@%d.%s() was called the second time.", fullClassName, hashCode, methodName));
}
}
}
现在,由于我更喜欢在DI框架内构建应用程序,因此将Guard
声明为组件,然后注入它,然后调用实例方法requireCalledOnce
听起来很自然。但是由于其通用的风味,静态引用产生了更多的意义。现在我的代码如下:
private void init() {
Guard.requireCalledOnce(this);
...
}
这是对同一对象的init
的第二次调用的例外:
Exception in thread "main" java.lang.IllegalStateException: my.package.MyComponent@4121506.init() was called the second time.
at my.package.Guard.requireCalledOnce(Guard.java:20)
at my.package.MyComponent.init(MyComponent.java:232)
at my.package.MyComponent.launch(MyComponent.java:238)
at my.package.MyComponent.main(MyComponent.java:48)