精华内容
下载资源
问答
  • 2019-04-27 14:04:50

    1、Spring中的aop的介绍

    Spring能够为容器中管理的对象创建代理对象。

    以前我们创建动态代理对象要调用该方法:

    proxy.newProxyInstance(xx,xx,xx)。

    Spring中是使用动态代理和cglib代理混合使用,优先使用动态代理,如果不能使用动态代理,则使用cglib代理。

    2、cglib代理和动态代理

    动态代理:被代理对象必须要实现接口,才能产生代理对象.如果没有接口将不能使用动态代理技术。被代理对象和代理对象只是实现了同一接口。

    cglib代理:第三方代理技术(Spring中整合了该jar,所以不用导包),cglib代理.可以对任何类生成代理.代理的原理是对目标对象进行继承代理. 如果目标对象被final修饰.那么该类无法被cglib代理。被代理和代理对象是继承和被继承的关系。

    public class UserServiceProxyFactory2 implements MethodInterceptor {
    	
    
    	public UserService getUserServiceProxy(){
    		Enhancer en = new Enhancer();//帮我们生成代理对象
    		en.setSuperclass(UserServiceImpl.class);//设置对谁进行代理
    		en.setCallback(this);//代理要做什么
    		UserService us = (UserService) en.create();//创建代理对象
    		return us;
    	}
    
    	@Override
    	public Object intercept(Object prxoyobj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
    		//打开事务
    		System.out.println("打开事务!");
    		//调用原有方法
    		Object returnValue = methodProxy.invokeSuper(prxoyobj, arg);
    		//提交事务
    		System.out.println("提交事务!");
    		
    		return returnValue;
    	}
    
    
    }

    3、aop名词

    JoinPoint(连接点):目标对象中,所有可以增强的方法。

    Pintcut(切入点):目标对象,已经增强的方法。

    Advice(通知/增强):增强的代。

    Target(目标对象):被代理对象。

    Weaving(织入):将通知应用到切入点的过程。

    Proxy(代理):将通知织入到目标对象之后,形成代理对象。

    aspect(切面):切入点+通知

    4、Spring中使用aop步骤

    使用Spring创建代理对象

    (1)导包

          4+2+2+2

          Spring中的aop包

    Spring需要的第三方包

    (2)准备目标对象

    public class Target{
    
    	public void add() {
    		System.out.println("add");
    	}
    	
    	public void remove() {
    		System.out.println("remove");
    	}
    	
    	public void update() {
    		System.out.println("update");
    	}
    	
    	public void query() {
    		System.out.println("query");
    	}
    	
    }

    该对象可以实现接口,也可以不用实现,实现了接口则Spring会优先使用动态代理。如果没有实现接口,Spring会使用cglib代理。

    注意,如果使用接口,通过getBean获得对象时,记得强制转换对象时,使用接口,而不是目标类。否则会出现ClassCaseException

    (3)准备通知

    public class AdviceClass {
    
    	public void before() {
    		System.out.println("这是前置通知");
    	}
    	
    	public void afterReturning() {
    		System.out.println("这是后置通知(出现异常将不会调用)");
    	}
    	
    	public Object around(ProceedingJoinPoint pj) throws Throwable {
    		System.out.println("这是环绕通知之前的部分");
    		Object proceed = pj.proceed();//调用目标方法
    		System.out.println("这是环绕通知之后的部分");
    		return proceed;
    	}
    	
    	public void afterException() {
    		System.out.println("出现异常后调用");
    	}
    	
    	public void after() {
    		System.out.println("这是后置通知(无论会不会出现异常都会调用)");
    	}
    	
    }

    (4)配置:将通知织入到目标中

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     	xmlns="http://www.springframework.org/schema/beans"
      	xmlns:context="http://www.springframework.org/schema/context"
       	xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
    
    	<bean name="target" class="club.ityuchao.test.Target"></bean>
    	<bean name="advice" class="club.ityuchao.test.AdviceClass"></bean>
    	
    	<aop:config>
    		<!-- 配置切入点 
    			public void cn.itcast.service.UserServiceImpl.save() 
    			void cn.itcast.service.UserServiceImpl.save()
    			* cn.itcast.service.UserServiceImpl.save()
    			* cn.itcast.service.UserServiceImpl.*()
    			
    			* cn.itcast.service.*ServiceImpl.*(..)
    			* cn.itcast.service..*ServiceImpl.*(..)
    		-->
    		<aop:pointcut expression="execution(public void club.ityuchao.test.Target.add())" id="pc"/>
    		<aop:aspect ref="advice">
    			<aop:before method="before" pointcut-ref="pc"/>
    			<aop:after-returning method="afterReturning" pointcut-ref="pc"/>
    			<aop:around method="around" pointcut-ref="pc"/>
    			<aop:after method="after" pointcut-ref="pc"/>
    			<aop:after-throwing method="afterException" pointcut-ref="pc"/>
    		</aop:aspect>
    	</aop:config>
    
    </beans>

    5、测试

    public class AopTest {
    
    	@Test
    	public void test() {
    		ApplicationContext ac = new ClassPathXmlApplicationContext("club/ityuchao/test/applicationContext.xml");
    		Target target = (Target) ac.getBean("target");
    		target.add();
    	}
    	
    }

    测试结果:

    出现异常:

    出现异常后,环绕通知的后执行将不会执行,afterRetruning也不会执行

    6、使用注解配置aop(了解)

    (1)applicationContext.xml中配置

    <!-- 表示使用注解完成织入 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    (2)advice中使用注解

     

    //表示该类是一个通知类
    @Aspect
    public class AdviceClass {
    	
    	//使用该方法可以不用下面每一个方法都写expression
    	@Pointcut("execution(* club.ityuchao.test.Target.*(..))")
    	public void pc() {}
    
    	@Before("execution(* club.ityuchao.test.Target.*(..))")
    	public void before() {
    		System.out.println("这是前置通知");
    	}
    	
    	@AfterReturning("AdviceClass.pc()")
    	public void afterReturning() {
    		System.out.println("这是后置通知(出现异常将不会调用)");
    	}
    	
    	@Around("AdviceClass.pc()")
    	public Object around(ProceedingJoinPoint pj) throws Throwable {
    		System.out.println("这是环绕通知之前的部分");
    		Object proceed = pj.proceed();//调用目标方法
    		System.out.println("这是环绕通知之后的部分");
    		return proceed;
    	}
    	
    	@AfterThrowing("AdviceClass.pc()")
    	public void afterException() {
    		System.out.println("出现异常后调用");
    	}
    	
    	@After("AdviceClass.pc()")
    	public void after() {
    		System.out.println("这是后置通知(无论会不会出现异常都会调用)");
    	}
    	
    }

     

    更多相关内容
  • Spring IOC---AOP代理对象生成的时机

    千次阅读 多人点赞 2020-12-15 20:52:37
    前置知识3.Spring AOP代理对象生成的时机3.1非提前生成代理对象3.2 提前生成代理对象4. 为什么需要两种动态代理的时机 1.概述 Spring AOP可以采用注解或者xml配置的方式实现,那么在spring的生命周期当中,是在什么...

    1.概述

    Spring AOP可以采用注解或者xml配置的方式实现,那么在spring的生命周期当中,是在什么时候生成的代理对象呢?本文就AOP代理对象生成的时机进行介绍。不清楚spring生命周期的读者可以先阅读另一篇博客《Spring IOC—Bean的生命周期》

    2.前置知识

    • BeanPostProcessor接口的作用

    简单的讲就是在一个对象初始化的前后做一些事情,里面有两个方法,一个是postProcessBeforeInitialization,实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务。另一个是postProcessAfterInitialization,实例化、依赖注入、初始化完毕时执行

    3.Spring AOP代理对象生成的时机

    可以分成两类,提前和非提前生成代理对象。

    下面分别介绍这两类代理对象生成的时机,以及说明为什么会有这两种情况。

    3.1非提前生成代理对象

    下面给个例子,通过调试代码来说明代理的时机,读者可以跟着一边调试一边阅读。

    //被代理类 A
    package hdu.gongsenlin.aoptest.dao;
    @Component
    public class A {
    
        public void f(){
            System.out.println("AAAAA");
        }
    }
    //切面类
    package hdu.gongsenlin.aoptest.aop;
    @Aspect
    @Component
    public class Aop {
    
    
        @Pointcut("execution(* hdu.gongsenlin.aoptest.dao..*.*(..))")
        private void pointcut(){}
    
        @After("pointcut()")
        public void advice(){
            System.out.println("之后增强------------");
        }
    }
    //配置类
    package hdu.gongsenlin.aoptest;
    @Configuration
    @ComponentScan("hdu.gongsenlin.aoptest")
    @EnableAspectJAutoProxy
    public class Appconfig {
    }
    //启动类
    package hdu.gongsenlin.aoptest;
    public class AoptestApplication {
    
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
            A a = ac.getBean(A.class);
            a.f();
        }
    
    }
    

    上面的代码AOP会作用在A对象上,也就是生成A的代理对象。

    这一过程发生在A的生命周期当中,将代码定位到AbstractAutowireCapableBeanFactory#doCreateBean方法

    了解springBean的生命周期的读者都应该清楚,Bean生命周期中有一步是属性填充 population,在属性填充之后会执行initializeBean在这里插入图片描述

    initializeBean方法中会执行applyBeanPostProcessorsAfterInitialization方法在这里插入图片描述

    applyBeanPostProcessorsAfterInitialization具体的代码如下:

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
          throws BeansException {
    
       Object result = existingBean;
       for (BeanPostProcessor processor : getBeanPostProcessors()) {
          Object current = processor.postProcessAfterInitialization(result, beanName);
          if (current == null) {
             return result;
          }
          result = current;
       }
       return result;
    }
    

    逻辑比较简单,就是遍历所有实现了BeanPostProcessor接口的类,这里一共有这7个,一个一个去执行postProcessAfterInitialization方法。其中AnnotationAwareAspectJAutoProxyCreator就是会实现AOP动态代理,然后返回代理对象。在这里插入图片描述

    AnnotationAwareAspectJAutoProxyCreator中的postProcessAfterInitialization代码如下

    根据bean类型和名字创建缓存key,判断earlyProxyReferences提前动态代理的集合当中存不存在这个缓存key,若存在则说明已经进行过动态代理了,则不再进行动态代理,而本例子中,很明显是没有执行提前动态代理的,所以会执行wrapIfNecessary方法进行构建动态代理对象,本文仅介绍执行的时机,具体的动态代理的实现过程暂时先不考虑。

    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                return this.wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
    
        return bean;
    }
    

    所以非提前生成代理对象 是在属性填充populateBean完成之后,执行了initializeBean方法的时候进行的动态代理。

    3.2 提前生成代理对象

    这种情况比较的复杂,涉及到了循环依赖的问题,对于Bean的循环依赖不了解的读者,可以先阅读《Spring IOC—循环依赖》这篇博客再接着阅读。

    同样的以举例的方式说明

    //切面类 不变
    package hdu.gongsenlin.aoptest.aop;
    @Aspect
    @Component
    public class Aop {
    
    
        @Pointcut("execution(* hdu.gongsenlin.aoptest.dao..*.*(..))")
        private void pointcut(){}
      
        @After("pointcut()")
        public void advice(){
            System.out.println("之后增强------------");
        }
    }
    //启动类 不变
    public class AoptestApplication {
    
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
            A a = ac.getBean(A.class);
            a.f();
        }
    }
    //配置类 不变
    @Configuration
    @ComponentScan("hdu.gongsenlin.aoptest")
    @EnableAspectJAutoProxy
    public class Appconfig {
    }
    //被代理对象 A 依赖了 普通对象B
    package hdu.gongsenlin.aoptest.dao;
    @Component
    public class A {
    
        @Autowired
        B b;
    
        public void f(){
            System.out.println("AAAAA");
        }
    }
    // 普通对象B 依赖了 A
    package hdu.gongsenlin.aoptest.service;
    @Component
    public class B {
    
        @Autowired
        A a;
    
        public void f(){
            System.out.println("BBBBBBBB");
    
        }
    }
    

    此时给的例子中,A和B相互依赖,形成了循环依赖,不同的是A需要被动态代理,而B不需要。

    继续将代码定位到创建A的时候,还没有执行属性填充populateBean的位置,如下:

    在这里插入图片描述

    因为涉及到了循环依赖,所以准备三个框框,来代表循环依赖需要用到的三个集合。在这里插入图片描述

    至于这三个集合的作用 在循环依赖的博客中已经介绍了,这里就不再赘述。

    在上面代码执行之前,会将a的工厂对象放入到singletonFactories,此时三个集合的情况如下:

    在这里插入图片描述

    之后执行构建A的populateBean进行属性填充,发现A依赖于B,而B又不存在于这三个集合当中,所以会递归的创建B,调用doCreateBean来构建B对象,和A相同,在执行populateBean填充属性之前,会将b的工厂对象放入到singletonFactories当中,此时三个集合的情况如下:

    在这里插入图片描述

    接着会执行构建B的populateBean进行属性填充,此时B依赖于A,所以会调用doGetBean的去找A对象在这里插入图片描述

    然后调用getSingleton,该方法是解决循环依赖的关键,代码如下:

    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
       // Quick check for existing instance without full singleton lock
       Object singletonObject = this.singletonObjects.get(beanName);//先从单例池中去获取
       if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {//如果单例池中没有,并且当前的bean正在创建
          singletonObject = this.earlySingletonObjects.get(beanName);// 看看有没有提前暴露的不完整的Bean
          if (singletonObject == null && allowEarlyReference) {// 如果还没有 且允许提前创建
             synchronized (this.singletonObjects) {
                // Consistent creation of early reference within full singleton lock
                singletonObject = this.singletonObjects.get(beanName);// 再检查一次 singletonObjects 双重检查
                if (singletonObject == null) {
                   singletonObject = this.earlySingletonObjects.get(beanName);// 再检查一次 earlySingletonObjects 双重检查
                   if (singletonObject == null) {//若都还没有
                      // 则获取beanName对应的单例工厂
                      ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                      if (singletonFactory != null) {
                         //通过工厂创建一个对象
                         singletonObject = singletonFactory.getObject();
                         //将这个实例化的对象放入到earlySingletonObjects
                         this.earlySingletonObjects.put(beanName, singletonObject);
                         //从单例工厂中移除 这个工厂。
                         this.singletonFactories.remove(beanName);
                      }
                   }
                }
             }
          }
       }
       return singletonObject;
    }
    

    一级缓存和二级缓存中没有找到A对象,而三级缓存singletonFactories当中有A的工厂对象,所以会调用

    singletonFactory.getObject()来获得A对象。

    这是之前添加工厂对象到singletonFactories的代码,所以其实执行getObject()也就是执行了getEarlyBeanReference方法

    在这里插入图片描述

    getEarlyBeanReference的方法代码如下:

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
       Object exposedObject = bean;
       if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
          for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
             exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
          }
       }
       return exposedObject;
    }
    

    此时又出现了AnnotationAwareAspectJAutoProxyCreator,这里会执行它的getEarlyBeanReference方法

    在这里插入图片描述

    基于类型和名字 创建缓存key,将其放入到earlyProxyReferences集合当中,用于表示进行了提前的动态代理。调用wrapIfNecessary来构建动态代理对象。

    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);
        return this.wrapIfNecessary(bean, beanName, cacheKey);
    }
    

    此时获得到了被代理后的A对象,三个集合的结果如下:

    在这里插入图片描述

    之后doCreateBean 构建B的过程结束了,获得了B对象

    在这里插入图片描述

    之后会将这个对象,添加到singletonObjects集合当中,三个集合的结果如下:

    在这里插入图片描述

    B对象构建完成了,那么此时A就可以完成它的填充了,所以走完剩下的逻辑之后,三个集合的结果如下:

    在这里插入图片描述

    综上提前动态代理,其实是在依赖注入的时候,也就是在populateBean属性填充方法内完成的。

    4. 为什么需要两个AOP动态代理的时机

    沿用3.2的例子,假设动态代理不提前,那么在构建B对象进行属性填充的时候,填充的A对象是还没有进行动态代理的A。

    此时B就完成了它的生命周期到了单例池当中,而后A执行完属性填充之后,再进行动态代理,生成一个被代理的A对象。放入到单例池当中。

    此时B中的A和单例池中的被代理的A对象不是同一个对象,这就造成了问题。

    展开全文
  • 引言 mybatis版本:3.5.1 mybatis-spring:2.0.1 ...Mybatis是如何为mapper接口生成代理对象的? Mybatis又是如何将mapper对象交给Spring管理? 我们在整合mybatis与spring时,都会使用MyBatis-Spring 将 MyBatis

    引言

    • mybatis版本:3.5.1
    • mybatis-spring:2.0.1

    使用过Mybatis框架的开发人员应该都知道,在编写dao层时,只需要提供mapper接口与相应的xxxMapper.xml,无需实现类,便可以将mapper接口对象交由Spring容器管理,疑问:

    1. Mybatis是如何为mapper接口生成代理对象的?
    2. Mybatis又是如何将mapper对象交给Spring管理?

    我们在整合mybatis与spring时,都会使用MyBatis-Spring 将 MyBatis 代码无缝地整合到 Spring 中。

    MyBatis-Spring出现的动机:

    Spring 2.0 只支持 iBatis 2.0。那么,我们就想将 MyBatis3 的支持添加到 Spring 3.0 中(参见 Spring Jira 中的 问题 )。不幸的是,Spring 3.0 的开发在 MyBatis 3.0 官方发布前就结束了。 由于 Spring 开发团队不想发布一个基于未发布版的 MyBatis 的整合支持,如果要获得 Spring 官方的支持,只能等待下一次的发布了。基于在 Spring 中对 MyBatis 提供支持的兴趣,MyBatis 社区认为,应该开始召集有兴趣参与其中的贡献者们,将对 Spring 的集成作为 MyBatis 的一个社区子项目。

    那么,在mybatis-spring中,mybatis对于集成spring做了哪些扩展?

    @MapperScan注解

    首先需要扫描指定mapper包下面的接口,为这些接口生成代理对象,再通过FactoryBean将这些代理对象交给Spring管理。

    注:对FactoryBean不熟悉的,可以查看:https://blog.csdn.net/a1036645146/article/details/111661211

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(MapperScannerRegistrar.class)
    @Repeatable(MapperScans.class)
    public @interface MapperScan {
        // 省略部分。。。
        Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
    }

    这个注解的主要作用是将MapperScannerConfigurer放到BeanDefinitionMap当中。

    那么它是如何工作的呢?在spring中,有一个内置类ConfigurationClassPostProcessor,这个类的作用是扫描带spring注解的类添加到bdmap中,并且解析配置类上的@Import标签,若这个标签中的类实现了ImportBeanDefinitionRegistrar接口,会将这这个类实例化后放入到 importBeanDefinitionRegistrars 缓存当中。扫描完成后会执行这个集合当中的对象的registerBeanDefinitions()方法。

    mybatis-spring中MapperScannerRegistrar#registerBeanDefinitions()这个方法的作用就是将MapperScannerConfigurer注册到BeanDefinitionMap当中。

    //MapperScannerRegistrar
    @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes mapperScanAttrs = AnnotationAttributes
            .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        if (mapperScanAttrs != null) {
          registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
              generateBaseBeanName(importingClassMetadata, 0));
        }
      }
     void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
        BeanDefinitionRegistry registry, String beanName) {
    
      BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
      // 省略部分。。。
    
      registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
    
    }

    MapperScannerConfigurer

    mybatis-spring框架中MapperScannerConfigurer 基于spring的扩展实现了BeanDefinitionRegistryPostProcessor接口。在Spring的内置扫描器扫描结束后会触发这个类的postProcessBeanDefinitionRegistry方法,构建一个mybatis的扫描器(ClassPathMapperScanner)来执行扫描mapper接口,将mapper接口对象交给了Spring管理。

    public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
    
    
      @Override
      public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
          processPropertyPlaceHolders();
        }
        // 创建一个mybatis 的扫描器,扫描mapper接口包,可将mapper.class到BeanDefinition的转换
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        // 下面的一系列set方法是对这个扫描器进行初始化
        scanner.setAddToConfig(this.addToConfig);
        scanner.setAnnotationClass(this.annotationClass);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
        scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
        scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
        scanner.setResourceLoader(this.applicationContext);
        scanner.setBeanNameGenerator(this.nameGenerator);
        scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        scanner.registerFilters();//注册一些扫描用的过滤器
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
      }
    
    。。。
    }

    其中,ClassPathMapperScanner是mybatis实现的一个扫描器,继承了spring内置的扫描器ClassPathBeanDefinitionScanner,但它并没有重写父类的scan方法,所以这里调用的是父类的scan方法,如下:

    public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
    	
        public int scan(String... basePackages) {
    		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    
            // 可由子类实现
    		doScan(basePackages);
    
    		// Register annotation config processors, if necessary.
    		if (this.includeAnnotationConfig) {
    			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    		}
    
    		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
    	}
    。。。
    }

    重点在执行doScan方法,该方法由ClassPathMapperScanner重写了,主要是将mapper.class到BeanDefinition的转换

    public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
    
      // 扫描mapper接口包,将mapper.class转为对应的beanDefinition
      @Override
      public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);//扫描mapper包下的接口
    
        if (beanDefinitions.isEmpty()) {
          LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
          processBeanDefinitions(beanDefinitions);// //将这些mapper接口变成FactoryBean
        }
    
        return beanDefinitions;
      }
    
    。。。
    }

    其中,processBeanDefinitions 这个方法会设置构造函数的参数为这个接口,然后将beanClass修改为MapperFactoryBean之后实例化的时候会调用这个FactoryBean的getObject()方法来构建一个代理Mapper对象。这就是如何将map接口转变成一个对象交给Spring管理的关键

      private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        GenericBeanDefinition definition;
        for (BeanDefinitionHolder holder : beanDefinitions) {
          definition = (GenericBeanDefinition) holder.getBeanDefinition();
          String beanClassName = definition.getBeanClassName();
          LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName()
              + "' and '" + beanClassName + "' mapperInterface");
    
          // the mapper interface is the original class of the bean
          // but, the actual class of the bean is MapperFactoryBean
          // 添加构造函数需要的参数
          definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
          //设置类型为mapperFactoryBeanClass。之后的实例化会调用MapperFactoryBean中的getObject()来构建出一个基于接口的代理对象交给spring管理
          definition.setBeanClass(this.mapperFactoryBeanClass);
          。。。
    
        }
      }

    MapperProxyFactory

    Mybatis框架中,MapperProxyFactory,是mapper代理工厂,可基于接口和MapperProxy来构建一个mapper代理对象,实现了将接口转变成一个对象:

    MapperProxy 实现了 InvocationHandler 接口的invoke方法,所以,我们明白了第1个问题,Mybatis是基于JDK动态代理来生成mapper接口的对象的。

    注:对jdk动态代理不熟悉的,可以查看:https://blog.csdn.net/a1036645146/article/details/111881599

    其中,MapperMethod#execute()如下:

    public class MapperMethod {
    
      private final SqlCommand command;
      private final MethodSignature method;
    
      public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new SqlCommand(config, mapperInterface, method);
        this.method = new MethodSignature(config, mapperInterface, method);
      }
    
      public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        // 根据sql语句的类型,选择不同的分支流程执行
        switch (command.getType()) {
          case INSERT: {// 新增语句
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.insert(command.getName(), param));
            break;
          }
          case UPDATE: {// 更新语句
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.update(command.getName(), param));
            break;
          }
          case DELETE: {// 删除语句
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.delete(command.getName(), param));
            break;
          }
          case SELECT:// 查询
            if (method.returnsVoid() && method.hasResultHandler()) {
              executeWithResultHandler(sqlSession, args);
              result = null;
            } else if (method.returnsMany()) {
              result = executeForMany(sqlSession, args);
            } else if (method.returnsMap()) {
              result = executeForMap(sqlSession, args);
            } else if (method.returnsCursor()) {
              result = executeForCursor(sqlSession, args);
            } else {
              Object param = method.convertArgsToSqlCommandParam(args);
              result = sqlSession.selectOne(command.getName(), param);
              if (method.returnsOptional()
                  && (result == null || !method.getReturnType().equals(result.getClass()))) {
                result = Optional.ofNullable(result);
              }
            }
            break;
          case FLUSH:
            result = sqlSession.flushStatements();
            break;
          default:
            throw new BindingException("Unknown execution method for: " + command.getName());
        }
        if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
          throw new BindingException("Mapper method '" + command.getName()
              + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
        }
        return result;
      }
    。。。
    }

    MapperFactoryBean

    MapperFactoryBean实现了Spring的FactoryBean扩展接口。FactoryBean是一个工厂Bean,可以生成某一类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。具体可以查看《【Spring源码:FactoryBean一】终于弄懂FactoryBean是如何自定义bean的创建过程了》

    它可以调用getObject()方法根据接口的不同,返回不同的接口代理对象,这个对象也就是通过上面的MapperProxyFactory产生的。

    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    
      private Class<T> mapperInterface;
      private boolean addToConfig = true;
    
      public MapperFactoryBean() {
        //intentionally empty 
      }
      
      public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      @Override
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }
    
    。。。
    
    }

    总结

    1. Mybatis首先扫描指定包下面的mapper接口,为每一个mapper接口创建一个代理工厂MapperProxyFactory,放入MapperRegistry的一个属性集合缓存中:

    Map<Class<?>, MapperProxyFactory<?>> knownMappers

    2. 使用这些代理工厂,通过实现JDK动态代理去创建mapper接口对应的代理对象,MapperProxy;
    3. 通过代理对象去调用实际的业务逻辑。

    参考:https://blog.csdn.net/gongsenlin341/article/details/108600008

    史上最强Tomcat8性能优化

    阿里巴巴为什么能抗住90秒100亿?--服务端高并发分布式架构演进之路

    B2B电商平台--ChinaPay银联电子支付功能

    学会Zookeeper分布式锁,让面试官对你刮目相看

    SpringCloud电商秒杀微服务-Redisson分布式锁方案

    查看更多好文,进入公众号--撩我--往期精彩

    一只 有深度 有灵魂 的公众号0.0

    展开全文
  • 我们平常在使用mybatis的时候只需要生成mapper...原因是虽然我们没有实现接口,但是通过配置文件,spring为我们生成了接口的代理类。 让我们从配置文件入手,从源码中一探究竟。 MapperScannerConfigurer这个类...

    我们平常在使用mybatis的时候只需要生成mapper接口和与其对应的xml文件就行了,我们就可以把这个接口当作一个bean,可以往其他的bean中注入了。我们没有实现mapper接口,为什么可以使用接口中的方法呢?原因是虽然我们没有实现接口,但是通过配置文件,spring为我们生成了接口的代理类。
    让我们从配置文件入手,从源码中一探究竟。
    在这里插入图片描述
    MapperScannerConfigurer这个类是负责扫描mapper接口所在的包的,它把扫描到的接口解析成一个个的bean定义(BeanDefinition),来看一下源码:
    在这里插入图片描述
    MapperScannerConfigurer实现了两个重要接口,如图所示。
    实现BeanDefinitionRegistryPostProcessor接口,我们可以自定义注册bean过程,要实现的方法是
    postProcessBeanDefinitionRegistry()
    实现InitializingBean接口的afterPropertiesSet()方法,可以在bean创建之后初始化的时候做一些操作。
    现在进入MapperScannerConfigurer的postProcessBeanDefinitionRegistry()方法
    在这里插入图片描述
    该方法内部把扫描mapper接口的工作委托给了ClassPathMapperScanner类,该类继承自ClassPathBeanDefinitionScanner,进入它的scan()方法:
    在这里插入图片描述
    ClassPathBeanDefinitionScanner中的doScan()方法:
    在这里插入图片描述
    doScan方法真正的负责生成bean定义。
    ClassPathMapperScanner重载的doScan()方法:
    在这里插入图片描述
    重载的doScan方法对生成的mapper的bean定义做了进一步处理,进入processBeanDefinitions()方法:
    在这里插入图片描述
    红色部分为该方法的核心,mapperInterface设置的值是mapper接口的带包名的路径名称;
    definition.setBeanClass()把原来的BeanClass的类型替换成了MapperFactoryBean类型,这个是Mapper接口加载定义阶段最重要的一步。是生成代理类的关键。
    查看MapperFactoryBean的定义:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    MapperFactoryBean实现了FactoryBean接口,实现了FactoryBean接口的类型在调用getBean(beanName)既通过名称获取对象时,返回的对象不是本身类型的对象,而是通过实现接口中的getObject()方法返回的对象。
    在这里插入图片描述
    MapperFactoryBean实现了FactoryBean接口InitializingBean接口,在对象初始化的时候会调用它的afterPropertiesSet方法,该方法中首先调用了checkDaoConfig()方法,MapperFactoryBean重载的checkDaoConfig()如下:
    在这里插入图片描述
    Configuration configuration = getSqlSession().getConfiguration();这一句中的getSqlSession()获取的SqlSession
    对象就是文章开头的配置文件中配置的。
    Configuration 是一个重要的配置类在这里插入图片描述
    进入 configuration.addMapper(this.mapperInterface)方法中:
    在这里插入图片描述
    在configuration内部对Mapper的操作都委托给了mapperRegistry对象,进入它的addMapper(type)方法,这里的参数type就是一个mapper接口的类型(如:com.st.mapper.UserMapper.java):
    在这里插入图片描述
    knownMappers.put(type, new MapperProxyFactory(type));这一步为我们创建了mapper 的代理工厂类对象,并把它放入了knownMappers这个Map中。MapperProxyFactory这个类我们下面再讲。
    MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
    parser.parse();
    这两句完成了mapper接口对应的xml文件的解析,xml文件中的每一个方法都被解析成了一个MappedStatement对象(代码太长,不再展开),并添加到了configuration对象中:
    在这里插入图片描述

    对上面的内容的总结:
    mapper接口的定义在bean加载阶段会被替换成MapperFactoryBean类型,在spring容器初始化的时候会给我们生成MapperFactoryBean类型的对象,在该对象生成的过程中调用afterPropertiesSet()方法,为我们生成了一个
    MapperProxyFactory类型的对象存放于Configuration里的MapperRegistry对象中,同时解析了mapper接口对应的xml文件,把每一个方法解析成一个MappedStatement对象,存放于Configuration里的mappedStatements
    这个Map集合中。

    下面看一下MapperFactoryBean的getObject()方法,看看mapper代理对象是如何生成的:
    在这里插入图片描述
    在这里插入图片描述
    跟踪代码最后调用的是MapperRegistry.getMapper()方法给我们返回了mapper代理对象。

    现在来看一下MapperProxyFactory这个类:
    在这里插入图片描述
    MapperProxy是被代理的对象,看下这个类:
    在这里插入图片描述
    在invoke()方法中最终执行的是mapperMethod.execute(sqlSession, args);方法。
    来看一下MapperMethod这个类:
    在这里插入图片描述
    execute()这个方法最终负责执行我们mapper接口中方法,它会判断要执行的方法的类型,然后调用sqlSession对应的方法类型来执行,并放回结果。

    展开全文
  • AOP(Aspect-Oriented Programming)面向切面或者面向方面编程的简称,将一些分散在对象或者类中的与业务逻辑无关的代码分离出来进行独立的管理编码,例如日志,事务处理,异常等。几个相关的重要概念: 1、方面/切面...
  • java代理对象

    千次阅读 2020-09-13 16:03:12
    代理对象 代理 是一种设计模式,他提供了对目标对象的一种访问方式(通过代理对象去访问目标对象) 使用的编程思想 不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法 最基本...
  • 一直以为spring的ioc容器生成的对象都是代理对象,其实这个是错误的。spring ioc默认的都是原生对象 只有通过aop增强的对象才是代理对象有@Transactional 注解或者配置文件&lt;aop:config&gt; &lt;...
  • Spring AOP源码分析(生成代理对象

    千次阅读 2016-05-08 23:36:55
    代理对象生成过程总览 以singleTon为例: 代理对象获取入口: ProxyFactoryBean的getObject AopProxy生成过程: ProxyFactoryBean的getObject获取代理对象 @Override public Object ...
  • mybatis框架的使用非常广泛,使用者只需要开发很简单的代码就能实现很多业务需求,真的非常方便,简化开发的同时还规范了开发,也因此成为在持久映射框架中最为流行的框架之一。...3.通过代理对象去调用实际的业务逻辑
  • spring有非常多的地方都是使用代理的,但是这些什么使用代理,使用哪种代理代理是在什么时候创建的,怎么运行起效的。这些问题都会是下面的源码中实现的。 1、代理是怎么创建的,什么时候创建的,为什么创建 ...
  • java动态创建代理对象

    千次阅读 2019-07-15 23:53:29
    》》》》》》代理模式的字面意思,就是代理一个类,即被代理对象,让代理对象可 以有代理对象的功能或同时能够加强这个功能的,当然他还有其他作用 package ceom.proxy.test; //Proxy,相当于工具类,帮...
  • 织入:将切面应用到目标对象来创建代理对象的过程。 通过JDK代理生成AOP代理对象 Spring中的AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理,...
  • 静态代理是代理模式实现方式之一,比较简单,主要分为三个角色...在动态代理中,不需要我们再手动创建代理类,只需要编写一个动态处理器及指定要代理的目标对象实现的接口类型,真正的代理对象由JDK在运行时为我们创建
  • Aop自动创建代理对象的原理我们在使用Spring Aop时,通常Spring会自动为我们创建目标bean的代理对象,以使用对应的Advisor。前提是我们在使用Spring Aop时是使用的或,这是因为当我们在applicationContext.xml文件中...
  • } } getProxy()方法为target对象生成一个动态代理对象,这个动态代理对象与target实现了相同的接口,所以具有相同的public方法,因此从这个角度看,动态代理对象可以当成target对象使用,当程序调用动态代理对象的...
  • 此时,从三级缓存里就获得了通过事务切面生成代理对象,并给UserRightService成员变量赋值,同时spring也会将其放入二级缓存。 4.通过initializeBean方法做后置工作 通过第3步,外层UserRightService就基本实例化...
  • 深入理解Spring AOP之二代理对象生成

    万次阅读 2015-06-30 08:51:00
    深入理解Spring AOP之二代理对象生成 springyuanm 上一篇博客中讲到了Spring的一些基本概念和初步讲了实现方法,其中提到了动态代理技术,包括JDK动态代理技术和Cglib动态代理 动态代理这部分我有过...
  • Spring AOP创建代理对象源码解析

    千次阅读 多人点赞 2021-03-23 00:57:15
    描述: 选择cglib、jdk代理生成代理对象。 描述: 创建AOP代理。 描述: CglibAopProxy获取代理对象。 描述: 获取到cglib增强代理对象。 5 StudentServiceImpl 代理Debug 描述: StudentServiceImpl 实现...
  • 使用动态代理生成的对象如何动态注入到spring? 如同mybatis的mapper接口自动生成代理类,亦如同spring data jpa生成接口...自己写了demo使用@Bean可以将生成的动态代理对象注入到spring容器,但无法动态的注入,求解?
  • 使用IntelliJ IDEA自动生成成员对象代理方法

    千次阅读 多人点赞 2015-11-25 11:05:12
    在《Thinking in Java 4th》7.3 代理 中 作者提到可以使用IntelliJ IDEA自动生成成员对象代理方法的代码。找了半天才找到怎么操作。 例子: package aaa; public class A { private B b; } class B { public ...
  • 创建动态代理对象bean,并动态注入到spring容器中

    万次阅读 多人点赞 2019-04-30 00:48:19
    这里mybatis就用到了JDK动态代理,并且将生成的接口代理对象动态注入到Spring容器中。 这里涉及到几个问题。也许有同学会有疑问,我们直接编写好类,加入@Component等注解不是可以注入了吗?或者在配置类(@...
  • 使用Enhance字节码增强创建动态代理对象的一般步骤为: Enhancre enhancer = new Enhancer(); enhancer.setSuperclass(目标对象的类对象); enhancer.setCallback(new MethodInterceptor(){ @Override public ...
  • jdk动态代理(底层动态生成代理类和编译过程)

    千次阅读 多人点赞 2018-09-29 21:23:43
    Java动态代理,顾名思义,动态的生成代理对象。其实难点就在这里—动态。到底怎么动态生成。代理解决的问题就是增强类。其实,实现代理的方式很多种,比如继承,子类对一个类增强,我们可以认为子类就是一个代理类,...
  • 此前一直有一个疑惑,那就是为什么CGLIB生成代理类的时候会出现三个class文件,按道理说应该只有一个。 public class GoodsService { @Test public void placeOrder() { System.out.println("place order")...
  • Advice简介 1. Before:在目标方法执行之前执行织入,如果Before的处理中没有进行特殊的处理,那么目标...2. AfterReturning: 返回之后执行(前提是目标方法执行成功),可以访问到目标对象的返回值,但是不可以改变返
  • 如果我们要研究JDK动态生成代理类,此时则需要将内存中的代理类作为.class文件保存到磁盘,再通过Java反编译工具查看其源码;这个过程可以通过设置系统属性让JRE来自动完成或者通过IO流写入到磁盘
  • 动态代理对象Proxy的三个参数

    千次阅读 2019-10-29 23:28:34
    可是自己不懂法,便找来律师作为代理为自己辩护。 接口类准备 1.打官司接口 public interface Court { //定义一个方法 要求所有参与到打官司中的角色都有 打官司的功能 int doCourt(int a); int speak(); } ...
  • 1.为什么会有这样的想法去生成接口的实现类 在学习mybatis的过程中...思考:由于我们没有写该接口的实现类,mybatis返回的对象肯定不是简单的动态代理 但是我们打印其返回对象的class发现 class com.sun.proxy.$...
  • CGLIB 动态代理

    千次阅读 2022-03-11 11:12:49
    而不能实现接口的类就不能使用JDK的动态代理,CGLIB是针对类来实现代理的,它的原理是对指定目标类生成一个子类,并覆盖其中的方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 简单结构: ...
  • Javaweb安全学习——Java动态代理

    千次阅读 2022-04-05 02:10:38
    Java的java.lang.reflect包下提供了一个Proxy类和InvocationHandler接口,可以生成JDK动态代理类或对象来完成程序无侵入式扩展(即不通过继承接口编写实现类来完成功能拓展)。 Java动态代理主要使用场景: 统计...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 321,141
精华内容 128,456
关键字:

代理对象是新生成的