2013-02-28 28 views
8

Tôi muốn viết một đường cắt khớp với việc thực hiện các phương thức công khai trên một trường được chú thích. Điều này bao giờ dường như không hoạt động. Lệnh get (@Important) hoạt động như bạn mong đợi (riêng nó) nhưng tất nhiên nó sẽ phù hợp với tất cả quyền truy cập vào trường. Tôi muốn giới hạn điều này để chỉ thực hiện phương thức công khai.Aspectj Pointcut để kết hợp các cuộc gọi phương thức công khai trên trường được chú thích

Điều này có thể thực hiện được không? Tôi nhận được không có lỗi biên dịch nhưng mặt khác, nó dường như không làm việc ..


public class Counter { 
    private int count = 0; 

    public void add(int value) { 
    count = count + value; 
    } 
} 

public class Visitors { 
    @Important 
    Counter counter = new Counter() 

    public void increaseCounter() { 
    counter.add(1); 
    } 
} 

trình:

@Pointcut(value = "get(@Important * *)") 
void testPointCut() { 
} 

Không làm việc:

@Pointcut(value = "get(@Important * *) && execution(public * *(..))") 
void testPointCut() { 
} 

Trả lời

2

Đố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.

+1

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

0

Bạn muốn sử dụng withinCode pointcut, như thế này:

@Pointcut(value = "get(@Important * *) && withinCode(public * *(..))") 
void testPointCut() { 
} 

Có một cái nhìn tại AspectJ programming guide.

+1

Điều này vẫn bắt được tất cả quyền truy cập vào trường được chú thích. Tôi chỉ muốn so khớp việc thực hiện các phương thức công khai trên trường được chú thích. – JustOneMoreQuestion

Các vấn đề liên quan