美文网首页
从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调用父类,因此子类调用子类自己的方法时也能进行增强,进而可以代理

    相关文章

      网友评论

          本文标题:从AOP到代理

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