Java 代理模式

301 阅读4分钟

什么是代理?

代理是一种设计模式,提供了对目标对象(委托对象)另外的访问方式,即通过代理对象来访问目标对象(委托对象)。这样做可以在目标对象(委托对象)实现的基础上,增加额外的功能操作,扩展目标对象(委托对象)的功能。

好处就是,可以不破坏目标对象(委托对象)的任何代码,通过代理的方式来进行扩展。

听起来是不是很厉害的样子?下面我们一一来看看。

代理模式又分三种:

1、静态代理。2、动态代理(JDK 动态代理)。3、Cglib 代理。

静态代理

需要定义接口,目标对象(委托对象)和代理对象实现相同的接口

/**
 * 接口
 */
public interface UserDao {
    public void saveUser();
}

/**
 * 实现接口(委托对象),目标对象(委托对象)
 */
public class UserDaoImpl implements UserDao {
    @Override
    public void saveUser() {
        System.out.println("保存一个用户-------------");
    }
}

/**
 * 静态代理对象
 */
public class UserProxy implements UserDao {
    /**
     * 需要代理的目标对象
     */
    private UserDaoImpl userDaoImpl;
    public UserProxy(UserDaoImpl userDaoImpl){
        this.userDaoImpl = userDaoImpl;
    }
    @Override
    public void saveUser() {
        System.out.println("静态代理开始");
        userDaoImpl.saveUser();
        System.out.println("静态代理结束");
    }
}

public class Test {
    public static void main(String[] args) {
        /**
         * 创建目标对象
         */
        UserDaoImpl userDao = new UserDaoImpl();
        /**
         * 代理对象
         */
        UserProxy proxy = new UserProxy(userDao);
        /**
         * 调用代理对象的方法
         */
        proxy.saveUser();
    }
}

很明显,我们没有更改目标对象(委托对象)的代码,调用了它的功能,并且还能进行扩展;那么有没有缺点呢?答案是有的。

试想一个,如果我们有很多的代理类的话,那么每个代理类都需要实现这个接口,如果这个接口修改了方法,或者添加了方法,那么我们所有的目标对象(委托对象)和代理对象都需要修改,这样的体验肯定不好。

动态代理(JDK 动态代理)

代理对象不需要实现接口,代理对象的生成,是利用 JDK 的 api,动态的在内存中构建代理对象(需要我们指定创建目标对象实现的接口的类型)

 * 接口
 */
public interface UserDao {
    public void saveUser();
}


/**
 * 实现接口,目标对象(委托对象)
 */
public class UserDaoImpl implements UserDao {
    @Override
    public void saveUser() {
        System.out.println("保存一个用户-------------");
    }
}

/**
 * JDK 动态代理
 */
public class JDKUserProxy implements InvocationHandler {

    /**
     * 被代理类的实例
     */
    private Object target;
    /**
     * 将被代理者的实例传进动态代理类的构造函数中
     */
    public  JDKUserProxy(Object target){
        this.target = target;
    }

    /**
     *
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK动态代理开始");
        /**
         * 调用目标对象的方法
         */
        Object obj = method.invoke(target,args);
        System.out.println("JDK动态代理结束");
        return obj;
    }
}

public class JDKProxyTest {
    public static void main(String[] args) {
        /**
         * 目标对象
         */
        UserDao userDao = new UserDaoImpl();
        /**
         * 要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
         */
        JDKUserProxy jdkUserProxy = new JDKUserProxy(userDao);
        /**
         * 给目标对象创建代理对象
         * 参数1:指定当前目标对象使用类加载器,获取加载器的方法是固定的
         * 参数2:目标对象实现的接口的类型,使用泛型方式确认类型
         * 参数3:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
         */
        UserDao proxy  = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),jdkUserProxy);
        proxy.saveUser();

    }
}

JDK 动态代理代理对象不需要实现接口,但是目标对象(委托对象)必须实现接口,否则不能使用动态代理。

Cglib 代理

导入 cglib 的 jar 包,然后在内存中构建一个目标的子类对象

    public void saveUser(){
        System.out.println("保存用户-------------");
    }
}

/**
 * Cglib 子类工厂代理,为目标对象构建一个子类对象
 */
public class CglibUserProxy implements MethodInterceptor {
    /**
     * 指定目标对象
     */
    private Object target;
    public CglibUserProxy(Object target){
        this.target = target;
    }

    //给目标对象创建一个代理对象
    public Object getProxyInstance(){
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Cglib 动态代理开始");
        /**
         * 执行目标对象的方法
         */
        Object obj = method.invoke(target,objects);
        System.out.println("Cglib 动态代理结束");
        return obj;
    }
}

public class CglibTest {
    public static void main(String[] args) {
        /**
         * 目标对象
         */
        User user = new User();
        /**
         * 代理对象
         */
        CglibUserProxy cglibUserProxy = new CglibUserProxy(user);
        User proxy = (User) cglibUserProxy.getProxyInstance();
        proxy.saveUser();

    }
}

目标类不能是 final 修饰的,方法不能被 static/final 修饰。

JDK 动态代理和 Cglib 动态代理的区别:

1、JDK 动态代理主要针对实现接口的类生成代理,不能正对类实现代理。

2、Cglib 动态代理主要针对类实现代理,针对指定的类生成一个子类,然后覆盖其中的方法。