【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的实现原理是什么,不要只说动态代理,要别责任链、连接器以及实现流程大致说详细。

 

posted @ 2020-07-05 16:17  boluo1230  阅读(348)  评论(0编辑  收藏  举报