• spring源码 — 三、AOP代理生成


    AOP代理生成

    AOP就是面向切面编程,主要作用就是抽取公共代码,无侵入的增强现有类的功能。从一个简单的spring AOP配置开始:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
        <!-- 定义target -->
        <bean id="human" class="org.lep.springtest.aop.Human">
        </bean>
        <!-- 定义advice -->
        <bean id="sleepHlper" class="org.lep.springtest.aop.SleepHelper">
        </bean>
    
        <!-- 定义切点 -->
        <bean id="sleepPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
            <property name="pattern" value=".*sleep"></property>
        </bean>
    
        <!-- 定义advisor -->
        <bean id="sleepAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
            <property name="advice" ref="sleepHlper"></property>
            <property name="pointcut" ref="sleepPointcut"></property>
        </bean>
    
        <!-- 定义代理 -->
        <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="target" ref="human"></property>
            <property name="interceptorNames" value="sleepAdvisor"></property>
        </bean>
    </beans>
    

    上面的配置涉及到AOP几个重要的概念:

    • pointcut:切点,定义具体什么地方需要增强
    • advice:通知,定义在切点处进行哪些增强,也就是在切点处干的事
    • advisor:通知器,将pointcut和advice结合起来,也就组成了一个切面,定义了在什么地方做什么事
    • target:需要进行增强的目标类,定义了对谁进行增强
    • proxy:将切面应用在target上,说明对谁(target)做什么事(advisor)

    从上面可以看出是proxy把这些东西结合起来,那么proxy是怎么实现的呢,比如:

    ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
    Sleep sleeper =(Sleep) context.getBean("proxy");
    sleeper.sleep();
    

    上面有两个问题:

    1. 定义的proxy是ProxyFactoryBean类型的,为什么可以强制转化为Sleep接口类型的
    2. 在调试的时候为什么 sleeper.sleep() 直接跳进了JdkDynamicAopProxy的invoke

    针对第一个问题我们分析getBean的过程,也就是ProxyFactoryBean的初始化

    ProxyFactoryBean初始化

    在spring IoC容器初始化之后我们分析了bean的初始化,最后提到了FactoryBean和BeanFactory的区别,但是没有详细分析涉及到的factoryBean的初始化过程,具体如下:

    ProxyFactoryBean初始化

    ProxyFactoryBean的初始化就是先当做普通的bean初始化,之后再获取具备factory能力的bean,这里分为代理对象的生成和获得真正的代理

    • createAopProxy
    • getProxy
      上面图中在ProxyFactoryBean的getSingletonInstance方法中
    private synchronized Object getSingletonInstance() {
        if (this.singletonInstance == null) {
            this.targetSource = freshTargetSource();
            if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
                // Rely on AOP infrastructure to tell us what interfaces to proxy.
                Class targetClass = getTargetClass();
                if (targetClass == null) {
                    throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
                }
                setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
            }
            // Initialize the shared singleton instance.
            super.setFrozen(this.freezeProxy);
            // createAopProxy里面会调用DefaultAopProxyFactory的createAopProxy方法来获取AopProxy
            // getProxy会利用上面生成的AopProxy来生成具体的代理对象
            this.singletonInstance = getProxy(createAopProxy());
        }
        return this.singletonInstance;
    }
    

    DefaultAopProxyFactory的createAopProxy如下:

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        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.");
            }
            // 如果目标类实现了接口则使用jdk生成proxy
            if (targetClass.isInterface()) {
                return new JdkDynamicAopProxy(config);
            }
            if (!cglibAvailable) {
                throw new AopConfigException(
                        "Cannot proxy target class because CGLIB2 is not available. " +
                        "Add CGLIB to the class path or specify proxy interfaces.");
            }
            // 如果没有实现接口,则使用cglib来生成proxy,因为jdk的生成方法只支持实现了接口的类的proxy生成
            return CglibProxyFactory.createCglibProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }
    

    在ProxyFactoryBean的getProxy方法中会调用上面获得到的AopProxy来生成proxy,生成的proxy返回,也就是最后getBean获得到对象,这个对象是实现了Sleep接口的类,只不过这个类存在内存里面,由JdkDynamicProxy动态生成的(也就是动态代理),所以可以向上转型为Sleep
    将动态代理类保存在本地,反编译得到:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    import org.lep.springtest.aop.Sleep;
    
    public final class $Proxy0 extends Proxy implements Sleep {
        private static Method m1;
        private static Method m0;
        private static Method m3;
        private static Method m2;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final void sleep() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
                m3 = Class.forName("org.lep.springtest.aop.Sleep").getMethod("sleep", new Class[0]);
                m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    
    

    为什么调用到JdkDynamicProxy的invoke方法

    上面已经说到了,因为是在内存中存在的动态代理,这个代理实现了Sleep接口,也就是说调试的时候应该跳转到的是这个代理对象的sleep方法,在sleep方法中调用了InvocationHandler的invoke方法,而JdkDynamicProxy实现了InvocationHandler接口,在JdkDynamicProxy的getProxy方法中

    public Object getProxy(ClassLoader classLoader) {
    		if (logger.isDebugEnabled()) {
    			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
    		}
    		Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
    		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
            // 把JdkDynamicProxy自身传入,在proxy中调用的就是JdkDynamicProxy的invoke方法
    		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    	}
    

    但是IDE不知道内存中的代理类类,所以就直接跳转到了JdkDynamicProxy的invoke方法

  • 相关阅读:
    JPA常见坑
    IDEA的快捷键使用
    java注解
    @ResponseBody注解之返回json
    mybatis坑之Integer值为null
    Java后端之SQL语句
    SSM项目配置swaggerUI
    mybatis入门案例2
    mybatis入门案例
    部署本地服务器的前端及后端
  • 原文地址:https://www.cnblogs.com/sunshine-2015/p/5990067.html
Copyright © 2020-2023  润新知