静态代理 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操作字节码来生成代理类.
那么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)的动态代理
首先介绍一下切面中的基本概念
-
切面 Advisor : 定义了增强的位置(Advice), 哪个方法&哪个类做增强(PointCut),
pointcut类关系图如下, 有几个重要的匹配规则类: 正则匹配, 静态方法匹配, 名称匹配,aspectj表达式匹配.
所以很多PointcutAdvisor内部就是依赖不同类型的PointCut.其中DefaultPointcutAdvisor就是代理所有的方法所有的类.
-
增强 Advice, 切面增强逻辑, 有前置(目标方法的前面)、后置(目标方法的前面)、环绕增强(包围目标方法), 引介增强(IntroductionInterceptor:在目标类中添加方法和属性)
简单实例:
定义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, 并创建它的代理.它源码中分了几步:
- 从容器中查找所有类型为 Advisor 的 bean
- 获取所有的bean, 解析它们的类中是否apsect注解, 如果有则构造Advisor
- 前面两步获取到了项目中所有的Advisor, 现在要选择匹配的Advisor
- 创建代理<aop:aspectj-autoproxy proxy-target-class=“true”/> 如果proxy-target-class=true则使用cglib代理,否则如果实现了接口则使用jdk代理.
- 最后执行的时候拦截器来实现增强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.