在后续分析AOP之前,先分析下动态代理,因为sprig的AOP实现的核心一直就是动态代理。既然有动态代理,那肯定有静态代理。
静态代理是一种组合,在代码编译阶段,代理对象就知道自己代理的是哪个对象实例。静态代理不多说,很简单,代理和实现类实现了同一个接口,内部持有了对象的引用。
先定义对象接口
public interface PersonInterface {
String say();
}
实现类
public class Person implements PersonInterface{
public String say() {
return "hello";
}
}
静态代理
public class StaticProxy implements PersonInterface{
PersonInterface target;
public StaticProxy(PersonInterface target) {
super();
this.target = target;
}
public String say() {
return target.say();
}
}
静态代理的缺点:
1.静态代理与实现类实现了相同的接口,一旦接口增加方法,代理类也需要改变。
2.如果需要代理的类太多,则每个类都需要一个代理对象
(1)动态代理的实现
动态代理之所以称为动态,一个是锁代理的对象在运行时才指定,第二个代理对象的class文件时在运行时才创建,不存在代理对象的java文件。
动态代理实现中比较重要的就是实现InvocationHandler接口,该接口的实现内部持有代理对象的引用,并通过反射调用方法。另外一个比较重要的是Proxy类,通过该类创建动态代理对象。
InvocationHandler的实现:
public class StuInvocationHandler implements InvocationHandler {
T target;
public StuInvocationHandler(T o) {
super();
target = o;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理过程内部使用了反射
Object a = method.invoke(target, args);
return a;
}
}
Proxy类静态方法获得代理对象
Person person = new Person();
InvocationHandler hander = new StuInvocationHandler(person);
PersonInterface newProxyInstance = (PersonInterface) Proxy.newProxyInstance(PersonInterface.class.getClassLoader(), new Class[] {PersonInterface.class}, hander);
newProxyInstance.say();
(2)原理分析
先看下创建代理对象的方法newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
//创建代理对象的class对象
Class> cl = getProxyClass(loader, interfaces);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
//构造器
Constructor cons = cl.getConstructor(constructorParams);
//构造器创建对象
return cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}
主要是创建代理类的class对象,再通过反射创建对象。再看下创建代理类的方法getProxyClass。
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);
}
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
ProxyClassFactory类是Proxy类的内部类,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());
}
}
}
最终调用的是ProxyGenerator的generateClassFile方法。
private byte[] generateClassFile() {
/* ============================================================
* Step 1: Assemble ProxyMethod objects for all methods to generate proxy dispatching code for.
* 为所有方法生成代理调度代码,将代理方法对象集合起来。
*/
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
//增加接口方法
for (Class> intf : interfaces) {
for (Method m : intf.getMethods()) {
addProxyMethod(m, intf);
}
}
/*
* 验证方法签名相同的一组方法,返回值类型是否相同,也就是检查重写方法
*/
for (List sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
/* ============================================================
* Step 2: Assemble FieldInfo and MethodInfo structs for all of fields and methods in the class we are generating.
* 为类中的方法生成字段信息和方法信息
*/
try {
//增加构造方法
methods.add(generateConstructor());
for (List sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for method's Method object
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
// generate code for proxy method and add it
methods.add(pm.generateMethod());
}
}
//增加静态初始化信息
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
/* ============================================================
* Step 3: Write the final class file.
*
*/
/*
* Make sure that constant pool indexes are reserved for the following items before starting to write the final class file.
* 在开始编写类文件之前,保留保留常量池索引。
*/
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (Class> intf: interfaces) {
cp.getClass(dotToSlash(intf.getName()));
}
/*
* Disallow new constant pool additions beyond this point, since we are about to write the final constant pool table.
* 设置只读,不允许在常量池中增加信息,因为要写常量池表
*/
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
//下面是组合class类文件结构,这个我们在分析JVM时候分析过
try {
// u4 magic;
dout.writeInt(0xCAFEBABE);
// u2 次要版本;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 主版本
dout.writeShort(CLASSFILE_MAJOR_VERSION);
cp.write(dout); // (write constant pool)
// u2 访问标识;
dout.writeShort(accessFlags);
// u2 本类名;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 父类名;
dout.writeShort(cp.getClass(superclassName));
// u2 接口;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (Class> intf : interfaces) {
dout.writeShort(cp.getClass(
dotToSlash(intf.getName())));
}
// u2 字段;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields) {
f.write(dout);
}
// u2 方法;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods) {
m.write(dout);
}
// u2 类文件属性:对于代理类来说没有类文件属性;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
return bout.toByteArray();
}
我们在运行时候加上一行代码用来生成代理类的class文件。
//生成代理类的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Person person = new Person();
InvocationHandler hander = new StuInvocationHandler(person);
PersonInterface newProxyInstance = (PersonInterface) Proxy.newProxyInstance(PersonInterface.class.getClassLoader(), new Class[] {PersonInterface.class}, hander);
newProxyInstance.say();
反编译后代理类为
package com.sun.proxy;
import com.enjoy.proxy.PersonInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
//实现了对象接口、继承了Proxy类
//Proxy类中有个字段h类型是InvocationHandler
public final class $Proxy0
extends Proxy
implements PersonInterface
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String say()
throws
{
try
{
//调用了InvocationHandler的invoke方法,其实就是反射
return (String)this.h.invoke(this, m3, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.enjoy.proxy.PersonInterface").getMethod("say", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
其实回想下上述过程,整个代理实际上包括三步:第一步生成代理类的class文件,代理类继承Proxy,内部包含一个字段属性InvocationHandler。第二步创建代理对象,并将我们创建的InvocationHandler当做构造函数传入。第三步就是调用InvocationHandler的invoke方法,实际上就是反射。
cglib动态代理大致思路和JDK动态代理是类似的,也是先生成代理class类,不过不同的是cglib没有使用反射,而是直接调用了对象的方法(实际上是保存了类方法的索引,这个不明白可以看下我之前JVM系列的文章)。
我们一样先看一下cglib的实现。首先是创建继承MethodInterceptor类,方法的增强就在这里。
public class CglibInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object result = proxy.invokeSuper(obj, args);
return result;
}
}
代理主体方法:
public static void main(String[] args) {
CglibInterceptor interceptor = new CglibInterceptor();
Enhancer eh = new Enhancer();
eh.setSuperclass(Person.class);
eh.setCallback(interceptor);
Person create = (Person)eh.create();
create.say();
}
具体原代理这里先不分析了,我们主要是了解动态代理原理为后续AOP准备。
(1)看了JDK动态代理过程就知道被代理对象一定要实现一个接口,cglib不用。
(2)JDK动态代理使用了反射,cglib直接保存了类方法引用,更高效
(3)当然还有代理类名称规则等其他不同,不是重点,不多说