2011-01-31 26 views
12

Làm cách nào để thay đổi phương thức đang thực hiện trong Java?Sửa đổi một phương thức bằng cách sử dụng Chú thích

Ý tôi là, tôi đang cố gắng sử dụng các chú thích để làm cho đoạn mã sau

@Anno1(Argument = "Option1") 
public class TestClass 
{  
    @Anno2 
    public void test() 
    { 
    } 

} 

Into

public class TestClass 
{ 
    private static StaticReference z; 

    public void test() 
    { 
      z.invokeToAll(); 
    } 

} 

Đây là một ví dụ rất đơn giản về những gì tôi đang cố gắng làm. Anno1 sẽ có nhiều kết hợp có thể, nhưng đây không phải là vấn đề của tôi cho đến nay. Vấn đề của tôi là cách thêm mã vào phương thức test()

Tôi đang tìm giải pháp chung chung hơn nếu có thể. Ví dụ. Một cách để thêm tất cả các loại mã trong phương pháp này (không chỉ là một cách để .invokeToAll())

Cho đến nay tôi đang sử dụng import javax.annotation.processing.*; và tôi có đoạn mã sau, nhưng tôi không biết làm thế nào để đi vào từ đó

private void processMethodAnnotations(RoundEnvironment env) 
{ 
    for (Element e : env.getElementsAnnotatedWith(Anno2.class)) 
    { 
     //If it is a valid annotation over a method 
     if (e.getKind() == ElementKind.METHOD) 
     { 
      //What to do here :S 
     }else 
     { 
      processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,"Not a method!", e);    
     }   
    } 
} 

Tôi đã tìm thấy một cái gì đó về Java Reflection nhưng tôi đã không tìm thấy bất kỳ nguồn nào để giúp tôi với những gì tôi đang làm.

Rõ ràng là tôi extends AbstractProcessor trong mã của tôi

Tôi đã tìm thấy hướng dẫn này (http://www.zdnetasia.com/writing-and-processing-custom-annotations-part-3-39362483.htm) Nhưng điều này các mối quan tâm tạo ra một lớp mới, không chỉ thay đổi một phương thức. và javax.lang.model.elements không cung cấp bất kỳ cách nào để chỉnh sửa phần tử đó (trong trường hợp của tôi là một Phương thức).

Tôi hy vọng câu hỏi của tôi rõ ràng và phù hợp với các quy tắc. Nếu không xin vui lòng bình luận và tôi sẽ làm rõ. Cảm ơn.

Trả lời

12

chế biến Chú là sai con đường để đi cho bạn, từ Wikipedia:

Khi mã nguồn Java được biên dịch, chú thích có thể được xử lý bởi biên dịch các plug-in được gọi là chú thích xử lý. Bộ vi xử lý có thể tạo ra thông báo thông tin hoặc tạo các tệp nguồn Java bổ sung hoặc tài nguyên, do đó có thể được biên soạn và xử lý, nhưng chú thích bộ xử lý không thể sửa đổi chú thích mã.

Mọi người đã đề xuất bạn đúng cách - AOP. Cụ thể là bạn có thể sử dụng AspectJ. "Kết quả nhanh" Cách thứ nhất là (nếu bạn sử dụng Eclipse):

1) Cài đặt AJDT (Công cụ phát triển AspectJ)
2) Tạo dự án AspectJ và thêm có các lớp học và các chú thích
3 của bạn) Tạo Aspect:

public aspect Processor { 

    private StaticReference z; 

    pointcut generic() 
      // intercept execution of method named test, annotated with @Anno1 
      // from any class type, annotated with @Anno2 
     : execution(@Anno2 * (@Anno1 *).test()) 
      // method takes no arguments 
     && args(); 

    // here you have write what you want method actually does 
    void around() : generic() { 
     z.invokeToAll(); 
    } 


} 

bây giờ bạn có thể thực hiện một thử nghiệm và bạn sẽ thấy rằng nó hoạt động;) AJDT biên dịch mã cho bạn tự động, do đó, không cần bất kỳ công việc chân tay để làm, hy vọng đó là những gì bạn gọi là "kỳ diệu";)

CẬP NHẬT:

nếu mã của bạn trong phương pháp thử nghiệm() phụ thuộc vào giá trị chú thích Anno1, sau đó bên trong khía cạnh bạn có thể nhận lớp chú thích mà nó được thực hiện theo cách này:

void around() : generic() { 

    Annotation[] classAnnotations = thisJoinPoint.getThis().getClass().getAnnotations(); 

    String ArgumentValue = null; 
    for (Annotation annotation : classAnnotations) { 
     if (annotation instanceof Anno1) { 
      ArgumentValue = ((Anno1) annotation).Argument(); 
      break; 
     } 
    } 

    if (ArgumentValue != null && ArgumentValue.equals("Option1")) { 
     z.invokeToAll(); 
    } 

} 

nơi thisJoinPoint là một biến tham chiếu đặc biệt.

UPDATE2:

nếu bạn muốn thêm System.out.println(this) trong khía cạnh của bạn, bạn cần phải viết có System.out.println(thisJoinPoint.getThis()), chỉ cần thử nghiệm và nó hoạt động. thisJoinPoint.getThis() trả lại cho bạn "điều này" nhưng không chính xác; trong thực tế, đây là biến Object và nếu bạn muốn nhận được bất kỳ sự thích hợp nào, bạn cần hoặc là cast hoặc sử dụng sự phản chiếu. Và thisJoinPoint.getThis() không cung cấp quyền truy cập vào các thuộc tính riêng tư.

Vâng, bây giờ có vẻ như câu hỏi của bạn được trả lời, nhưng nếu tôi bỏ lỡ bất cứ điều gì, hoặc bạn nhận được thêm câu hỏi/vấn đề với cách này - đừng ngần ngại hỏi;)

+0

Giải pháp của bạn có vẻ là những gì tôi muốn, không may vì lý do lạ tôi không thể sử dụng nó. Tôi đã tìm thấy một giải pháp ở nơi khác ('Javassist'). Dù sao tôi đã cho bạn +1 và bây giờ là một câu trả lời được chấp nhận cho những nỗ lực và câu trả lời tốt. Cảm ơn rất nhiều! – Muggen

+0

@Muggen hm, bạn có thể cho tôi biết về "lý do kỳ lạ", vì vậy tôi có thể thấy chúng có thực sự quan trọng hay không; có thể có một cách khác để gắn bó với AOP (có vẻ dễ dàng hơn để hỗ trợ ..) – Maxym

+0

những lý do kỳ lạ là 'Hạn chế học thuật';). Cám ơn sự giúp đở cuả bạn. – Muggen

1

Vâng, bạn có thể thấy nếu mã soạn sẵn sau đây sẽ hữu ích:

public void magic(Object bean, String[] args) throws Exception { 
    for (Method method : bean.getClass().getDeclaredMethods()) { 
     if (method.isAnnotationPresent(Anno2.class)) { 
      // Invoke the original method 
      method.invoke(bean, args); 
      // Invoke your 'z' method 
      StaticReference.invokeAll(); 
     } 
    } 
} 

Là một thay thế của bạn có thể sử dụng lập trình hướng khía cạnh, ví dụ bạn có dự án AspectJ.

+0

Hm thông minh, nhưng nếu thay vì 'z.invokeToAll();' Tôi muốn ví dụ để làm một cái gì đó như 'System.out.println (this);' Tôi đang tìm một giải pháp chung chung hơn nếu có thể. – Muggen

+0

In 'this' có nghĩa là để gọi' TestClass.toString() '. Bạn vẫn có thể làm điều đó nếu bạn thích. Hơn nữa, với sự phản chiếu, bạn có thể truy cập tất cả các thuộc tính của các lớp chú thích của bạn. –

1

Tôi không chắc chắn chút nào nếu có thể thay đổi mã nguồn hoặc mã byte thông qua chú thích. Từ những gì bạn mô tả có vẻ như là aspect oriented programming có thể cung cấp giải pháp cho vấn đề của bạn.

chú thích của bạn là khá tương tự với khái niệmpointcut (họ đánh dấu vị trí nơi mã cần phải được chèn vào) và mã chèn gần các lời khuyên khái niệm.

Một cách tiếp cận khác sẽ phân tích cú pháp tệp nguồn java thành cây cú pháp trừu tượng, sửa đổi AST này và tuần tự hóa thành đầu vào trình biên dịch java.

+1

AOP là cách để đến đây (+1). Sửa đổi AST là có thể (tôi đã thực hiện thành công), nhưng đó là một hack bẩn mà dựa trên API nội bộ không có giấy tờ có thể thay đổi –

0

Nếu lớp của bạn mở rộng một giao diện phù hợp, bạn có thể bọc nó trong một DynamicProxy, ủy nhiệm tất cả các cuộc gọi đến các phương thức ban đầu, ngoại trừ cuộc gọi để kiểm tra.

3

Đó là hoàn toàn có thể làm những gì bạn yêu cầu, mặc dù có một cảnh báo: dựa vào các API trình biên dịch riêng. Âm thanh đáng sợ, nhưng nó không thực sự (triển khai trình biên dịch có xu hướng ổn định).

Có giấy giải thích quy trình: The Hacker's Guide to Javac.

Điều đáng chú ý là điều này được sử dụng bởi Project Lombok để cung cấp tính năng tạo bộ thu/phát tự động (trong số những thứ khác). Các following article giải thích nó như thế nào, về cơ bản tái lặp lại những gì được nói trên giấy nói trên.

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