zoukankan      html  css  js  c++  java
  • AOP基础-JDK动态代理

      动态代理技术就是用来产生一个目标对象的代理对象的,代理对象应与目标对象(被代理的对象)有相同的方法,实现对目标对象访问的拦截,并增强目标对象的一些功能,而不需要目标对象去做任何的更改,使得目标对象有更加纯粹的业务逻辑。不多解释,上代码。

      需求:在一个业务模块方法中添加日志。

    一、常规的写法

      1.1、接口Animals

    package com.duchong.proxy_test;
    
    /**
     * @author DUCHONG
     * @since 2017-12-28 9:10
     **/
    public interface Animals {
    /** * 吃 * @param name * @return */ String eat(String name); /** * 吠 * @param name * @return */ String bark(String name); /** * 跑 * @param name * @return */ String move(String name); }

      1.2、接口的实现类Dog

    package com.duchong.proxy_test;
    
    /**
     * @author DUCHONG
     * @since 2017-12-28 9:21
     **/
    public class Dog implements Animals {
    
        @Override
        public String eat(String name) {
            System.out.println("Dog eat method start.....");
            return "dog eat "+name;
        }
    
        @Override
        public String move(String name) {
            System.out.println("Dog move method start.....");
            return "dog move "+name;
        }
    
        @Override
        public String bark(String name) {
            System.out.println("Dog bark method start.....");
            return "dog bark "+name;
        }
    
        public static void main(String[] args) {
            Dog dog=new Dog();
            System.out.println(dog.eat("Bone"));
            System.out.println(dog.move("Faster"));
            System.out.println(dog.bark("Wang Wang Wang"));
        }
    }
    

      1.3、输出

    以上写法,能实现要求,但是缺点是,像日志这种非业务逻辑相关的代码混在里面,看起来混乱,不易维护,而且这些代码块大多类似,对于这样的代码块,我们都知道,是可以抽取出来的,定义公共功能,这样就使得业务模块更简洁, 只包含核心业务代码。

    二、基于动态代理的写法

    2.1、接口的实现类Dog

    package com.duchong.proxy_test;
    
    /**
     * @author DUCHONG
     * @since 2017-12-28 9:21
     **/
    public class Dog implements Animals {
        @Override
        public String eat(String name) {
            return "dog eat "+name;
        }
    
        @Override
        public String move(String name) {
            return "dog move "+name;
        }
    
        @Override
        public String bark(String name) {
            return "dog bark "+name;
        }
    
        
    }
    

    2.2、使用java.lang.reflect.Proxy动态代理实现,即提取目标对象的接口(或者说JDK生成代理必须使用接口),然后对接口创建代理.

    @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
    

    使用Proxy.newProxyInstance方法就可以返回一个代理对象,这个方法总共有3个参数

    • ClassLoader loader 生成代理对象使用哪个类装载器加载
    • Class<?>[] interfaces 生成哪个对象的代理对象,通过接口指定,或者说代理要实现的接口
    • InvocationHandler h 产生的这个代理对象要做什么,这个接口里面只有一个方法,可以使用一个匿名内部类来实现

    2.3、代理类

    package com.duchong.proxy_test;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * dog代理类
     *
     * @author DUCHONG
     * @since 2017-12-28 10:17
     **/
    public class DogProxy{
    
        //需要被代理的目标对象
        private Animals target;
    
        public DogProxy(Animals target){
            this.target=target;
        }
    
        /**
         * 返回基于接口创建的代理
         * @return
         */
        public Animals getDogProxy(){
    
            return (Animals) Proxy.newProxyInstance(DogProxy.class.getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
                    if(method.getName().equals("eat")){
                        System.out.println("Dog eat method start.....");
                        return method.invoke(target,args);
                    }
                    else if(method.getName().equals("move")){
                        System.out.println("Dog move method start.....");
                        return method.invoke(target,args);
                    }
                    else if(method.getName().equals("bark")){
                        System.out.println("Dog bark method start.....");
                        return method.invoke(target,args);
                    }
                    else{
                        return null;
                    }
                }
            });
    
        }
    
    }
    

    2.4、Main运行

    package com.duchong.proxy_test;
    /**
     * @author DUCHONG
     * @since 2017-12-28 10:35
     **/
    public class Main {
        public static void main(String[] args) {
            //目标对象
            Animals animals=new Dog();
            //获得代理对象
            Animals dog= new DogProxy(animals).getDogProxy();
            //调用代理对象的方法
            System.out.println(dog.eat("Bone"));
            System.out.println(dog.move("Faster"));
            System.out.println(dog.bark("Wang Wang Wang"));
        }
    }
    

    2.5、输出

    2.6、InvocationHandler原理

      如果Proxy生成代理对象的时候,指定了InvocationHandler,那么用户调用代理对象的任何方法,该方法都是调用InvocationHandler的invoke方法,而Method就是当前调用的那个方法,通过getName 即可获取当前被调用方法的名称,从而在invoke被调用之前,添加日志等处理,而invoke方法被调用是需要两个参数,一个是调用改方法的对象,一个是该方法的参数,即为代码中的target和args.

  • 相关阅读:
    numpy函数:[6]arange()详解
    python中的list和array的不同之处
    python 矩阵转置transpose
    PowerDesigner(一)-PowerDesigner概述(系统分析与建模)
    MDX中Filter 与Exist的区别
    SQL Server 2016 —— 聚集列存储索引的功能增强
    SQL Server 2016:内存列存储索引
    PXE
    setjmp
    skb head/data/tail/end/介绍
  • 原文地址:https://www.cnblogs.com/geekdc/p/8135145.html
Copyright © 2011-2022 走看看