代理模式--Proxy类&CGLib

代理模式(Proxy)

–>接口 / 继承的应用

代理模式是Java开发中使用较多的一种设计模式。代理设计模式就是为其他对象提供一种代理以控制对这个对象的访问。

1. 静态代理

/**
 * 接口的应用: 代理设计模式
 */
public class NetWorkTest {
    public static void main(String[] args) {
        new ProxySever().browse();
    }
}

//代理类和被代理类都需要实现的功能接口
interface NetWork{
    void browse();
}

//被代理类
class Sever implements NetWork{
    @Override
    public void browse() {
        System.out.println("实际的 被代理的服务器浏览网络");
    }
}

//代理类
class ProxySever implements NetWork{
    //代理类持有被代理对象实例
    private NetWork work = new NetWork();

    @Override
    public void browse() {
        //代理类的附加操作
        check();

        //实际调用被代理对象进行操作
        work.browse();
    }

    private void check(){
        System.out.println("代理类 进行联网前的检查工作");
    }
}

2. JDK-Proxy类-基于接口的动态代理

2.1 动态代理接口 实现类

package com.qy34.sm.dynamicproxy;

import com.qy34.sm.staticproxy.DoSomething;
import com.qy34.sm.staticproxy.User;

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

/**
 * @author chenyao
 */
public class ReflectTest {
    public static void main(String[] args) {
        /**
         * 被代理对象
         */
        User user = new User();

        /**
         * JDK动态代理的核心, 使用Proxy.newProxyInstance()构建一个动态代理对象
         */
        Object proxyIns = Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("代理类的增强功能");
                return method.invoke(user, args);
            }
        });
        /**
         * 通过接口来接收代理类对象, 实现接口的代理
         */
        DoSomething doSomethingProxy = (DoSomething) proxyIns;
        doSomethingProxy.doSomething();
    }
}

2.2 无实现类, 动态代理接管接口的方法(MyBatis的Dao接口)

代理工厂类

import com.qy34.sm.dao.BasicDao;
import com.qy34.sm.domain.SqlMessage;

import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * @author chenyao
 * 代理对象的简单工厂类
 */
public class ProxyFactory {
    Map<String, SqlMessage> sqlMap;

    public ProxyFactory(Map<String, SqlMessage> sqlMap) {
        this.sqlMap = sqlMap;
    }

    public Object getProxy(Class<?> clazz) {
        Object proxyIns = Proxy.newProxyInstance(
                clazz.getClassLoader(),
                new Class<?>[]{clazz},
                (proxy, method, args) -> {
                    String sqlId = method.getDeclaringClass().getName() + "." + method.getName();
                    //com.qy34.sm.dynamicproxy.StudentDao.select

                    SqlMessage sqlMessage = sqlMap.get(sqlId);
                    String sql = sqlMessage.getSql();
                    Class<?> returnType = Class.forName(sqlMessage.getReturnType());

                    Object result = null;
                    if ("select".equals(sqlMessage.getSqlCommand())) {
                        result = new BasicDao<>().select(sql, (Class<Object>) returnType, args);
                    } else {
                        //其他类型的方法
                        System.out.println("insert, update, delete 方法未实现");
                    }
                    return result;
                });
        return proxyIns;
    }
}

测试类

package com.qy34.sm.dynamicproxy;

import com.qy34.sm.domain.SqlMessage;
import org.junit.Test;

import java.util.HashMap;

public class ProxyFactoryTest {

    @Test
    /**
     * 获取代理对象
     */
    public void getProxy() {

        SqlMessage sqlMessage1 = new SqlMessage();
        sqlMessage1.setId("com.qy34.sm.dynamicproxy.StudentDao.select");
        sqlMessage1.setSql("select * from student where id = ?");
        sqlMessage1.setSqlCommand("select");
        sqlMessage1.setReturnType("com.qy34.sm.domain.Student");
        /*
        假装注入sql配置文件
         */
        HashMap<String, SqlMessage> sqlMap = new HashMap<>();
        sqlMap.put(sqlMessage1.getId(),sqlMessage1);

        /**
         * 获取动态代理对象, 执行代理方法
         */
        ProxyFactory proxyFactory = new ProxyFactory(sqlMap);
        Object proxy = proxyFactory.getProxy(StudentDao.class);
        StudentDao studentDao = (StudentDao) proxy;
        System.out.println(studentDao.select(1005));
    }
}

3. CGLib-基于继承的动态代理(也能基于接口)

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * CGLIB: 基于继承的动态代理实现
 * 依赖: cglib.jar + asm.jar
 * @author chenyao
 */
public class CglibProxy {
    public static void main(String[] args) {
        /**
         * 被代理对象
         */
        Teacher teacher = new Teacher();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(teacher.getClass());
        enhancer.setClassLoader(teacher.getClass().getClassLoader());
        //设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            @Override   //重写方法拦截器的 intercept() 方法
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("代理对象增强的功能");
                //System.out.println(o);//StackOverflowError 栈溢出, 代理对象没有重写父类的toString()方法
                /**
                   调用被代理对象的实际功能
                 */
                Object result = method.invoke(teacher, objects);
                return result;
            }
        });
        //创建代理对象
        Object proxyObject = enhancer.create();

        Teacher proxy = (Teacher) proxyObject;
        proxy.doSomething();
    }
}

应用场景:

​ 安全代理: 屏蔽对真实角色的直接访问

​ 远程代理: 通过代理类处理远程方法调用(RMI), 分布式, 通过网路和协议实现

​ 延迟加载: 先加载轻量级的代理对象, 真正需要再加载真实对象

​ 比如开发一个大文档查看软件, 大文档中有大的图片, 在打开文件时, 不可能把所有的图片都完整显示出来, 这样就可以使用代理模式, 当需要查看图片时, 用代理对象proxy来进行大图片的打开。

分类:

​ 静态代理 (静态定义代理类)

​ 动态代理 (动态生成代理类)

​ JDK自带的动态代理, 基于接口实现。

​ CGLib的动态代理, 基于继承实现。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

code tea

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

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

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

打赏作者

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

抵扣说明:

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

余额充值