spring学习笔记(八):代理模式


代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。

这样做的好处:可以在目标对象的功能基础上,增加额外的功能,即 扩展目标对象的功能。

 

1、静态代理:代理对象 需要与 目标对象 实现一样的接口;

接口 IUserDao.java:

public interface IUserDao {
    void save();
}

目标对象 UserDao.java:实现一个 save 方法;

public class UserDao implements IUserDao {
    @Override
    public void save() {
        System.out.println("----- 保存数据 -----");
    }
}

代理对象 UserDaoProxy.java:在目标对象的 save 功能之上,增加事务的功能;

public class UserDaoProxy implements IUserDao {

    // 接收保存目标对象
    private IUserDao target;
    public UserDaoProxy(IUserDao target){
        this.target = target;
    }

    /**
     * 代理对象在目标对象的功能基础之上,增加对事物的处理功能
     */
    @Override
    public void save() {
        System.out.println("开始事务...");

        target.save(); // 执行目标对象的方法

        System.out.println("提交事务...");
    }
}

测试程序 Demo.java:

public class Demo {
    @Test
    public void test(){
        // 目标对象
        UserDao userDao = new UserDao();

        // 代理对象:将目标对象传入代理对象
        UserDaoProxy proxy = new UserDaoProxy(userDao);
        proxy.save(); // 执行代理对象的方法,然后通过代理对象去执行目标对象的方法
    }
}

输出结果:

静态代理总结:可以做到在不修改目标对象功能的前提下,对目标对象的功能进行扩展;

静态代理缺点:

        1)、因为代理对象 需要与目标对象实现一样的接口,所如果接口和目标对象比较多的话,代理对象也会有很多;

        2)、一旦接口增加方法,目标对象和代理对象都要维护,比较麻烦;

解决静态代理缺点的方法:使用代理工厂,通过一个代理工厂类,可以产生很多的代理对象。

使用代理工厂的方法叫做动态代理。

 

2、动态代理:

1)、代理对象,不需要实现接口;

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

3)、动态代理,也叫做 JDK 代理,或者叫 接口代理;

 

JDK 中生成代理对象的 API:

|---- java.lang.reflect.Proxy

static ObjectnewProxyInstance(ClassLoader loader, <?>[] interfaces, InvocationHandler h)

参数:

loader:目标对象使用的类加载器;

interfaces:目标对象实现的接口类型;

h:事件处理器,执行目标对象的方法时会触发事件处理器,并把当前执行的方法作为事件处理器的参数传入;

 

接口 IUserDao.java 、目标对象 UserDao.java 和上面一样,代理工厂 ProxyFactory.java 如下:

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("开启事务...");

                        // 执行目标对象的方法:method是当前执行的目标对象的方法
                        Object object = method.invoke(target, args);

                        System.out.println("提交事务...");
                        return object;
                    }
                });
    }
}

测试程序 Demo.java:

public class Demo {
    @Test
    public void test(){
        // 目标对象
        UserDao userDao = new UserDao();

        // 给目标对象 创建代理对象
        IUserDao proxy = (IUserDao) new ProxyFactory(userDao).getProxyInstance();

        // 执行方法
        proxy.save();
    }
}

动态代理总结:代理对象不需要实现接口,但是目标对象必须要实现接口,否则不能使用动态代理。

问题:如果有一个目标对象,需要扩展功能,但是该目标对象没有实现接口,那么应该怎样扩展?

可以使用 Cglib 代理。

 

3、Cglib 代理:

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

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

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

 

cglib 子类代理:

1)、需要引入 cglib jar 包,但是 spring 高版本的核心包中已经包含了 cglib 功能,所以直接引入 spring-core-4.3.7.RELEASE.jar 接口;

2)、使用 cglib 代理需要实现 MethodInterceptor 接口;

3)、代理的类不能为 final,否则报错;

4)、目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法;

 

目标对象 UserDao.java:

public class UserDao{
    public void save() {
        System.out.println("----- 保存数据 -----");
    }
}

Cglib 子类代理工厂 ProxyFactory.java:

/**
 * Cglib 子类代理工厂
 *  使用 cglib 代理需要实现 MethodInterceptor 接口
 */
public class ProxyFactory implements MethodInterceptor {

    // 维护一个目标对象
    private Object target;
    public ProxyFactory(Object target){
        this.target = target;
    }

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

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开启事务...");

        // 执行目标对象的方法
        Object object = method.invoke(target, objects);

        System.out.println("提交事务...");
        return object;
    }
}

 测试程序 Demo.java:

public class Demo {
    @Test
    public void test(){
        // 目标对象
        UserDao userDao = new UserDao();

        // 给目标对象 创建代理对象:userDao 是父对象,proxy 是子对象;
        UserDao proxy = (UserDao) new ProxyFactory(userDao).getProxyInstance();

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

 

在 spring 的 AOP 编程中:

        如果目标对象有实现接口,就用 JDK 代理;

        如果目标对象没有实现接口,就用 Cglib 代理;

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值