美文网首页
从AOP到代理

从AOP到代理

作者: 玖柒叁 | 来源:发表于2023-09-09 17:19 被阅读0次

spring的AOP是如何实现的

什么是AOP

面向切面编程,能够让我们在不影响系统原有功能的前提下,增加横向扩展。比如增加日志、鉴权、迁移时接口转发等。

AOP的实现

引入依赖

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
      <version>3.0.4</version>
</dependency>

spring配置

package edu.wyn.spring;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@EnableAspectJAutoProxy
@Configuration
@ComponentScan(value = "edu.wyn.spring")
public class AppConfig {

}

自定义切面注解

package edu.wyn.spring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//指定在方法上
@Target(ElementType.METHOD)
//运行时
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {

}

自定义切面类

package edu.wyn.spring;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
public class MyLogAspect {

    @Pointcut("@annotation(edu.wyn.spring.MyLog)")
    public void doLog(){}

    @Before("doLog()")
    public void before(JoinPoint joinPoint) {
        System.out.println("MyLogAspect before:" + Arrays.toString(joinPoint.getArgs()));
    }

    @AfterReturning(value = "doLog()", returning = "returnObj")
    public void doReturn(JoinPoint joinPoint, Object returnObj) {
        System.out.println("MyLogAspect return:" + returnObj.toString());
    }
}

使用的bean

package edu.wyn.spring;

import org.springframework.stereotype.Service;

@Service
public class OrderService {

    public String getOrderId(String name) {
        return name+System.currentTimeMillis();
    }
}

package edu.wyn.spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private OrderService orderService;

    @MyLog
    public String getOrderInfo(String userName) {
        return orderService.getOrderId(userName);
    }
}

测试类

package edu.wyn.spring;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = (UserService)context.getBean("userService");
        userService.getOrderInfo("test");
    }
}

测试结果

image.png

AOP的原理

image.png
由上图可见,我们获取到的UserService对象其实是通过CGLIB生成的代理对象,也就是AOP是通过代理来实现的,而AOP在何时生成代理对象可见spring的三级缓存

如何实现代理

代理的优势

通过代理可以处理被代理对象的非核心业务,让被代理对象专注自己的业务处理,代理模式可以在不影响被代理对象的前提下增强被代理对象的能力。

静态代理

代理类和目标类都实现同一个接口,并且代理类中包含目标类的对象,代理类在真正执行对应方法的时候调用了目标类的方法。
问题:每一个目标类都需要实现一个代理类,如果目标类过多则会出现代理类的冗余

动态代理

cglib

目标类、代理类的处理类需要编写

引入依赖

<dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.3.0</version>
    </dependency>

目标类实现

package edu.wyn.proxy.cglibproxy;

public class Cat {

    public String getName(int no) {
        return String.valueOf(no);
    }
}

代理类的处理类

package edu.wyn.proxy.cglibproxy;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CatProxy implements MethodInterceptor {

    private Object target;

    public CatProxy(Object obj) {
        this.target = obj;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理方法执行前:" + method.getName() + ",参数:" + String.valueOf(params));
        //调用目标类
        //Object result = methodProxy.invoke(o, params);
        //调用子类
        Object result = methodProxy.invokeSuper(o, params);
        System.out.println("代理方法执行后:" + method.getName() + ",参数:" + String.valueOf(params));
        return result;
    }
}

测试代码

package edu.wyn.proxy.cglibproxy;

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyDemo{

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //设置需要代理的目标类
        enhancer.setSuperclass(Cat.class);
        //拦截对象,回调的实现类
        enhancer.setCallback(new CatProxy(new Cat()));
        Cat cat = (Cat) enhancer.create();
        System.out.println(cat.getName(12));
    }
}

总结

1、通过ASM第三方框架生成3个代理类,其中两个是为了能快速定位到具体哪个方法,一个是invokesuper使用,一个是invoke使用
2、无需实现接口,使用的是继承方式,因此无法代理final修饰的类和方法
3、调用目标类是子类调用父类的形式
4、通过invokesuper调用目标类时,在目标类调用本类方法会再一次代理

jdk

接口、目标类、代理类的处理类需要开发编写,代理类则动态生成

接口实现

package edu.wyn.proxy.jdkproxy;

public interface IAnimal {

    String getAge(String name);
}

目标类实现

package edu.wyn.proxy.jdkproxy;

public class UserInfo implements IAnimal {

    @Override
    public String getAge(String name) {
        return "25";
    }
}

代理类的处理类

package edu.wyn.proxy.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class AnimalProxy implements InvocationHandler {

    private Object obj;

    public AnimalProxy(Object obj) {
        this.obj = obj;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理方法执行前:" + method.getName() + ",参数:" + String.valueOf(args));
        Object result = method.invoke(obj, args);
        System.out.println("代理方法执行后:" + method.getName() + ",参数:" + String.valueOf(args));
        return result;
    }
}

测试代码

package edu.wyn.proxy.jdkproxy;

import java.lang.reflect.Proxy;

public class JdkProxyDemo {

    public static void main(String[] args) {
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        System.out.println("start...");
        IAnimal proxy = (IAnimal)Proxy.newProxyInstance(
                IAnimal.class.getClassLoader(),
                new Class[]{IAnimal.class},
                new AnimalProxy(new UserInfo()));
        proxy.getAge("abs");
    }
}

总结

1、动态生成一个代理类
2、通过实现接口生成代理类,因此目标类必须实现接口
3、调用目标类的时候通过反射调用,所以也是jdk的反射找到了具体哪个方法(1.8jdk优化后效率提升了)
4、目标类调用本类方法只会代理一次

为什么jdk一定要用接口

代理类是实现了接口的方式来进行对象代理,之后通过反射调用目标类,如果不实现对应的接口则无法进行透明调用

为什么@Transactional不能在同一个类中调用被代理的方法

通过jdk进行动态代理的时候,在类中调用本类其他方法时调用的其实是原始类的方法,而不是代理类的方法,而原始方法没有包含事务处理,事务自然就失效了。
通过cglib进行动态代理的时候,如果用invokeSuper因为cglib在实现代理类的时候是继承了父类,调用代理类方法的时候子类进行增强然后super调用父类,因此子类调用子类自己的方法时也能进行增强,进而可以代理

相关文章

  • spring aop 汇总

    静态代理、动态代理和cglib代理 aop 使用 Spring AOP - 注解方式使用介绍spring aop ...

  • Spring学习系列--3.AOP

    Spring Aop Aop面向切面编程 Aop入门动态代理 动态代理,其实与Aop的原理有些相似,可以用动态代理...

  • AOP代理:

    AOP代理:AOP框架创建的对象,代理就是对目标对象的增强。Spring中的AOP代理可以是JDK动态代理,也可以...

  • Spring之代理模式

    九、代理模式 目录:静态代理、动态代理AOP的底层机制就是动态代理。代理模式分为静态代理和动态代理。接触aop之前...

  • Spring AOP实现

    使用SpringBoot实现AOP动态代理 1 使用CGLIB实现AOP动态代理 .properties .xml

  • Spring AOP 学习笔记(1) ---- 代理模式

    参考文章 spring aop 官方文档 掘金spring aop 教程 掘金动态代理 代理模式分类 根据代理类的...

  • Spring AOP 实现原理

    Spring AOP 实现原理 静态代理 众所周知 Spring 的 AOP 是基于动态代理实现的,谈到动态代理就...

  • 2018-09-16

    AOP的XML配置: AOP的先关术语: Spring底层的AOP实现原理 动态代理: JDK动态代理:只能对实现...

  • 第三章:Spring AOP

    什么是AOP AOP——面向切面编程。是面向对象编程(OOP)的补充。 AOP术语 JDK动态代理 动态代理对象必...

  • Spring AOP从原理到源码(三)

    接着上一节Spring AOP从原理到源码(二),本节关注spring aop创建代理对象的过程。 Spring ...

网友评论

      本文标题:从AOP到代理

      本文链接:https://www.haomeiwen.com/subject/eyvdvdtx.html