1. 引入:
Q :为什么Spring项目里推荐写 Interface Service 然后再实现 Impl ?
A :解耦,测试方便,面向接口编程,代理方式不同(代理细节)。Spring @Autowired 的对象是通过接口的话会使用JDK动态代理,而JDK动态代理只能对实现了接口的类生成代理,而不能针对类来生成代理;而cglib是针对类来实现代理。
对于这个问题我们可能都知道,jdk代理与cglib代理的区别,但是对其底层的实现却知之甚少。
2. JDK代理与源码实现
2.1 JDK代理实现
-
编写代理接口
public interface PrintService { void print(String message); }
-
编写接口实现类
public class PrintServiceImpl implements PrintService { @Override public void print(String message) { System.out.println("message : "+message); } }
-
编写Proxy的调用处理程序 InvocationHandler
public class MyInvocationHandler implements InvocationHandler { private Object proxyTarget;//要代理的真实对象 public MyInvocationHandler(Object proxyTarget) { this.proxyTarget = proxyTarget; } //为PrintService 生成代理对象 public Object getProxy(){ return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), proxyTarget.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before proxy invoke ================"+method.getName()); Object invoke = method.invoke(proxyTarget, args); System.out.println("after proxy invoke ================="+method.getName()); return invoke; } }
-
编写测试代码
public static void main(String[] args) { PrintServiceImpl printService = new PrintServiceImpl(); MyInvocationHandler invokeHandler = new MyInvocationHandler(printService); PrintService proxy =(PrintService) invokeHandler.getProxy(); System.out.println("proxy class : "+proxy.getClass().getName()); proxy.print("my proxy test"); } /******************************************************************************************/ /** proxy class : com.sun.proxy.$Proxy0 before proxy invoke ================print message : my proxy test after proxy invoke =================print */
2.2 JDK代理源码分析
通过上述的示例,我们可以发现Proxy类以及接口 InvocationHandler是实现JDK动态代理的核心。
2.2.1.InvocationHandler:
package java.lang.reflect;
/**
* {@code InvocationHandler} is the interface implemented by
* the invocation handler of a proxy instance.
*
* Each proxy instance has an associated invocation handler.
* When a method is invoked on a proxy instance, the method
* invocation is encoded and dispatched to the {@code invoke}
* method of its invocation handler.
*
* @author Peter Jones
* @see Proxy
* @since 1.3
*/
public interface InvocationHandler {
/**
* Processes a method invocation on a proxy instance and returns
* the result. This method will be invoked on an invocation handler
* when a method is invoked on a proxy instance that it is
* associated with.
*
* @param proxy the proxy instance that the method was invoked on
*
* @param method the {@code Method} instance corresponding to
* the interface method invoked on the proxy instance. The declaring
* class of the {@code Method} object will be the interface that
* the method was declared in, which may be a superinterface of the
* proxy interface that the proxy class inherits the method through.
*
* @param args an array of objects containing the values of the
* arguments passed in the method invocation on the proxy instance,
* or {@code null} if interface method takes no arguments.
* Arguments of primitive types are wrapped in instances of the
* appropriate primitive wrapper class, such as
* {@code java.lang.Integer} or {@code java.lang.Boolean}.
*
* @return the value to return from the method invocation on the
* proxy instance. If the declared return type of the interface
* method is a primitive type, then the value returned by
* this method must be an instance of the corresponding primitive
* wrapper class; otherwise, it must be a type assignable to the
* declared return type. If the value returned by this method is
* {@code null} and the interface method's return type is
* primitive, then a {@code NullPointerException} will be
* thrown by the method invocation on the proxy instance. If the
* value returned by this method is otherwise not compatible with
* the interface method's declared return type as described above,
* a {@code ClassCastException} will be thrown by the method
* invocation on the proxy instance.
*
* @throws Throwable the exception to throw from the method
* invocation on the proxy instance. The exception's type must be
* assignable either to any of the exception types declared in the
* {@code throws} clause of the interface method or to the
* unchecked exception types {@code java.lang.RuntimeException}
* or {@code java.lang.Error}. If a checked exception is
* thrown by this method that is not assignable to any of the
* exception types declared in the {@code throws} clause of
* the interface method, then an
* {@link UndeclaredThrowableException} containing the
* exception that was thrown by this method will be thrown by the
* method invocation on the proxy instance.
*
* @see UndeclaredThrowableException
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
通过InvocationHandler接口上的注释我们可以了解到,每一个动态代理类的调用处理类都必须实现此接口,并且每个调用处理器类里都要关联代理类实例,当我们通过代理对象去调用一个方法的时候,方法调用就会被转发到InvocationHandler接口实现类中的invoke方法里。
2.2.2. Proxy类
2.2.2.1 newProxyInstance
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
首先我们会调用Proxy.newProxyInstance来获取代理对象,这个方法接收三个参数:
- loader : 类加载器classLoader
- interfaces : 代理类所要实现的接口
- h : InvocationHandler对象,当前动态代理对象调用方法时所关联的InvocationHandler对象
精简后的代码
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
/*
* Look up or generate the designated proxy class.
*/
Class> cl = getProxyClass0(loader, intfs);
final Constructor> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});
}
2.2.2.2 getProxyClass0(loader, intfs)获取代理类的字节码
private static Class> getProxyClass0(ClassLoader loader,
Class>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
get方法涉及到了代理类的缓存对象ProxyClassCache ,该对象采用WeakReference 实现,GC回收的时候会被回收掉,这里不做过多讲解。
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap
接着看get方法里的subKeyFactory.apply(key, parameter)方法,该方法最终调到ProxyClassFactory.apply方法中
2.2.2.3 ProxyClassFactory.apply(生成代理类的主要方法)
public Class> apply(ClassLoader loader, Class>[] interfaces) {
Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
apply方法中,首先加载了要实现的接口,然后做了一系列配置,接着通过ProxyGenerator.generateProxyClass来生成Class字节码文件,再调用defineClass0(native方法)对其进行加载。
public static byte[] generateProxyClass(final String var0, Class>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
2.2.2.4 接着看回newProxyInstance中的最后两行代码
final Constructor> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});
最后通过代理类获取构造对象,将InvocationHanlder h 作为参数,创建实例对象,所有constructorParams的类型肯定是InvocationHandler.class。至此整代理类的生成过程已经全部完成,可以看出 JDK生成代理类时是基于接口生成代理类把目标类 当做属性放在代理类里面。
3. Cglib代理与底层实现
3.1 Cglib代理实现
-
编写代理对象(不需要接口)
public class PrintServiceImpl { public void print(String message) { System.out.println("message : "+message); } }
-
编写MethodInterceptor
public class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("proxy by cglib ....."+method.getName()); Object res = methodProxy.invokeSuper(o, objects);//此处一定要是methodProxy来调用,要不会死循环 return res; } }
Object o :所生成的代理对象
method :要被拦截的方法
args : 方法参数
methodProxy : 要触发的父类的方法对象(即父类方法代理)
-
编写测试方法
public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\Cglib");//将生成的代理class写入到文件夹下 Enhancer enhancer = new Enhancer(); //设置代理类的对象 enhancer.setSuperclass(PrintServiceImpl.class); //设置代理 enhancer.setCallback(new MyMethodInterceptor()); PrintServiceImpl printServiceProxy = (PrintServiceImpl)enhancer.create(); System.out.println(printServiceProxy.getClass().getName()); printServiceProxy.print("print aaa "); } /*************************************************************************************/ /** leonardo.ezio.boot.demo.proxy.PrintServiceImpl$$EnhancerByCGLIB$$f8e5363 proxy by cglib .....print message : print aaa */
3.2 Cglib代理实现原理
3.2.1 create()
public Object create() {
this.classOnly = false;
this.argumentTypes = null;
return this.createHelper();
}
private Object createHelper() {
this.preValidate();
Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID);
this.currentKey = key;
Object result = super.create(key);
return result;
}
createHelper中通过newInstance创建了一个EnhancerKey对象,并以其为参数调用分类的create()方法。
protected Object create(Object key) {
try {
ClassLoader loader = this.getClassLoader();
Map cache = CACHE;
AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Class var5 = AbstractClassGenerator.class;
synchronized(AbstractClassGenerator.class) {
cache = CACHE;
data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Map newCache = new WeakHashMap(cache);
data = new AbstractClassGenerator.ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
Object obj = data.get(this, this.getUseCache());
return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
} catch (Error | RuntimeException var9) {
throw var9;
} catch (Exception var10) {
throw new CodeGenerationException(var10);
}
}
3.2.2 newInstance()
/**Enhance类中*/
protected Object nextInstance(Object instance) {
Enhancer.EnhancerFactoryData data = (Enhancer.EnhancerFactoryData)instance;
if (this.classOnly) {
return data.generatedClass;
} else {
Class[] argumentTypes = this.argumentTypes;
Object[] arguments = this.arguments;
if (argumentTypes == null) {
argumentTypes = Constants.EMPTY_CLASS_ARRAY;
arguments = null;
}
return data.newInstance(argumentTypes, arguments, this.callbacks);
}
}
public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
this.setThreadCallbacks(callbacks);
Object var4;
try {
if (this.primaryConstructorArgTypes == argumentTypes || Arrays.equals(this.primaryConstructorArgTypes, argumentTypes)) {
var4 = ReflectUtils.newInstance(this.primaryConstructor, arguments);
return var4;
}
var4 = ReflectUtils.newInstance(this.generatedClass, argumentTypes, arguments);
} finally {
this.setThreadCallbacks((Callback[])null);
}
return var4;
}
最后,通过反射来生成代理对象,阅读源码的过程中我们可以发现cglib生成动态代理的过程中使用了WeakHashMap做为缓存,与jdk中的ProxyClassCache有相似之处,都是采用弱引用实现的。
最后我们可以总结出:
JDK基于接口生成代理类 把目标类当做属性放在代理类里面
cglib基于class生成实现类 继承目标对象加强