Creation of proxy in spring

[toc]

Creation of spring agent

Jdk and Cglib are used to create proxy in spring. Jdkdynamicaoppaxy and ObjenesisCglibAopProxy are used to manage the configuration by using the configuration Advised and ProxyConfig. The method used to create proxy is determined according to the configuration. The following describes these key classes.

Advised

Advised is an interface for managing AOP proxy factory configuration. All aopproxys in spring can be converted to advised.

ProxyConfig

In spring, use ProxyConfig to configure agent creation properties.

/**
 * The superclass of the agent factory is used to uniformly manage the properties of the agent factory class.
 */
public class ProxyConfig implements Serializable {
   // true: use subclass proxy, false: use interface proxy
   private boolean proxyTargetClass = false;
   // Start agent optimization
   private boolean optimize = false;
   // Whether the agent created by the agent foreman can be converted to Advised. The default is false: it means yes,
   // If false, the bean can be converted to Advised: Advised testbean = (Advised) context getBean("testBean");
   boolean opaque = false;
   // Expose the proxy and bind it to the currentProxy of ThreadLocal, which is used to call its own scene by the proxy class's own method.
   boolean exposeProxy = false;
   // Freeze configuration, true: the configuration of the agent foreman cannot be modified.
   private boolean frozen = false;
}

There are four direct subclasses that implement ProxyConfig:

ScopedProxyFactoryBean, ProxyProcessorSupport, AbstractSingletonProxyFactoryBean and AdvisedSupport use different methods to create agents, but in the end, they will delegate the creation of agents to ProxyFactory. Let's see the relevant codes of the four direct subclasses.

  1. ScopedProxyFactoryBean: for @Scope Annotation to realize the scope control of the bean. He has implemented BeanFactory and BeanFactoryAware interfaces, and has the ability to create and manage beans.

    The proxy generated by this class will only record the name of the class, and then obtain the bean according to the scope. If it is prototype, beanFactory will create a new bean.

    public class ScopedProxyFactoryBean extends ProxyConfig
          implements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean{
    
        // It is used to manage the scope of beans. Its implementation is very simple. When obtaining objects, it is delegated to beanFactory, and then beanFactory obtains the corresponding beans according to the scope.
        private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();
        @Override
    	public void setBeanFactory(BeanFactory beanFactory) {
    		if (!(beanFactory instanceof ConfigurableBeanFactory)) {
    			throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
    		}
    		ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
    
            // Associate beanFactory with scopedTargetSource. When obtaining the proxy target class, obtain it from scopedTargetSource,
            // SimpleBeanTargetSource delegates the operation of getting the bean to 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.");
    		}
            // Use interface proxy
    		if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
    			pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
    		}
    		// Simple proxy enhancement. When calling the proxy class, get the bean from beanFactory to call, so as to control the scope.
    		ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
    		pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
    
            // Whether the tag agent needs to be intercepted by AOP, and there is timely pointcut matching 
    		pf.addInterface(AopInfrastructureBean.class);
    
            // Create proxy object: give the created proxy to ProxyFactory
    		this.proxy = pf.getProxy(cbf.getBeanClassLoader());
    	}
    }
    
  2. ProxyProcessorSupport: provides common public methods for ProxyFactory.

public class ProxyProcessorSupport extends ProxyConfig implements Ordered, BeanClassLoaderAware, AopInfrastructureBean {
    /**
     * You can customize sorting
     */
    public void setOrder(int order) { this.order = order; }
	@Override
	public int getOrder() { return this.order; }
    
    /**
     * When the interface is implemented, the interface proxy is used. If the interface is not implemented, the class proxy is used.
     */
    protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
		Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
		boolean hasReasonableProxyInterface = false;
		for (Class<?> ifc : targetInterfaces) {
			if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
					ifc.getMethods().length > 0) {
				hasReasonableProxyInterface = true;
				break;
			}
		}
		if (hasReasonableProxyInterface) {
			for (Class<?> ifc : targetInterfaces) {
				proxyFactory.addInterface(ifc);
			}
		}
		else {
			proxyFactory.setProxyTargetClass(true);
		}
	}
}
  1. AbstractSingletonProxyFactoryBean: create a singleton proxy object. After the object to be proxy is instantiated, use initializingbean#afterpropertieset() to create the proxy and set the pre notification and post notification for it.

    public abstract class AbstractSingletonProxyFactoryBean extends ProxyConfig
    		implements FactoryBean<Object>, BeanClassLoaderAware, InitializingBean {
    	// Proxy target object
        private Object target;
    
    	// Interface requiring proxy
    	private Class<?>[] proxyInterfaces;
    
    	// Front interceptor
    	private Object[] preInterceptors;
    
    	// Rear interceptor
    	private Object[] postInterceptors;
    
    	// Global Advisor registrar
    	private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
    
    	// Class loader
    	private transient ClassLoader proxyClassLoader;
    
    	// Proxy object
    	private Object proxy;
    
        // After instantiation calls
        @Override
    	public void afterPropertiesSet() {
            // ....
    
            // Delegate agent creation to ProxyFactory
    		ProxyFactory proxyFactory = new ProxyFactory();
    
            // Adding pre processor, main processor and post processor in order can form a processor chain and execute all processors according to the addition order.
            // Add preprocessor
    		if (this.preInterceptors != null) {
    			for (Object interceptor : this.preInterceptors) {
    				proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
    			}
    		}
            // Add the main processor and give it to the subclass implementation
    		proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));
            // Add post processor
    		if (this.postInterceptors != null) {
    			for (Object interceptor : this.postInterceptors) {
    				proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
    			}
    		}
            // Copy properties
    		proxyFactory.copyFrom(this);
    		// Create proxy target source: the default is SingletonTargetSource 
    		TargetSource targetSource = createTargetSource(this.target);
    		proxyFactory.setTargetSource(targetSource);
    
            // Set the interface of the agent
    		if (this.proxyInterfaces != null) {
    			proxyFactory.setInterfaces(this.proxyInterfaces);
    		}
            // If you do not use class proxy, resolve the interface of the target class.
    		else if (!isProxyTargetClass()) {
    			// Rely on AOP infrastructure to tell us what interfaces to proxy.
    			Class<?> targetClass = targetSource.getTargetClass();
    			if (targetClass != null) {
    				proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
    			}
    		}
    
            // The post-processing method of the agent factory is implemented by subclasses to change the agent configuration
    		postProcessProxyFactory(proxyFactory);
            // Create a proxy object and delegate it to ProxyFactory
    		this.proxy = proxyFactory.getProxy(this.proxyClassLoader);
    	}
    }		
    
  2. Advised support: it implements advised, adapts ProxyConfig to advised and provides support for advised. Its only subclass proxycreator support provides support for creating agents.

    public class AdvisedSupport extends ProxyConfig implements Advised {
        // Empty proxy object
    	public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE;
    	// Proxy target source: null target source by default
    	TargetSource targetSource = EMPTY_TARGET_SOURCE;
    	// Has the advisor been considered
    	private boolean preFiltered = false;
        // Advisor call chain foreman
    	AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
    	// The Advisor call chain corresponding to the cache method.
    	private transient Map<MethodCacheKey, List<Object>> methodCache;
    	// The proxy interfaces to be implemented are stored in order.
    	private List<Class<?>> interfaces = new ArrayList<>();
    	// Advisor list
    	private List<Advisor> advisors = new ArrayList<>();
    	// Advisor data, in order to facilitate internal operation.
    	private Advisor[] advisorArray = new Advisor[0];
    }
    

    Proxycreator support supports the creation of proxies. It uses AopProxyFactory to create AopProxy. Finally, ProxyFactory uses AopProxy to create proxy objects.

    When creating proxycreator support, DefaultAopProxyFactory is created by default. It is up to him to determine whether to use interface proxy or subclass proxy.

    public class ProxyCreatorSupport extends AdvisedSupport {
    	private AopProxyFactory aopProxyFactory;
    
    	private final List<AdvisedSupportListener> listeners = new LinkedList<>();
    
        // After the first agent is created, it will be set to true, indicating that it enters the active state and will trigger listeners.
    	private boolean active = false;
    
      	/**
      	 * With no parameter constructor, a default aopProxyFactory will be created.
      	 * DefaultAopProxyFactory Is a foreman who creates an agent, which is used to create an agent according to the configuration.
      	 */
        public ProxyCreatorSupport() {
    		this.aopProxyFactory = new DefaultAopProxyFactory();
    	}
    
        // Create an AOP agent, and decide whether to use JDK agent or Cglib agent according to its own configuration attributes.
        protected final synchronized AopProxy createAopProxy() {
    		if (!this.active) {
    			activate();
    		}
    		return getAopProxyFactory().createAopProxy(this);
    	}
    }
    

    As mentioned above, DefaultAopProxyFactory is used to decide whether to use jdk agent or Cglib agent. It receives an advised support

    ‚Äč

    // AopProxy factory
    public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    
    	@Override
    	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
            // If you enable optimization or use subclass proxy and do not implement the interface, the subclass proxy method will be used.
    		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.");
    			}
                // The proxy target is an interface or a proxy object. Use jdk proxy, otherwise use Cglib proxy
    			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
    				return new JdkDynamicAopProxy(config);
    			}
    			return new ObjenesisCglibAopProxy(config);
    		}
            // Using interface proxy: JDK proxy 
    		else {
    			return new JdkDynamicAopProxy(config);
    		}
    	}
    }
    

    ProxyFactory is a subclass of proxycreator support. It creates the target proxy object by calling the method of the parent class to obtain AopProxy.

    public class ProxyFactory extends ProxyCreatorSupport {
        public Object getProxy() {
            // After using the 'proxycreator support #createaopproxy' method, judge whether to use JDK to generate proxy or Cglib to generate proxy according to the configuration
    		return createAopProxy().getProxy();
    	}
        // The difference from the above method is that the class loader is passed in
    	public Object getProxy(@Nullable ClassLoader classLoader) {
    		return createAopProxy().getProxy(classLoader);
    	}
    }
    
JdkDynamicAopProxy
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
	/** Agent configuration */
	private final AdvisedSupport advised;

	/**
	 * Is the equals method defined on the interface of the agent
	 */
	private boolean equalsDefined;

	/**
	 * Does the interface of the agent define the hashCode method
	 */
	private boolean hashCodeDefined;
    
    public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
		Assert.notNull(config, "AdvisedSupport must not be null");
        // The notification is not empty and the destination source is not empty.
		if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
			throw new AopConfigException("No advisors and no TargetSource specified");
		}
		this.advised = config;
	}

	// Create proxy
	@Override
	public Object getProxy() {
        // Pass in default class loader
		return getProxy(ClassUtils.getDefaultClassLoader());
	}
	
	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
        // Gets all interfaces of the proxy target class
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        // Check whether the interface implements the equals and hashCode methods
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        // Create a proxy object. This object is passed in here, because JdkDynamicAopProxy implements InvocationHandler and uses this section of proxy logic to proxy
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
    
    /**
     * aop The agent uses the logic that the jdk agent will execute
     */
    @Override
	@Nullable
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object oldProxy = null;
		boolean setProxyContext = false;
		TargetSource targetSource = this.advised.targetSource;
		Object target = null;
		try {
           	// When executing the equals method, the interface does not define the equals method. Execute the equals method of JdkDynamicAopProxy
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				return equals(args[0]);
			}
            //  When executing the hashCode method, the interface does not define the hashCode method. Execute the hashCode method of JdkDynamicAopProxy
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				return hashCode();
			}
            // 
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
           	// It can be converted to Advised, converted to Advised, and then executed
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;
			// Whether to expose the current proxy and bind it to ThreadLocal,
			if (this.advised.exposeProxy) {
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}
			// Get target object
			target = targetSource.getTarget();
			Class<?> targetClass = (target != null ? target.getClass() : null);

            // Obtain the pointcut, method interceptor, etc. according to the proxy target object and method.
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
			
            // If the interceptor or or notification matching the method is empty, it will be called directly to avoid creating MethodInvocation
			if (chain.isEmpty()) {
                // Find a way
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                // Call the original object method directly
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
                // Call pointcuts, method interceptors, and target classes
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				retVal = invocation.proceed();
			}
			// 
			Class<?> returnType = method.getReturnType();
            // If the return value is the target object and the proxy object is an instance of the return value type, replace the return value with the proxy object
            // Method's declaration class does not implement RawTargetAccess
			if (retVal != null && retVal == target &&
					returnType != Object.class && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				retVal = proxy;
			}
            // If the return value type is the underlying data type and is null.
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}
}

In JdkDynamicAopProxy, there are two key codes: one is the interface to obtain the proxy target, and the other is the execution pointcut and interceptor.

  1. AopProxyUtils#completeProxiedInterfaces() method obtains the interface of the proxy target, and adds some interfaces SpringProxy, Advised and DecoratingProxy according to the rules.

    // AopProxyUtils
    static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
       // Implementation of interface class
       Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
       // The interface of the target class is empty
       if (specifiedInterfaces.length == 0) {
          // Get proxy target class
          Class<?> targetClass = advised.getTargetClass();
          if (targetClass != null) {
              // Determine whether the target type is an interface
             if (targetClass.isInterface()) {
                advised.setInterfaces(targetClass);
             }
              // Agent target type is agent
             else if (Proxy.isProxyClass(targetClass)) {
                advised.setInterfaces(targetClass.getInterfaces());
             }
             // Retrieve the interface set of the proxy object
             specifiedInterfaces = advised.getProxiedInterfaces();
          }
       }
    
       // If the target class does not implement the SpringProxy interface, SpringProxy will be added to the interface set.
       boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
       // If the target class can be converted to Advised and the Advised interface is not implemented, add Advised to the interface set
       boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
       // DecoratingProxy is true, and the target class does not implement the DecoratingProxy interface. Add DecoratingProxy to the interface set
       boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
    
       // Partition interface array length
       int nonUserIfcCount = 0;
       if (addSpringProxy) {
          nonUserIfcCount++;
       }
       if (addAdvised) {
          nonUserIfcCount++;
       }
       if (addDecoratingProxy) {
          nonUserIfcCount++;
       }
       Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
       // Copy
       System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
       // Set the interface class into the corresponding array position
       int index = specifiedInterfaces.length;
       if (addSpringProxy) {
          proxiedInterfaces[index] = SpringProxy.class;
          index++;
       }
       if (addAdvised) {
          proxiedInterfaces[index] = Advised.class;
          index++;
       }
       if (addDecoratingProxy) {
          proxiedInterfaces[index] = DecoratingProxy.class;
       }
       // Returns the set of interfaces that require a proxy.
       return proxiedInterfaces;
    }	
    
  2. Execute facet and method interceptor logic reflective method invocation #proceeded

    public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
    
        public Object proceed() throws Throwable {
            // After the post execution notification or interceptor, the business method will be executed
    		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    			return invokeJoinpoint();
    		}
    
            // Get notifications or interceptors
    		Object interceptorOrInterceptionAdvice =
    				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            // The notification or interceptor is InterceptorAndDynamicMethodMatcher 
            // InterceptorAndDynamicMethodMatcher is used to combine the method matcher with the interceptor. If the method matcher matches, it is called with the interceptor
    		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
    			InterceptorAndDynamicMethodMatcher dm =
    					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
    			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
    			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
    				return dm.interceptor.invoke(this);
    			}
    			else {
                    // Matching failed, call the next matching interceptor
    				return proceed();
    			}
    		}
            // Call other interceptors. Other interceptors need to be called. Because this is passed in, the interceptor chain can call this method by reference to execute the next section or interceptor.
    		else {
    			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    		}
    	}
    }
    

Added by GooberDLX on Sat, 05 Mar 2022 02:24:43 +0200