自己實現簡易版AOP,含AOP實現的步驟分解


一、需求:

  自己實現AOP:1.0版本:在某個方法上加"@InOutLog"注解,那么執行到該方法時,方法的前面、后面會輸出日志信息。

  【自己實現AOP 2.0版本(實現Spring的有前置通知、后置通知、返回通知等各種通知的AOP):https://www.cnblogs.com/laipimei/p/11163377.html

二、思路整理:

  1.涉及的角色:

    ①被代理類;

    ②被代理類要實現的接口;

    ③代理類;

    ④動態創建“代理類的對象”的類;

    ⑤注解類(注解在方法上);

    ⑥IOC容器:BeanFactory(自己實現IOC容器:https://www.cnblogs.com/laipimei/p/11205510.html)。

  2.實現步驟:

    (1)被代理類、被代理類的接口、InOutLog注解類的創建;

    (2)創建一個“動態代理類”,並把“被代理類的實例”傳給該代理類;在該動態代理類的invoke()方法中,實現日志的輸出,也是在該invoke()方法中調用、執行真正的代理類要執行的那個方法。

    (3)創建一個可以動態創建“代理類的實例”的類,通過該類的getProxyInstance(Object obj)方法可以得到一個動態代理類的實例。
    (4)給方法加通知注解,該方法的實例須已交由IOC容器管理的;
    (5)遍歷BeanFactory,找出方法上有@InOutLog注解的bean,為這些bean生成代理類對象(步驟:MyProxy3.getProxyInstance(Object obj));

    (6)用代理類的實例去替代BeanFactory中的被代理類的實例;

三、代碼實現:

被代理類的接口:

package MyIOCAndMyAop.bean;

public interface Subject { void test(); }

 

被代理類:

 1 package MyIOCAndMyAop.bean;
 2 
 3 import MyIOCAndMyAop.Annotations.InOutLog; 4 import MyIOCAndMyAop.Annotations.MyAutowired; 5 import MyIOCAndMyAop.Annotations.MyComponent; 6 7 /** 8 * 被代理類 9 */ 10 @MyComponent 11 public class Person implements Subject{ 12 13  @MyAutowired 14 private Student student; 15 16  @InOutLog 17 public void test(){ 18 System.out.println(this + ".test() : " + student); 19  } 20 }

 

InOutLog注解類:

package MyIOCAndMyAop.Annotations;

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface InOutLog { }

 

動態代理類:

public class MyInvocationHandler2 implements InvocationHandler{
    private Object object;//被代理類
    private Object invoke; public void setObject(Object object) { this.object = object; } /** * 在BeanFactory中,方法上有@InOutLog注解,則生成動態代理方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //這里做判斷,看是否需要做下面的輸出 Boolean bool = false; //!!!注意,要用被代理類的對象去判斷其method方法上是否有@InOutLog注解,而不是用入參method對象(該method對象是被代理類的接口的) //怎么處理入參的類型:見MyAOP2.這里沒有做入參處理,可能會報方法找不到異常,注意!!! Method declaredMethod = object.getClass().getDeclaredMethod(method.getName()); if(null != declaredMethod.getAnnotation(InOutLog.class)) { System.out.println("我是日志輸出~~~start~~~"); bool = true; } invoke = method.invoke(object, args); //這里做判斷,同上 if(bool) { System.out.println("我是日志輸出~~~end~~~"); System.out.println("------------------------------------------------------------------------------"); } return invoke; } }

 

動態創建“代理類的對象”的類:

public class MyProxy2 {
    
    /**
     * 動態的創建一個代理類的對象
     * MyProxy動態創建的“代理類的對象”:
     *     class A implements Subject{
     *         private Handler  handler;
     *         public void test() {
     *             //獲得到當前方法名
     *             handler.invoke();
     *         }
     *     }
     */
    public static Object getProxyInstance(Object obj) { MyInvocationHandler2 handler = new MyInvocationHandler2(); handler.setObject(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler); } /** * 對於有@InOutLog注解的,用代理類的bean來替代BeanFactory中的被代理類的bean。 * 這一步很重要,因為當執行到bean.method(),執行的就一定是bean對應的method()方法, * 如果此時沒有用代理類對象去替換,那么執行的就是沒有InOutLog的原來的那個方法。 */ public static void updateBean(String completeClassName, Object object) { MyIOC.updateBeanFromBeanFactory(completeClassName, getProxyInstance(object));//(全類名,代理類的bean)  } }

 

①掃描BeanFactory,找出方法上有@InOutLog注解的bean,為其創建代理類對象,並替代原bean。②使用測試:

public class MyAOP2 {
    public static void main(String[] args) { /** * 使用步驟: * ① 給指定類的某個方法加@InOutLog注解; * ② 通過BeanFactory或的該類的實例; * ③ 執行bean.method(); * 效果:method()方法的前后有了log的輸出。 */ String completeClassName = "MyIOCAndMyAop.bean.Person"; Object bean = MyIOC.getBean(completeClassName); Subject person = (Subject)bean; person.test(); } static { init(); } public static void init() { updateBeanFromBeanFactory(); } /** * 掃描BeanFactory,找出方法上有@InOutLog注解的bean,為其創建代理類對象,並替代原bean。 */ public static void updateBeanFromBeanFactory() { for(Map.Entry<String, Object> entry : MyIOC.getBeanFactory().entrySet()) { for(Method method : entry.getValue().getClass().getDeclaredMethods()) { if(null != method.getAnnotation(InOutLog.class)) { MyProxy2.updateBean(entry.getKey(), entry.getValue()); } } } } }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM