• 面试真题--------spring源码解析AOP


    接着上一章对IOC的理解之后,再看看AOP的底层是如何工作的。

    1.实现AOP的过程

       首先我们要明白,Spring中实现AOP,就是生成一个代理,然后在使用的时候调用代理。

              1.1 创建代理工厂

                  代码中首先创建一个代理工厂实例ProxyFactory proxyFactory = new ProxyFactory();代理工厂的作用就是使用编程的方式创建AOP代理。ProxyFactory继承自AdvisedSupport,AdvicedSupport是AOP代理的配置管理器。然后是设置要代理的目标对                 象proxyFactory.setTarget(new LoginServiceImpl());,看下setTarget方法:

    public void setTarget(Object target) {
    //先根据给定的目标实现类,创建一个单例的TargetSource
    //然后设置TargetSource
    setTargetSource(new SingletonTargetSource(target));
    }

             1.2  添加通知                      

    上面设置了要代理的目标类之后,接着是添加通知,也就是添加增强类,proxyFactory.addAdvice()方法是添加增强类的方法。我们在例子中是这么使用的:

    1
    2
    3
    proxyFactory.addAdvice(new LogBeforeLogin());//前置增强
    proxyFactory.addAdvice(new LogAfterLogin());//后置增强
    //proxyFactory.addAdvice(new LogAroundLogin());//环绕增强

    addAdvice方法的参数是一个Advice类型的类,也就是通知或者叫增强,可以去我们的增强类中查看,我们都继承了各种Advice,比如MethodBeforeAdviceAfterReturningAdviceMethodInterceptor。

            1.3 获取代理

             

    JDK动态代理方式获取代理

    JDK动态代理方式获取代理,实现在JdkDynamicAopProxy中:

    1
    2
    3
    public Object getProxy() {
    return getProxy(Thread.currentThread().getContextClassLoader());
    }
    1
    2
    3
    4
    5
    6
    7
    public Object getProxy(ClassLoader cl) {
    //JDK动态代理只能代理接口类型,先获取接口
    //就是从AdvisedSupport中获取保存在interfaces中的接口
    Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advisedSupport);
    //使用Java的反射机制创建一个代理实例
    return Proxy.newProxyInstance(cl, proxiedInterfaces, this);
    }

    关于JDK反射创建代理之类的,这里不做解析。

    CGLIB方式获取代理

    CGLIB获取方式,实现在Cglib2AopProxy中:

    1
    2
    3
    4
    public Object getProxy() {
    //使用CGLIB的方式来获取,CGLIB这里不做解析
    return getProxy(Thread.currentThread().getContextClassLoader());
    }

    使用代理

    上面获取代理之后,就剩最后一步,使用,当我们调用业务方法的时候,实际上是调用代理中的方法,对于CGLIB生成的代理,调用的是DynamicAdvisedInterceptor的intercept方法;JDK动态代理生成的代理是调用invoke方法。

    调用ProxyFactoryBean的getObject方法,也就是对bean增强的地方。下面我们着重来看一下是如何对bean进行增强的。首先我们进入到ProxyFactoryBean的getObject方法来看一下。

    public Object getObject() throws BeansException {
            initializeAdvisorChain();
            if (isSingleton()) {
                return getSingletonInstance();
            }
            else {
                if (this.targetName == null) {
                    logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                            "Enable prototype proxies by setting the 'targetName' property.");
                }
                return newPrototypeInstance();
            }
        }
    复制代码

    此处主要是先初始化了一下通知器链,然后就会根据是否单例做相应的动作,我们看一下初始化通知器链的进行。

    复制代码
    private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
            if (this.advisorChainInitialized) {
                return;
            }
    
            if (!ObjectUtils.isEmpty(this.interceptorNames)) {
                if (this.beanFactory == null) {
                    throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
                            "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
                }
    
                // Globals can't be last unless we specified a targetSource using the property...
                if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
                        this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
                    throw new AopConfigException("Target required after globals");
                }
    
                // Materialize interceptor chain from bean names.
                for (String name : this.interceptorNames) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Configuring advisor or advice '" + name + "'");
                    }
    
                    if (name.endsWith(GLOBAL_SUFFIX)) {
                        if (!(this.beanFactory instanceof ListableBeanFactory)) {
                            throw new AopConfigException(
                                    "Can only use global advisors or interceptors with a ListableBeanFactory");
                        }
                        addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
                                name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
                    }
    
                    else {
                        // If we get here, we need to add a named interceptor.
                        // We must check if it's a singleton or prototype.
                        Object advice;
                        if (this.singleton || this.beanFactory.isSingleton(name)) {
                            // Add the real Advisor/Advice to the chain.
                            advice = this.beanFactory.getBean(name);
                        }
                        else {
                            // It's a prototype Advice or Advisor: replace with a prototype.
                            // Avoid unnecessary creation of prototype bean just for advisor chain initialization.
                            advice = new PrototypePlaceholderAdvisor(name);
                        }
                        addAdvisorOnChainCreation(advice, name);
                    }
                }
            }
    
            this.advisorChainInitialized = true;
        }
    复制代码

    可以看到,其中针对我们配置的interpretorNames进行了循环,我们并非是配置的全局通知器,所以会进入else块,然后因为我们配置的testAdvisor默认是单例的,所以会从bean工厂中去获取这个实例,此时TestAdvisor已经实例化完成的,我们只是去取一下而已。然后就会进入addAdvisorOnChainCreation方法,就是把通知器加到了通知链当中。

                  值得注意的是在这个过程中,触发了一个这样的方法this.advisorAdapterRegistry.wrap(next)。这个方法就是用来包装通知器的,如果不是advisor而是advice,就会包装一下返回。

                  好了,接着刚才的过程,初始化通知器链完成以后,就会进入getSingletonInstance方法,这是用来获取单例实例的,而真正的加强也是在这里发生的,我们来看一下。

    复制代码
    private synchronized Object getSingletonInstance() {
            if (this.singletonInstance == null) {
                this.targetSource = freshTargetSource();
                if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
                    // Rely on AOP infrastructure to tell us what interfaces to proxy.
                    Class targetClass = getTargetClass();
                    if (targetClass == null) {
                        throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
                    }
                    setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
                }
                // Initialize the shared singleton instance.
                super.setFrozen(this.freezeProxy);
                this.singletonInstance = getProxy(createAopProxy());
            }
            return this.singletonInstance;
        }
    复制代码

    此时第一次获取,单例实例为null,所以会进入if块,首先刷新targetSource,因为我们的Target类没有实现targetSource接口,所以会由spring帮我们产生一个targetSource适配,这里是使用的适配器的模式,有兴趣可以进去看一下,我们此处不关注这个。接下来,会去判断代理接口,并且设置代理接口,但是我们的target未实现任何接口,所以此处interfaces仍然为空的,所以最后一步createAopProxy时,会帮我们创建cglib的proxy。最终由cglib生成代理返回。

    2.spring aop通知(advice)分成五类

    前置通知[Before advice]:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。 
    正常返回通知[After returning advice]:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。 
    异常返回通知[After throwing advice]:在连接点抛出异常后执行。 
    返回通知[After (finally) advice]:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。 
    环绕通知[Around advice]:环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。 

    总结一下,主要说几点:

    1.在IOC容器初始化的过程中,并没有发生增强的动作,而是初始化了proxyFactoryBean。

    2.如果配置中不指定,所有bean默认都是单例和非延迟加载的,也就是说所有的bean都将在第一次IOC容器初始化时全部实例化,所以上一章中所配置的三个bean都是在IOC容器初始化时进行的实例化。

    3.springAOP代理有两种方式,一种是JDK提供的动态代理,一种是cglib字节码生成的技术,当要代理的类有实现的接口的时候,就会针对接口进行代理,否则就会采用cglib直接生成字节码产生子类。

    参考---http://www.cnblogs.com/zuoxiaolong/p/spring7.html

               http://cxis.me/2017/04/12/Spring%E4%B8%ADAOP%E6%BA%90%E7%A0%81%E6%B7%B1%E5%85%A5%E8%A7%A3%E6%9E%90/

  • 相关阅读:
    数据库系统load飙高问题解决思路
    随Linux开机自动启动mysql
    mysql_safe和mysql_multi
    mysqlslap压测
    “努力就会成功”
    MongonDB 知识
    Linux系统下MongoDB的简单安装与基本操作
    性能压测,更新库存时间长
    Buffer Latch Timeout的解析
    SQL Server 性能调优(一)——从等待状态判断系统资源瓶颈
  • 原文地址:https://www.cnblogs.com/technologykai/p/9046752.html
Copyright © 2020-2023  润新知