Spring AOP拦截功能主要是由JAVA动态代理实现,java中提供两种方式实现动态代理,一种基于jdk proxy代理类实现,第二种基于cglib字节码实现。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效。
1、jdk代理类实现方式
第一步:代理工厂(模拟生成一个拥有对象相同的属性和方法的代理类$proxy0)
public class DDProxy{
public static Object newProxyInstance(DDClassLoader classLoader,Class<?>[] interfaces,DDInvocationHandler h) throws IllegalArgumentException{
//step 1:生成java文件
String javaSource = generaSrc(interfaces);
//step 2:java文件写入磁盘
String javaPath = classLoader.getClass().getResource("").getPath();
File file= null;
FileWriter writer = null;
try {
file = new File(javaPath+"$Proxy0.java");
writer = new FileWriter(file);
writer.write(javaSource);
} catch (IOException e) {
e.printStackTrace();
}finally {
if(writer!=null){
try {
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//step 3:编译生成class文件
JavaCompiler compliler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compliler.getStandardFileManager(null, null, null);
Iterable iterable = manager.getJavaFileObjects(file);
CompilationTask task = compliler.getTask(null, manager, null, null, null, iterable);
task.call();
try {
manager.close();
} catch (IOException e) {
e.printStackTrace();
}
//step 4:加载class文件
try {
Class cls = classLoader.findClass("$Proxy0");
Constructor<DDInvocationHandler> constructor = cls.getConstructor(DDInvocationHandler.class);
return constructor.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
//step 5:删除生成的java和class临时文件
//TODO
return null;
}
/**
* 生成java源代码
* @param interfaces
* @param methodes
*/
public static String generaSrc(Class<?>[] interfaces){
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("package "+DDProxy.class.getPackage().getName()+";");
stringBuffer.append("import "+DDInvocationHandler.class.getName()+";");
stringBuffer.append("import java.lang.reflect.UndeclaredThrowableException;");
stringBuffer.append("import java.lang.reflect.Method;");
stringBuffer.append("public class $Proxy0 implements "+interfaces[0].getName()+"{");
stringBuffer.append("private "+DDInvocationHandler.class.getName()+" handler;");
stringBuffer.append("public $Proxy0("+DDInvocationHandler.class.getName()+" handler)"+"{");
stringBuffer.append("this.handler=handler;");
stringBuffer.append("}");
for(Method method:interfaces[0].getMethods()){
stringBuffer.append("public "+method.getReturnType().getName()+ " "+ method.getName()+"(){" );
stringBuffer.append("try{");
stringBuffer.append("Method method="+interfaces[0].getName()+".class.getMethod(\""+method.getName()+"\",new Class[]{});");
stringBuffer.append("this.handler.invoke(this,method,"+"null"+");");
stringBuffer.append("} catch (RuntimeException | Error var2){");
stringBuffer.append(" throw var2;");
stringBuffer.append("} catch (Throwable var3) {");
stringBuffer.append("throw new UndeclaredThrowableException(var3);");
stringBuffer.append("}");
stringBuffer.append("}");
}
stringBuffer.append("}");
return stringBuffer.toString();
}
public static void main(String[] args) {
System.out.println(DDProxy.class.getResource("").getPath());
}
}
第二步:类加载器
/**
* 类加载器
* @author liguorui
*/
public class DDClassLoader extends ClassLoader {
private File file;
public DDClassLoader() {
String classPath = DDClassLoader.class.getResource("").getPath();
file = new File(classPath);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = DDClassLoader.class.getPackage().getName()+"."+name;
if(file!=null){
file = new File(file, name.replaceAll("\\.", "/")+".class");
if(file.exists()){
FileInputStream input = null;
ByteArrayOutputStream out = null;
try {
input = new FileInputStream(file);
out = new ByteArrayOutputStream();
int len=-1;
byte[] bytes = new byte[1024];
while ((len=input.read(bytes))!=-1) {
out.write(bytes, 0, len);
}
Class<?> cls = defineClass(className, out.toByteArray(), 0, out.toByteArray().length);
return cls;
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
input.close();
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return null;
}
}
第三步:InvocationHandler主要实现代理对象与目标对象的转换
public class DDIntercept implements DDInvocationHandler{
private Object target;
public Object getInstance(Class<?> cls){
try {
this.target = cls.newInstance();
Object object = DDProxy.newProxyInstance(new DDClassLoader(), target.getClass().getInterfaces(),this);
return object;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("\n--------begin+"+method.getName()+"-----------");
method.invoke(target, args);
System.out.println("--------end+"+method.getName()+"-----------\n");
return null;
}
}
public class DDBoss {
public static void main(String[] args) {
Object object = new DDIntercept().getInstance(Employer.class);
((Person)object).start();
((Person)object).working();
((Person)object).stop();
}
}
运行效果:
--------begin+start-----------
开始工作
--------end+start-----------
--------begin+working-----------
工作中
--------end+working-----------
--------begin+stop-----------
结束工作
--------end+stop-----------
2、CGLib代理类
CGlib是一个优秀的动态代理框架,它的底层会使用ASM在内存中产生被代理类的子类,使用CGLib过程中被代理类不用实现任何接口。CGLIB使用简单,运行速度要远快于JDK的Proxy动态代理
//拦截类,它是Callback接口的子接口,需要用户实现
public class CGLibIntercept implements MethodInterceptor{
public Object getInstance(Class<?> cls){
Enhancer enhancer = new Enhancer();//主要的增强类
enhancer.setSuperclass(cls);//设置父类,被增强的类
enhancer.setCallback(this);//回调对象
return enhancer.create();//用cglibProxy来增强被代理对象
}
@Override
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("\n--------begin+"+method.getName()+"-----------");
methodProxy.invokeSuper(obj, objects);
System.out.println("--------end+"+method.getName()+"-----------\n");
return null;
}
}
public class CGLibBoss {
public static void main(String[] args) {
Person person = (Person)new CGLibIntercept().getInstance(Employer.class);
person.start();
person.working();
person.stop();
}
}
运行效果:
--------begin+start-----------
开始工作
--------end+start-----------
--------begin+working-----------
工作中
--------end+working-----------
--------begin+stop-----------
结束工作
--------end+stop-----------