preface

Before using Spring declarative things to develop with annotations, which is very convenient, you probably know that dynamic proxies are used to implement it. Today, we’ll take a look at the underlying implementation details.

Environment to prepare

Simply build a SpirngBoot project, add Spring Data Jpa as the framework of Data access layer. And added a simple query method with object annotations.

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<User> findAll(a) {
        returnuserRepository.findAll(); }}Copy the code

Basic knowledge of

The term describe
Joinpoint Join points, every method has join points. AOP implementations in Spring also support only join points of method types
Pointcut Join points that need to be enhanced
Advice To enhance
Target The proxied object
Proxy Proxy objects
Weaving Enhances the process of creating proxy objects applied to target objects
Aspect Pointcut plus enhancement

The principle behind the @ EnableTransactionManagement

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

	boolean proxyTargetClass(a) default false;

	AdviceMode mode(a) default AdviceMode.PROXY;

	int order(a) default Ordered.LOWEST_PRECEDENCE;

}
Copy the code

This annotation import TransactionManagementConfigurationSelector

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

	/**
	 * Returns {@link ProxyTransactionManagementConfiguration} or
	 * {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY}
	 * and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()},
	 * respectively.
	 */
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null; }}private String determineTransactionAspectClass(a) {
		return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ? TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME : TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME); }}Copy the code

In the org. Springframework. Transaction. The annotation. TransactionManagementConfigurationSelector# selectImports method introduces two important components

  • AutoProxyRegistrar
  • ProxyTransactionManagementConfiguration

AutoProxyRegistrar

org.springframework.context.annotation.AutoProxyRegistrar#registerBeanDefinitionsAn important method is called in theAopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)Finally registeredInfrastructureAdvisorAutoProxyCreator.You can see that from the inheritance diagramInfrastructureAdvisorAutoProxyCreatorIs aSmartInstantiationAwareBeanPostProcessor. Focus onAbstractAutoProxyCreatorIn an abstract classpostProcessAfterInitialization

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor.BeanFactoryAware {
	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if(bean ! =null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) ! = bean) {returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
	}
    
    	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && 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);
		returnbean; }}Copy the code

As can be seen from the above code, the main function is to create the proxy object of the target bean after the bean is instantiated, and to achieve the purpose of controlling things through the enhancement of the target object. The userServiceImpl injected at the Controller layer is the proxy object that Spring created for us.

ProxyTransactionManagementConfiguration

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(a) {
		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		advisor.setTransactionAttributeSource(transactionAttributeSource());
		advisor.setAdvice(transactionInterceptor());
		if (this.enableTx ! =null) {
			advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
		}
		return advisor;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionAttributeSource transactionAttributeSource(a) {
		return new AnnotationTransactionAttributeSource();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionInterceptor transactionInterceptor(a) {
		TransactionInterceptor interceptor = new TransactionInterceptor();
		interceptor.setTransactionAttributeSource(transactionAttributeSource());
		if (this.txManager ! =null) {
			interceptor.setTransactionManager(this.txManager);
		}
		returninterceptor; }}Copy the code

This configuration class configures three Java Beans

  • BeanFactoryTransactionAttributeSourceAdvisor
  • TransactionAttributeSource
  • TransactionInterceptor

In the initializationProxyTransactionManagementConfiguration“Is setTransactionAttributeSourceTransaction resolver andTransactionInterceptorTransaction interceptor two components. Above mentionedInfrastructureAdvisorAutoProxyCreatorThe two components are connected in series.

CglibAopProxy

The UserServiceImpl proxy object will eventually be created using CglibAopProxy because it is the class proxy that says Spring will choose to use Cglib to generate the proxy object. The proxy object creation logic is as follows:

@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
		}

		try{ Class<? > rootClass =this.advised.getTargetClass(); Assert.state(rootClass ! =null."Target class must be available for creating a CGLIB proxy"); Class<? > proxySuperClass = rootClass;if(ClassUtils.isCglibProxyClass(rootClass)) { proxySuperClass = rootClass.getSuperclass(); Class<? >[] additionalInterfaces = rootClass.getInterfaces();for(Class<? > additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface); }}// Validate the class, writing log messages as necessary.
			validateClassIfNecessary(proxySuperClass, classLoader);

			// Configure CGLIB Enhancer...
			Enhancer enhancer = createEnhancer();
			if(classLoader ! =null) {
				enhancer.setClassLoader(classLoader);
				if (classLoader instanceof SmartClassLoader &&
						((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
					enhancer.setUseCache(false);
				}
			}
			enhancer.setSuperclass(proxySuperClass);
			enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
			enhancer.setStrategy(newClassLoaderAwareUndeclaredThrowableStrategy(classLoader)); Callback[] callbacks = getCallbacks(rootClass); Class<? >[] types =newClass<? >[callbacks.length];for (int x = 0; x < types.length; x++) {
				types[x] = callbacks[x].getClass();
			}
			// fixedInterceptorMap only populated at this point, after getCallbacks call above
			enhancer.setCallbackFilter(new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
			enhancer.setCallbackTypes(types);

			// Generate the proxy class and create a proxy instance.
			return createProxyClassAndInstance(enhancer, callbacks);
		}
		catch (CodeGenerationException | IllegalArgumentException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
					": Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (Throwable ex) {
			// TargetSource.getTarget() failed
			throw new AopConfigException("Unexpected AOP exception", ex); }}Copy the code

Callback[] callbacks = getCallbacks(rootClass); Gets the enhanced logic for the proxy class, GetCallbacks method will have things in the interceptor TransactionInterceptor BeanFactoryTransactionAttributeSourceAdvisor into DynamicAdvisedInterceptor. This gives the proxy object the ability to manage things.