Spring中主要使用cglib和jdk动态代理,主要在SpringAop中有大量应用。
JDK动态代理
jdk动态代理主要使用场景是被代理的对象有实现的接口。最终生成的代理类:
class $Proxy0 extends Proxy implements IDao
jdk动态代理主要是基于反射,其实我们完全可以自己模拟;其中两个比较关键的思路:
- 使用反射解析目标对象的属性、方法等
- 根据解析的内容生成proxy.class,说白了就是把要生成的class按照字符串的形式拼接,最终通过ClassLoader加载。
package com.tian.proxy; import com.sun.jndi.toolkit.url.UrlUtil; import javax.tools.JavaCompiler; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; public class ProxyUtil { public static Object newInstance(Object target){ Object proxy=null; Class targetInf = target.getClass().getInterfaces()[0]; Method methods[] =targetInf.getDeclaredMethods(); String line="\n"; String tab ="\t"; String infName = targetInf.getSimpleName(); String content =""; String packageContent = "package com.tian;"+line; String importContent = "import "+targetInf.getName()+";"+line; String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line; String filedContent =tab+"private "+infName+" target;"+line; String constructorContent =tab+"public $Proxy ("+infName+" target){" +line +tab+tab+"this.target =target;" +line+tab+"}"+line; String methodContent = ""; for (Method method : methods) { String returnTypeName = method.getReturnType().getSimpleName(); String methodName =method.getName(); // Sting.class String.class Class args[] = method.getParameterTypes(); String argsContent = ""; String paramsContent=""; int flag =0; for (Class arg : args) { String temp = arg.getSimpleName(); //String //String p0,Sting p1, argsContent+=temp+" p"+flag+","; paramsContent+="p"+flag+","; flag++; } if (argsContent.length()>0){ argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1); paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(",")-1); } methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+") {"+line +tab+tab+"System.out.println(\"log\");"+line +tab+tab+"target."+methodName+"("+paramsContent+");"+line +tab+"}"+line; } content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}"; File file =new File("d:\\com\\tian\\$Proxy.java"); try { if (!file.exists()) { file.createNewFile(); } FileWriter fw = new FileWriter(file); fw.write(content); fw.flush(); fw.close(); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); Iterable units = fileMgr.getJavaFileObjects(file); JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); t.call(); fileMgr.close(); URL[] urls = new URL[]{new URL("file:D:\\\\")}; URLClassLoader urlClassLoader = new URLClassLoader(urls); Class clazz = urlClassLoader.loadClass("com.tian.$Proxy"); Constructor constructor = clazz.getConstructor(targetInf); proxy = constructor.newInstance(target); }catch (Exception e){ e.printStackTrace(); } return proxy; } } IDao proxy = (IDao) ProxyUtil.newInstance(new
CGLIB动态代理
cglib代理主要使用场景是:被代理对象的是类而没有任何接口实现。通过字节码增强实现动态代理(底层使用了asm)。在Spring中一个比较重要的应用就是解析@Configuration注解。Spring应用中只要在相应的class上添加了@Configuration注解,就被认为是一个全注解的类(看spring源码看实例化BeanDefinition对象时,会设置一个属性标识为full;当然与之对应一个是lite,就是标注了@Component,@ComponentScan,@Import,@ImportResource,@Bean这些注解的类)。
添加了@Configuration一个比较重要的作用就是会把该配置类使用cglib进行字节码增强,其实主要目的就是Spring可以更好的管理Bean的依赖关系了,如下示例:
定义要给配置类:
@Configuration @ComponentScan("com.tian.*") public class AppConfig { @Bean public UserDaoImpl1 userDaoImpl1(){ return new UserDaoImpl1(); } @Bean public UserDaoImpl2 userDaoImp2(){ userDaoImpl1(); return new UserDaoImpl2(); } }
相应的类:
public class UserDaoImpl1 { public UserDaoImpl1() { System.out.println("UserDaoImpl1 init......"); } public void query() { System.out.println("UserDaoImpl1 query......."); } } public class UserDaoImpl2 { public UserDaoImpl2() { System.out.println("UserDaoImpl2 init......"); } public void query() { System.out.println("UserDaoImpl2 query......"); }
执行如下:会发现UserDaoImpl1 init......只会打印一次,当然如果把@Configuration注解去掉就会打印两次。增强带来的好处是:Spring可以更好的管理Bean的依赖关系了。比如@Bean之间方法之间的调用,其实是去Spring容器里去找Bean了,而并不是再生成了一个实例。
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
通过查看源码发现使用cglib做了动态代理:
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
同样可以简单模拟下,依然使用如上相关类。新增一个关键的类,主要就是通过它进行回调增强:
public class MyEnhancerCallBack implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("cglib proxy......."); return methodProxy.invokeSuper(o, objects); } }
测试:
//可以查看cglib生成的class System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G:\\demo"); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserDaoImpl1.class); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setCallback(new MyEnhancerCallBack()); UserDaoImpl1 userDaoImpl1 =(UserDaoImpl1) enhancer.create(); userDaoImpl1.query();
可以把生成class拷贝到idea中
取部分代码:
//其实就是把代理的对象作为父类 public class UserDaoImpl1$$EnhancerBySpringCGLIB$$a779f942 extends UserDaoImpl1 implements Factory { final void CGLIB$query$0() { super.query(); } public final void query() { //通过这个进行回调增强 MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { var10000.intercept(this, CGLIB$query$0$Method, CGLIB$emptyArgs, CGLIB$query$0$Proxy); } else { super.query(); } } }
原文地址:https://www.cnblogs.com/tianboblog/p/12625334.html