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.

原文地址:https://www.cnblogs.com/geekdc/p/8135145.html