Spring之AOP实现

文章详细介绍了AOP的三种实现方式:AspectJ编译器增强、代理方式(JDK和CGLIB)以及Spring中的@Aspect和Advisor低级切面。JDK代理只能针对接口,而CGLIB基于继承。Spring的@Aspect切面可以包含多个通知,而Advisor则更底层,只包含一个通知和切点。
摘要由CSDN通过智能技术生成

1. AOP的实现方式

  1. 使用AspectJ的编译器来改动class类文件实现增强(使用不广泛) ----- 编译阶段

    这种对class类文件增强的, 也可以增强static静态方法, 而通过代理方式就无法实现静态方法的增强

    可通过查看编译后class文件反编译后的java代码验证

  2. agent增强(使用不广泛) ----- 类加载阶段

这种方式的增强也会使得代理类在内部自己调用自己的方法也获得增强, 原因是这种方式是通过类加载阶段进行的增强, 在通过this.xxFunc()调用自己本类的方法也是触发代理类的方法。

可通过arthas等工具查看jvm内存中增强后的类的反编译后的java代码验证

  1. Proxy代理方式实现AOP(绝大多数的选择)
  • JDK 代理 (只能代理实现了接口的类, 而且也只能代理那些接口方法, 代理类与目标类同级关系)
  • CGLIB 代理 (能代理类和接口, 但该类或代理的方法不能使用final修饰, 因为是基于继承进行增强的, 代理类与目标类是父 / 子 关系)

代理创建的两个时机:

  1. 初始化之后
  2. 实例创建之后, 依赖注入之前(有循环依赖时), 并暂存于二级缓存 earlySingletonObjects

JDK实现的代理:

public class JdkProxyDemo {
    interface Foo{
        void foo();
    }
    // 将被用作jdk代理的实例类
    static class Target implements Foo{

        @Override
        public void foo() {
            System.out.println("target foo");
        }
        // 这个方法无法被代理,因为不是来自接口的方法
        public void test(){
            System.out.println("test");
        }
    }

    // jdk代理 只能针对接口代理
    // cglib代理
    public static void main(String[] args) {
        // 目标对象
        Target target = new Target();
        ClassLoader classLoader = JdkProxyDemo.class.getClassLoader();//用来加载在运行期间动态生成的字节码
        Foo proxyInstance = (Foo) Proxy.newProxyInstance(classLoader, new Class[]{Foo.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("before...");
                // 目标.方法(参数)
                // method.invoke(proxy, args); 错误写法, 这里的proxy代表代理类自身, 传入proxy会陷入无限循环
                Object result = method.invoke(target, args);
                System.out.println("after...");
                return result;
            }
        });
        proxyInstance.foo();
    }
}

Cglib实现的代理:

注意: 在MethodInterceptor实现的intercept()方法中,传入了一个参数methodProxy, 通过该对象调用methodProxy.invoke(target, args)也能执行目标对象, 但其内部不是通过反射的方式。

public class CglibProxyDemo {
    static class Target{
        public void foo(){
            System.out.println("target foo");
        }
    }

    public static void main(String[] args){
        Target target = new Target();
        Target proxyInstance = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("before...");
                Object result = method.invoke(target, args); //Spring使用的方式
                //Object result = methodProxy.invoke(target, args); 非反射方式调用, 而且可以不用目标就调用
                System.out.println("after...");
                return result;
            }
        });
        proxyInstance.foo();
    }
}

2.Spring中的两种切面

@Aspect: 高级切面, 可以包含多组通知和切面

Advisor: 低级切面, 适合框架内部使用, 只有一个通知和一个切面

Spring在处理这两个切面时, @Aspect切面最终还是转为Advisor

两种方式使用示例:

public class AspectDemo {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("aspect1", Aspect1.class);
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
    }

    static class Target1{
        public void foo(){
            System.out.println("target1 foo");
        }
    }

    static class Target2{
        public void bar(){
            System.out.println("target bar");
        }
    }
    //高级切面,Aspect
    @Aspect
    static class Aspect1{
        @Before("execution(* foo())")
        public void before(){
            System.out.println("aspect1 before...");
        }
        @After("execution(* foo())")
        public void after(){
            System.out.println("aspect1 after...");
        }
    }

    //低级切面, 由一个切点(AspectJExpressionPointcut)和一个通知(MethodInterceptor)组成
    @Configuration
    static class Config{
        @Bean
        public Advisor advisor3(MethodInterceptor advice3){
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");
            return new DefaultPointcutAdvisor(pointcut , advice3);
        }

        @Bean
        public MethodInterceptor advice3(){
            return new MethodInterceptor() {
                @Override
                public Object invoke(MethodInvocation methodInvocation) throws Throwable {
                    System.out.println("advice3 before...");
                    Object result = methodInvocation.proceed();
                    System.out.println("advice3 after...");
                    return result;
                }
            };
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

浔汐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值