代理模式的优缺点和使用场景

一、代理模式

        在Java中,代理模式是一种结构型设计模式,它允许通过创建一个代理对象来控制对另一个对象的访问。代理对象充当了客户端和实际对象之间的中介,可以在访问实际对象前后进行一些额外的操作。

        代理模式的主要目的是为了提供一种间接访问方式,以便于控制对真实对象的访问。它可以用于实现访问控制、远程访问、延迟加载等功能。

二、实现代理模式的思路

        实现代理模式的关键是创建一个代理类,该代理类持有一个真实对象的引用,并在方法中调用真实对象的对应方法。通过代理类来控制对真实对象的访问,并可以在访问前后进行一些额外的操作。

三、什么情况下使用代理模式

        业务中的功能不得不写,但是写了又会影响代码的耦合性 这样的问题适合使用代理模式

事务管理

Spring的事务管理功能通常使用代理模式来实现。通过在业务方法前后添加事务管理的逻辑,代理对象可以控制事务的开始、提交或回滚,并提供了对事务的管理和控制。

AOP(面向切面编程)

Spring的AOP功能也是基于代理模式实现的。通过定义切点和切面,代理对象可以在目标对象的方法执行前后插入额外的横切逻辑,如日志记录、性能监控、安全验证等。

四、需要注意的地方

        确定代理类型:在Java中,有两种常见的代理类型:静态代理和动态代理。静态代理需要手动编写代理类,而动态代理则是在运行时生成代理对象。

        注意代理对象的生命周期:在使用代理模式时,需要注意代理对象的生命周期。确保在适当的时候创建和销毁代理对象,以避免资源泄漏或不必要的开销。

五、代理模式的优缺点

1.优点

松耦合

代理模式可以将目标对象与代理对象解耦,使得它们可以独立地进行修改和扩展。

保护目标对象

代理对象可以保护目标对象,隐藏其真实实现细节,提高系统的安全性。

控制访问

代理对象可以控制对目标对象的访问,可以在调用目标方法前后添加额外的逻辑,如权限验证、日志记录等。

AOP支持

代理模式是实现面向切面编程(AOP)的一种重要方式,在Spring框架中广泛应用于事务管理、日志记录、性能监控等方面

2.缺点

        复杂性增加:代理模式会增加代码的复杂性,特别是在使用动态代理时,需要理解和处理动态生成的代理类。

六、实现代理模式的原则和过程

1.代理模式原则

        代理对象应该只关注与目标对象相关的职责,不应该承担过多的额外职责。

        代理对象和被代理者实现同一个接口

        代理对象继承被代理者. 父子关系

2.代理模式的分类

1> 静态代理

构建接口

public interface UserService {
    public void addUser();
    public void deleteUser();
    public void updateUser();
}

目标方法(实现接口)业务的处理

//被代理者 只需要专注于业务
public class UserServiceImpl implements UserService{
    @Override
    public void addUser() {
        System.out.println("增加用户");
    }

    @Override
    public void deleteUser() {
        System.out.println("删除用户");
    }

    @Override
    public void updateUser() {
        System.out.println("修改用户");
    }
}

创建代理对象(也要实现接口)

public class ProxyService implements UserService {

    private UserService target = new UserServiceImpl();

    @Override
    public void addUser() {
        long startTime = System.currentTimeMillis();
        target.addUser();
        long endTime = System.currentTimeMillis();
        System.out.println("程序执行时间为:" + (endTime - startTime) + "毫秒");
    }

    @Override
    public void deleteUser() {
        long startTime = System.currentTimeMillis();
        target.deleteUser();
        long endTime = System.currentTimeMillis();
        System.out.println("程序执行时间为:" + (endTime - startTime) + "毫秒");
    }

    @Override
    public void updateUser() {
        long startTime = System.currentTimeMillis();
        target.updateUser();
        long endTime = System.currentTimeMillis();
        System.out.println("程序执行时间为:" + (endTime - startTime) + "毫秒");
    }
}

测试

public void text(){
        ProxyService proxyService = new ProxyService();
        proxyService.addUser();
        proxyService.deleteUser();
        proxyService.updateUser();
}

优点

        1.设计结构上解耦.

        2.过代理对象将耦合的代码与目标对象进行分离

缺点

        1代码冗余的部分 依然没有解决.

        2.工作中不使用静态代理. 一般都用动态代理.

2>动态代理

1.JDK动态代理

要求: 被代理者必须有接口

编辑代理对象

package com.yl.service;

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

/**
 * @Author yl
 * @Date 2023/8/30 18:45
 * @Version 1.0
 */
public class JDKProxy {
    /**
     * 对外暴露一个获取代理对象的方法
     * @param target  目标对象(即处理业务类的对象)
     * @return
     */
    public static Object getProxy(Object target){
        ClassLoader classLoader = target.getClass().getClassLoader();;
        Class<?>[] interfaces = target.getClass().getInterfaces();

        /**
         * Proxy.newProxyInstance 创建动态代理对象的方法
         * @param classLoader 加载代理类的ClassLoader
         * @param interfaces 指定代理类需要实现的接口数组
         * @param InvocationHandler 指定用于处理代理对象方法调用的InvocationHandler接口的实现类
         */
        return Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
            /**
             * @param proxy 代理对象本身
             * @param method 被调用的方法对象
             * @param args 方法的参数数组
             * @return
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long startTime = System.currentTimeMillis();
                Object result = method.invoke(target, args);
                long endTime = System.currentTimeMillis();
                System.out.println("方法"+method.getName()+"耗时:"+(endTime - startTime) +"毫秒");
                return result;
            }
        });
    }
}

测试

public void test01(){
    UserService target = new UserServiceImpl();
    UserService proxy = (UserService) JDKProxy.getProxy(target);
    proxy.addUser();
    proxy.deleteUser();
    proxy.updateUser();
}

优点

        简单易用:使用JDK动态代理可以快速创建代理对象,无需手动编写代理类。

        无侵入性:对目标对象无需做任何修改,只需要定义接口即可。

        可扩展性:由于基于接口,可以灵活地添加新的接口或方法,以满足不同的需求。

        高效性:JDK动态代理使用了字节码生成技术,生成的代理类在运行时性能较高。

缺点

        基于接口:JDK动态代理只能代理实现了接口的类,无法代理没有实现接口的类。

        限制:JDK动态代理只能代理接口中定义的方法,无法代理类中的非接口方法。

        直接调用:由于代理对象实际上是通过反射来调用目标对象的方法,因此相比直接调用目标对象的方法,会有一定的性能损耗。

        无法处理final方法:JDK动态代理无法代理目标对象中的final方法。

2.Cglib动态代理

要求: 代理对象是目标对象的子类.

编辑代理对象

package com.yl.service;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * @Author yl
 * @Date 2023/8/30 19:28
 * @Version 1.0
 */
public class CgLibProxy{
    public static Object  getProxy(Object target){
        //创建增强器对象
        Enhancer enhancer = new Enhancer();
        //添加父级对象
        enhancer.setSuperclass(target.getClass());
        //可以添加接口  可以不写
        enhancer.setInterfaces(target.getClass().getInterfaces());

        //设定回调方法   代理对象调用方法时  进行拦截
        enhancer.setCallback(new MethodInterceptor() {

            /**
             * 参数说明:
             *      1.proxy  代理对象
             *      2.method 目标方法执行的对象
             *      3.args   参数信息
             *      4.methodProxy  方法的代理
             */
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                long startTime = System.currentTimeMillis();
                method.setAccessible(true);
                //如果方法是私有的可以通过暴力反射
                //method.setAccessible(true);
                Object result = method.invoke(target,args);
                long endTime = System.currentTimeMillis();
                System.out.println("方法"+method.getName()+"耗时:"+(endTime - startTime) +"毫秒");
                return result;
            }
        });
        //创建代理对象
        return enhancer.create();
    }
}

测试

public void test02(){
    UserService target = new UserServiceImpl();
    UserService proxy = (UserService) CgLibProxy.getProxy(target);
    proxy.addUser();
    proxy.updateUser();
    proxy.deleteUser();
}

优缺点

        CGLIB动态代理适用于需要代理没有实现接口的类或需要代理类中的final方法的场景。它具有强大的功能和较高的性能。

        需要注意可能导致内存溢出的问题,并且无法代理final类和构造函数。如果目标类是一个普通的类并且需要代理其中的非final方法,可以考虑使用CGLIB动态代理。

3.动态代理JDK和Cglib代理区别

 

JDK

Cglib

代理方式

通过反射创建代理对象

通过字节码文件创建生成代理对象

效率

创建对象的效率高,但是执行的效率低

读取字节码文件 所以创建对象的效率低, 执行效率高

引入jar包的方式

是java原生提供的

需要额外的引入jar包

代理方式不同

需要实现接口

只需要添加父类就可以.

七、总结

        在不修改源码的条件下,对方法的功能进行扩展解耦,一般采用JDK动态代理,但如果需要代理没有实现接口的类或处理final方法,就需要考虑使用Cglib动态代理。

 

 

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拉姆罗布

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值