代理模式的实现以及运用

本文详细介绍了代理模式的概念及应用场景,对比了JDK动态代理与CGLib动态代理的区别,并通过Spring AOP进行了实操演示,最后给出了一个分布式文件上传代理服务的设计案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代理模式实际上就是对于不能直接访问的对象提供给一个代理对象,通过代理对象可以访问该对象的功能,这样做的有点就每个对象的职责清晰,具有高扩展性更智能化,但是由于访问的对象当中增加了代理对象,有些类型的代理模式会造成请求的处理变慢,实现代理模式需要额外的工作会导致更加复杂;代理实现的技术方案:1、基于接口的动态代理(JDK动态代理,JDK官方的Proxy类),要求被代理的类最少实在这里插入代码片现一个接口;2、基于子类的动态代理(第三方的CGLib)要求被代理的类不能用final修饰的类(最终类,为什么不能用final修饰,看源码可以了解到底层实际是重新实现了被代理类的子类,final修饰的类不能被继承)

JDK动态代理

1、被代理的对象必须实现一个接口;2、用JDK代理,被代理的过程需要实现InvocationHandler接口;3、代理的过程在invoke中实现;4、创建代理对象用Proxy.newProxyInstance实现;
实例:比如房东,中介和租客,那么中介可以代理房东实现收房租的功能;

public interface fangDongService {
    void pay(String name);
}
public class fangDongServiceImpl implements fangDongService {
    
    @Override
    public void pay(String name) {
        
        System.out.println(name+"来交租!");
    }
}

创建代理处理过程对象


public class woaiwojia implements InvocationHandler {

    //被代理的对象
    private Object instance;
    
    public woaiwojia(Object instance) {
        this.instance = instance;
    }
    
    //执行调用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

       //proxy -->woaiwojia 创建的代理
       //  method-->pay方法 代理类执行的方法
       //args  pay方法的参数
        args[0]="我爱我家带"+args[0];
        Object invoke = method.invoke(instance, args);

        return invoke;
    }
}
  public static void main(String[] args) {

        //指定被创建代理的对象
        fangDongService fangDongService = new fangDongServiceImpl();

        //创建代理
        fangDongService service=(fangDongService)Proxy.newProxyInstance(
                        //获取被代理对象的类加载器
                        fangDongServiceImpl.class.getClassLoader(),
                        //被创建代理的对象实现的所有接口
                         fangDongServiceImpl.class.getInterfaces(),
                        new woaiwojia(fangDongService));//代理
        service.pay("张三");


    }

CGLib动态代理

1、代理过程可以实现MethodInterceptor(Callback)接口中的invoke来实现;2、通过Enhancer来创建代理对象
cglib代理方式与jdk代理及其相似,可以理解为在jdk的代理上进行了封装,将上述的我爱我家改成链家代理;

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

import java.lang.reflect.Method;

public class lianjia  implements MethodInterceptor {

    //被代理的对象
    private Object instance;

    public lianjia(Object instance) {
        this.instance = instance;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //proxy -->woaiwojia 创建的代理
        //  method-->pay方法 代理类执行的方法
        //args  pay方法的参数
        objects[0]="链家带"+objects[0];
        Object invoke = method.invoke(instance, objects);

 return invoke;
    }
}

import org.springframework.cglib.proxy.Enhancer;

public class test2 {

    public static void main(String[] args) {

        //给房东生产代理,被代理的对象
        fangDongService fangDongService = new fangDongServiceImpl();
        //将需要代理的对象给代理过程对象
        lianjia lianjia = new lianjia(fangDongService);
        fangDongService proxy = (fangDongService)Enhancer.create(fangDongServiceImpl.class, lianjia);
        proxy.pay("王五");

    }

}

用cglib代理的话是否实现接口都可以完成

AOP动态代理

基于SpringAOP可以实现非常强大的功能,例如声明式事务、基于AOP的日志管理、基于AOP的权限管理等功能, 利用AOP可以将重复的代码抽取,重复利用,节省开发时间,提升开发效率。Spring的AOP其实底层就是基于动态 代理而来,并且支持JDK动态代理和CGLib动态代理,动态代理的集中体现在 DefaultAopProxyFactory 类中

在这里插入图片描述
如果我们在spring的配置文件中不配置 <aop:config proxy-target-class=“true”> ,此时默认使用的将是JDK动态 代理,如果配置了,则会使用CGLib动态代理
Spring中的aop动态代理实际上用的是jdk代理和cglib代理,如果实现接口则用jdk代理,如果没有实现接口则用cglib代理
AOP中jdk代理实现如下
在这里插入图片描述
cglib代理实现如下

在这里插入图片描述

SpingAop的简单运用

实现一个简单的AOP 比如对某一个方法进行增加日志的操作 ,比如下面这个add操作前后操作后增加一些日志

public interface UserService {
    void add();
}

public class UserServiceImpl implements UserService{
//public class UserServiceImpl {
    //@Override
    public void add(){
        System.out.println("增加用户!");
    }
}

在调用add方法前和方法后进行一个记录日志的操作,在调用add前先调用before方法,调用add方法后调用after方法

//增强(切面)类
public class Log {

    public void before(JoinPoint jp){
        System.out.println("执行对象:"+jp.getSignature().getName());
        System.out.println("前置通知记录!");
    }
    public void after(){
        System.out.println("后置操作记录");
    }
}

配置以下的xml

  <!--userService-->
    <bean id="userService" class="com.****.UserServiceImpl"/>
    <!--注册增强(切面)类-->
    <bean id="log" class="com.******.Log"/>
    <!--AOP配置-->
    <aop:config proxy-target-class="false"> <!-- -->
        <!--配置切面(切入点和通知的结合)-->
        <aop:aspect ref="log">
            <!--前置通知-->
            <aop:before method="before" pointcut="execution(* com.***.service.*.*(..))"></aop:before>
            <aop:after  method="after"  pointcut="execution(* com.***.aop.service.*.*(..))"></aop:after>
        </aop:aspect>
    </aop:config>

在spring 源码中,

public static void main(String[] args) {
		//先加载容器,然后从容器中获取对象调用add方法
        ApplicationContext act = new ClassPathXmlApplicationContext("spring-aop.xml");
        UserServiceImpl userService = (UserServiceImpl) act.getBean("userService");
        userService.add();
    }

代理模式的理解

代理实际是一种思想想,jdk与cglib只是实现代理的一种技术手段,比如:代理模式-分布式文件代理服务,代理模式的应用场景除了代码级别,还可以将代理模式迁移到应用以及架构级别,如下图文件上传代理服务,针对一些图片小文件,我们可以直接把文件存储到FastDFS服务,针对大文件,例如视频文件,我们可以把它存储到第三方OSS。用户通过文件上传代理服务可以间接访问OSS和本地FastDFS,这种分布式海量文件管理解决方案,这里不仅在代码层面充分运用了代理模式,在架构层面也充分运用了代理模式。下面基于springboot简单的实现一下实现流程如下:

在这里插入图片描述先做一个fileuplaod接口,然后阿里云上传和fastDfs上传都实现这个接口的upload方法,创建一个代理类FileUploadProxy,在代理类中根据根据上传文件的类型调用不同的上传方式,(为了更好的扩展)将上传方式和文件类型存放到以map的形式存放到yml文件中,key作为文件上传方式,Value为文件类型是一个list集合,将AliyunOSS和FastDfs注入到spring容器中,获取到yml文件中的map并且遍历,根据实际上传的文件类型来决定上传方式,然后通过代理调用上传

抽象出来的上传接口
public interface FileUpload {
    String upload(byte[] buffers , String extName);
}

阿里云上传实现该接口

@Component(value = "aliyunOSSFileUpload")
public class AliyunOSSFileUpload implements FileUpload{

    @Value("${aliyun.oss.endpoint}")
    private String endpoint;
    @Value("${aliyun.oss.accessKey}")
    private String accessKey;
    @Value("${aliyun.oss.accessKeySecret}")
    private String accessKeySecret;
    @Value("${aliyun.oss.key}")
    private String key;
    @Value("${aliyun.oss.bucketName}")
    private String bucketName;
    @Value("${aliyun.oss.backurl}")
    private String backurl;

    @Override
    public String upload(byte[] buffers,String extName) {
        String realName = UUID.randomUUID().toString()+"."+extName ;
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKey, accessKeySecret);
        // <yourObjectName>表示上传文件到OSS时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key+realName, new ByteArrayInputStream(buffers));
        // 上传字符串。
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(FileUtil.getContentType("."+extName));
        putObjectRequest.setMetadata(objectMetadata);
        ossClient.putObject(putObjectRequest);

        // 关闭OSSClient。
        ossClient.shutdown();
        return backurl+realName;
    }
}

FastDfs实现接口

@Component(value = "fastdfsFileUpoad")
public class FastdfsFileUpoad implements FileUpload{

    @Value("${fastdfs.url}")
    private String url;

    /***
     * 文件上传
     * @param buffers:文件字节数组
     * @param extName:后缀名
     * @return
     */
    @Override
    public String upload(byte[] buffers, String extName) {
        /***
         * 文件上传后的返回值
         * uploadResults[0]:文件上传所存储的组名,例如:group1
         * uploadResults[1]:文件存储路径,例如:M00/00/00/wKjThF0DBzaAP23MAAXz2mMp9oM26.jpeg
         */
        String[] uploadResults = null;
        try {
            //获取StorageClient对象
            StorageClient storageClient = getStorageClient();
            //执行文件上传
            uploadResults = storageClient.upload_file(buffers, extName, null);
            return url+uploadResults[0]+"/"+uploadResults[1];
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /***
     * 初始化tracker信息
     */
    static {
        try {
            //获取tracker的配置文件fdfs_client.conf的位置
            String filePath = new ClassPathResource("fdfs_client.conf").getPath();
            //加载tracker配置信息
            ClientGlobal.init(filePath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static StorageClient getStorageClient() throws Exception{
        //创建TrackerClient对象
        TrackerClient trackerClient = new TrackerClient();
        //通过TrackerClient获取TrackerServer对象
        TrackerServer trackerServer = trackerClient.getConnection();
        //通过TrackerServer创建StorageClient
        StorageClient storageClient = new StorageClient(trackerServer,null);
        return storageClient;
    }

代理类实现方式如下

@Component
@ConfigurationProperties(prefix = "upload") //获取yml文件中的map
public class FileUploadProxy implements ApplicationContextAware{

    //注入application.yml中配置实例对应处理的文件类型
    private Map<String,List<String>> filemap;

    //注入Spring的容器对象ApplicationContext
    private ApplicationContext act;

    /***
     * 上传方法
     * 接收文件对象:MultipartFile
     */
    public String upload(MultipartFile file) throws Exception{
        //buffers:文件字节数组
        byte[] buffers = file.getBytes();
        //extName:后缀名  1.jpg->jpg
        String fileName = file.getOriginalFilename();
        String extName = StringUtils.getFilenameExtension(fileName);

        //循环filemap映射关系对象
        for (Map.Entry<String, List<String>> entry : filemap.entrySet()) {
            //获取指定的value  mp4,avi  |  png,jpg
            List<String> suffixList = entry.getValue();

            //匹配当前用户上传的文件扩展名是否匹配
            for (String suffix : suffixList) {
                if(extName.equalsIgnoreCase(suffix)){
                    //获取指定key   aliyunOSSFileUpload | fastdfsFileUpload
                    //一旦匹配执行文件上传
                    String key = entry.getKey();
                    return act.getBean(key,FileUpload.class).upload(buffers,extName);
                }
            }
        }
        
        return "";
    }

    //注入映射配置
    public void setFilemap(Map<String, List<String>> filemap) {
        this.filemap = filemap;
    }

    //注入容器
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        act = applicationContext;
    }
}

yml文件
在这里插入图片描述

@RestController
@RequestMapping(value = "/file")
public class FileController {

    @Autowired
    private FileUploadProxy fileUploadProxy;

    /***
     * 文件上传
     * @param file
     * @return
     * @throws IOException
     */
    @PostMapping(value = "/upload")
    public String upload(MultipartFile file) throws Exception {
        return fileUploadProxy.upload(file);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值