自己实现简易版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());
                }
            }
        }
    }
}
作者:赖皮梅
声明:
1.原创博文,欢迎转载、引用;转载、引用请注明作者并附上原文链接,否则保留追究法律责任的权利。
2.本博文中引用他人的博文内容时均已注明出处,如有侵权,请联系作者删除。
3.博文内容如有错误、不妥之处,欢迎留言指正,还请不吝赐教 =^_^=
原文地址:https://www.cnblogs.com/laipimei/p/11137250.html