springAOP底层原理浅析

Spring的AOP的底层实现原理

aop是ioc的一个扩展功,先有的ioc,再有的aop,只是ioc的整个流程中新增的一个扩展点而已:BeanPostProcessor。

bean的创建过程中,有一个步骤可以对bean进行扩展实现,aop本身就是一个扩展功能,所以在BeanPostProcessor的后置处理方法中来进行实现。

如何扩展实现?

1、代理对象的创建过程(advice,切面,切点)

2、通过jdk或cglib的方式生成代理对象

3、在执行方法调用的时候,会调用到生成的字节码文件中,直接会找到DynamicAdvisoredinterce类中的intercept方法,从此方法开始执行。

4、根据之前定义好的通知来生成拦截器链

5、从拦截器链中依次获取第一个通知开始进行执行,在执行过程中,为了方便找到下一个通知是哪一个,会有CgibMethodInvocation的对象,找的时候是从-1的位置依次开始查找的并且执行的。

JDK 代理

如果代理类实现了接口,Spring默认会使用JDK动态代理。JDK的动态代理是基于反射实现。JDK通过反射,生成一个代理类,这个代理类实现了原来那个类的全部接口,并对接口中定义的所有方法进行了代理。当我们通过代理对象执行原来那个类的方法时,代理类底层会通过反射机制,调用我们实现的InvocationHandler接口的invoke方法

public class TargetProxy {
    public static  <T> Object getTarget(T t) {
        return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
                        //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
                        System.out.println("执行方法前...");
                        Object invoke = method.invoke(t, args);
                        System.out.println("执行方法后...");
                        return invoke;
            }
        });
    }
}

优点:

  1. JDK动态代理是JDK原生的,不需要任何依赖即可使用
  2. 通过反射机制生成代理类的速度要比CGLib操作字节码生成代理类的速度更快
    缺点:
  3. 如果要使用JDK动态代理,被代理的类必须实现了接口,否则无法代理(InvocationHandler)
  4. JDK动态代理无法为没有在接口中定义的方法实现代理
  5. JDK动态代理执行代理方法时,需要通过反射机制进行回调,此时方法执行的效率比较低

cglib 代理

若需要代理的类没有实现接口,JDK的动态代理就无法使用,Spring会使用CGLiB动态代理来生成代理对象。CGLiB直接操作字节码,生成类的子类,重写类的方法完成代理。

public class TargetProxy {
 
    public static <T> Object getProxy(T t) {
        Enhancer en = new Enhancer(); //帮我们生成代理对象
        en.setSuperclass(t.getClass());//设置要代理的目标类
        en.setCallback(new MethodInterceptor() {//代理要做什么
            @Override
            public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("执行方法前。。。");
                //调用原有方法  
                Object invoke = methodProxy.invokeSuper(object, args);
                // Object invoke = method.invoke(t, args);// 作用等同与上面。
                System.out.println("执行方法后。。。");
                return invoke;
            }
        });
        return en.create();
    }
}

优点:

  1. 使用CGLiB代理的类,不需要实现接口,因为CGLib生成的代理类是直接继承自需要被代理的类
  2. 因为CGLiB实现方式是重写父类的方法,所以对final方法,或者private方法是没有办法代理的
  3. CGLiB是通过修改字节码生成的代理类,所以CGLib执行代理方法的效率要高于JDK的动态代理
    缺点:
  4. 因为CGLiB实现方式是重写父类的方法,所以对final方法,或者private方法是没有办法代理的
  5. 因为CGLiB生成代理类的方式是通过操作字节码(asm工具包),这种生成的代理类的方式比JDK通过反射生成代理类的方式的效率低

AOP的简单实现

此处使用的CGLIB代理的方式,配置文件中分别给被代理类和增强方法(拦截器) 创建bean,然后创建ProxyFactoryBean对象

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
 
    <bean id="target" class="com.heaboy.aopdemo.aop.Target"/>
 
    <bean id="targetAdvice" class="com.heaboy.aopdemo.aop.TargetAdvice"/>
 
    <bean id="targetProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="target"/> <!--被代理的类-->
        <property name="interceptorNames" value="targetAdvice"/>  <!--如果用多种增强方式,value的值使用逗号(,)分割-->
        <property name="proxyTargetClass" value="true"/> <!--强制使用CGLIB代理方式-->
        <property name="interfaces" value="com.heaboy.aopdemo.aop.TargetInteface"/>  <!--target实现的接口-->
    </bean>
</beans>

增强类(拦截器)

public class TargetAdvice implements MethodInterceptor, MethodBeforeAdvice, AfterReturningAdvice {
 
    /*
    * 通知/增强
    **/
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("前置环绕通知");
        Object proceed = methodInvocation.proceed();
        System.out.println("后置环绕通知");
        return proceed;
    }
 
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("后置返回通知");
    }
 
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("前置通知");
    }
}
/**
 * 测试
 */
public class AopTest {
    public static void main(String[] args) {
        ApplicationContext appCtx = new ClassPathXmlApplicationContext("spring-aop.xml");
        Target targetProxy = (Target) appCtx.getBean("targetProxy");
        targetProxy.method1();
    }
}

利用ClassPathXmlApplicationContext实现类加载类路径下的配置文件创建ApplicationContext 对象appCtx
再从appCtx获取生成的targetProxy代理类强转为Target类对象,执行其方法在这里插入图片描述

  • 41
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值