Spring 依赖注入问题深入讨论和研究

单例bean注入原型bean问题

在上面这篇文章中我讲了一些解决方案,但漏了几个方案,比如@Lazy、使用@Scope注解的proxyMode属性,这个属性也能解决注入原型bean问题,之所以没有讲,是因为使用这个会完与你的预期完全不符合,最好不要使用这个属性。下面分别讨论其实现原理。

列个大纲:

                            图1

本文讨论的点是注入原型bean无效问题,下面说的无效都是指的是每次通过单例bean获取原型bean都是获取同一个对象。

先做个总结,根据总结来根据源码来分析问题。

  • @Lazy:与@Autowired一起使用,则会为该类创建代理,注意:这里是为注入对象创建代理,而不是为当前被注入类创建代理,效果和@Scop 设置proxyMode一致,只要代理注入对象都会造成同一个效果:调用注入对象(被代理了)方法都会被代理拦截,然后执行TargetSource的getTarget,每次执行这个方法都会创建一个全新的对象。
  • @Optional:spring并不是创建代理,也不是类似ObjectFactory那样实际效果,Optional只是将你注入对象包装了,如果注入类在IOC容器中不存在也不会报错,因为spring会返回Optional<null>,这个就是与不加Optional的唯一区别,不加Optional的情况下,IOC容器中没有,就会报错。
    问题:为什么spring不将Optional设计与ObjectFactory一样的效果呢?
    因为API设计问题。Optional是JDK的,spring无法在调用Optional方法的时候作逻辑处理,而ObjectFactory是一个接口,spring可以完全返回一个ObjectFactory的实现类,在getObject方法中进行逻辑处理,这样当我们在程序中显示调用getObject方法时,就能够执行spring设定的处理逻辑了。
     
  • @Lookup:这个就是将当前类(被依赖类)创建代理,这个就是跟@Lazy的最大区别
     
  • ObjectFactory、ObjectProvider:这个原理就很简单,就是返回一个接口的实现类,由应用程序显示调用接口方法来执行注入问题。
     
  •  @Scop:作用于类上或者于@Bean一起使用,两者效果一样,这里我还会其他作用域的bean注入行为: 
  1. 一个设置@Scope为非单例也非原型,比如request、session作用域,常见的问题就是单例bean注入HttpServletRequest为什么是线程安全的(由于篇幅过大,看的累,暂时留在下篇文章解析

    源码分析

首先先要确定源码位置,如果自己不知道源码在哪个位置作处理,那么可以在IDE中全项目搜索关键字,比如我向知道@Lazy依赖注入在哪里处理的,我就搜索Lazy.class,然后找,找到熟悉的类查看代码就差不多能够找到位置了。当你对spring依赖注入有一定了解时,在找之前,我们可以猜测下大致范围,比如延迟依赖注入@Lazy,它是与手动注入注解@Autowired注解一起使用的(我们这里只讨论注入),没有@Lazy,spring并不会对@Lazy进行解析,因为你并没有进行手动注入,spring又如何解析得到呢?那么@Autowired又是在哪里被注入的呢,当然是在AutowiredAnnotationBeanPostProcessor类,这个类就是解析注解@Autowired(当然还有@Value和@Lookup注解)并进行处理的,解析在postProcessMergedBeanDefinition方法,注入处理在postProcessProperties方法,这两个方法都是spring扩展接口回调的方法,思考一个问题:为什么需要在postProcessMergedBeanDefinition方法中进行解析呢?为啥不在postProcessProperties方法中一步到位呢?
个人分析理解:postProcessMergedBeanDefinition是MergedBeanDefinitionPostProcessor接口的方法,我们来看下api注释,在头部又对这个方法解释:

The postProcessMergedBeanDefinition method may for example introspect the bean definition in order to prepare some cached metadata before post-processing actual instances of a bean. It is also allowed to modify the bean definition but only for definition properties which are actually intended for concurrent modification. Essentially, this only applies to operations defined on the RootBeanDefinition itself but not to the properties of its base classes.

这个方法有两个目的:

  1. 可以在对刚实例化的对象进行后处理之前进行一些缓存处理逻辑,为后续后处理作准备
  2. 可以操作BeanDefinition进行一些修改

代码处理位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

// Instantiate the bean.  对象实例化
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
        //紧接在实例化后进行applyMergedBeanDefinitionPostProcessors处理
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

在实例化后,初始化之前,给开发者预留接口来对BeanDefinition进行修改或者添加什么属性,之后开发者就不能对BeanDefinition进行修改了(后面只给了InstantiationAwareBeanPostProcessor#postProcessProperties修改PropertyValues的扩展方法)。给后面初始化作准备这个功能也是,这个设计也是合理的。

回归正题,那么我们找到代码入口:AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues-->AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject----->

value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

最后调用DefaultListableBeanFactory#resolveDependency 

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());

        //对Optional处理
		if (Optional.class == descriptor.getDependencyType()) {
			return createOptionalDependency(descriptor, requestingBeanName);
		}
        //对ObjectFactory和ObjectProvider处理
		else if (ObjectFactory.class == descriptor.getDependencyType() ||
				ObjectProvider.class == descriptor.getDependencyType()) {
			return new DependencyObjectProvider(descriptor, requestingBeanName);
		}

		else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
			return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
		}

        //对@Lazy处理
		else {
			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);
			if (result == null) {
				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
			}
			return result;
		}
	}

Optional:

看下createOptionalDependency方法

private Optional<?> createOptionalDependency(
			DependencyDescriptor descriptor, @Nullable String beanName, final Object... args) {

		DependencyDescriptor descriptorToUse = new NestedDependencyDescriptor(descriptor) {
			@Override
			public boolean isRequired() {
                //1:注意这里将isRequired设置为false,表示在IOC中找不到对应的bean不报错返回null
				return false;
			}
			@Override
			public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) {
				return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, args) :
						super.resolveCandidate(beanName, requiredType, beanFactory));
			}
		};

        //2:直接执行doResolveDependency进行查找依赖返回需要注入的对象实例
		Object result = doResolveDependency(descriptorToUse, beanName, null, null);

		return (result instanceof Optional ? (Optional<?>) result : Optional.ofNullable(result));
	}

看代码解释已经很清楚了,下面看下ObjectFactory的处理:

ObjectFactory、ObjectProvider

return new DependencyObjectProvider(descriptor, requestingBeanName),直接返回了一个ObjectProvider实例,而不是先创建了一个ObjectProvider实例,再调用其getObject方法,这样的化就失去了ObjectProvider的意义,这个类就是弥补Optional的不足才设计的。看下其getObject方法,内部调用doResolveDependency方法并对result进行判断,如果IOC容器中没有这个Bean就会报错,这个会造成我们运行程序没有问题,但是当我们的程序调用getObject方法时就会报NoSuchBeanDefinitionException错误,这个是一个隐藏很深的代码设计。当然你写成这样:ObjectProvider<Optional<T>>类型就不会报错,看下面还对Optional进行了处理。

@Override
		public Object getObject() throws BeansException {
			if (this.optional) {
				return createOptionalDependency(this.descriptor, this.beanName);
			}
			else {
				Object result = doResolveDependency(this.descriptor, this.beanName, null, null);
				if (result == null) {
					throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());
				}
				return result;
			}
		}

 那么Optional和ObjectProvider的应用场景呢?

Optional和ObjectProvider通常只针对于那些可有可无的类而言的,没有也是没有关系的,这些类通常留给开发者作为扩展接口,开发者可以不实现,对于那些我们需要依赖注入对象才能完成指定工作的时候,这个时候就要不要使用Optional或ObjectProvider,因为这种不会再容器启动阶段报错而是再运行期间开发者主动调方法的时候才会出现错误,这个是绝对禁止的。

@Lazy

Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);

AutowireCandidateResolver类,对注入时候的逻辑判断,就是该不该注入,比如泛型、@Value、@Qualifier、@Lazy解析,这个另起一篇文章解析。我们直接进入ContextAnnotationAutowireCandidateResolver类的getLazyResolutionProxyIfNecessary方法:

@Override
	@Nullable
	public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
		return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
	}

    //标注了@Lazy注解就返回true
	protected boolean isLazy(DependencyDescriptor descriptor) {
		for (Annotation ann : descriptor.getAnnotations()) {
			Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
			if (lazy != null && lazy.value()) {
				return true;
			}
		}
		MethodParameter methodParam = descriptor.getMethodParameter();
		if (methodParam != null) {
			Method method = methodParam.getMethod();
			if (method == null || void.class == method.getReturnType()) {
				Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);
				if (lazy != null && lazy.value()) {
					return true;
				}
			}
		}
		return false;
	}

	protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
		BeanFactory beanFactory = getBeanFactory();
		Assert.state(beanFactory instanceof DefaultListableBeanFactory,
				"BeanFactory needs to be a DefaultListableBeanFactory");
		final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;

        //创建TargetSource,看见这个,说明后面肯定是做代理了
		TargetSource ts = new TargetSource() {
			@Override
			public Class<?> getTargetClass() {
				return descriptor.getDependencyType();
			}
			@Override
			public boolean isStatic() {
				return false;
			}
			@Override
			public Object getTarget() {
				Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);
                //返回依赖
				Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);
				if (target == null) {
					Class<?> type = getTargetClass();
					if (Map.class == type) {
						return Collections.emptyMap();
					}
					else if (List.class == type) {
						return Collections.emptyList();
					}
					else if (Set.class == type || Collection.class == type) {
						return Collections.emptySet();
					}
					throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
							"Optional dependency not present for lazy injection point");
				}
				if (autowiredBeanNames != null) {
					for (String autowiredBeanName : autowiredBeanNames) {
						if (dlbf.containsBean(autowiredBeanName)) {
							dlbf.registerDependentBean(autowiredBeanName, beanName);
						}
					}
				}
				return target;
			}
			@Override
			public void releaseTarget(Object target) {
			}
		};

		ProxyFactory pf = new ProxyFactory();
		pf.setTargetSource(ts);
        
		Class<?> dependencyType = descriptor.getDependencyType();
        
        //如果@Autowired依赖的的一个接口,则采用JDK代理
		if (dependencyType.isInterface()) {
			pf.addInterface(dependencyType);
		}
		return pf.getProxy(dlbf.getBeanClassLoader());
	}

给我们创建了代理对象,给谁创建了代理呢?当然是给注入对象代理的,比如如下代码就是给Shop类型的goods对象创建代理的,并不是给Order创建代理的。这个很重要!!!所以我们每次获取都是同一个代理对象,但是调用代理对象的方法就可以执行到TargetSource的getTarget方法,会获取一个全新的对象进行方法调用。这个最终效果和@Scope proxyMode设置为代理是一样的效果,@Scope proxyMode下面会分析。

 

@Scope注解的proxyMode属性

这里以代理类(即使用cglib代理)为例,讨论讲proxyMode设置为ScopedProxyMode#TARGET_CLASS的时候出现的问题

我这里将@scope与@Bean结合使用(跟@scope与@Component一起使用效果一致,后面分析源码位置)

先看下示例代码

@Configuration(proxyBeanMethods = false)
public class ScopeTest {
    @Bean
    public Order order(){
        return new Order();
    }

    @Bean
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = TARGET_CLASS)
    //proxyMode = TARGET_CLASS时,Goods的BeanDefinition的beanClass为ScopedProxyFactoryBean,
    //是一个工厂Bean
    public Goods goods(){
        return new Goods("苹果");
    }

    public static void main(String[] args) {
        //输出代理
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\class");
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(ScopeTest.class);
        context.refresh();

        //获取的goods其实被Cglib代理对象
        Goods goods = context.getBean(Order.class).getGoods();
        //@1检验代理对象
        System.out.println("代理对象的hashCode:" + goods.hashCode());
        System.out.println("代理对象的hashCode:" + goods.hashCode());

        //@2,这个输出结果可能让你不适,O(∩_∩)O
        System.out.println("真实goods-1:" + goods.toString());
        System.out.println("真实goods-2:" + goods.toString());

        goods.setName("华为");
        //@3
        System.out.println(goods.getName());

        //代理对象还是一个不会变,是单例的
        System.out.println("代理对象的hashCode:" + goods.hashCode());
    }

    static class Order {
        @Autowired
        private Goods goods;

        public Goods getGoods() {
            return goods;
        }

    }

    static public class Goods{

        private String name;

        public Goods(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Goods{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

}

看下测试代码中的@1、@2、@3,先猜想下结果:

System.out.println("真实goods-1:" + goods.toString());和System.out.println("真实goods-2:" + goods.toString());输出的hashcode一样吗??

System.out.println(goods.getName()); 输出真的是“华为”吗??

看下运行结果:

goods的class类型:class com.duxd.annotation.scope.ScopeTest$Goods$$EnhancerBySpringCGLIB$$2c04a4d4
代理对象的hashCode:-578548707
代理对象的hashCode:-578548707
真实goods-1:Goods{name='苹果'hashCode='447212746'}
真实goods-2:Goods{name='苹果'hashCode='1496355635'}
苹果
代理对象的hashCode:-578548707

大家是不是也是一年懵逼,我当时测试的时候也是如此。

下面分别讨论:

根据结果,Goods类被cglib代理了,既然被代理,那么Goods对象方法肯定是被代理的(包含toString方法),那么为什么后面的输出结果会如此的看不懂呢?既然通过cglib代理,而且是通过spring aop实现的代理(不是单存的使用spring的Enhancer API创建的代理,@Lookup注解是简单基于spring内置的Enhancer API实现的,两种实现方式不同导致最终的效果完全不一致,后面会讨论),既然通过spring aop,那么肯定需要设置TargetSource,关键点就在于这个TargetSource,下面源码分析

入口在哪呢?既然是与@Bean结合使用的,那么肯定在与解析@Bean相同的类中解析的,那就是ConfigurationClassPostProcessor,哪个方法呢?这个类是处理注入BeanDefinition的注册任务的,所以在postProcessBeanDefinitionRegistry方法中,下面给出调用链路:

ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry-->
ConfigurationClassPostProcessor#processConfigBeanDefinitions-->
this.reader.loadBeanDefinitions(configClasses);-->ConfigurationClassBeanDefinitionReader#loadBeanDefinitions-->
ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass-->

这个loadBeanDefinitionsForConfigurationClass方法内部:
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}

在loadBeanDefinitionsForBeanMethod方法中:
if (proxyMode != ScopedProxyMode.NO) {
			BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
					new BeanDefinitionHolder(beanDef, beanName), this.registry,
					proxyMode == ScopedProxyMode.TARGET_CLASS);
			beanDefToRegister = new ConfigurationClassBeanDefinition(
					(RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata, beanName);
		}

如果proxyMode != ScopedProxyMode.NO,则通过ScopedProxyCreator创建一个新的BeanDefinitionHolder,来作为当前Bean的BeanDefinition。

继续跟踪,执行到org.springframework.aop.scope.ScopedProxyUtils#createScopedProxy

关键代码:

RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);

设置beanclass类型为ScopedProxyFactoryBean,这个是一个Factory bean,党我们从IOC容器中获取这个Bean的时候,会执行其getObject方法:

public class ScopedProxyFactoryBean extends ProxyConfig
		implements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {

	/** The TargetSource that manages scoping. */

    //这个TagetSource为SimpleBeanTargetSource
	private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();

	/** The name of the target bean. */
	@Nullable
	private String targetBeanName;

	/** The cached singleton proxy. */
	@Nullable
	private Object proxy;


	/**
	 * Create a new ScopedProxyFactoryBean instance.
	 */
	public ScopedProxyFactoryBean() {
		setProxyTargetClass(true);
	}


	/**
	 * Set the name of the bean that is to be scoped.
	 */
	public void setTargetBeanName(String targetBeanName) {
		this.targetBeanName = targetBeanName;
		this.scopedTargetSource.setTargetBeanName(targetBeanName);
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		if (!(beanFactory instanceof ConfigurableBeanFactory)) {
			throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
		}
		ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;

		this.scopedTargetSource.setBeanFactory(beanFactory);

		ProxyFactory pf = new ProxyFactory();
		pf.copyFrom(this);
		pf.setTargetSource(this.scopedTargetSource);

		Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
		Class<?> beanType = beanFactory.getType(this.targetBeanName);
		if (beanType == null) {
			throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
					"': Target type could not be determined at the time of proxy creation.");
		}
		if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
			pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
		}

		// Add an introduction that implements only the methods on ScopedObject.
		ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
		pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));

		// Add the AopInfrastructureBean marker to indicate that the scoped proxy
		// itself is not subject to auto-proxying! Only its target bean is.
		pf.addInterface(AopInfrastructureBean.class);

		this.proxy = pf.getProxy(cbf.getBeanClassLoader());
	}


	@Override
	public Object getObject() {
		if (this.proxy == null) {
			throw new FactoryBeanNotInitializedException();
		}
        
        //返回proxy,这个proxy是在setBeanFactory方法创建的,proxy用于缓存,这个也就是与isSingleton方法目标是一致的
		return this.proxy;
	}

	@Override
	public Class<?> getObjectType() {
		if (this.proxy != null) {
			return this.proxy.getClass();
		}
		return this.scopedTargetSource.getTargetClass();
	}

    //当前代理实例是单例的,前面我们的代码已经验证了
	@Override
	public boolean isSingleton() {
		return true;
	}

}

创建代理这边主要是看TargetSource,因为代理逻辑都在这个类中处理的,这里的TargetSource是SimpleBeanTargetSource,而且是final的,不能修改的,再看下这个类的getTarget方法:

@Override
	public Object getTarget() throws Exception {
		return getBeanFactory().getBean(getTargetBeanName());
	}

直接调用BeanFactory的getBean方法获取实例,所以这个方法每次被执行,而且这个bean不是单例的,而是原型的,那么每次都返回不同的对象,那么这个getTarget方法什么时候会被执行呢?

这里是cglib创建代理的,那么我们跟踪上面的代码:this.proxy = pf.getProxy(cbf.getBeanClassLoader());

执行到org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader)

对于cglib代理,代理逻辑处理要看它的拦截器,也就是Callback,定位到:Callback[] callbacks = getCallbacks(rootClass);

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
		// Parameters used for optimization choices...
		boolean exposeProxy = this.advised.isExposeProxy();
		boolean isFrozen = this.advised.isFrozen();
		boolean isStatic = this.advised.getTargetSource().isStatic();

		// Choose an "aop" interceptor (used for AOP calls).
		Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

		// Choose a "straight to target" interceptor. (used for calls that are
		// unadvised but can return this). May be required to expose the proxy.
		Callback targetInterceptor;
		if (exposeProxy) {
			targetInterceptor = (isStatic ?
					new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
					new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
		}
		else {
			targetInterceptor = (isStatic ?
					new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
					new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
		}

		// Choose a "direct to target" dispatcher (used for
		// unadvised calls to static targets that cannot return this).
		Callback targetDispatcher = (isStatic ?
				new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());

		Callback[] mainCallbacks = new Callback[] {
				aopInterceptor,  // for normal advice
				targetInterceptor,  // invoke target without considering advice, if optimized
				new SerializableNoOp(),  // no override for methods mapped to this
				targetDispatcher, this.advisedDispatcher,
				new EqualsInterceptor(this.advised),
				new HashCodeInterceptor(this.advised)
		};

		Callback[] callbacks;

		// If the target is a static one and the advice chain is frozen,
		// then we can make some optimizations by sending the AOP calls
		// direct to the target using the fixed chain for that method.
		if (isStatic && isFrozen) {
			Method[] methods = rootClass.getMethods();
			Callback[] fixedCallbacks = new Callback[methods.length];
			this.fixedInterceptorMap = new HashMap<>(methods.length);

			// TODO: small memory optimization here (can skip creation for methods with no advice)
			for (int x = 0; x < methods.length; x++) {
				Method method = methods[x];
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);
				fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
						chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
				this.fixedInterceptorMap.put(method, x);
			}

			// Now copy both the callbacks from mainCallbacks
			// and fixedCallbacks into the callbacks array.
			callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
			System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
			System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
			this.fixedInterceptorOffset = mainCallbacks.length;
		}
		else {
			callbacks = mainCallbacks;
		}
		return callbacks;
	}

添加了主要如下两个CallBack:

  1. aopInterceptor:DynamicAdvisedInterceptor
  2. targetInterceptor:这里是DynamicUnadvisedInterceptor

DynamicAdvisedInterceptor类解析:

下面文言来自书籍《Spring源码深度解析(第二版)》      

         [ 将 advised 属性封装在 DynamicAdvisedlnterc巳ptor 并加入在 callbacks 中,这么做的
目的是什么 呢,如何调用呢?在前面的示例中,我们了解到 CGLIB 中对于方法的拦截是通过
将自定义的拦截器(实现 Methodlnterceptor 接口)加入 Callback 中并在调用代理时直接激活拦
截器中的 intercept 方法来实现的,那么在 getCallback 中正是实现了这样一个 目的, DynamicAdvisedlnterceptor 继承自 Methodlnterceptor ,加入 Callback 中后,在再次调用代理时会直接调
用 DynamicAdvisedlnterceptor 中的 intercept 方法,由此推断,对于 CGLIB 方式实现的代理,
其核心逻辑必然在 DynamicAdvisedlnterc巳ptor 中的 intercept 中。]

它的回调方法实现和JdkDynamicAopProxy的回调实现非常类似,但还是有区别的:

在CglibAopProxy中,构造CglibMethodInvocation对象来完成;

在JdkDynamicAopProxy中,构造ReflectiveMethodInvocation对象来完成。

根据以上源码分析,可以得出结论:

调用goods的方法都会被代理,代理就会执行TargetSource的getTarget来执行对象目标方法,但是这个TargetSource是SimpleBeanTargetSource,这个类的getObject方法调用BeanFactory的getBean方法,对于原型bean每次返回不同的实例,虽然我们都是通过同一个代理对象来操作的,每次通过同一个代理对象执行方法都会创建一个全新的bean。

回头看下,其实@Scope的proxyMode如果设置为代理模式,它的目的就是给我们创建一个代理,不管你在@Scope的value是单例还是其他的,如果是单例的,则调用SimpleBeanTargetSource的getObject方法返回的是单例对象。那么@Scope的proxyMode

这个属性的意义在哪呢?

目前不清楚使用场景。

 

@Lookup

研究注解,一般两个步骤,一个是解析,一个是实现,有的时候会在同一个地方处理,有的时候会在两个地方分开处理。

先看下注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lookup {

	/**
	 * This annotation attribute may suggest a target bean name to look up.
	 * If not specified, the target bean will be resolved based on the
	 * annotated method's return type declaration.
	 */
	String value() default "";

}

只能作用于方法,作用于方法,方法解析,我们常见的就是AutowiredAnnotationBeanPostProcessor,在这个类的说明中我们也能够发现(你可以项目搜索Lookup.class也能够快速定位)。

注解解析位置:AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors

mbd.getMethodOverrides().addOverride(override)

创建一个LookupOverride添加到overrides属性集合里。

@Lookup实现位置:逆序看

LookupOverrideMethodInterceptor 《-----SimpleInstantiationStrategy#instantiate
		if (!bd.hasMethodOverrides()) {}else{cglib代理}
		《----AbstractAutowireCapableBeanFactory#createBeanInstance
		《----org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

然后org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy.CglibSubclassCreator#instantiate方法的实现,主要还是看CallBack,看源码得知,添加了LookupOverrideMethodInterceptor。

@Override
		public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
			// Cast is safe, as CallbackFilter filters are used selectively.
			LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
			Assert.state(lo != null, "LookupOverride not found");
			Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
			if (StringUtils.hasText(lo.getBeanName())) {
				return (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
						this.owner.getBean(lo.getBeanName()));
			}
			else {
				return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
						this.owner.getBean(method.getReturnType()));
			}
		}

通过BeanFactory的getbean获取实例。所以能够实现每次调用代理对象的@Lookup标注的方法即进入代理逻辑了。

那么调用代理对象的其他方法会走代理吗,也就是会走intercept方法吗??

直接代码演示:

@Configuration(proxyBeanMethods = false)
@ComponentScan(value = "com.duxd.prototype")
public class LookupTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(LookupTest.class);
        context.refresh();

        User user = context.getBean(User.class);
        System.out.println("user对象class类型:" + user.getClass());
        Car car1 = user.getCar();
        Car car2 = user.getCar();
        System.out.println("car1对象:" + car1);
        System.out.println("car2对象:" + car2);
        Car car = user.getDefaultCar();
        System.out.println(car);

    }

    //向容器注入car,并且scope是原型
    @Bean("car")
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Car car(){
        return new Car("法拉利");
    }

}
public class Car {

    private String name;

    Car(){
        System.out.println("Car构造函数被执行....");
    }

    public Car(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                "hashCode='" + this.hashCode() + '\'' +
                '}';
    }
}

 

@Component
public class User {
    static final Car DEFAULT_CAR = new Car("保时捷");

    @Lookup
    public Car getCar(){
        return DEFAULT_CAR;
    }

    public Car getDefaultCar(){
        return DEFAULT_CAR;
    }

}

输出结果: 

user对象class类型:class com.duxd.prototype.User$$EnhancerBySpringCGLIB$$f761903c
car1对象:Car{name='法拉利'hashCode='1259014228'}
car2对象:Car{name='法拉利'hashCode='431687661'}
Car{name='保时捷'hashCode='283383329'}

执行getDefaultCar方法根据输出结果 ,其他方法没有进入代理。

为什么呢?一般被cglib代理,所有方法不都会被执行吗?我先不给出答案。

总结

不要选择代理的是注入对象的解决方案,比如@Lazy、@Scope的proxyMode,这样会让你在调用注入对象的任何方法都会创建一个全新的对象。

不管注入哪种作用域的bean,根据需求选择不同的方案:

对于强依赖:推荐选择使用最原始的ApplicationContext,可以在程序启动时刻发现问题

对于可选依赖:使用ObjectProvider,相当于程序留给开发者的一个扩展实现

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值