动态代理(笔记)

本文详细介绍了Java中的JDK动态代理和Cglib代理模式,包括它们的实现原理、应用场景及优缺点。JDK动态代理基于接口实现,适用于需对接口方法进行增强的场景;Cglib代理则通过子类化目标类来实现,适用于无接口或接口方法不足的情况。两者在业务逻辑扩展和维护性方面均表现出优势。
摘要由CSDN通过智能技术生成

JDK代理模式

/**
 * @version 1.0
 * @Description Bank的业务逻辑接口
 * @Author chenmin
 * @Date 2021/10/27 14:22
 */
public interface IBankService {
    //登陆
    public boolean login(Long id, String pwd);

    //注册
    public void register(Long id, String pwd, String name);
}

/**
 * @version 1.0
 * @Description 业务逻辑的实现类 - 目标类
 * @Author chenmin
 * @Date 2021/10/27 14:24
 */
public class BankService implements IBankService {
    //登陆
    @Override
    public boolean login(Long id, String pwd) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if(id.equals(1L) && "123".equals(pwd)){
            return true;
        }
        return false;
    }

    //注册
    @Override
    public void register(Long id, String pwd, String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("id = "+id+" , pwd = "+pwd+" , name = "+name);
    }
}

/**
 * @version 1.0
 * @Description 动态代理 - JDK动态代理
 * @Author chenmin
 * @Date 2021/10/27 15:08
 *
 * Spring的AOP原理 - 面向切口编程
 * 默认使用的是JDK动态代理,但是若未发现接口,则会转而使用Cglib动态代理
 *
 * JDK动态代理  也成为"接口代理"
 * 1.在不改变目标类中方法的基础上,通过动态代理,实现对原有方法的增强
 * 2.目标类要实现接口,否则不能用动态代理
 * 3.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
 *
 * 优点:
 *      1.在做目标类中方法的增强时,可以不去改变目标类原有方法的业务逻辑
 *      2.在程序的拓展性能上更好了,维护更加方便
 *
 * 缺点:只能针对接口的实现类做代理对象,普通类是不能做代理对象的
 */
public class DynamicProxyBankLogService {

    //目标对象
    private IBankService target;

    public DynamicProxyBankLogService(IBankService target){
        this.target = target;
    }

    //获得代理对象的方法
    public IBankService getProxy(){
        //代理对象
        IBankService proxy = null;

        //类加载器
        ClassLoader classLoader = target.getClass().getClassLoader();

        //获取目标对象实现的所有接口
        Class<?>[] interfaces = target.getClass().getInterfaces();

        //执行器
        InvocationHandler handler = new InvocationHandler() {
            /**
             * 将当前编写的增强功能,作用在目标对象的方法之上
             * @param proxy  代理对象,一般不使用
             * @param method 目标类中的所有方法
             * @param args   目标类中的所有方法的形参列表
             * @return 目标类中方法的返回值,若没有返回值则返回null
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //long l1 = System.currentTimeMillis();
                //method 当前正在作用在哪一个方法之上  methodName方法名
                String methodName = method.getName();
                //日志追踪
                System.out.println("开启日志追踪:the method "+methodName+" begin with "+ Arrays.asList(args));

                //利用反射执行目标方法  invoke(执行哪一个对象的方法 , 方法所需要的形参)
                //返回方法的执行结果  若目标方法没有返回值则返回null
                Object result = method.invoke(target, args);

                //计算耗时  +" , 此方法耗时:"+(l2-l1)/1000.0+"s"
                //long l2 = System.currentTimeMillis();
                System.out.println("结束日志追踪:the method "+methodName+" end with "+result );
                return result;
            }
        };

        proxy = (IBankService)Proxy.newProxyInstance(classLoader, interfaces, handler);
        return proxy;
    }
}
/**
 * @version 1.0
 * @Description 测试JDK动态代理
 * @Author chenmin
 * @Date 2021/10/27 15:28
 */
public class Test {

    public static void main(String[] args) {
        //目标对象
        IBankService target = new BankService();
        //com.igeek.javase.ch22.proxy.dynamic.BankService
        System.out.println(target.getClass().getName());

        //获取代理对象
        IBankService proxy = new DynamicProxyBankLogService(target).getProxy();
        //com.sun.proxy.$Proxy0  利用JDK的API,动态的在内存中构建代理对象
        System.out.println(proxy.getClass().getName());

        //测试登陆方法
        proxy.login(1L,"123");

        //测试注册方法
        proxy.register(2L,"222","张二蛋");
    }

}

运行结果

com.igeek.javase.ch22.proxy.dynamic.BankService
com.sun.proxy.$Proxy0
开启日志追踪:the method login begin with [1, 123]
结束日志追踪:the method login end with true
开启日志追踪:the method register begin with [2, 222, 张二蛋]
id = 2 , pwd = 222 , name = 张二蛋
结束日志追踪:the method register end with null

因为register 没有返回值所以最后为null

Cglib代理模式

/**
 * @version 1.0
 * @Description 业务逻辑的实现类 - 目标类
 * @Author chenmin
 * @Date 2021/10/27 14:24
 */
public /*final*/ class BankService {

    //默认提供无参构造方法,所以省略不写
    /*public BankService(int a){

    }*/

    //登陆
    public /*final*/ /*static*/ boolean login(Long id, String pwd) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if(id.equals(1L) && "123".equals(pwd)){
            return true;
        }
        return false;
    }

    //注册
    public void register(Long id, String pwd, String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("id = "+id+" , pwd = "+pwd+" , name = "+name);
    }
}
/**
 * @version 1.0
 * @Description 代理模式 - Cglib代理模式
 * @Author chenmin
 * @Date 2021/10/27 16:07
 *
 * Cglib代理模式
 * 在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理。
 *
 * 优点:
 *      1.若目标对象不需要实现接口,用Cglib代理。
 *      2.做业务逻辑方法的增强时,不需要在原有方法上去做改变。
 *      3.让程序拓展性更好,维护更加方便
 *
 * 缺点:
 *      实现MethodInterceptor拦截器接口
 *
 * 步骤:
 * 1.当前代理类实现MethodInterceptor拦截器接口,重写拦截器方法
 * 2.提供一个目标对象,提供一个获得代理对象的方法
 *
 * 注意事项:
 * 1.必须给目标类中提供公开的无参构造方法
 * IllegalArgumentException: Superclass has no null constructors but no arguments were given
 * 2.目标类不可以使用final修饰,使用后将无法为其创建子类
 * IllegalArgumentException: Cannot subclass final class
 * 3.目标类中的目标方法不可以使用final修饰,使用后,拦截器中的拦截方法,会拦截不到final的方法
 * 4.目标类中的目标方法不可以使用static修饰,使用后,拦截器中的拦截方法,会拦截不到static的方法
 */
public class CglibProxyBankLogService implements MethodInterceptor {

    //目标对象
    private BankService target;

    public CglibProxyBankLogService(BankService target){
        this.target = target;
    }

    //获取代理对象的方法
    public BankService getProxy(){
        BankService proxy = null;
        //在内存中构建一个子类对象从而实现对目标对象功能扩展
        Enhancer enhancer = new Enhancer();
        //设置其父类
        enhancer.setSuperclass(BankService.class);
        //设置回调函数  this就是CglibProxyBankLogService,CglibProxyBankLogService implements MethodInterceptor extends Callback
        enhancer.setCallback(this);
        /*
         * enhancer.create()  在内存中构建一个子类对象
         * 1.找到其父类类型Superclass,利用其无参构造方法来创建子类对象
         * 2.创建完毕,回调执行Callback,此处this是拦截器 -->  执行回调函数,就是在执行拦截方法intercept()
         */
        proxy = (BankService)enhancer.create();
        return proxy;
    }

    /**
     * 拦截器方法中实现,目标类中希望增强的非业务逻辑
     * @param proxy   代理对象,此处不用
     * @param method  目标类中的方法对象
     * @param args    目标类中的方法的形参列表
     * @param methodProxy 方法的代理对象,此处不用
     * @return  执行目标类中方法的返回值,若没有返回值则返回null
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        long l1 = System.currentTimeMillis();
        //method 当前正在作用在哪一个方法之上  methodName方法名
        String methodName = method.getName();
        //日志追踪
        System.out.println("开启日志追踪:the method "+methodName+" begin with "+ Arrays.asList(args));

        //利用反射执行目标方法  invoke(执行哪一个对象的方法 , 方法所需要的形参)
        //返回方法的执行结果  若目标方法没有返回值则返回null
        Object result = method.invoke(target, args);

        //计算耗时
        long l2 = System.currentTimeMillis();
        System.out.println("结束日志追踪:the method "+methodName+" end with "+result +" , 此方法耗时:"+(l2-l1)/1000.0+"s" );
        return result;
    }
}

/**
 * @version 1.0
 * @Description 测试
 * @Author chenmin
 * @Date 2021/10/27 16:07
 */
public class Test {

    public static void main(String[] args) {
        //目标对象
        BankService target = new BankService();
        //com.igeek.javase.ch22.proxy.cglib.BankService
        System.out.println("target = "+target.getClass().getName());

        //代理对象
        BankService proxy = new CglibProxyBankLogService(target).getProxy();
        //com.igeek.javase.ch22.proxy.cglib.BankService$$EnhancerByCGLIB$$2ff6b48a
        System.out.println("proxy = "+proxy.getClass().getName());

        //执行登陆方法
        proxy.login(1L,"123");

        //执行注册方法
        proxy.register(2L,"222","陈二蛋");
    }

}

运行结果

target = com.igeek.javase.ch22.proxy.cglib.BankService
proxy = com.igeek.javase.ch22.proxy.cglib.BankService E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBac9bd6b1
开启日志追踪:the method login begin with [1, 123]
结束日志追踪:the method login end with true , 此方法耗时:1.007s
开启日志追踪:the method register begin with [2, 222, 陈二蛋]
id = 2 , pwd = 222 , name = 陈二蛋
结束日志追踪:the method register end with null , 此方法耗时:1.007s

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值