Spring两种动态代理原理分析+AOP的坑

AOP原理分析

AOP(面向切面编程)的本质就是Spring的动态代理开发,通过代理类为原始类增加额外功能。

AOP的开发方式

MethodInterceptor

MethodInterceptor方法拦截器接口---->实现类–>invoke方法(书写额外功能)

参数:MethodInvocation: 额外功能所增加给的原始方法

//object为原始方法的返回值
public Object invoke(MethodInvocation invocation)throws Throwable{
    //前置额外方法(befor)
    ----
    Object ret = invocation.proceed(); //目标(原始)方法运行
    ----
    //后置额外方法
    return ret;
}

@Aspect

@Order(0) //优先级
@Aspect //切面类
@Component
public class DataBaseAop {
    @Around("@annotation(database)") //切入点
    public Object setWrite(ProceedingJoinPoint joinPoint, Database database) throws Throwable {
            DbContextHolder.setDbType(database.databaseName());
            return joinPoint.proceed(); //原始方法运行
    }
}

AOP如何创建动态代理类动态字节码技术

JDK动态代理

代码实例:

    public void TestProxy(){
        //1.创建原始方法
        ApiOaService apiOaService = new ApiOaServiceImpl();
        //2.JDK创建动态代理
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("--------log-----------");
                //1.原始方法运行
                Object ret = method.invoke(apiOaService, args);
                //2.添加额外功能
                System.out.println("----------log----------");
                return ret;
            }
        };
        ApiOaService proxyApiOAService = (ApiOaService) Proxy.newProxyInstance(apiOaService.getClass().getClassLoader(), apiOaService.getClass().getInterfaces(), handler);
        //调用被代理的方法
        proxyApiOAService.applyOA();

    }

图解分析:

请添加图片描述

代理类和原始类实现相同接口的原因:

请添加图片描述

CGLIB动态代理

如果目标类(原始类)没有实现接口,这时候JDK的方法就无法创建动态代理。CGLIB会已继承的方式创建动态代理。

CGLIB创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证2者方法一致,同时在代理中提供新的实现。

请添加图片描述

代码实例:

    public void CglibTestProxy(){
        //1.原始对象
        WechatServiceImpl wechatService = new WechatServiceImpl();
        //2.通过cglib创建代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(wechatService.getClass().getClassLoader());
        enhancer.setSuperclass(wechatService.getClass());
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("-----------log------------");
                Object ret = method.invoke(wechatService, args); //原始方法运行
                System.out.println("-----------log------------");
                return ret;
            }
        });
        WechatServiceImpl proxy = (WechatServiceImpl) enhancer.create();
        proxy.getAccessToken();
    }

总结

- 1.JDK实现动态代理    Proxy.newProxyInstance(); //通过接口创建动态代理
- 2.CGLIB实现动态代理  Enhancer      //通过父子继承的方式创建动态代理

底层默认是JDK的方式,如果需要切换CGlib

@EnableAspectJAutoProxy(proxyTargetClass = true)

Spring工厂如何加工创建代理对象

BeanPostProcessor
AOP的坑

同一个service中,不同方法之间会有调用的情况。

举个栗子:

  1. 首先创建一个切面类

    /**
     * @ClassName: TestAop
     * @Description:
     * @author: wangz48
     * @date: 2022-1-12 14:50
     */
    @Aspect
    @Component
    public class TestAop {
        @Around("@annotation(Log)")
        public Object setLog(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("======log=======");
            Object ret = joinPoint.proceed();
            System.out.println("=======log======");
            return ret;
        }
    }
    
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    public @interface Log {
    
    }
    
    
  2. 正常情况:

    请添加图片描述

  3. 不同方法间调用的情况:

    请添加图片描述

    可以发现,a并没有调用b的代理方法,而是调用了原始方法。

如何才能获取到代理对象呢??

这里提供两种解决方案:

  • 启用类加入 @EnableAspectJAutoProxy(exposeProxy = true)

  • 从ApplicationContext获取当前Service对象

    - 1.实现 ApplicationContextAware接口
    - 2. 重写setApplicationConetext方法获取工厂
    - 3. 用工厂的getBean()方法创建对象,创建的对象几位代理对象。
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

结构化思维wz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值