In our last article, we showed you how to use scoped-Proxy to solve singleton’s prototype dependency problem and some of spring’s behavior (in fact, Singleton relies on Prototype’s proxy for each protyType method call, The proxy generates a real instance of Protytpe for us.

Article address (in Spring yuan design pattern) : juejin.cn/post/686752…

Article address (about Sping Singleton dependency prototype) : juejin.cn/post/686789…

Today we’ll see how Spring does this (with Chinese annotations for key code).

1. RegisterScopedProxyBeanDefinitionDecoratorobject

	<bean id="singletonReferencePrototype" class="com.cafebabe.spring.bean.SingletonReferencePrototype">
		<property name="myTestBean" ref="myTestBean"></property>
	</bean>

	<bean id="myTestBean" class="com.cafebabe.spring.bean.MyTestBean" scope="prototype">
		<property name="userId" value="1"></property>
		<property name="age" value="18"></property>
		<property name="userName" value="akun" ></property>
      	<! Tell Spring to use scoped-proxy when generating this class -->
		<aop:scoped-proxy></aop:scoped-proxy>
	</bean>
Copy the code

The first step is to add a < AOP :scoped-proxy/> to the myTestBean XML declaration, and Spring will do what we need. Those familiar with Spring should know that spring uses aop’s namespace handler to parse < AOP > tags. When reading < AOP >, it uses the AopNamespaceHandler

    @Override
 public void init(a) {
 	// In 2.0 XSD as well as in 2.5+ XSDs
 	registerBeanDefinitionParser("config".new ConfigBeanDefinitionParser());
 	registerBeanDefinitionParser("aspectj-autoproxy".new AspectJAutoProxyBeanDefinitionParser());

 	/ / to the nameSpacehandler decorator to inject ScopedProxy statement decorator, is actually placed into the Map, the key for the "scoped - proxy", the value is ScopedProxyBeanDefinitionDecorator object
 	registerBeanDefinitionDecorator("scoped-proxy".new ScopedProxyBeanDefinitionDecorator());

 	// Only in 2.0 XSD: moved to context namespace in 2.5+
 	registerBeanDefinitionParser("spring-configured".new SpringConfiguredBeanDefinitionParser());
 }
 
 
Copy the code

This line of code registerBeanDefinitionDecorator (” scoped proxy “, new ScopedProxyBeanDefinitionDecorator ()); Inject the decorator declared by ScopedProxy into the nameSpacehandler decorator (currently just injection).

2. Decorate the BeanDefinition

Let’s look at the code to inject BeanDefinition into the Spring container:

	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        Parse the bean's various attribute declarations from XML elements
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if(bdHolder ! =null) {
        	// Decorate the parsed BeanDefinition object, if necessary
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
                // Register the parsed bean declaration (decorated bean declaration holder if needed) with the beanFactory
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder)); }}Copy the code

We see the decorateBeanDefinitionIfRequired () method, and introduced to the Element (the current parse XML nodes) and BeanDefinitionHolder object, We continue to follow the code found BeanDefinitionParserDelegate (bean declaration parser consignor) decorateBeanDefinitionIfRequired () method

/**
   * Decorate the given bean definition through a namespace handler, if applicable.
   * @param ele the current element
   * @param originalDef the current bean definition
   * @param containingBd the containing bean definition (if any)
   * @return the decorated bean definition
   */
  public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
  		Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

  	BeanDefinitionHolder finalDefinition = originalDef;

  	// Decorate based on custom attributes first.
      / / first to check the custom attributes, namely our Xml defined id = "myTestBean" class = "com. Cafebabe. Spring. Beans. MyTestBean" scope = "prototype"
  	NamedNodeMap attributes = ele.getAttributes();
  	for (int i = 0; i < attributes.getLength(); i++) {
  		Node node = attributes.item(i);
  		finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
  	}

  	// Decorate based on custom nested elements.
      
      
      
  	NodeList children = ele.getChildNodes();
  	for (int i = 0; i < children.getLength(); i++) {
  		Node node = children.item(i);
  		if(node.getNodeType() == Node.ELEMENT_NODE) { finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); }}return finalDefinition;
  }
Copy the code

Next look for the decorateIfRequired method,

public BeanDefinitionHolder decorateIfRequired(
   		Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
   	// Get the nameSpace of the corresponding node from the node
   	String namespaceUri = getNamespaceURI(node);
       / / if namespaceUri is not null and is not the default namespace (the default namespace is http://www.springframework.org/schema/beans)
   	if(namespaceUri ! =null && !isDefaultNamespace(namespaceUri)) {
        // Get a NamespaceHandlerResolver (AopNamespaceHandler
   		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
   		if(handler ! =null) {
   			BeanDefinitionHolder decorated =
   					handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
   			if(decorated ! =null) {
   				returndecorated; }}else if (namespaceUri.startsWith("http://www.springframework.org/")) {
   			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
   		}
   		else {
   			// A custom namespace, not to be handled by Spring - maybe "xml:..." .
   			if (logger.isDebugEnabled()) {
   				logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]"); }}}return originalDef;
   }
Copy the code

Once we get the NamespaceHandler, we call the Decorated method of that handler:

	public BeanDefinitionHolder decorate( Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
   	
       
   	BeanDefinitionDecorator decorator = findDecoratorForNode(node, parserContext);
   	return(decorator ! =null ? decorator.decorate(node, definition, parserContext) : null);
   }
Copy the code

Aop :scoped-proxy
So recall that the init() method of the AopNamespaceHandler at the beginning of this article just injects a key with “scoped-proxy” ScopedProxyBeanDefinitionDecorator, is actually invoked the ScopedProxyBeanDefinitionDecorator decorate (node, definition, ParserContext) method.

@Override
	public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
    	// Whether to use Class proxy is to use CGlib proxy generation
		boolean proxyTargetClass = true;
		if (node instanceof Element) {
			Element ele = (Element) node;
            // If the attribute is indicated in the XML, the indicated attribute is used
			if(ele.hasAttribute(PROXY_TARGET_CLASS)) { proxyTargetClass = Boolean.valueOf(ele.getAttribute(PROXY_TARGET_CLASS)); }}// Register the original bean definition as it will be referenced by the scoped proxy
		// and is relevant for tooling (validation, navigation).
        // Create a bean declaration holder for the ScopedProxy proxy class
		BeanDefinitionHolder holder = ScopedProxyUtils.createScopedProxy(definition, parserContext.getRegistry(), proxyTargetClass);
		String targetBeanName = ScopedProxyUtils.getTargetBeanName(definition.getBeanName());
		parserContext.getReaderContext().fireComponentRegistered(
				new BeanComponentDefinition(definition.getBeanDefinition(), targetBeanName));
		return holder;
	}
Copy the code

This code creates a BeanDefinitionHolder using ScopedProxyUtils, returns the BeanDefinitionHolder, and registers it with the beanFactory.

3. Generate the bean declaration and holder for the broker

How does ScopedProxyUtils generate a proxy object

public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
			BeanDefinitionRegistry registry, boolean proxyTargetClass) {
		
        // Get the beanName of the original bean, i.e. 'myTestBean'
		String originalBeanName = definition.getBeanName();
        // The bean declaration for the target bean
		BeanDefinition targetDefinition = definition.getBeanDefinition();
        // Generate the target beanName as "scopedTarget.mytestBean" from the original beanName.
		String targetBeanName = getTargetBeanName(originalBeanName);

		// Create a scoped proxy definition for the original bean name,
		// "hiding" the target bean in an internal target definition.
        // Create a new RootBeanDefinitiond proxy bean to declare proxyDefinition using ScopedProxyFactoryBean's Class
		RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
        // Set proxyDefinition's decorated bean declaration to the original myTestBean declaration and some other properties;
		proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
		proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
		proxyDefinition.setSource(definition.getSource());
		proxyDefinition.setRole(targetDefinition.getRole());
        //proxyDefinition has a String attribute targetBeanName, defined as "scopedTarget.myTestBean"
		proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
		if (proxyTargetClass) {
			targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
			// ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
		}
		else {
			proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
		}

		// Copy autowire settings from original bean definition.
		proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
		proxyDefinition.setPrimary(targetDefinition.isPrimary());
		if (targetDefinition instanceof AbstractBeanDefinition) {
			proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
		}

		// The target bean should be ignored in favor of the scoped proxy.
		targetDefinition.setAutowireCandidate(false);
		targetDefinition.setPrimary(false);

		// Register the target bean as separate bean in the factory.
        // Registering the name "scopedTarget.myTestBean" in beanFactory is actually a declaration of myTestBean.
		registry.registerBeanDefinition(targetBeanName, targetDefinition);
		// Return the scoped proxy definition as primary bean definition
		// (potentially an inner bean).
        // Returns a proxy declaration holder whose actual object is already ScopedProxyFactoryBean
		return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
	}
Copy the code

We know from the code that the decorator returns a proxy bean declaration named “myTestBean” that is already ScopedProxyFactoryBean At this point Spring calls getBean(“myTestBean”) and has actually gotten the getObject() method of the ScopedProxyFactoryBean

Let’s look again at the structure and methods of the ScopedProxyFactoryBean object

/** Inherits from the ProxyConfig proxy configuration class and implements the BeanFactoryAware interface (getObject() is called when Spring obtains an instance of this object). * implements shiBeanFactoryAware spring injection current the beanFactory object * * /
public class ScopedProxyFactoryBean extends ProxyConfig
		implements FactoryBean<Object>, BeanFactoryAware.AopInfrastructureBean {

	/** The TargetSource that manages scoping. */
    // Define a new SimpleBeanTargetSource object containing beanFactory and targetBeanName objects.
    // In fact, when the proxy calls any method of MyTestBean, it will retrieve the targetBeanName object from the beanFactory of that object, namely our MyTestBean instance whose scope is Prototype. This ensures that each use is a new instance of MyTestBean
	private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();

	/** The name of the target bean. */
	@Nullable
    // When the bean declaration is re-registered, the value of the property is added, in this case "scopedTarget.mytestBean"
	private String targetBeanName;

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


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


	/** * Set the name of the bean that is to be scoped. */
     // Inject targetBeanName, here "scopedTarget.mytestBean"
	public void setTargetBeanName(String targetBeanName) {
		this.targetBeanName = targetBeanName;
		this.scopedTargetSource.setTargetBeanName(targetBeanName);
	}
	
	
    @Override
    // The BeanFactoryAware interface is implemented, and Spring will inject beanFactory into it
	public void setBeanFactory(BeanFactory beanFactory) {
		if(! (beanFactoryinstanceof ConfigurableBeanFactory)) {
			throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
		}
		ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
		
        / / scopedTargetSource inject the beanFactory
		this.scopedTargetSource.setBeanFactory(beanFactory);
	    // Create a new agent factory
		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());
        // The agent factory sets Advice
		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.
        // Add an interface
		pf.addInterface(AopInfrastructureBean.class);
		// Generate a proxy object
		this.proxy = pf.getProxy(cbf.getBeanClassLoader());
	}


	@Override
    // Implements the FactoryBean interface. Spring calls getObject() when it obtains an instance of this object.
    // The proxy object is returned
	public Object getObject(a) {
		if (this.proxy == null) {
			throw new FactoryBeanNotInitializedException();
		}
		return this.proxy;
	}

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

	@Override
	public boolean isSingleton(a) {
		return true; }}Copy the code

Spring generates a singleton proxy object for us, but on each method call, the singleton proxy object actually fetches a prototype real proxy object from the beanFactory. Executes the proxied object’s method.

For an overview of the process, see the picture in the appendix or go to Processon

Link: www.processon.com/view/link/5…

Password: cafebabe

The appendix