老生常谈系列之Aop--JDK动态代理的底层实现原理

前言

在Aop系列里面有两篇文章,分别是老生常谈系列之Aop--Spring Aop原理浅析老生常谈系列之Aop--Spring Aop源码解析(二)都有涉及JDK动态代理的使用,但是没有详细分析JDK动态代理的实现原理,只是贴出了使用方法。本着知其然更要知其所以然的目标,这一篇文章,我们就来深扒一下JDK动态代理的实现原理。

原理分析

这里的代码分析是基于JDK1.8

Proxy.newProxyInstance()

说到Proxy.newProxyInstance()方法,首先我们来回忆一下老生常谈系列之Aop--Spring Aop源码解析(二)文章中调用JDK动态代理的例子,可以看到如下代码。

    public Object getProxy(){
return Proxy.newProxyInstance(calculateService.getClass().getClassLoader(),
calculateService.getClass().getInterfaces(),
new MyInvocationHandler
(newCalculateServiceImpl()));
}

我们是通过Proxy.newProxyInstance()方法来获取一个代理类的,那么Proxy.newProxyInstance()帮我们做了什么呢?

直接跟进源码,这里保留了方法上的英文注释,去除了抛出异常部分的注释,各位看官用心体会。同时这里的代码精简了一部分JDK的权限校验,只留下了核心代码并且添加了注释,逻辑还是比较简单的。

    /**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
*
* <p>{@code Proxy.newProxyInstance} throws
* {@code IllegalArgumentException} for the same reasons that
* {@code Proxy.getProxyClass} does.
*
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone();
// 去除一些权限校验 /*
* 查找或生成指定的代理类
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs); /*
* 获取指定的构造函数,这里指定的构造函数为入参为InvocationHandler的构造函数
* Invoke its constructor with the designated invocation handler.
*/
try {
// 去除一些权限校验
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// 去除一些权限校验
// 调用构造函数生产代理类实例
return cons.newInstance(new Object[]{h});
} catch (... e) {
// 省略异常处理
}
}

翻译一下:返回将方法调用分派到指定调用处理器的指定接口的代理类的实例。

显而易见,这里通过了一层转发,实现了在调用方法之前先回调到自定义的InvocationHandlerinvoke()方法。这一实现原理,在前面的文章是有说到的。但是JDK动态代理是怎么生成了一个可以回调到自定义InvocationHandler的代理类的呢?它是怎么将具体实现类的方法和生成的代理类的方法进行关联的呢?下面让我们来逐一解释。

getProxyClass0()

很显然,getProxyClass0()是需要重点关注的方法。从这个方法的注释就可以看出来,这个方法是查找或生成指定的代理类,很显然,我们要获取的类对象的结构和内容是在这个方法里完成的。也就是说,这个方法会通过我们传入的ClassLoader loaderClass<?>[] interfaces来构造完成一个Class<?>对象。

跟进代码

    /**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
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);
}

好家伙,一进来发现这个方法异常简单,可以说就一行代码,只是通过proxyClassCache去获取,如果存在缓存直接返回,否则通过ProxyClassFactory来生成代理类,那我们来看一下proxyClassCache是个什么东东。

    /**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

proxyClassCache是一个WeakCache类型的缓存集合,该对象维护了两个BiFunction<T, U, R>属性

    private final BiFunction<K, P, ?> subKeyFactory;
private final BiFunction<K, P, V> valueFactory;

对应到这里也就是上面代码的KeyFactoryProxyClassFactory,分别对应一个Key生成工厂和value生成工厂,这两个属性会在new WeakCache的时候赋值。我们可以重点关注ProxyClassFactory里面的apply(ClassLoader loader, Class<?>[] interfaces) 方法,该方法就是实现了生成代理类Class对象的方法。

ProxyClassFactory

跟进ProxyClassFactory代码,JDK的源码很多校验和控制,看起来没那么清晰,但是不要慌,把这些边边角角和异常控制去掉,逻辑还是很清晰的。

    /**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
// 代理类的名称
private static final String proxyClassNamePrefix = "$Proxy"; // next number to use for generation of unique proxy class names
// 生成代理类的序号
private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* 验证类加载器是否将此接口的名称解析为相同的 Class 对象。
* 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");
}
/*
* 验证 Class 对象实际上是不是代表一个接口。
* 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; // 前面的都是校验和生成accessFlags,proxyName等信息 /*
* 生成指定的代理类,可以看到委托给了ProxyGenerator.generateProxyClass()实现
* 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());
}
}
}

上面的代码很长?

无所谓的,看到这一句就好了 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags),这里生产了代理类的byte[]对象,然后再调用defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length)生成一个Class对象。

ProxyGenerator.generateProxyClass()

跟进ProxyGenerator.generateProxyClass()方法。

    /**
* Generate a proxy class given a name and a list of proxy interfaces.
*
* @param name the class name of the proxy class
* @param interfaces proxy interfaces
* @param accessFlags access flags of the proxy class
*/
public static byte[] generateProxyClass(final String name,
Class<?>[] interfaces,
int accessFlags)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
// 生成代理类的byte[]数组
final byte[] classFile = gen.generateClassFile(); // 是否保存动态代理生成的代码,sun.misc.ProxyGenerator.saveGeneratedFiles
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
int i = name.lastIndexOf('.');
Path path;
if (i > 0) {
Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
Files.createDirectories(dir);
path = dir.resolve(name.substring(i+1, name.length()) + ".class");
} else {
path = Paths.get(name + ".class");
}
Files.write(path, classFile);
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
} return classFile;
}

这个属性看着是不是很眼熟,我们通过设置这个属性来保存动态代理生成的字节码,这里也可以看到生效的原理,以及之前用的时候为啥说只对JDK动态代理生效。

    /** debugging flag for saving generated class files */
private final static boolean saveGeneratedFiles =
java.security.AccessController.doPrivileged(
new GetBooleanAction(
"sun.misc.ProxyGenerator.saveGeneratedFiles")).booleanValue();

到这里这个ProxyGenerator才是真正的生产代理类的文件的byte[]数组

    /**
* Construct a ProxyGenerator to generate a proxy class with the
* specified name and for the given interfaces.
*
* A ProxyGenerator object contains the state for the ongoing
* generation of a particular proxy class.
*/
private ProxyGenerator(String className, Class<?>[] interfaces, int accessFlags) {
this.className = className;
this.interfaces = interfaces;
this.accessFlags = accessFlags;
}

这个就是生成的具体逻辑了。

  • 为所有方法组装 ProxyMethod 对象以生成代理调度代码。
  • 为我们正在生成的类中的所有字段和方法组装 FieldInfo 和 MethodInfo 结构。
  • 编写最终的类文件。

其中前面第一第二点的逻辑都较好理解,第三点就是在写class文件了,这个按照JVM的字节码结构去拼接一份class字节码。例如dout.writeInt(0xCAFEBABE)在文件开头写入魔数,dout.writeShort(CLASSFILE_MINOR_VERSION)dout.writeShort(CLASSFILE_MAJOR_VERSION)分别为写小版本号和大版本号。这些顺序都是JVM字节码规定的顺序,需要严格按照此顺序拼接。关于字节码更多的知识,可以看JVM字节码结构。这个方法的代码很长,但是我不打算删减,这里的步骤缺一不可。

    /**
* Generate a class file for the proxy class. This method drives the
* class file generation process.
*/
private byte[] generateClassFile() { /* ============================================================
* Step 1: Assemble ProxyMethod objects for all methods to
* generate proxy dispatching code for.
*/ /*
* Record that proxy methods are needed for the hashCode, equals,
* and toString methods of java.lang.Object. This is done before
* the methods from the proxy interfaces so that the methods from
* java.lang.Object take precedence over duplicate methods in the
* proxy interfaces.
*/
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class); /*
* Now record all of the methods from the proxy interfaces, giving
* earlier interfaces precedence over later ones with duplicate
* methods.
*/
for (Class<?> intf : interfaces) {
for (Method m : intf.getMethods()) {
addProxyMethod(m, intf);
}
} /*
* For each set of proxy methods with the same signature,
* verify that the methods' return types are compatible.
*/
for (List<ProxyMethod> 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<ProxyMethod> 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); try {
/*
* Write all the items of the "ClassFile" structure.
* See JVMS section 4.1.
*/
// u4 magic;
dout.writeInt(0xCAFEBABE);
// u2 minor_version;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 major_version;
dout.writeShort(CLASSFILE_MAJOR_VERSION); cp.write(dout); // (write constant pool) // u2 access_flags;
dout.writeShort(accessFlags);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(superclassName)); // u2 interfaces_count;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (Class<?> intf : interfaces) {
dout.writeShort(cp.getClass(
dotToSlash(intf.getName())));
} // u2 fields_count;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields) {
f.write(dout);
} // u2 methods_count;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods) {
m.write(dout);
} // u2 attributes_count;
dout.writeShort(0); // (no ClassFile attributes for proxy classes) } catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
} return bout.toByteArray();
}

这个方法最终生成了byte[]数组,也就是说,我们已经拥有了一份类的二进制文件了,可以说到这一步,几乎已经完成了所有工作,只需要把这一份二进制文件加载进JVM,就可以得到一个可运行的Class<?>对象了。

最后这一步由defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length)实现,这是一个native方法,由JVM实现,具体代码在ClassLoader.c

// The existence or signature of this method is not guaranteed since it
// supports a private method. This method will be changed in 1.7.
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_defineClass0(JNIEnv *env,
jobject loader,
jstring name,
jbyteArray data,
jint offset,
jint length,
jobject pd)
{
return Java_java_lang_ClassLoader_defineClass1(env, loader, name, data, offset,
length, pd, NULL);
}

到这里已经获取了Class<?>,让我们回到Proxy.newProxyInstance()方法,生成代理类已经完成了。接下来是调用

Constructor<?> cons = cl.getConstructor(constructorParams);

获取入参为InvocationHandler的构造函数,最后调用构造函数生产代理对象实例。

cons.newInstance(new Object[]{h});

到这,生成动态代理的逻辑已经全部完成。脑子里有没有一定收获,记不记得发生了什么?没有?那不怪你,我也是乱写的。

手动实现一个JDK动态代理

既然原理都已经清晰了,那我们能不能自己实现一个动态代理。为了简单起见,我这里不会直接去写二进制的byte[]数组,因为我不会字节码的结构。这个例子的实现是拼接字符串,然后调用编译器去编译成class文件,再调用defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length)去构造Class<?>对象,然后照葫芦画瓢获取它的构造函数,生成一个代理对象。

还是复用这篇文章老生常谈系列之Aop--Spring Aop原理浅析中JDK动态代理里面的代码。

生成动态代理是这行代码

        return Proxy.newProxyInstance(calculateService.getClass().getClassLoader(),
calculateService.getClass().getInterfaces(),
new MyInvocationHandler(new CalculateServiceImpl()));

实现MyProxy类

那么我们自己实现一个MyProxy类。

首先那就搞个MyProxy类,实现newProxyInstance()方法。这里的实现很简单,分为以下几步:

  • 根据传入接口,拼接源码,保存为.java文件。注意:这里我为了简单,只支持传入一个接口(我太懒了)。
  • 调用JavaCompiler编译源码
  • loader.findClass("$Proxy0")加载编译好的class文件
  • 获取构造器生成Class对象
package io.codegitz.proxy;
/**
* @author Codegitz
* @date 2022/1/14 16:41
**/
public class MyProxy { /**
* 生成代理对象
* @param loader
* @param interfaces
* @param h
* @return
* @throws IllegalArgumentException
*/
public static Object newProxyInstance(MyClassLoader loader,
Class<?> interfaces,
InvocationHandler h)
throws IllegalArgumentException, IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
// 根据传入接口获取源代码
String sourceCode = getSourceCode(interfaces);
String path = MyProxy.class.getResource("").getPath();
File file = new File(path+"$Proxy0.java"); FileWriter fw = new FileWriter(file);
fw.write(sourceCode);
fw.close(); // 获取java编译器
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
// 标注java文件管理器,用来获取java字节码文件
StandardJavaFileManager manager = javaCompiler.getStandardFileManager(null,null,null);
Iterable iterable = manager.getJavaFileObjects(file); // 创建task,通过java字节码文件将类信息加载到JVM中
JavaCompiler.CompilationTask task = javaCompiler.getTask(null,manager,null,null,null,iterable);
// 开始执行task
Boolean call = task.call();
// 关闭管理器
manager.close();
Class proxyClass = loader.findClass("$Proxy0");
// 返回被代理后的代理对象
Constructor c = proxyClass.getConstructor(InvocationHandler.class);
return c.newInstance(h);
} private static String getSourceCode(Class<?> interfaces){
StringBuilder src = new StringBuilder();
src.append("package io.codegitz.proxy;").append("\n")
.append("import java.lang.reflect.Method;").append("\n")
.append("public class $Proxy0 implements ").append(interfaces.getName()).append("{").append("\n")
.append("private java.lang.reflect.InvocationHandler h;").append("\n")
.append("public $Proxy0(java.lang.reflect.InvocationHandler h){").append("\n")
.append("this.h=h;").append("\n")
.append("}").append("\n"); for(Method method:interfaces.getMethods()){
src.append("public ").append(method.getReturnType()).append(" ").append(method.getName()).append("() {").append("\n")
.append("try {").append("\n")
.append("Method m = ").append(interfaces.getName()).append(".class.getMethod(\"").append(method.getName()).append("\");").append("\n")
.append("this.h.invoke(this, m, new Object[]{});").append("\n")
.append("}catch (Throwable e){").append("\n")
.append("e.printStackTrace();").append("\n")
.append("}").append("\n")
.append("}").append("\n");
}
src.append("}"); return src.toString(); }
}

实现MyClassLoader类

定义MyClassLoader对象,实现findClass()方法,这里主要是为了定制获取自己编译好的class文件,否则直接使用原有的ClassLoader是没问题的。

/**
* @author Codegitz
* @date 2022/1/14 17:24
**/
public class MyClassLoader extends ClassLoader { private String baseDir; public MyClassLoader() {
this.baseDir = MyClassLoader.class.getResource("").getPath();
} /**
* 通过类名称加载类字节码文件到JVM中
*
* @param name 类名
* @return 类的Class独享
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 获取类名
String className = MyClassLoader.class.getPackage().getName() + "." + name;
if (null == baseDir) {
throw new ClassNotFoundException();
} // 获取类文件
File file = new File(baseDir, name + ".class");
if (!file.exists()) {
throw new ClassNotFoundException();
} // 将类文件转换为字节数组
try (
FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream();
) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
} // 调用父类方法生成class实例
return defineClass(className, out.toByteArray(), 0, out.size());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

测试运行

最后测试方法改成使用自己的方法

    public Object getMyProxy() throws IOException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IllegalAccessException {
return MyProxy.newProxyInstance(new MyClassLoader(),calculateService.getClass().getInterfaces()[0],new MyInvocationHandler(new CalculateServiceImpl()));
}

万事俱备只欠东风,搞个方法测试一把,代码如下:

    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
CalculateService proxy = (CalculateService) new CalculateServiceProxy(new CalculateServiceImpl()).getMyProxy();
proxy.calculate();
}

如图,这里已经实现了自定义返回动态代理。

运行结果如下,跟我们使用原生的JDK动态代理实现了一样的效果。虽然实现得非常简陋,但是原理就是这么个原理。

总结

好了,到这里文章已经结束。我们首先介绍了JDK动态代理的实现逻辑,随后自己动手实现了一个简陋版的JDK动态代理。

这里再回顾一下思路:

  • 根据传入接口拼接源代码,这一步跟我们平时在IDEA写代码是一样的
  • 调用编译器编译代码,这一步跟我们点一下IDEA的build是一样的
  • 加载编译好的class文件,这一步跟我们在IDEA点击运行是一样的
  • 执行逻辑,输出结果

是不是还是很简单的,跟我们平时的操作是一样的,只不过是手动搞了一遍。又水一篇,简简单单。

如果有人看到这里,那在这里老话重提。与君共勉,路漫漫其修远兮,吾将上下而求索。

老生常谈系列之Aop--JDK动态代理的底层实现原理的更多相关文章

  1. 老生常谈系列之Aop--CGLIB动态代理的底层实现原理

    老生常谈系列之Aop--CGLIB动态代理的底层实现原理 前言 上一篇老生常谈系列之Aop--JDK动态代理的底层实现原理简单讲解了JDK动态代理的实现,动态代理常用实现里面的双子星还有另一位--CG ...

  2. Java之美[从菜鸟到高手演练]之JDK动态代理的实现及原理

    Java之美[从菜鸟到高手演练]之JDK动态代理的实现及原理 JDK动态代理的实现及原理 作者:二青 邮箱:xtfggef@gmail.com     微博:http://weibo.com/xtfg ...

  3. AOP jdk动态代理

    一: jdk动态代理是Spring AOP默认的代理方法.要求 被代理类要实现接口,只有接口里的方法才能被代理,主要步骤是先创建接口,接口里创建要被代理的方法,然后定义一个实现类实现该接口,接着将被代 ...

  4. Spring AOP --JDK动态代理方式

    我们知道Spring是通过JDK或者CGLib实现动态代理的,今天我们讨论一下JDK实现动态代理的原理. 一.简述 Spring在解析Bean的定义之后会将Bean的定义生成一个BeanDefinit ...

  5. Spring AOP JDK动态代理与CGLib动态代理区别

    静态代理与动态代理 静态代理 代理模式 (1)代理模式是常用设计模式的一种,我们在软件设计时常用的代理一般是指静态代理,也就是在代码中显式指定的代理. (2)静态代理由 业务实现类.业务代理类 两部分 ...

  6. JDK动态代理的实现及原理

    Proxy.newProxyInstance(classloader,Class,invocationHandler) 调用getProxyClass0(loader, interfaces)生成代理 ...

  7. Java JDK 动态代理使用及实现原理分析

    转载:http://blog.csdn.net/jiankunking   一.什么是代理? 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理 ...

  8. JDK动态代理[2]----JDK动态代理的底层实现之Proxy源码分析

    在上一篇里为大家简单介绍了什么是代理模式?为什么要使用代理模式?并用例子演示了一下静态代理和动态代理的实现,分析了静态代理和动态代理各自的优缺点.在这一篇中笔者打算深入源码为大家剖析JDK动态代理实现 ...

  9. AOP的底层实现:JDK动态代理与Cglib动态代理

    转载自 https://www.cnblogs.com/ltfxy/p/9872870.html SpringAOP底层的实现原理: JDK动态代理:只能对实现了接口的类产生代理.(实现接口默认JDK ...

随机推荐

  1. CAS 的问题 ?

    1.CAS 容易造成 ABA 问题 一个线程 a 将数值改成了 b,接着又改成了 a,此时 CAS 认为是没有变化,其实 是已经变化过了,而这个问题的解决方案可以使用版本号标识,每操作一次 versi ...

  2. CKEditor禁用浏览服务器的功能

    在CKeditor的config.js文件中,添加以下内容,重启服务器,图片.flash.video中的浏览服务器按钮就会消失掉 /*按下" 浏览服务器"按钮时应启动的外部文件管理 ...

  3. 顺利通过EMC实验(15)

  4. 12 Web Development Trends That Will Dominate 2022

    12 Web Development Trends That Will Dominate 2022 (mindinventory.com) Progressive Web Apps (PWAs) An ...

  5. 总结一下各种0.5px的线

    在PC端用1px的边框线,看起来还好,但在手机端看起来就很难看了,而0.5px的分割线会有种精致的感觉.用普通写法border:solid 0.5px red;iPhone可以正常显示,android ...

  6. js中的bool值转换及"&&" 、"||"、 "!!"详解

    bool值转换 数据类型 bool值转化 undefined undefined 转化为 false Object null 转化为false,其他为 true Boolean false 转化为 f ...

  7. 彻底搞懂CSS层叠上下文、层叠等级、层叠顺序、z-index

    前言 最近,在项目中遇到一个关于CSS中元素z-index属性的问题,具体问题不太好描述,总结起来就是当给元素和父元素色设置position属性和z-index相关属性后,页面上渲染的元素层级结果和我 ...

  8. css使div居中

    每次想要使div居中都会设置position:absolute;,发现设置其他控件位置时会出现问题,所以采用以下办法: margin:0 auto;

  9. java中如何使用接口继承(Extending Interfaces)

    5.接口继承(Extending Interfaces)和通话talk的功能.而Moto888更为高级,除了照相和通话功能以外,还有mp3的功能.接口继承到底有什么意义呢?马克-to-win:1)通过 ...

  10. Struts的Logic标签的用途

    Struts的Logic标签可以根据特定的逻辑条件来判断网页的内容,或者循环遍历集合元素,它和HTML,Bean标签是Struts应用中最常用的三个标签. 它的功能主要是比较运算,进行字符串的匹配,判 ...