1. 程式人生 > >spring 依賴注入時,什麼時候會建立代理類

spring 依賴注入時,什麼時候會建立代理類

問題來源

 以前一直有個疑惑,為什麼我建立的controller中注入的service類有時候是代理類,有時候是普通javabean,當時能力不夠,現在已經有了點經驗就大膽跟了跟原始碼,看看到底咋回事。

  首先看看問題現象:

   a1:service是代理類,並且是CGLIB型別代理

  

    a2:service是代理類,並且是jdk 動態代理

  b:serivce不是代理類,而是普通類 

問題分析

 我對service類進行了以下的測試:(前提開啟事務註解<tx:annotation-driven/>)

  1)service方法新增@Transactional註解或者加入其它的aop攔截配置,沒有實現任何介面。   對應問題現狀 a1

  2)service方法新增@Transactional註解或者加入其它的aop攔截配置,實現了介面。              對應問題現狀a2

  3)serice方法沒有新增@Transactional註解或者其它的aop攔截配置。                                      對應問題現狀b

 看來出現這種問題的原因就是spring的問題,因為這個類是它建立的,這就需要我們來看下spring建立bean的程式碼,由於spring太龐大了

我們只看最關鍵的部分,在建立bean是都會呼叫getBean()方法,

    @SuppressWarnings("unchecked")
    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
      return createBean(beanName, mbd, args);
    }

     經過不斷的流轉會進入AbstractAutowireCapableBeanFactory的createBean方法

複製程式碼

@Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {try {
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isDebugEnabled()) {
                logger.debug("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
            // A previously detected exception with proper bean creation context already,
            // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
            throw ex;
        }

    }

複製程式碼

然後呼叫doCreateBean方法

複製程式碼

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
if (instanceWrapper == null) {
       
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }// Initialize the bean instance.
        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }return exposedObject;
    }

複製程式碼

然後進入核心的createBeanInstance方法,省去了不相關方法

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// No special handling: simply use no-arg constructor.
        return instantiateBean(beanName, mbd);
    }

然後呼叫instantiateBean進行bea的例項化

複製程式碼

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
        try {
            Object beanInstance;
            final BeanFactory parent = this;
            if (System.getSecurityManager() != null) {
                beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
                        getInstantiationStrategy().instantiate(mbd, beanName, parent),
                        getAccessControlContext());
            }
            else {
                beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
            }
            BeanWrapper bw = new BeanWrapperImpl(beanInstance);
            initBeanWrapper(bw);
            return bw;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
        }
    }

複製程式碼

例項化時會呼叫SimpleInstantiationStrategy的instantiate方法

複製程式碼

@Override
    public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        if (!bd.hasMethodOverrides()) {
            Constructor<?> constructorToUse;
            synchronized (bd.constructorArgumentLock) {
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(
                                    (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
                        }
                        else {
                            constructorToUse =    clazz.getDeclaredConstructor();
                        }
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Throwable ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // Must generate CGLIB subclass.
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }

複製程式碼

該方法就是真正的例項化bean,根據不同情況通過CGLIB的方式

instantiateWithMethodInjection(bd, beanName, owner)

或者java的反射方式

BeanUtils.instantiateClass(constructorToUse)

例項化一個bean,這是時候都是一個純潔無瑕的javabean,那每個bean的額外加工,例如為某個bean新增事務支援,

新增aop配置,還有就是將springmvc的controller進行url和handler的對映,等等這些都是在spring的擴充套件點完成的,回到

上面的doCreateBean方法

執行完例項化bean後執行

populateBean(beanName, mbd, instanceWrapper);

initializeBean(beanName, exposedObject, mbd);

其中的populateBean是為了給生成的bean裝配屬性,這不是我們這次討論的重點,關鍵是initializebean方法

複製程式碼

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

複製程式碼

 這個方法就是對生成的bean進行一些擴充套件處理,主要是這個方法就,會呼叫我們自定義的擴充套件點

applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

複製程式碼

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

複製程式碼

可以看到這裡是獲取所有的beanProcessor,呼叫postProcessAfterInitialization方法,我們要關注是的一個叫InfrastructureAdvisorAutoProxyCreator

的擴充套件類。

複製程式碼

/**
 * Auto-proxy creator that considers infrastructure Advisor beans only,
 * ignoring any application-defined Advisors.
 *
 * @author Juergen Hoeller
 * @since 2.0.7
 */
@SuppressWarnings("serial")
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {}

複製程式碼

看下這個類的註釋可以發現這個類是為配置了aop配置(包括註解和xml配置兩種方式)的類,生成代理類。

核心方法是下面這個方法wrapIfNecessary方法。

複製程式碼

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

複製程式碼

下面解析下這個函式

首先看下getAdvicesAndAdvisorsForBean這個方法:名字很明顯用來獲取當前bean的advisor和adices的,這些都是生成代理類時需要的資訊。

複製程式碼

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }

複製程式碼

然後呼叫findEligibleAdvisors,獲取配置的advisor資訊

複製程式碼

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

複製程式碼

來看下findCandidateAdvisors方法,最終呼叫BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans

複製程式碼

public List<Advisor> findAdvisorBeans() {
        // Determine list of advisor bean names, if not cached already.
        String[] advisorNames = null;
        synchronized (this) {
            advisorNames = this.cachedAdvisorBeanNames;
            if (advisorNames == null) {
                // Do not initialize FactoryBeans here: We need to leave all regular beans
                // uninitialized to let the auto-proxy creator apply to them!
                advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Advisor.class, true, false);
                this.cachedAdvisorBeanNames = advisorNames;
            }
        }
        if (advisorNames.length == 0) {
            return new LinkedList<>();
        }

        List<Advisor> advisors = new LinkedList<>();
        for (String name : advisorNames) {
            if (isEligibleBean(name)) {
                if (this.beanFactory.isCurrentlyInCreation(name)) {
                    
                }
                else {
                    try {
                        advisors.add(this.beanFactory.getBean(name, Advisor.class));
                    }
                    catch (BeanCreationException ex) {
                      throw ex;
                    }
                }
            }
        }
        return advisors;
    }

複製程式碼

1)首先獲取spring管理的Advisor型別的類名稱。

2)通過beanFactory獲取該bean對應的實體類,並裝入advisors。

生成的這個advisor可是相當複雜,這裡我們以事務advisor為例說明

可以看到這個advisor包含了advice(aop中的通知),pointcut(aop中的切入點),

advice是TransactionInterceptor,這個通知是用來管理spring的事務的可以看到包含事務的管理器等管理事務的屬性,具體的方法見TransactionAspectSupport.invokeWithinTransaction
pointcut是TransactionAttributeSourcePointcut,

複製程式碼

public boolean matches(Method method, @Nullable Class<?> targetClass) {
        if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
            return false;
        }
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }

複製程式碼

這個是pointcut的核心方法,用來匹配某個類是否符合事務管理的aop攔截要求。
ok,回到之前的wrapIfNecessary方法

複製程式碼

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

複製程式碼

我們之前分析道getAdvicesAndAdvisorsForBean方法,可以看到如果得到的結果是DO_NOT_PROXY,就會將這個bean直接返回,

如果不是DO_NOT_PROXY,(其實DO_NOT_PROXY就是null,但是使用DO_NOT_PROXY會使得程式碼邏輯更加清晰),就會執行

createProxy方法,建立一個代理類,然後返回一個代理類,ok,現在我們就清楚了問題分析中的 第3)和第 1) 2) 區別,那就是

service類是否配置了相關的aop攔截配置,無論是註解還是xml形式,目前我們還不清楚第1)和 第2)的區別,就是為什麼有時候

生成jdk代理,有時候生成cglib代理,這就需要繼續向下看creatProxy方法了,最終會進入一個DefaultAopProxyFactory的createAopProxy

方法:

複製程式碼

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

複製程式碼

如果目標類是介面就一定會使用jdk代理,如果目標類沒有可以代理的介面就一定會使用Cglib代理。