动态代理的作用:目标方法代码不修改,为其添加功能。
动态代理底层涉及知识点:反射,类加载器,可以参考博客反射、类对象和类加载器_source code August的博客-CSDN博客
前言
Spring的AOP,可以理解为动态代理实现的一种规范化,因为我们自己实现动态代理可以使用JDK动态代理,也可以使用CGLIB动态代理。这两种都是Spring AOP的底层实现原理,使用场景:
若目标对象实现了接口,spring默认使用JDK的动态代理。
优点:因为有接口,所以使系统更加松耦合
缺点:为每一个目标类创建接口
若目标对象没有实现任何接口,spring使用CGLIB进行动态代理。
优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。
若目标对象实现了接口,但是强制cglib代理,则使用cglib代理
JDk动态代理的代码实现
1.目标对象SomeService。(JDK动态代理要求目标对象必须实现接口)
2.InvocationHandler接口实现
3.测试类
4.输出结果
5.原理分析
1.目标对象SomeService
//定义目标接口
public interface SomeService {
public void doSome();
public void doOther();
}
//定义目标实现类
public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
System.out.println("doSome");
}
@Override
public void doOther() {
System.out.println("domeOther");
}
}
2.InvocationHandler接口实现
public class MyInvocationHandler implements InvocationHandler {
//动态代理的目标对象
private Object target;
//构造方法,在new InvocationHandler的时候传入目标类对象
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通过代理对象执行方法时,会调用执行这个invoke()
Object res=null;
//如果代理类对象执行的是doSome才进行功能增强。
if (method.getName().equals("doSome")){
System.out.println("在doSome之前执行");
//执行目标类的方法,通过method类执行invoke方法实现
res=method.invoke(target,args);
System.out.println("在doSome之后执行");
}else {
res=method.invoke(target,args);
}
return res;
}
}
3.测试类
public class Test {
public static void main(String[] args) {
SomeService someService = new SomeServiceImpl();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(someService);
//使用proxy创建代理,newProxyInstance是静态方法,可以直接"类名.方法"使用,第二个参数是获取有哪些接口方法
Object o = Proxy.newProxyInstance(someService.getClass().getClassLoader(), someService.getClass().getInterfaces(), myInvocationHandler);
//上面Object可以使用强转,为SomeService
SomeService proxy=(SomeService) o;
//通过代理执行方法,会调用handler中的invoke()
proxy.doSome();//调用doSome方法
}
}
4.输出结果
测试类执行doSome
测试类执行doOther
通过代理,实现了在不修改目标类方法代码的情况下,通过代理执行目标类方法,在目标类方法执行的时候,为其增加了功能。比如这里的"在domeSome之前执行" 和“在domeSome之后执行”就是增加的功能。这些增加的代码,就是我们常说的"切面"。
关于详细的Spring AOP使用可以参考博客“https://正在连载”
5.原理分析
动态代理底层涉及知识点:反射,类加载器,可以参考博客反射、类对象和类加载器_source code August的博客-CSDN博客
可以发现前面创建的代理类对象进行了强制类型转换,SomeService proxy=(SomeService) o;然而强制类型转化的前提是,类之间存在继承关系(或者接口和实现的关系),然而由于Java的单继承特性(只能继承一个类),并且动态生成的代理类已经继承了Proxy类的,就不能再继承其他的类,所以我们大胆推测,代理类一定实现了目标对象类(这里是SomeService类)。 (关于强制类型转化的理解可以参考:Java强制类型转化和自动类型转化_source code August的博客-CSDN博客)
为什么JDK动态代理要求目标对象必须实现接口?
我们通过代理执行方法,proxy.doSome();这里的doSome方法和目标类的doSome方法名一样,我们执行proxy.doSome()就好像执行了目标类的doSome(),这是不是很像多态?多态可以参考"https://mp.csdn.net/mp_blog/creation/editor/120889953"。代理类实现了目标对象类,并且每一个被代理接口的方法都会在代理类中生成一个对应的实现方法,并在实现方法中最终调用invocationHandler 的invoke方法,这就解释了为何执行代理类的方法会自动进入到我们自定义的invocationHandler的invoke方法中,然后在我们的invoke方法中再利用jdk反射的方式去调用真正的被代理类的业务方法。
6.源码分析(参考文末转载链接)
以Proxy.newProxyInstance()方法为切入点来剖析代理类的生成及代理方法的调用。
(最核心的三行代码前已经加了中文注释)
@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<Void>() {
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);
}
}
newProxyInstance()方法帮我们执行了生成代理类----获取构造器----生成代理对象这三步;
生成代理类: Class<?> cl = getProxyClass0(loader, intfs);
获取构造器: final Constructor<?> cons = cl.getConstructor(constructorParams);
生成代理对象: cons.newInstance(new Object[]{h});
Proxy.getProxyClass0()如何生成代理类?
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
// 接口数不得超过65535个,这么大,足够使用的了
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
// 译: 如果缓存中有代理类了直接返回,否则将由代理类工厂ProxyClassFactory创建代理类
return proxyClassCache.get(loader, interfaces);
}
如果缓存中没有代理类,Proxy中的ProxyClassFactory如何创建代理类?从get()方法追踪进去看看。
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<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
// 根据代理类实现的接口数组来生成二级缓存key
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
get方法中Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
subKeyFactory调用apply,具体实现在ProxyClassFactory中完成。
ProxyClassFactory.apply()实现代理类创建。
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, 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.generateProxyClass()方法中,方法签名如下:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
public static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) {
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
// 真正生成字节码的方法
final byte[] classFile = gen.generateClassFile();
// 如果saveGeneratedFiles为true 则生成字节码文件,所以在开始我们要设置这个参数
// 当然,也可以通过返回的bytes自己输出
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;
}
代理类生成的最终方法是ProxyGenerator.generateClassFile()
private byte[] generateClassFile() {
/* ============================================================
* Step 1: Assemble ProxyMethod objects for all methods to generate proxy dispatching code for.
* 步骤1:为所有方法生成代理调度代码,将代理方法对象集合起来。
*/
//增加 hashcode、equals、toString方法
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<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.
* 步骤3:编写最终类文件
*/
/*
* 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 {
// 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();
}
通过addProxyMethod()添加hashcode、equals、toString方法。
private void addProxyMethod(Method var1, Class var2) {
String var3 = var1.getName(); //方法名
Class[] var4 = var1.getParameterTypes(); //方法参数类型数组
Class var5 = var1.getReturnType(); //返回值类型
Class[] var6 = var1.getExceptionTypes(); //异常类型
String var7 = var3 + getParameterDescriptors(var4); //方法签名
Object var8 = (List)this.proxyMethods.get(var7); //根据方法签名却获得proxyMethods的Value
if(var8 != null) { //处理多个代理接口中重复的方法的情况
Iterator var9 = ((List)var8).iterator();
while(var9.hasNext()) {
ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next();
if(var5 == var10.returnType) {
/*归约异常类型以至于让重写的方法抛出合适的异常类型,我认为这里可能是多个接口中有相同的方法,而这些相同的方法抛出的异常类 型又不同,所以对这些相同方法抛出的异常进行了归约*/
ArrayList var11 = new ArrayList();
collectCompatibleTypes(var6, var10.exceptionTypes, var11);
collectCompatibleTypes(var10.exceptionTypes, var6, var11);
var10.exceptionTypes = new Class[var11.size()];
//将ArrayList转换为Class对象数组
var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes);
return;
}
}
} else {
var8 = new ArrayList(3);
this.proxyMethods.put(var7, var8);
}
((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2, null));
/*如果var8为空,就创建一个数组,并以方法签名为key,proxymethod对象数组为value添加到proxyMethods*/
}
详细参考:【Spring基础】JDK动态代理实现原理(jdk8)_yhl_jxy的博客-CSDN博客_jdk动态代理实现