代理模式

代理模式的三种实现方式

  • 静态代理
  • JDK动态代理
  • CGlib动态代理

1、静态代理

  • 目标对象
  • 代理对象
  • 接口

实现关键点:目标对象与代理对象实现同一接口,将目标对象作为成员变量引入到代理对象中,实现目标对象执行前或执行后的额外处理。

例子
保存用户
Dao 直接保存
DaoProxy 给保存的方法增加事务

代码实现
接口类

package com.jkxy.design01.proxy.staticproxy.dao;

/**
 * @author Jcon
 * @version V1.0
 * @Description: (接口类)
 * @date 2019年01月27日
 */
public interface IDao {

    void save();

}

目标对象

package com.jkxy.design01.proxy.staticproxy.dao;

/**
 * @author Jcon
 * @version V1.0
 * @Description: (目标对象)
 * @date 2019年01月27日
 */
public class UserDao implements IDao {
    @Override
    public void save() {
        System.out.println("------保存用户到数据库-------");
    }
}

代理对象

package com.jkxy.design01.proxy.staticproxy.dao;

/**
 * @author Jcon
 * @version V1.0
 * @Description: (代理对象)
 * @date 2019年01月27日
 */
public class UserDaoProxy implements IDao {

    private IDao target;

    public UserDaoProxy(IDao target) {
        this.target = target;
    }

    @Override
    public void save() {
        System.out.println("---事务开始----");
        target.save();
        System.out.println("---事务结束----");
    }
}

测试类

package com.jkxy.design01.proxy.staticproxy.dao;

/**
 * @author Jcon
 * @version V1.0
 * @Description: (测试类)
 * @date 2019年01月27日
 */
public class App {

    public static void main(String[] args) {
        UserDao userDao = new UserDao();
        new UserDaoProxy(userDao).save();
    }
}

总结
优点:可以做到在不修改目标对象功能的前提下,对目标对象功能扩展
缺点:①每个目标对象都需要有一个代理对象,类爆炸 ②目标对象和代理对象必须实现同一个接口 ③一旦接口增加方法,目标对象与代理对象都要维护增加的方法

静态代理缺点解决
动态代理

2、JDK动态代理

  • 目标对象
  • 代理对象
  • 接口

JDK动态代理实现细节

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

接口类

package com.jkxy.design01.proxy.dynamic;

/**
 * @author Jcon
 * @version V1.0
 * @Description: (接口类)
 * @date 2019年01月27日
 */
public interface IDao {

    void save();

}

目标对象

package com.jkxy.design01.proxy.dynamic;

/**
 * @author Jcon
 * @version V1.0
 * @Description: (目标对象)
 * @date 2019年01月27日
 */
public class UserDao implements IDao {
    @Override
    public void save() {
        System.out.println("------保存用户到数据库-------");
    }
}

代理对象,JDK动态代理,代理对象不需要实现接口

package com.jkxy.design01.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author Jcon
 * @version V1.0
 * @Description: (代理对象代理工厂 - 动态代理)
 * @date 2019年01月27日
 */
public class ProxyFactory {

    // 维护一个目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    // 给目标对象生成代理对象
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(
                // 指定当前目标对象使用类加载器
                target.getClass().getClassLoader(),
                // 目标对象实现的接口类型
                target.getClass().getInterfaces(),
                // 事件处理器
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("---开启事务---");
                        // 执行目标对象方法
                        Object result = method.invoke(target, args);
                        System.out.println("---提交事务---");
                        return result;
                    }
                }
        );
    }
}

使用泛型写法

package com.jkxy.design01.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author Jcon
 * @version V1.0
 * @Description: (代理对象 , 使用泛型写法)
 * @date 2019年01月27日
 */
public class ProxyFactoryT<T> {

    private T target;

    public ProxyFactoryT(T target) {
        this.target = target;
    }

    public T getProxyInstance(){
        return (T)Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("----执行目标对象前业务处理---");
                        Object result = method.invoke(target, args);
                        System.out.println("----执行目标对象后业务处理---");
                        return result;
                    }
                }
        );
    }
}

测试类

package com.jkxy.design01.proxy.dynamic;

/**
 * @author Jcon
 * @version V1.0
 * @Description: (测试类)
 * @date 2019年01月27日
 */
public class App {

    public static void main(String[] args) {
        // 创建目标对象
        UserDao userDao = new UserDao();
        // 创建代理对象
        IDao proxyInstance = (IDao) new ProxyFactory(userDao).getProxyInstance();
        proxyInstance.save();

        System.out.println("=========使用泛型写法测试类========");
        // 创建目标对象
        UserDao userDao2 = new UserDao();
        // 创建代理对象
        IDao proxyInstance2 = new ProxyFactoryT<IDao>(userDao).getProxyInstance();
        // 调用目标对象方法
        proxyInstance2.save();
    }
}

总结
优点:①代理对象不需要实现接口,不同目标对象都可以使用通用的代理对象
②弥补了静态代理中的类爆炸问题
缺点:如果目标对象没有实现接口的话则使用不了

3、cglib动态代理

  • 目标对象
  • 代理对象

Cglib代理,也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展。

CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)

CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉

代码实现
目标对象类

package com.jkxy.design01.proxy.cglib;

import com.jkxy.design01.proxy.dynamic.IDao;

/**
 * @author Jcon
 * @version V1.0
 * @Description: (目标对象)
 * @date 2019年01月27日
 */
public class UserDao implements IDao {
    @Override
    public void save() {
        System.out.println("------保存用户到数据库-------");
    }
}

代理对象类

package com.jkxy.design01.proxy.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author Jcon
 * @version V1.0
 * @Description: (cglib代理对象 对UserDao 在内存中动态构建一个子类对象)
 * @date 2019年01月27日
 */
public class ProxyFactory implements MethodInterceptor {

    // 维护目标对象
    private Object target;

    public ProxyFactory(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[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("-----执行目标对象前业务处理-----");
        // 执行目标对象
        Object result = method.invoke(target, args);
        System.out.println("-----执行目标对象后业务处理------");
        return result;

    }
}

测试类

package com.jkxy.design01.proxy.cglib;

/**
 * @author Jcon
 * @version V1.0
 * @Description: (cglib代理测试类)
 * @date 2019年01月27日
 */
public class App {

    public static void main(String[] args) {
        // 目标对象
        UserDao target = new UserDao();

        // 创建代理对象
        UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();

        // 执行代理对象的方法
        proxy.save();

    }
}

总结:
①需要引入cglib – jar文件, 但是spring的核心包中已经包括了cglib功能,所以直接引入spring-core.jar即可,引入功能包后,就可以在内存中动态构建子类
②代理的类不能为final, 否则报错。
③目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法

在Spring的AOP编程中:

  • 如果加入容器的目标对象有实现接口,用JDK代理;
  • 如果目标对象没有实现接口,用Cglib代理;
posted @ 2019-01-27 22:57  Jcon  阅读(104)  评论(0)    收藏  举报