精华内容
下载资源
问答
  • 代理对象
    千次阅读 多人点赞
    2020-09-13 16:03:12

    代理对象

    代理

    是一种设计模式,他提供了对目标对象的一种访问方式(通过代理对象去访问目标对象)

    使用的编程思想

    不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法

    最基本的例子

    如果你要邀请一个明星来参加活动,你要联系明星的经纪人而不是联系明星本人来达到同样的目的,明星是咱们的目标对象,他是要来参加活动中的节目,剩下的其他事情都是他的经纪人来处理

    代理模式的关键点是

    代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象

    静态代理

    静态代理在使用时,需要定义接口或者父类,目标对象与代理对象一起实现相同的接口或者是继承相同父类.

    好处:

    在目标对象的基础上增加额外的功能操作。

    缺点:

    因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.

    public interface IUserDao {
        public void save();
    }
    
    /**
     *
     * 接口实现
     * 目标对象
     *
     */
    public class UserDao implements IUserDao {
    
        @Override
        public void save() {
            System.out.println("明星参加活动做节目");
        }
    }
    
    
    /**
     *
     * 实现接口
     * 代理对象,静态代理
     *
     */
    public class UserDaoProxy  implements  IUserDao{
    
        private IUserDao target;
    
        public UserDaoProxy(IUserDao target) {
            this.target = target;
        }
    
        @Override
        public void save() {
            System.out.println("活动之前经纪人处理琐碎的事情");
            target.save();
            System.out.println("活动之后经纪人处理琐碎的事情");
        }
    }
    
    
    public class ProxyTest {
        public static void main(String[] args) {
            //目标对象
            UserDao target = new UserDao();
    
            //代理对象
            UserDaoProxy userDaoProxy = new UserDaoProxy(target);
    
            //执行的是代理的方法
            userDaoProxy.save();
    
        }
    }
    
    

    运行结果

    在这里插入图片描述

    动态代理

    特点:
    1. 代理对象,不需要实现接口
    2. 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
    3. 动态代理也叫做:JDK代理,接口代理

    JDK中生成代理对象的API
    代理类所在包:java.lang.reflect.Proxy
    JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
    /*
    注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
    
    ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
    Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
    InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
    */
    

    注意

    代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理

    
    public interface IUserDao {
        public void save();
    }
    
    
    
    /**
     *
     * 接口实现
     * 目标对象
     *
     */
    public class UserDao implements IUserDao {
    
        @Override
        public void save() {
            System.out.println("明星参加活动做节目");
        }
    }
    
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyFactory {
        //维护一个目标对象
        private Object target;
        public ProxyFactory(Object target){
            this.target=target;
        }
    
        //给目标对象生成代理对象
        public Object getProxyInstance(){
            return Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("活动之前经纪人处理琐碎的事情");
                            //执行目标对象方法
                            Object returnValue = method.invoke(target, args);
                            System.out.println("活动结束后经纪人处理琐碎的事情");
                            return returnValue;
                        }
                    }
            );
        }
    }
    
    public class DHCPProxyTest {
    public static void main(String[] args) {
            // 目标对象7
            IUserDao target = new UserDao();
            //原始的类型 class com.rsw.DHCPproxy.dao.UserDaoo
            System.out.println(target.getClass());
            // 给目标对象,创建代理对象
            IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
            // class com.sun.proxy.$Proxy0   内存中动态生成的代理对象
            System.out.println(proxy.getClass());
            // 执行方法执行的是代理对象的方法!
            proxy.save();
            }
    
    }
    
    

    运行结果

    在这里插入图片描述
    代码打包

    更多相关内容
  • Spring AOP创建代理对象源码解析

    千次阅读 多人点赞 2021-03-23 00:57:15
    引言 概述: AOP系列文章: 【1】Spring Aop初始化源码分析 【2】Spring AOP创建代理对象源码解析 【3】Spring AOP 链式调用过程源码解析 1 工程简介 1.1 pom <properties> <project.build.sourceEncoding>UTF-8...

    引言

    概述: AOP系列文章:
    【1】Spring Aop初始化源码分析
    【2】Spring AOP创建代理对象源码解析
    【3】Spring AOP 链式调用过程源码解析
    【4】Spring 事务执行过程源码解析

    1 工程简介

    在这里插入图片描述

    1.1 pom

      <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <spring.version>5.2.8.RELEASE</spring.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.20</version>
            </dependency>
            <!-- 日志相关依赖 -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.10</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>1.1.2</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>1.1.2</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>${spring.version}</version>
            </dependency>
        </dependencies>
    
    

    1.2 AOP配置文件

    
    @Configuration
    @EnableAspectJAutoProxy
    @Import({ServiceAopConfig.class, DaoAopConfig.class})
    public class AopConfig {
    
    
    }
    
    @Configuration
    @Aspect
    public class DaoAopConfig {
    
        /**
         * 声明切点
         */
        @Pointcut("execution(* com.rosh.dao.*.*(..))")
        public void pointCut() {
    
        }
    
        @Around("pointCut()")
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("DaoAopConfig invoke around start");
            Object proceed = proceedingJoinPoint.proceed();
            System.out.println("DaoAopConfig invoke around end");
            return proceed;
        }
    }
    
    
    
    @Configuration
    @Aspect
    public class ServiceAopConfig {
    
        /**
         * 声明切点
         */
        @Pointcut("execution(* com.rosh.service.*.*(..))")
        public void pointCut() {
    
        }
    
        @Around("pointCut()")
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("ServiceAopConfig invoke around start");
            Object proceed = proceedingJoinPoint.proceed();
            System.out.println("ServiceAopConfig invoke around end");
            return proceed;
        }
    
        @Before("pointCut()")
        public void before(){
            System.out.println("ServiceAopConfig invoke before");
        }
    
        @After("pointCut()")
        public void after() {
            System.out.println("ServiceAopConfig invoke after");
        }
    }
    
    
    

    1.3 StudentDao

    @Repository
    public class StudentDao {
    
        public void addStudent() {
    
            System.out.println("StudentDao invoke addStudent");
    
        }
    
    }
    
    

    1.4 Service

    public interface StudentService {
    
         void addStudent();
    
    }
    
    
    @Service
    public class StudentServiceImpl implements StudentService {
    
        @Override
        public void addStudent() {
            System.out.println("StudentServiceImpl invoke addStudent()");
        }
    }
    
    
    

    1.5 RoshTest

    public class RoshTest {
    
        @Test
        public void mainTest(){
    
            AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext("com.rosh");
            StudentService studentService = (StudentService) applicationContext.getBean("studentServiceImpl");
            studentService.addStudent();
    
            System.out.println("===============分割线================");
    
            StudentDao studentDao=(StudentDao) applicationContext.getBean("studentDao");
            studentDao.addStudent();
    
    
        }
    
    }
    

    在这里插入图片描述

    2 AbstractAutoProxyCreator概述

    描述: 该类作用在,Spring Aop初始化源码分析中具体分析过,核心作用是,在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.
    		/**
    		 * 【1】获取当前bean有效的Advisor,Advisor = advice + pointCut
    		 */
    		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    
    		if (specificInterceptors != DO_NOT_PROXY) {
    			this.advisedBeans.put(cacheKey, Boolean.TRUE);
    
    			/**
    			 * 【2】创建代理对象
    			 */
    			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;
    	}
    

    3 创建代理对象源码解析

    3.1 createProxy

    描述: 主流程调用方法AbstractAutoProxyCreator类createProxy 方法。

    protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
    			@Nullable Object[] specificInterceptors, TargetSource targetSource) {
    
    		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
    			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    		}
    
    		/**
    		 *  【1】创建代理工厂,赋值。
    		 */
    		ProxyFactory proxyFactory = new ProxyFactory();
    		proxyFactory.copyFrom(this);
    
    		/**
    		 * 【2】 proxyTargetClass为true时代表使用cglib,为false使用jdk,默认值是false。
    		 *  判断当前被代理对象是使用jdk增强还是使用cglib增强
    		 *
    		 */
    		if (!proxyFactory.isProxyTargetClass()) {
    			//根据最开始@EnableAspectJAutoProxy注解中的proxyTargetClass参数判断是否应该使用cglib代理
    			if (shouldProxyTargetClass(beanClass, beanName)) {
    				proxyFactory.setProxyTargetClass(true);
    			}
    			else {
    				//如果是接口增强那么使用jdk,否则使用cglib
    				evaluateProxyInterfaces(beanClass, proxyFactory);
    			}
    		}
    		//构建advisor
    		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    		//把advisor加入到代理工厂中
    		proxyFactory.addAdvisors(advisors);
    		//把targetSource对象加入代理工厂(实际对象、被代理对象)
    		proxyFactory.setTargetSource(targetSource);
    		customizeProxyFactory(proxyFactory);
    
    		proxyFactory.setFrozen(this.freezeProxy);
    		if (advisorsPreFiltered()) {
    			proxyFactory.setPreFiltered(true);
    		}
    
    		/**
    		 * 【3】 获取代理对象
    		 */
    		return proxyFactory.getProxy(getProxyClassLoader());
    	}
    

    3.2 ProxyFactory 获取代理对象

    描述: 先获取AOP代理

    	public Object getProxy(@Nullable ClassLoader classLoader) {
    		/**
    		 *  创建AOP代理(JDK、CGLIB)并且获得代理对象
    		 */
    		return createAopProxy().getProxy(classLoader);
    	}
    
    protected final synchronized AopProxy createAopProxy() {
    		if (!this.active) {
    			activate();
    		}
    		return getAopProxyFactory().createAopProxy(this);
    	}
    
    
    @Override
    	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
    
    			// 如果targetClass是接口类,使用JDK来生成Proxy
    			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() || Proxy.isProxyClass(targetClass)) {
    				return new JdkDynamicAopProxy(config);
    			}
    
    			// 如果不是接口类,使用CGLIB生成Proxy
    			return new ObjenesisCglibAopProxy(config);
    		}
    		else {
    			return new JdkDynamicAopProxy(config);
    		}
    	}
    

    3.3 根据AOP不同的代理获取代理对象

    在这里插入图片描述
    在这里插入图片描述

    4 StudentDao 代理Debug

    描述: 每个代理对象都对应一个代理工厂,工厂是new 创建的。
    在这里插入图片描述
    描述: 选择cglib、jdk代理生成代理对象。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    描述: 创建AOP代理。
    在这里插入图片描述
    描述: CglibAopProxy获取代理对象。
    在这里插入图片描述
    描述: 获取到cglib增强代理对象。
    在这里插入图片描述

    5 StudentServiceImpl 代理Debug

    描述: StudentServiceImpl 实现StudentService接口使用jdk代理。
    在这里插入图片描述
    描述: AOP获取JDK代理。
    在这里插入图片描述
    描述: 获取代理对象。
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 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

    展开全文
  • 由于类中存在加了事务注解的方法,于是最终注入的UserRightService成员是通过事务切面生产的代理对象。 2.但此时仅仅是给自身生成员变量赋值,外层UserRightService类实例化过程还未结束。 那么最终实例化完成后,...
  • CGLib代理对象解释

    千次阅读 2018-10-23 19:15:02
    我们通常通过代理来实现aop,spring中常常用JDK代理,因为JDK代理要实现接口,灵活性相较于CGLib较低,本文讲解一下cglib代理对象如何实现aop。 首先aop是在原对象调用方法时,在调用前后加一些业务,比如打印日志...
  • Java的代理对象实例

    千次阅读 2018-05-05 13:57:02
    Java的代理对象,可以通过代理来操作一个正真的实例对象,通过代理模式来重写那些我们需要增强的原对象的方法,下面是一个关于代理的简单实例:1.定义一个接口类public interface MyBusiness { public void ...
  • Java中代理对象

    千次阅读 2018-10-11 21:47:09
    静态代理通常用于对原有业务逻辑的扩充 ,通过对真实对象的封装,来实现扩展性。 三要素: 共同接口 public interface Action{ void doSomething(); } 真实对象 public class RealObject implements ...
  • 问题描述:: 我现在遇到个棘手的问题,要通过spring托管的service类保存对象,这个类是通过反射拿到的,经过实验发现这个类只能反射取得sservice实现了接口的方法,而extends类的方法一律不出现,debug后发现这个servie...
  • 创建动态代理对象bean,并动态注入到spring容器中

    万次阅读 多人点赞 2019-04-30 00:48:19
    这里mybatis就用到了JDK动态代理,并且将生成的接口代理对象动态注入到Spring容器中。 这里涉及到几个问题。也许有同学会有疑问,我们直接编写好类,加入@Component等注解不是可以注入了吗?或者在配置类(@...
  • Spring中的aop(生成代理对象

    千次阅读 2019-04-27 14:04:50
    Spring能够为容器中管理的对象创建代理对象。 以前我们创建动态代理对象要调用该方法: proxy.newProxyInstance(xx,xx,xx)。 Spring中是使用动态代理和cglib代理混合使用,优先使用动态代理,如果不能使用动态...
  • 如何产生代理对象 Object proxy = Proxy.newProxyInstance(classloader,interfaces,InvocationHandler); classloader —作用是加载jdk帮我们写出来的代理类的字节码 interfaces — 目标类的接口数组 Inv...
  • 文章目录第6章-AOP的后置处理器和代理对象的创建测试类MainConfigAopOpenConfigLogAspectHelloServiceAnnotationMainTest如何分析Spring的一个新功能分析@EnableXXX找到负责AOP功能的后置处理器@...
  • 从Spring代理的bean中获取代理对象

    千次阅读 2019-09-30 15:50:07
    public class SpringTargetBeanUtils { /** * 获取 目标对象 * * @param proxy 代理对象 * @return * @throws Exception */ public static Object getTarget(Object proxy) throws Exc...
  • AOP(Aspect-Oriented Programming)面向切面或者面向方面编程的简称,将一些分散在对象或者类中的与业务逻辑无关的代码分离出来进行独立的管理编码,例如日志,事务处理,异常等。几个相关的重要概念: 1、方面/切面...
  • Spring AOP的代理对象和被代理对象是什么意思啊 分别指的是什么啊
  • 大家好,今天我会用一个例子来讲解Spring创建bean代理对象的过程,为大家揭开Spring AOP的神秘面纱。在看这篇博客前我强烈建议读者先看下这两篇博客《Spring3.1.0实现原理分析(六).实例化》,《Spring3.1.0实现原理...
  • 后来发现因为对象是从Spring容器中获取的,为代理对象,所以拿不到真实实例,于是在网上参考到别人写的代码。 问题初解决 参考别人如下的工具代码,问题得到了解决,成功拿到了实例对象。 package ...
  • 动态代理对象Proxy的三个参数

    千次阅读 2019-10-29 23:28:34
    可是自己不懂法,便找来律师作为代理为自己辩护。 接口类准备 1.打官司接口 public interface Court { //定义一个方法 要求所有参与到打官司中的角色都有 打官司的功能 int doCourt(int a); int speak(); } ...
  • * 进行创建一个动态代理对象,然后注册到spring容器中,客户端通过注解引用这个代理对象进行一系列我们封装的操作,如网络io等。 * @author garine * @date 2018年07月10日 **/ @Data public class ...
  • Spring获取当前类的代理对象

    千次阅读 2018-11-12 09:59:32
    摘要:在项目中,涉及到同一个类...答案是:通过spring aop类里面的AopContext类获取当前类的代理对象, 这样就能切换对应的事务管理器了,具体做法如下: (1).在applicationContext.xml文件中配置如下: [html...
  • 昨天晚上一哥们需要获取代理对象的目标对象,查找了文档发现没有相应的工具类,因此自己写了一个分享给大家。能获取JDK动态代理/CGLIB代理对象代理的目标对象。 问题描述 我现在遇到个棘手的问题,要通过spring...
  • 代理对象中获取原始对象

    千次阅读 2018-05-02 23:00:46
    使用JDK动态代理时,有时需要获取原始对象,那怎么通过代理对象获取原始对象呢?接口public interface Person { void doWork(); }实现类public class PersonImpl implements Person { public void doWork() { ...
  • java动态创建代理对象

    千次阅读 2019-07-15 23:53:29
    》》》》》》代理模式的字面意思,就是代理一个类,即被代理对象,让代理对象可 以有代理对象的功能或同时能够加强这个功能的,当然他还有其他作用 package ceom.proxy.test; //Proxy,相当于工具类,帮...
  • 使用Enhance字节码增强创建动态代理对象的一般步骤为: Enhancre enhancer = new Enhancer(); enhancer.setSuperclass(目标对象的类对象); enhancer.setCallback(new MethodInterceptor(){ @Override public ...
  • :// @EnableAspectJAutoProxy(exposeProxy = true)//基于 AopContext 暴露代理对象 获取代理对象: // @Autowired // public static JestClient jestClientProxy=(JestClient) AopContext.currentProxy();// new ...
  • //返回AOP代理对象,也就是com.sun.proxy.$Proxy18 Object getTarget(); //返回目标对象,一般我们都需要它或者(也就是定义方法的接口或类,为什么会是接口呢?这主要是在目标对象本身是动态代理的情况下,例如...
  • 一直以为spring的ioc容器生成的对象都是代理对象,其实这个是错误的。spring ioc默认的都是原生对象 只有通过aop增强的对象才是代理对象有@Transactional 注解或者配置文件&lt;aop:config&gt; &lt;...
  • 这个类的作用就是作为一个委托代理对象,将消息转发给一个真实的对象或者自己加载的对象。 当然,在大部分情况下,使用NSObject类也可以实现消息转发,实现方式与NSProxy类相同。但是,大部分情况下使用NSProxy类...
  • ProxyFactoryBean创建代理对象ProxyFactoryBean实现了Spring的FactoryBean接口,所以它跟Spring中的其它FactoryBean一样,都是基于工厂模式来获取一个bean的。ProxyFactoryBean就是用来获取一个对象的代理对象的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 701,042
精华内容 280,416
关键字:

代理对象