以下示例是减少真正的问题,因为它尽可能地尝试简化。
我有一个java接口,以及几个实现该接口的对象,如:
public interface Shape{
public void draw();
public void erase();
public boolean isDrawn();
}
public class Square implements Shape{
@Override
public void draw(){
//TODO: method implementation
}
@Override
public void erase(){
//TODO: method implementation
}
Override
public boolean isDrawn(){
//TODO: method implementation
return false;
}
}
public Triangle implements Shape{
//same as above
}
public Circle implements Shape{
//same as above
}
这是我程序的结构。通过使用AspectJ,我想拥有一个包含实现接口的每个对象的映射。为此,我尝试使用以下方面捕获构造函数:
public aspect ShapeHolderAspect{
private Map<Integer, Shape> map = new HashMap<>();
private int count = 0;
pointcut shapeInit(): call((Shape+).new(..));
Object around(): shapeInit() {
System.out.println("capturing new");
Shape shapeType = (Shape)proceed();
map.put(++count, shapeType);
return shapeType;
}
}
如果我使用以下场景创建一个Shape,则此代码将起作用:
public static void main(String[] args){
Shape myShape = new Circle();
}
但是,我使用的是java语言反射,所以从技术上讲,我不会调用“new”构造函数。相反,我找到了包的路径,并创建了一个传递带有类名称的字符串的对象:
public static void main(String[] args){
String shapeClassName = args[0];
Class<?> classType = Class.forName("myPackage.figures" + "." + shapeClassName);
Shape myShape =(Shape)classType.getConstructor().newInstance();
}
通过这种方式,AspectJ无法检测到我正在创建形状。我该如何解决这个问题?
答案 0 :(得分:2)
新的,更好的版本:
好吧,虽然下面的旧版本实际上捕获了所有构造函数的执行,但构造函数执行的around
建议返回null,因为有问题的对象尚未初始化。所以你最终会在你的方面得到一个空指针的映射。为了解决这个问题,您需要将this()
绑定到变量(示例代码使用默认包名称):
public class Application {
public static void main(String[] args) throws Exception {
new Circle().draw();
((Shape) Class.forName("Triangle").getConstructor().newInstance()).isDrawn();
((Shape) Class.forName("Square").getConstructor().newInstance()).erase();
}
}
import java.util.HashMap;
import java.util.Map;
public aspect ShapeHolderAspect {
private Map<Integer, Shape> map = new HashMap<Integer, Shape>();
private int count = 0;
after(Shape shape): execution(Shape+.new(..)) && this(shape) {
System.out.println(thisJoinPointStaticPart);
map.put(++count, shape);
}
after() : execution(* Application.main(..)) {
System.out.println("\nList of shapes:");
for (int key : map.keySet())
System.out.println(" " + key + " -> " + map.get(key));
}
}
输出如下:
initialization(Circle())
initialization(Triangle())
initialization(Square())
List of shapes:
1 -> Circle@1a2961b
2 -> Triangle@12d03f9
3 -> Square@5ffb18
顺便说一句,如果您绝对需要around
建议,因为您想在创建对象之前在和之前做其他事情,它将如下所示:
void around(Shape shape): execution(Shape+.new(..)) && this(shape) {
System.out.println(thisJoinPointStaticPart);
proceed(shape);
map.put(++count, shape);
}
旧的,不完整的版本:
很简单,只需拦截构造函数execution
而不是call
:
pointcut shapeInit(): execution(Shape+.new(..));
这样你编织成被调用的代码(被调用者),而不是调用代码(调用者)。因此,呼叫者是否发出反射或正常呼叫并不重要。
答案 1 :(得分:0)
发现以下切入点可以完成这项工作:
pointcut lockReflectInit(): call(public Object java.lang.reflect.Constructor.newInstance(..));
然而,这将捕获newInstance的所有调用,而不仅仅是返回Shape =(