zoukankan      html  css  js  c++  java
  • Spring Trasnaction管理(2)- 事务AOP

    问题导读

    • spring AOP是在如何进行的
    • spring 用cglib和jdkProxy管理的事务有何区别

    Spring AOP管理

    Spring主要的两个核心功能IOC与AOP。IOC的代码解析可以参照这里
    在IOC过程中会调用AbstractAutowireCapableBeanFactory.initializeBean初始化对象并将对象放入Spring容器
    在这个方法中,等实例创建好会调用
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    来包装生成的bean(这时还没有进行AOP)在这个方法中会调用wrapIfNecessary来进行包装

    	//InfrastructureAdvisorAutoProxyCreator.class
     	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
                    ...
    		// 这里会获得响应的Advisor 其中就包含通过注解注释的@Transactional
    		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    		if (specificInterceptors != DO_NOT_PROXY) {
    			this.advisedBeans.put(cacheKey, Boolean.TRUE);
    			//这里就是开始创建proxy
    			Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    			this.proxyTypes.put(cacheKey, proxy.getClass());
    			return proxy;
    		}
    
    		this.advisedBeans.put(cacheKey, Boolean.FALSE);
    		return bean;
    	}
    

    在上面的createProxy其实会调用到ProxyFactory的getProxy方法

    	//ProxyFactory.class
     	public Object getProxy(ClassLoader classLoader) {
    		return createAopProxy().getProxy(classLoader);
    	}
    
    	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    	        //检查proxy_target_class标志和是否有接口
    		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.");
    			}
    			if (targetClass.isInterface()) {
                                    //如果类本生是接口则使用JDKProxy
    				return new JdkDynamicAopProxy(config);
    			}
    			//如果无接口或ProxyTargetClass为true使用CGLib
    			return CglibProxyFactory.createCglibProxy(config);
    		}
    		else {
    			//无ProxyTargetClass或且有接口使用JDK
    			return new JdkDynamicAopProxy(config);
    		}
    	}
    

    上面的代码的就是Spring判断使用的JDK还是CGlib的情况

    cglib代理模式

    下面看下cglib的实现
    在上面的getProxy方法中,Spring或默认注册几个默认的Callback(其中一个是DynamicAdvisedInterceptor他包含负责调用TransactionInterceptor也就是负责管理事务的Interceptor),这些Intercepter的构造函数如下

    	private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
    
    		private AdvisedSupport advised;
    
    		public DynamicAdvisedInterceptor(AdvisedSupport advised) {
    			this.advised = advised;
    		}
    

    在构造方法中都会传入一个advised变量,其实这个变量内部包含了在之前IOC过程中创建的那个原对象。

    事务调用

    当运行到需要事务管理的类时,以上所说的Interceptor将会进行拦截注入代码并invoke原本的对象
    CglibAopProxy$CglibMethodInvocation.invokeJoinpoint()

                    //CglibAopProxy$CglibMethodInvocation.invokeJoinpoint()
                    //获得上文所述advised对象中包含的原对象 
    		protected Object getTarget() throws Exception {
    			return this.advised.getTargetSource().getTarget();
    		}
    		
                    //invoke原方法
    		protected Object invokeJoinpoint() throws Throwable {
    			if (this.protectedMethod) {
    				return super.invokeJoinpoint();
    			}
    			else {
    				return this.methodProxy.invoke(this.target, this.arguments);
    			}
    		}
    

    在这里invoke时是调用的IOC阶段创立的bean而并非cglib自动创建的bean,其实这个时候如果使用visualVM等内存dump工具会看到内存中有两个对象(一个是IOC时创建的,一个是cglib create时创建的)。
    在这里Spring AOP过程中使用的的是代理模式(就算使用的是CGLIB),这也就是为什么在使用Transaction注解时,如果调用自己的另一个Transactional的方法时,这个Transactional将不会起作用。

    @Service
    public class Test(){
    
        public void fun1(){
            fun2()
        }
        
        @Transactional
        public void fun2(){
            
        }
    }
    

    以上如果调用fun1运行时fun2并不会被注入事务。
    有几个解决办法
    1.将自己注入进自己的一个变量,调用使用这个注入的变量,但是需要注意Transactional只支持在public方法中适用
    2.使用AspectJ注入(mode="aspectj")

    小结

    本文主要简介了Transactional的注入过程,以及注意事项。

  • 相关阅读:
    php+GTK2 学习第二篇
    PHPMailer + qq邮箱 实现邮件发送
    HTTP状态码200、301、403、404、500等(转)
    LNMP环境搭建(转载)
    PHP+GTK2 初体验,简单计算器客户端
    mysql 用户权限管理(转)
    提高php执行效率的10条编程习惯(转)
    添加php拓展(以phppcntl及phpredis及phppcntl为例)
    centos7 &后台运行 受终端关闭影响问题
    sklearn学习笔记之简单线性回归
  • 原文地址:https://www.cnblogs.com/resentment/p/5749284.html
Copyright © 2011-2022 走看看