java动态代理和spring aop

静态代理 VS 动态代理

静态代理
静态代理类图
静态代理的相关代码编译前就要写好.如果代理当中处理的逻辑一样, 每个被代理对象RealSubject 也需要实例化一个代理Proxy代理类,这样做会造成代理类泛滥。所以衍生了在程序运行时动态生成代理类的字节码(.class), 然后返回实例化的代理对象, 就叫动态代理.

动态代理

java动态代理生成方式分四种: jdk动态代理、cglib、javassist

jdk动态代理:首先说说jdk自带的动态代理使用方式和原理, 下面是一个使用实例.,角色和上图类图结构中命名一致.

//代理对象的抽象接口
public interface Subject {
     void dosomething();
}

//被代理对象的实现
public class RealSubject implements Subject {
    
    @Override
    public void dosomething() {
        System.out.println("i am the first implement!");
    }
}

//生成代理和使用测试

public class JdkProxyTest {

    //InvocationHandler定义
    public static class InvokerHandlerTest implements InvocationHandler {

        private Subject subject;

        public InvokerHandlerTest(Subject subject) { //具体被调用的对象
            this.subject = subject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before implement"); //代理前通用逻辑实现
            Object object = method.invoke(subject, args);
            System.out.println("end implement");//代理后通用逻辑实现
            return object;
        }
    }


    public static void main(String[] args) {
        //保存代理类字节码
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        //实例具体实现
        Subject subject = new RealSubject();
        //生成代理对象
        Subject proxy = (Subject)Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new InvokerHandlerTest(subject));
        //调用代理行为
        proxy.dosomething();
    }
}

最后输出结果:
before implement
i am the first implement!
end implement

jdk动态代理的实现原理在Proxy.newProxyInstance逻辑中.动态生成了代理类的字节码,而这个代理类实现了Subject接口,并且依赖InvocationHandler实现在其属性中. 当调用dosomething方法时便会将触发InvocationHandler的invoke方法来实现.具体可以从生成的代理类的class字节码中看出来.

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.youzan.mei.open.test.proxy.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void dosomething() throws  {    //最核心的代码, 就是调用了InvocationHandler对象的invoke方法.
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.youzan.mei.open.test.proxy.Subject").getMethod("dosomething");  // 获取到被代理对象的抽象接口所有的方法. 其它三个是object都有的
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

cglib动态代理: 与jdk生成方式不同,jdk动态代理对象是以实现被代理的抽象接口为基础.而cglib代理则是通过继承的方式来实现.

public class CglibSubject {

    public void dosomething() {
        System.out.println("i am doing something by cglib!");
    }

}


public class CglibProxyTest {

    public static void main(String[] args) {
       //保存生成的class文件
        System.getProperties().put("cglib.debugLocation", "/Users/ivy/IdeaProjects/mei-open/com/sun/proxy");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CglibSubject.class);
        enhancer.setCallback(new MethodInterceptor() { //匿名MethodInterceptor类实例
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("before method run...");
                Object result = methodProxy.invokeSuper(o, args);
                System.out.println("after method run...");
                return result;
            }
        });

        CglibSubject cglibSubject = (CglibSubject) enhancer.create();
        cglibSubject.dosomething();

    }
}

cglib是code generate library, 代码生成库.底层是通过asm操作字节码来生成代理类.
cglib依赖图
那么enhancer到底做了什么呢?

public class CglibSubject$$EnhancerByCGLIB$$32756d3e extends CglibSubject implements Factory {

生成代理类的字节码
可以看到class是extends被代理类的.是被代理类的子类.核心代码如下,dosomething中最终调用了callback实例的intercept方法.
由此可以总结: cglib动态代理,继承了被代理类,并且重写了目标方法, 通过执行MethodInterceptor的interceptor方法中的methodProxy.invokeSuper最终调用被代理类(就是代理类的超类)的目标方法. MethodInterceptor和InvocationHandler作用简直一毛一样.

在上面提到了ASM字节码操作框架, ASM和javassit都属于动态编程范畴. 直接使用ASM需要对字节码有非常熟悉的了解, 一般情况下不用. dubbo底层用的是javassist来生成.javassit就更容易上手,属于应用层,编写效率高,但是执行效率肯定比ASM低.但根据dubbo作者的性能测试来看,在动态代理方面javassist>cglib>jdk.所以dubbo底层动态代理的实现是选择javassist来实现.javassist跟cglib是属于同一级别,javaassist也是可以实现动态代理.具体可以自行搜索了解.

spring aop(aspect orient programing)的动态代理

首先介绍一下切面中的基本概念

  1. 切面 Advisor : 定义了增强的位置(Advice), 哪个方法&哪个类做增强(PointCut),
    切面接口继承
    pointcut类关系图如下, 有几个重要的匹配规则类: 正则匹配, 静态方法匹配, 名称匹配,aspectj表达式匹配.
    在这里插入图片描述
    所以很多PointcutAdvisor内部就是依赖不同类型的PointCut.其中DefaultPointcutAdvisor就是代理所有的方法所有的类.
    在这里插入图片描述

  2. 增强 Advice, 切面增强逻辑, 有前置(目标方法的前面)、后置(目标方法的前面)、环绕增强(包围目标方法), 引介增强(IntroductionInterceptor:在目标类中添加方法和属性)
    advice继承关系

简单实例:

定义Advice增强
public class BeforeGreetingAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("befor greeting, i want to do something!");
    }
}

//定义代理类
接口
public interface IGreetingService {
     void greeting();
     void greeting2();

}
实现
public class GreetingServiceImpl implements IGreetingService {

    @Override
    public void greeting() {
        System.out.println("hi ivy, how are you?");
    }

    @Override
    public void greeting2(){
        System.out.println("hi ivy2, how are you?");    }
}


测试:
IGreetingService greetingService = new GreetingServiceImpl();
BeforeGreetingAdvice beforeGreetingAdvice = new BeforeGreetingAdvice();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(greetingService);
proxyFactory.setInterfaces(greetingService.getClass().getInterfaces());
proxyFactory.addAdvice(beforeGreetingAdvice);
//和上面的等同效果, 代理所有方法 proxyFactory.addAdvisor(new DefaultPointcutAdvisor(beforeGreetingAdvice));
/*
只匹配greeting2方法
NameMatchMethodPointcutAdvisor regexpMethodPointcutAdvisor = new NameMatchMethodPointcutAdvisor(beforeGreetingAdvice);
regexpMethodPointcutAdvisor.addMethodName("greeting2");
proxyFactory.addAdvisor(regexpMethodPointcutAdvisor);
*/
IGreetingService proxy = (IGreetingService) proxyFactory.getProxy();
proxy.greeting();
proxy.greeting2();

第一种输出: 拦截所有方法做增强
befor greeting, i want to do something!
hi ivy, how are you?
befor greeting, i want to do something!
hi ivy2, how are you?
第二种输出: 拦截greeting2做增强
hi ivy, how are you?
befor greeting, i want to do something!
hi ivy2, how are you?
这种接口代理的方式使用的是jdk代理.可以从ProxyFactory的源码中得出,sOptimize属性如果是true可能会采取会采用cglib代理方式.

@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

JdkDynamicAopProxy和ObjenesisCglibAopProxy的代码可以自行去看, 两者都会通过org.springframework.aop.framework.AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice来串联和递归调用增强.

其中两者都是通过 AdvisorAdapter适配器将Advisor适配成了MethodInterceptor, PointCut提取了MethodMatcher方法, 通过递归调用去invoke Advice和代理类方法.增强chain核心代码都在:org.springframework.aop.framework.ReflectiveMethodInvocation#proceed方法中.

讲了这么多,那么spring框架中是什么时候开始为bean生成代理的呢. 其入口在AbstractAutoProxyCreator类中, 此类实现了spring BeanPostProcessor扩展点, 在bean实例化后,找到匹配的Advisor, 并创建它的代理.它源码中分了几步:

  1. 从容器中查找所有类型为 Advisor 的 bean
  2. 获取所有的bean, 解析它们的类中是否apsect注解, 如果有则构造Advisor
  3. 前面两步获取到了项目中所有的Advisor, 现在要选择匹配的Advisor
  4. 创建代理<aop:aspectj-autoproxy proxy-target-class=“true”/> 如果proxy-target-class=true则使用cglib代理,否则如果实现了接口则使用jdk代理.
  5. 最后执行的时候拦截器来实现增强org.springframework.aop.framework.ReflectiveMethodInvocation#proceed递归调用增强的逻辑.

感兴趣源码可参考: Spring AOP 源码分析系列文章导读

总结:

1.无论是jdk还是cglib代理都殊途同归, 创建的代理类, 通过注入中间属性(InvocationHandler / MethodInterceptor)来嵌入增强逻辑, 最后在增强属性中调用目标方法.
spring就是通过解析项目中所有的Advisor,找到bean适合的Advisor,构造出中间属性.最后实际调用的时候会触发中间属性实例, 实例中会包含需要调用的增强, 通过递归调用的方式来完成所有代码增强行为.
2.jdk和cglib代理各有千秋,性能比较方面cglib更胜一筹.网传cglib创建代理需要的时间更长, 但是创建的代理运行性能要比jdk强, 所有适合singleton单实例模式.但是自从jdk版本上升,对jdk代理的优化, jdk目测性能已经快赶上cglib. 但两者场景不同, jdk只能代理接口实现类, 而cglib可以通过继承目标类来代理~~~~所以没有接口的不适合用jdk, 不能继承的方法final,private也不能是cglib. dubbo底层动态代理使用的是javassist.

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值