目标
汇总spring中ioc和aop核心思想,jdk和cglib原理及区别
ioc分析
IoC不是一种spring独有的技术,只是一种设计思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。ioc的思想给予依赖导致原则,如典型的先造汽车还是先早轮子的话题:(以下内容引用出处,看参考资料)
这样的设计看起来没问题,但是可维护性却很低。假设设计完工之后,上司却突然说根据市场需求的变动,要我们把车子的轮子设计都改大一码。这下我们就蛋疼了:因为我们是根据轮子的尺寸设计的底盘,轮子的尺寸一改,底盘的设计就得修改;同样因为我们是根据底盘设计的车身,那么车身也得改,同理汽车设计也得改——整个设计几乎都得改!
我们现在换一种思路。我们先设计汽车的大概样子,然后根据汽车的样子来设计车身,根据车身来设计底盘,最后根据底盘来设计轮子。这时候,依赖关系就倒置过来了:轮子依赖底盘, 底盘依赖车身, 车身依赖汽车。
这时候,上司再说要改动轮子的设计,我们就只需要改动轮子的设计,而不需要动底盘,车身,汽车的设计了。
这就是依赖倒置原则——把原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑依赖高层建筑。高层建筑决定需要什么,底层去实现这样的需求,但是高层并不用管底层是怎么实现的。这样就不会出现前面的“牵一发动全身”的情况。
aop思想
-
静态代理 ,不方便,使用麻烦
- 针对每个具体类分别编写代理类;
- 针对一个接口编写一个代理类;
-
动态代理
- JDK实现动态代理,通过
Proxy.newProxyInstance(class.tInterfaces,this);
要求:只能对实现了接口的类生成代理,而不能针对类 - cglib实现:针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法, 因为是继承,所以该类或方法最好不要声明成final
- JDK实现动态代理,通过
jdk动态代理实现
动态代理原理(字节码重组,下面为JDK动态代理,cglib类似区别在于它是通过继承实现的):
1.拿到被代理对象的引用,并且通过反射获取到它的所有接口
2.JDK Proxy类重新生成一个新的类,同时新的类要实现被代理类的所有实现方法
3.动态生产java代码,把新加的业务逻辑方法有一定的逻辑代码取调用
4.编译新生成的iava代码.class
5.再重新加载到jvm中运行
6.以上为字节码重组的全过程
4.JDK动态代理源码分析:
手写JDK动态代理过程,关键代码:
(1)代理类的newInstance方法
public static Object newInstance(GPClassLoader cl, Class>[] interfaces, GPInvocationHandler invokehandler) throws Throwable {
//0.动态生成java文件
String s = ganeteFile(interfaces);
//1.写入磁盘
String path = GPProxy.class.getResource("").getPath();
System.out.println(path);
File f=new File(path+"KYProxy$0.java");
FileOutputStream fileOutputStream=new FileOutputStream(f);
fileOutputStream.write(s.getBytes());
fileOutputStream.close();
//2.把java文件编译成.class文件
JavaCompiler compiler= ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager=compiler.getStandardFileManager(null,null,null);
Iterable extends JavaFileObject> javaFileObjects = manager.getJavaFileObjects(f);
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, javaFileObjects);
task.call();
manager.close();
//3.将class文件加载到jvm中
Class> aClass = cl.findClass("KYProxy$0");
//4.返回字节码生成到代理对象
Constructor>[] constructors = aClass.getConstructors();
return constructors[0].newInstance(invokehandler);
}
(2)classloader加载类方法
public class GPClassLoader extends ClassLoader{
private File classPathFile;
public GPClassLoader() {
String path = GPClassLoader.class.getResource("").getPath();
this.classPathFile = new File(path);
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
String className= GPClassLoader.class.getPackage().getName()+"."+ name;
try {
if(classPathFile!=null){
File file=new File(classPathFile,name.replaceAll(".","/"));
if(file.exists()){
FileInputStream fis=new FileInputStream(file+"/"+name+".class");
ByteArrayOutputStream byteArrayInputStream=new ByteArrayOutputStream();
byte[] bt=new byte[1024];
int lend;
while ( (lend = fis.read(bt))!=-1){
byteArrayInputStream.write(bt,0,lend);
}
return defineClass(className,byteArrayInputStream.toByteArray(),0,byteArrayInputStream.size());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
(2)拼代理类字节码的方法,此处我们可以通过ProxyGenerator.generateProxyClass生成对应的代理源码然后仿写代理类,如: byte[] bytes = ProxyGenerator.generateProxyClass("proxy.XieMu$$EnhancerByCGLIB$$6e794e95@3d012ddd”, FileOutputStream os=new FileOutputStream("/Users/kai.yang/Desktop/test.class");
public static String ganeteFile(Class>[] classes) {
StringBuilder sb = new StringBuilder();
append(sb, "package proxy.custom;");
append(sb, "import java.lang.reflect.*;");
append(sb, "public class KYProxy$0 implements");
StringBuilder clName = new StringBuilder();
for (Class> clazz : classes
) {
clName.append(" ").append(clazz.getName()).append(",");
}
sb.append(clName.toString().substring(0, clName.length() - 1)).append("{\n");
sb.append("public KYProxy$0(GPInvocationHandler handler){\n" +
"this.h=handler;" +
"\n}\n");
append(sb, "proxy.custom.GPInvocationHandler h=null;");
for (Class> clazz : classes
) {
Method[] methods = clazz.getMethods();
for (int i=0;i clazz : classes
) {
Method[] methods = clazz.getMethods();
for (int i=0;i
cglib动态代理实现:
先说下两种动态代理区别:
- JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
- JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
- JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。
(1)FastClass机制
Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制,它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。
这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。
//根据方法签名获取index
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -2077043409:
if(var10000.equals("getPerson(Ljava/lang/String;)Lcom/demo/pojo/Person;")) {
return 21;
}
break;
case -2055565910:
if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
return 12;
}
break;
case -1902447170:
if(var10000.equals("setPerson()V")) {
return 7;
}
break;
//省略部分代码.....
//根据index直接定位执行方法
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
eaaaed75 var10000 = (eaaaed75)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
return new Boolean(var10000.equals(var3[0]));
case 1:
return var10000.toString();
case 2:
return new Integer(var10000.hashCode());
case 3:
return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
case 4:
return var10000.newInstance((Callback)var3[0]);
case 5:
return var10000.newInstance((Callback[])var3[0]);
case 6:
var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
return null;
case 7:
var10000.setPerson();
return null;
case 8:
var10000.setCallbacks((Callback[])var3[0]);
return null;
case 9:
return var10000.getCallback(((Number)var3[0]).intValue());
case 10:
return var10000.getCallbacks();
case 11:
eaaaed75.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
return null;
case 12:
eaaaed75.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
return null;
case 13:
return eaaaed75.CGLIB$findMethodProxy((Signature)var3[0]);
case 14:
return var10000.CGLIB$toString$3();
case 15:
return new Boolean(var10000.CGLIB$equals$2(var3[0]));
case 16:
return var10000.CGLIB$clone$5();
case 17:
return new Integer(var10000.CGLIB$hashCode$4());
case 18:
var10000.CGLIB$finalize$1();
return null;
case 19:
var10000.CGLIB$setPerson$0();
return null;
//省略部分代码....
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
参考资料:
1.ioc理解:https://www.zhihu.com/question/23277575