Đối với những gì bạn muốn không có giải pháp AspectJ ngoài hộp, bởi vì nếu bạn chặn thực thi phương thức của bất kỳ đối tượng nào, không có kết nối với trường được chú thích có thể trỏ đến các đối tượng đó. Sẽ dễ dàng hơn khi đánh chặn các thực thi phương thức của các lớp được chú thích hoặc các phương thức được chú thích, nhưng đây không phải là những gì bạn muốn làm.
Dưới đây là một chút mã ví dụ trong đó cho thấy bạn một cách giải quyết, mà còn hạn chế của nó:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Important {}
public class Counter {
private int count = 0;
public void add(int value) {
count = count + value;
}
@Override
public String toString() {
return super.toString() + "[count=" + count + "]";
}
}
public class Visitors {
@Important
Counter counter = new Counter();
public void increaseCounter() {
counter.add(1);
}
public static void main(String[] args) {
Visitors visitors = new Visitors();
visitors.increaseCounter();
visitors.counter.add(3);
System.out.println("visitors.counter = " + visitors.counter);
System.out.println("--------------------");
Counter unimportantCounter = new Counter();
unimportantCounter.add(11);
unimportantCounter.add(22);
System.out.println("unimportantCounter = " + unimportantCounter);
System.out.println("--------------------");
unimportantCounter = visitors.counter;
unimportantCounter.add(5);
System.out.println("visitors.counter = " + visitors.counter);
System.out.println("unimportantCounter = " + unimportantCounter);
System.out.println("--------------------");
visitors.counter = new Counter();
visitors.increaseCounter();
visitors.counter.add(3);
unimportantCounter.add(100);
System.out.println("visitors.counter = " + visitors.counter);
System.out.println("unimportantCounter = " + unimportantCounter);
System.out.println("--------------------");
Visitors otherVisitors = new Visitors();
otherVisitors.increaseCounter();
otherVisitors.counter.add(50);
System.out.println("otherVisitors.counter = " + otherVisitors.counter);
System.out.println("--------------------");
otherVisitors.counter = visitors.counter;
System.out.println("visitors.counter = " + visitors.counter);
System.out.println("otherVisitors.counter = " + otherVisitors.counter);
System.out.println("--------------------");
otherVisitors.counter = new Counter();
visitors.increaseCounter();
otherVisitors.increaseCounter();
System.out.println("visitors.counter = " + visitors.counter);
System.out.println("otherVisitors.counter = " + otherVisitors.counter);
}
}
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.aspectj.lang.Signature;
import org.aspectj.lang.SoftException;
public aspect ImportantMethodInterceptor {
Map<Object, Set<Object>> importantObjects = new HashMap<Object, Set<Object>>();
pointcut importantSetter(Object newValue, Object target) :
set(@Important * *) && args(newValue) && target(target);
pointcut unimportantSetter(Object newValue, Object target) :
!set(@Important * *) && set(* *) && !withincode(*.new(..)) && args(newValue) && target(target);
pointcut publicMethod(Object target) :
execution(public * *(..)) && target(target) && !execution(public String *..toString());
before(Object newValue, Object target) : importantSetter(newValue, target) {
Object oldValue = getFieldValue(thisJoinPoint.getSignature(), target);
System.out.println("Important object for target " + target + ": " + oldValue + " -> " + newValue);
synchronized (importantObjects) {
Set<Object> referrers;
if (oldValue != null) {
referrers = importantObjects.get(oldValue);
if (referrers != null) {
referrers.remove(target);
if (referrers.size() == 0)
importantObjects.remove(oldValue);
}
}
if (newValue != null) {
referrers = importantObjects.get(newValue);
if (referrers == null) {
referrers = new HashSet<Object>();
importantObjects.put(newValue, referrers);
}
referrers.add(target);
}
}
}
// before(Object newValue, Object target) : unimportantSetter(newValue, target) {
// Object oldValue = getFieldValue(thisJoinPoint.getSignature(), target);
// System.out.println("Unimportant object for target " + target + ": " + oldValue + " -> " + newValue);
// }
before(Object target) : publicMethod(target) {
synchronized (importantObjects) {
if (importantObjects.get(target) != null)
System.out.println("Important method on " + target + ": " + thisJoinPointStaticPart);
else
System.out.println("Unimportant method on " + target + ": " + thisJoinPointStaticPart);
}
}
private Object getFieldValue(Signature signature, Object target) {
try {
Field field = signature.getDeclaringType().getDeclaredField(signature.getName());
field.setAccessible(true);
return field.get(target);
}
catch (Exception e) { throw new SoftException(e); }
}
}
Như bạn thấy, khía cạnh tôi giữ một bộ "quan trọng các đối tượng". Chính xác hơn, nó là Map
trong đó các khóa là "đối tượng quan trọng" và giá trị là tập hợp các liên kết giới thiệu. Điều này là cần thiết bởi vì một số giới thiệu về mặt lý thuyết (ví dụ: Visitors
đối tượng) có thể trỏ đến "các đối tượng quan trọng" giống hệt nhau (ví dụ: Counter
). Trong phiên bản trước của mã mẫu của tôi khi tôi chỉ ghi lại "các đối tượng quan trọng" trong một bộ đơn giản, tôi đã chọn không bao giờ xóa "các đối tượng quan trọng" trước đây khỏi tập hợp ngay cả khi chúng không được tham chiếu nữa hoặc luôn xóa chúng nếu một liên kết giới thiệu thứ hai vẫn trỏ đến một "đối tượng quan trọng". Cách tiếp cận bản đồ cho phép tôi ghi lại nhiều liên kết giới thiệu cho mỗi "đối tượng quan trọng".
Nếu bạn chạy Visitors.main(String[])
, bạn sẽ thấy kết quả như sau (xin bỏ ghi chú những lời khuyên before ... : unimportantSetter ...
nếu bạn muốn xem nhiều hơn đăng nhập đầu ra):
Important object for target [email protected]: null -> [email protected][count=0]
Unimportant method on [email protected]: execution(void Visitors.increaseCounter())
Important method on [email protected][count=0]: execution(void Counter.add(int))
Important method on [email protected][count=1]: execution(void Counter.add(int))
visitors.counter = [email protected][count=4]
--------------------
Unimportant method on [email protected][count=0]: execution(void Counter.add(int))
Unimportant method on [email protected][count=11]: execution(void Counter.add(int))
unimportantCounter = [email protected][count=33]
--------------------
Important method on [email protected][count=4]: execution(void Counter.add(int))
visitors.counter = [email protected][count=9]
unimportantCounter = [email protected][count=9]
--------------------
Important object for target [email protected]: [email protected][count=9] -> [email protected][count=0]
Unimportant method on [email protected]: execution(void Visitors.increaseCounter())
Important method on [email protected][count=0]: execution(void Counter.add(int))
Important method on [email protected][count=1]: execution(void Counter.add(int))
Unimportant method on [email protected][count=9]: execution(void Counter.add(int))
visitors.counter = [email protected][count=4]
unimportantCounter = [email protected][count=109]
--------------------
Important object for target [email protected]: null -> [email protected][count=0]
Unimportant method on [email protected]: execution(void Visitors.increaseCounter())
Important method on [email protected][count=0]: execution(void Counter.add(int))
Important method on [email protected][count=1]: execution(void Counter.add(int))
otherVisitors.counter = [email protected][count=51]
--------------------
Important object for target [email protected]: [email protected][count=51] -> [email protected][count=4]
visitors.counter = [email protected][count=4]
otherVisitors.counter = [email protected][count=4]
--------------------
Important object for target [email protected]: [email protected][count=4] -> [email protected][count=0]
Unimportant method on [email protected]: execution(void Visitors.increaseCounter())
Important method on [email protected][count=4]: execution(void Counter.add(int))
Unimportant method on [email protected]: execution(void Visitors.increaseCounter())
Important method on [email protected][count=0]: execution(void Counter.add(int))
visitors.counter = [email protected][count=5]
otherVisitors.counter = [email protected][count=1]
Hãy cẩn thận so sánh các mã trong main
với dữ liệu ghi nhận để xem những trường hợp thường xuyên và đặc biệt nào tôi đã thử nghiệm.
Như tôi đã nói, phương pháp này có những hạn chế của nó:
- tôi đã không kiểm tra những gì sẽ xảy ra nếu các lĩnh vực quan trọng có các loại nguyên thủy như
int
hay là String
s mà về mặt lý thuyết có thể xảy ra nhiều lần là "đối tượng quan trọng" bởi vì một số thành viên quan trọng không liên quan tạo ra các đối tượng bằng nhau. Tôi cũng đã không thử nghiệm những gì xảy ra liên quan đến boxing (un) boxing, hãy thử một mình.
- Mã khía cạnh hơi phức tạp và có thể không phải nhanh chóng.
- Tôi không thể đảm bảo rằng có thể không có các vấn đề khác mà tôi chưa từng nghĩ đến.
Nhưng nếu bạn kiểm soát các điều kiện biên và trường hợp sử dụng, bạn có thể đưa ra quyết định sáng suốt và sử dụng mã như hoặc biến thể của nó để đạt được những gì bạn cần. Mã có thể có tiềm năng để cải thiện, tôi chỉ tò mò và muốn hack một bằng chứng về khái niệm.
Cảm ơn bạn đã xác nhận những gì tôi nghi ngờ, không thể làm những gì tôi muốn với AJ out-of-the-box. Mặc dù tôi rất mới với AJ Tôi có thể thấy những gì bạn đang cố gắng làm trong POC của bạn. Rất thú vị và giáo dục! Cảm ơn bạn! – JustOneMoreQuestion