zoukankan      html  css  js  c++  java
  • 自己实现简易版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.博文内容如有错误、不妥之处,欢迎留言指正,还请不吝赐教 =^_^=
  • 相关阅读:
    如何编写CMakeLists.txt
    C++11 condition_variable
    TCP/IP 收发数据
    CLion 远程开发和调试C/C++代码
    Python unittest mock 在哪里打patch
    MVCC版本链
    数据无法修改?解密MVCC原理
    MVCC ReadView介绍
    正则表达式备忘(基于JavaScript)
    《C+编程规范 101条规则、准则与最佳实践》笔记
  • 原文地址:https://www.cnblogs.com/laipimei/p/11137250.html
Copyright © 2011-2022 走看看