[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.
-
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()); } }
-
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); } } }
-
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); } }
-
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.
-
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; }
-
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); } } }