1.aop实例化模型
我们在使用切面时,当目标对象是多例时,默认生成的切面对象是单例的,为了符合业务场景,因此在某些情况下切面的创建也必须是多例的,可通过上述方式对切面类进行设置。
2.aop实现原理
1)手写jdk动态代理(山寨版)
为了更好的理解jdk动态代理的底层实现,因此自己手写了一个jdk的实现过程,主要原理一样,可以仔细研究一下哈。
public class ProxyUtilTwo
{
public static Object newInstance( Class targetInf,CxjInvocationHandler cxjInvocationHandler)
{
//获取接口名称
String infName = targetInf.getSimpleName();
String context = “”;
String line="\n";
String tab ="\t";
//构建代理类中的通用数据
String packageContext = "package com;"+line; //包名称
String importantContent = "import "+targetInf.getName()+";"+line
+"import java.lang.reflect.Method;"+line
+"import java.lang.Exception;"+line
+"import com.cxj.til.CxjInvocationHandler;"+line; //引入类名称
//创建代理类
String classFirstLineContent = "public class $Proxy implements "+infName+"{"+line;
//创建属性
String filedContnt = tab+"private CxjInvocationHandler h;"+line;
//创建构造方法
String constructorContent=tab+"public $Proxy (CxjInvocationHandler h ){"+line
+tab+tab+"this.h=h;"
+line+tab+"}"+line;
String methodContent="";
//获取接口的所有方法
Method methods[] = targetInf.getDeclaredMethods();
for (Method method : methods )
{
String returnTypeName = method.getReturnType().getSimpleName();
String methodName = method.getName();
Class args[] = method.getParameterTypes();
//代理类中方法的定义形参
String argsContent = "";
//只包括形参名称
String paramsContent="";
//通过参数名获取到对应的类型
String paramsTypes="";
int flag = 0;
//构造代理类中的方法
for (Class arg : args) {
String tmp = arg.getSimpleName();
argsContent+=tmp+" p"+flag+",";
paramsContent+="p"+flag+",";
paramsTypes = "(p"+flag+").getClass(),";
flag++;
}
if(argsContent.length() > 0)
{
argsContent = argsContent.substring(0,argsContent.length()-1);
paramsContent = paramsContent.substring(0,paramsContent.length()-1);
paramsTypes = paramsTypes.substring(0,paramsTypes.length()-1);
}
if(!paramsTypes.isEmpty())
{
methodContent += tab+"public "+returnTypeName+" "+methodName+"("+argsContent+") throws Exception {"+line
+tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\","+paramsTypes+");"+line
;
}else
{
methodContent += tab+"public "+returnTypeName+" "+methodName+"("+argsContent+") throws Exception {"+line
+tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\");"+line
;
}
if(!returnTypeName.equals("void"))
{
methodContent +=tab+tab+"return ("+returnTypeName+") h.invoke(method,new Object[]{"+paramsContent+"});"+line
+tab+"}"+line;
}else
{
methodContent +=tab+tab+" h.invoke(method,new Object[]{"+paramsContent+"});"+line
+tab+"}"+line;
}
}
//拼接形成类字符串
context = packageContext+importantContent+classFirstLineContent+filedContnt+constructorContent+methodContent+line+"}";
//创建保留字符串的java文件
File file = new File("e:\\com\\$Proxy.java");
try {
if(!file.exists())
{
file.createNewFile();
}
FileWriter fileWriter = new FileWriter(file);
fileWriter.write(context);
fileWriter.flush();
fileWriter.close();
//将java文件动态编译为class文件
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:E:\\\\")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class clazz = urlClassLoader.loadClass("com.$Proxy");
return clazz.getConstructor(CxjInvocationHandler.class).newInstance(cxjInvocationHandler);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
相比与java的动态代理,山寨版的动态代理只是为了让读者更容易理解其中涉及的原理,很多细节和具体的实现没
有具体的实现,当然,其中最主要的是字节码的生成过程有较大的差异,望读者明白。
2)cglib动态代理
该代理有较多参考博客,读者可以查看,底层的字节码生成技术asm比较复杂,读者可以不用太深入研究,当然
,也不反对。