zoukankan      html  css  js  c++  java
  • 【Spring】大白话聊聊代理模式 —— 动态代理

    • 为什么要使用代理模式呢?

    打个比方,我虽然不会打篮球,但我很喜欢A锥,可是国内我又买不起,

    发现海淘的A锥,既经济又实惠(咱也不知道是不是真货,咱也不敢问),

    不过我发现我去访问海外电商极度费劲,此时我该怎么办?

    我突然想到了代购老黄,我们可以通过老黄啊,来帮我们获取到我们心仪的A锥。

    从静态代理的角度我们分析一下老黄代理如何运作:

     所以对于老黄,也就代理来说,其实就是一个搬运工。

    也比如我们的博客系统,我们查看一个文章,如果文章中有个特别大的图片需要加载,

    此时如果等在加载完图片再进行展示图片以后的文章内容,是非常耗时的操作,

    那么此时我们就可以使用Proxy来先代替那个图片。

    • 我们先看看静态代理是如何实现上述鞋工厂的:

    1》先创建一个鞋工厂接口:

    public interface ShoeFactory {
    
        void saleShoe(int size);
    
    }

    2》创建一个AJ鞋厂来实现这个接口:

    public class AJFactory implements ShoeFactory {
    
        @Override
        public void saleShoe(int size) {
            System.out.println("根据您的需求,为您定制了一个size为:" + size + "的AJ");
        }
    
    }

     3》看看老黄是如何代理的:

    /**
     * 代理类
     */
    public class LaohuangProxy implements ShoeFactory {
    
        private ShoeFactory factory;
    
        public LaohuangProxy(ShoeFactory factory) {
            super();
            this.factory = factory;
        }
    
        @Override
        public void saleShoe(int size) {
            doSomethingBefore();//前置增强
            factory.saleShoe(size);
            doSomethingAfter();//后置增强
        }
    
        private void doSomethingAfter() {
            System.out.println("提供快递服务一条龙");
        }
    
        private void doSomethingBefore() {
            System.out.println("根据您的需求进行市场调研");
        }
    
        public static void main(String[] args) {
            AJFactory factory = new AJFactory();//1.有一个AJ鞋工厂
            LaohuangProxy laohuangProxy = new LaohuangProxy(factory);//2.老黄代理
            laohuangProxy.saleShoe(43);//3.帮我订购一双43码鞋
        }
    }

    但此时,快过情人节了,这空手确实不太行啊,我准备给我的女神小娜个迪奥999口红(就是这么大气),

    但是专柜已经被抢空了,所以我又想到了无所不能的老黄,找他帮我代购一波:

    如果继续使用静态代理,我们需要再创建一个口红制造厂接口和迪奥口红工厂类,话不多说,撸码就完了:

    1》声明一个口红工厂接口

    public interface LipstickFactory {
    
        void saleLipstick(String color);
    
    }

    2》创建一个迪奥工厂类:

    public class DiorFactory implements LipstickFactory {
        @Override
        public void saleLipstick(String color) {
            System.out.println("根据您的需求,为您包装了一个颜色为:" + color + "的迪奥口红");
        }
    }

    3》看看老黄代理的改动:

    public class LaohuangProxy implements ShoeFactory, LipstickFactory { //改动1:需要多继承一个接口
    
        private ShoeFactory factory;
        private LipstickFactory lipstickFactory;//改动2:由于代理类只是一个搬运工,所以需要多包含一个口红工厂
    
        public LaohuangProxy(ShoeFactory factory) {
            super();
            this.factory = factory;
        }
    
        //改动3:需要实现一个新的构造函数
        public LaohuangProxy(LipstickFactory lipstickFactory) {
            super();
            this.lipstickFactory = lipstickFactory;
        }
    
        @Override
        public void saleShoe(int size) {
            doSomethingBefore();//前置增强
            factory.saleShoe(size);
            doSomethingAfter();//后置增强
        }
    
        //改动4:需要新增一个口红售卖类
        @Override
        public void saleLipstick(String color) {
            doSomethingBefore();//前置增强
            lipstickFactory.saleLipstick(color);
            doSomethingAfter();//后置增强
        }
    
        private void doSomethingAfter() {
            System.out.println("提供快递服务一条龙");
        }
    
        private void doSomethingBefore() {
            System.out.println("根据您的需求进行市场调研");
        }
    
        main{....}
    }

    可以看出一共有四处改动,所以静态代理的弊端就是,我么如果每增加一个代理对象,代理类就需要在原有基础上有很大的改动。

    • 所以此时我们必须要引入动态代理,那动态代理是如何实现的呢?

    动态代理的话,老黄就不是一个人代购了,老黄开了一家代购公司,

     

     话不多BB,撸代码:

    (口红工厂和鞋工厂对象同上)

    动态代理的总代理对象如下:

    public class LaohuangCompanyProxy implements InvocationHandler {
    
        private Object factory;
    
        public Object getFactory() {
            return factory;
        }
    
        public void setFactory(Object factory) {
            this.factory = factory;
        }
    
        public Object getProxyInstance() {
            //传入this说明调度的所有员工必须遵循当前类的业务增强
            return Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), this);
        }
    
        /**
         * 通过动态代理对象方法进行增强(动态代理对象调用方法时调用本方法)
         *
         * @param proxy
         * @param method 方法名
         * @param args   方法参数
         * @return
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            doSomethingBefore();
            Object res = method.invoke(factory, args);
            doSomethingAfter();
            return res;
        }
    
        private void doSomethingAfter() {
            System.out.println("提供快递服务一条龙");
        }
    
        private void doSomethingBefore() {
            System.out.println("根据您的需求进行市场调研");
        }
    
        public static void main(String[] args) {
            ShoeFactory shoeFactory = new AJFactory();
            LipstickFactory lipstickFactory = new DiorFactory();
    
            LaohuangCompanyProxy laohuangCompanyProxy = new LaohuangCompanyProxy();//声明总代理对象
            laohuangCompanyProxy.setFactory(shoeFactory);
            ShoeFactory shoeFactory1 = (ShoeFactory) laohuangCompanyProxy.getProxyInstance();//通过总代理对象分配出一个代理运行对象
            shoeFactory1.saleShoe(43);
    
            laohuangCompanyProxy.setFactory(lipstickFactory);
            LipstickFactory lipstickFactory1 = (LipstickFactory) laohuangCompanyProxy.getProxyInstance();
            lipstickFactory1.saleLipstick("斩男色");
        }
    
    }

    关键代码:

    1》【Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), this);】

    生成一个代理对象,通过总代理对象,创建一个动态代理对象:
    Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)


    ClassLoader loader: 定义了由哪个类加载器来对生成的代理对象进行加载

    Class<?>[] interfaces: 表示给代理对象提供一组什么接口,相当于用代理对象去实现这些接口中的方法,这样就可以直接调用了

    InvocationHandler h:表示我这个动态代理对象在调用方法时,会关联到哪一个InvocationHandler上

    2》【Object invoke(Object proxy, Method method, Object[] args)】

    Object invoke(Object proxy, Method method, Object[] args)

    Object proxy: 所代理的真实对象

    Method method:所要调用的某个方法

    Object[] args: 调用方法中的参数

    我们Debug看一下【shoeFactory1】的真正类型,是一个JDK帮我们生成的动态代理类型。

    • 我们分析下AOP的是如何使用JDK动态代理的呢?

    在Spring-Aop源文件中类【JdkDynamicAopProxy】实现了【InvocationHandler】:

    所以同样也实现了invoke方法,那么【@Transactional】注解实现是如何的呢?

    调用了【invoke】方法后,通过责任链的方式,交给对应的拦截器【TransactionInterceptor】去进行aop拦截

        @Override
        @Nullable
        public Object invoke(MethodInvocation invocation) throws Throwable {
            // Work out the target class: may be {@code null}.
            // The TransactionAttributeSource should be passed the target class
            // as well as the method, which may be from an interface.
            Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    
            // Adapt to TransactionAspectSupport's invokeWithinTransaction...
            return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
        }

    然后进入到【invokeWithinTransaction】,我们可以看下源码:

    @Nullable
        protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
                final InvocationCallback invocation) throws Throwable {
    
            // If the transaction attribute is null, the method is non-transactional.
            TransactionAttributeSource tas = getTransactionAttributeSource();
            final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
            final PlatformTransactionManager tm = determineTransactionManager(txAttr);
            final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
            if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
                //开启事务,关闭自动提交
                TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
                Object retVal = null;
                try {
                    // This is an around advice: Invoke the next interceptor in the chain.
                    // This will normally result in a target object being invoked.
                    retVal = invocation.proceedWithInvocation();//执行方法本身
                }
                catch (Throwable ex) {
                    // target invocation exception
                    completeTransactionAfterThrowing(txInfo, ex);//异常提交回滚
                    throw ex;
                }
                finally {
                    cleanupTransactionInfo(txInfo);
                }
                commitTransactionAfterReturning(txInfo);//未出现异常则直接进行提交
                return retVal;
            }
    
            else {
                final ThrowableHolder throwableHolder = new ThrowableHolder();
    
                // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
                try {
                    Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
                        TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                        try {
                            return invocation.proceedWithInvocation();
                        }
                        catch (Throwable ex) {
                            if (txAttr.rollbackOn(ex)) {
                                // A RuntimeException: will lead to a rollback.
                                if (ex instanceof RuntimeException) {
                                    throw (RuntimeException) ex;
                                }
                                else {
                                    throw new ThrowableHolderException(ex);
                                }
                            }
                            else {
                                // A normal return value: will lead to a commit.
                                throwableHolder.throwable = ex;
                                return null;
                            }
                        }
                        finally {
                            cleanupTransactionInfo(txInfo);
                        }
                    });
    
                    // Check result state: It might indicate a Throwable to rethrow.
                    if (throwableHolder.throwable != null) {
                        throw throwableHolder.throwable;
                    }
                    return result;
                }
                catch (ThrowableHolderException ex) {
                    throw ex.getCause();
                }
                catch (TransactionSystemException ex2) {
                    if (throwableHolder.throwable != null) {
                        logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                        ex2.initApplicationException(throwableHolder.throwable);
                    }
                    throw ex2;
                }
                catch (Throwable ex2) {
                    if (throwableHolder.throwable != null) {
                        logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                    }
                    throw ex2;
                }
            }
        }

    总结,所有的AOP都是基于动态代理增强,并且如果面试官问@Transactional的实现原理是什么,不要只说动态代理,要别责任链、连接器以及实现流程大致说详细。

  • 相关阅读:
    实现FTP断点续传
    系统软件自动部署实现方案
    QT实现多语言切换
    QTreeWidget实现动态加载本地文件系统
    QuaZip实现多文件打包
    FileZilla命令行实现文件上传以及CreateProcess实现静默调用
    ctkPlugin插件系统实现项目插件式开发
    Windows与Linux下文件操作监控的实现
    QT皮肤系统的动态切换
    OpenGL 学习
  • 原文地址:https://www.cnblogs.com/boluopabo/p/13228002.html
Copyright © 2011-2022 走看看