代理模式
所谓代理模式,即为其他对象创建代理以控制对这个对象的访问。主要在于解决需要被访问的对象不适合直接访问的问题。静态代理的实现简单理解就是继承代理类通过重写方法扩展代理类,而动态代理的实现较为复杂,原理上是JDK通过字节码技术和IO流直接生成继承了代理对象的.class文件,再通过ClassLoader将字节码加载到JVM中进行使用的技术。
JDK动态代理使用
JDK动态代理一般指的是调用Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法动态生成代理对象。举个例子,现在有一个接口叫UserService,有一个自定义注解叫Select,我想通过动态代理的方式生成一个即实现UserService,并将重写后方法的参数传入Select注解的value中并打印该value的对象,代码如下:
main方法:
public static void main(String[] args) {
/*JDK动态代理生成UserService的子类*/
Object o=Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(), new Class[]{UserService.class}, new MyInvocationHandle());
/*获取代理对象类信息发现生成的是一个叫$Proxy0的类*/
System.out.println("JDK生成的代理对象:"+o.getClass());
UserService userService=(UserService)o;
userService.query();
System.out.println("------");
userService.getNo("SunderLiu","SunderLiu's email");
}
运行结果:
JDK生成的代理对象:class com.sun.proxy.$Proxy0
This is UserService.query()
------
This is UserService.getNo(SunderLiu,SunderLiu's email)
UserService 类:
public interface UserService {
@Select("This is UserService.query()")
public void query();
@Select("This is UserService.getNo(${name},${email})")
public String getNo(String name,String email);
}
Select 类:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
String value();
}
MyInvocationHandle 类:
public class MyInvocationHandle implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Select select=method.getAnnotation(Select.class);
String annoValue=select.value();
if(args!=null){
String[] argsName=new String[]{"${name}","${email}"};
for(int i=0;i<args.length;i++) {
annoValue=annoValue.replace(argsName[i], args[i].toString());
}
}
System.out.println(annoValue);
return null;
}
}
手写JDK动态代理
思路解析
JDK动态代理是直接生成类的字节码放进类加载器中的,由于能力有限,笔者无法做到,但是换个思路,笔者的想法是先通过IO流生成类的java文件,然后再由JavaCompiler进行编译,最后通过URLClassLoader指定类文件路径放入虚拟中。
代码
例子和上面一样,动态生成代理类根据参数替换Select注解value的值。
MyProxy类:
public class MyProxy {
public static void main(String[] args) {
UserService userService=(UserService)newProxyInstance(UserService.class,new MyInvocationHandle());
userService.query();
userService.getNo("SunderLiu", "SunderLiu@gmail.com");
}
public static Object newProxyInstance(Class clazz, MyInvocationHandle myInvocationHandle){
boolean isInterface=clazz.isInterface();
//文件
String javaFile=buildJavaFile(clazz,isInterface);
System.out.println(javaFile);
//文件
File file = buildFile(javaFile);
Object o=newInstance(file,clazz,isInterface,myInvocationHandle);
return o;
// return o;
}
/*编译$Proxy.java并放入虚拟机*/
private static Object newInstance(File file,Class clazz,boolean isInterface, MyInvocationHandle myInvocationHandle){
/*获取java编译器,jdk1.6以后需要把JAVA_HOME的tools.jar复制到JRE_HOME/lib目录下执行*/
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);
/*编译$Proxy.java*/
t.call();
URL[] urls = null;
try {
urls = new URL[]{new URL("file:D:\\\\")};
} catch (MalformedURLException e1) {
e1.printStackTrace();
}
URLClassLoader urlClassLoader = new URLClassLoader(urls);
/*通过urlClassLoader加载$Proxy.class*/
Class cls = null;
try {
cls = urlClassLoader.loadClass("com.$Proxy");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Constructor constructor = null;
try {
constructor = isInterface?cls.getConstructor(InvocationHandler.class):cls.getConstructor(clazz,InvocationHandler.class);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
Object o = null;
try {
o = isInterface?constructor.newInstance(myInvocationHandle):constructor.newInstance(clazz.newInstance(),myInvocationHandle);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
e.printStackTrace();
}
return o;
}
/*生成$Proxy.java文件*/
private static File buildFile(String javaFile){
File file = new File("D:\\com\\$Proxy.java");
try {
File fileParent = file.getParentFile();
if (!file.exists()) {
fileParent.mkdirs();
}
file.createNewFile();
FileWriter fw = new FileWriter(file);
fw.write(javaFile);
fw.flush();
fw.close();
} catch (Exception e) {
e.printStackTrace();
}
return file;
}
private static String buildJavaFile(Class clazz,boolean isInterface) {
if(clazz==null){
return null;
}
if(!isInterface){
Class[] interfaces=clazz.getInterfaces();
if(interfaces.length==0){
return null;
}
clazz=clazz.getInterfaces()[0];
}
String clazzName=clazz.getName();
String clazzSimpleName=clazz.getSimpleName();
StringBuilder proxyFile=new StringBuilder();
/*开始手写动态代理类*/
/*包名、接口引入路径、类名*/
proxyFile.append("package com;");
proxyFile.append("import "+clazzName+";");
proxyFile.append("import dynamicProxy.Select;");
proxyFile.append("import java.lang.reflect.InvocationHandler;");
proxyFile.append("import java.lang.reflect.Method;");
proxyFile.append("public class $Proxy implements "+clazzSimpleName+"{");
proxyFile.append("public $Proxy(){}");
proxyFile.append("private InvocationHandler ih;");
proxyFile.append("public $Proxy(InvocationHandler ih){this.ih=ih;}");
if(!isInterface){
/*申明私有的目标接口属性*/
proxyFile.append("private "+clazzSimpleName+" target;");
/*构造方法*/
proxyFile.append("public $Proxy("+clazzSimpleName+" target,InvocationHandler ih){");
proxyFile.append("this.target=target;");
proxyFile.append("this.ih=ih;");
proxyFile.append("}");
}
/*准备写方法*/
Method[] methods = clazz.getDeclaredMethods();
for(Method method:methods){
String methodName=method.getName();
Class returnClass=method.getReturnType();
boolean hasReturnClass=!"void".equals(returnClass.getName());
Select select=method.getAnnotation(Select.class);
/*方法传参*/
String paramContent="";
/*父类参数传值*/
String paramPass="";
/*invoke方法传值*/
String methodParamClassArr="";
Class args[] = method.getParameterTypes();
int i=0;
for (Class arg : args) {
String argDeclare=arg.getSimpleName();
String argName="p"+i++;
paramContent+=","+argDeclare+" "+argName;
paramPass+=","+argName;
methodParamClassArr+=","+argDeclare+".class";
}
paramContent=i==0?"":paramContent.substring(1);
paramPass=i==0?"":paramPass.substring(1);
methodParamClassArr=i==0?"":methodParamClassArr.substring(1);
if(select!=null){
proxyFile.append("@Select(value=\""+select.value()+"\")");
}
proxyFile.append("public "+(hasReturnClass?returnClass.getSimpleName():"void")+" "+ methodName+"("+paramContent+"){");
proxyFile.append("Method m=getMethod(this.getClass(),\""+methodName+"\",new Class[]{"+methodParamClassArr+"});");
proxyFile.append("try{ih.invoke(this,m,new Object[]{"+paramPass+"});}catch(Throwable e){e.printStackTrace();}");
if(!isInterface){
proxyFile.append("target."+methodName+"("+paramPass+");");
}
if(hasReturnClass){
proxyFile.append("return null;");
}
proxyFile.append("}");
}
proxyFile.append("@SuppressWarnings({ \"rawtypes\", \"unchecked\" })");
proxyFile.append("private static Method getMethod(Class clazz,String methodName,Class[] cs){");
proxyFile.append("Method method=null;try {method= clazz.getMethod(methodName, cs);} catch");
proxyFile.append("(NoSuchMethodException | SecurityException e) {e.printStackTrace();}return method;");
proxyFile.append("}");
proxyFile.append("}");
return proxyFile.toString();
}
}
生成的$Proxy.java文件:
package dynamicProxy;
import dynamicProxy.UserService;
import dynamicProxy.Select;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class $Proxy implements UserService {
public $Proxy() {
}
private InvocationHandler ih;
public $Proxy(InvocationHandler ih) {
this.ih = ih;
}
@Select(value = "This is UserService.query()")
public void query() {
Method m = getMethod(this.getClass(), "query", new Class[] {});
try {
ih.invoke(this, m, new Object[] {});
} catch (Throwable e) {
e.printStackTrace();
}
}
@Select(value = "This is UserService.getNo(${name},${email})")
public String getNo(String p0, String p1) {
Method m = getMethod(this.getClass(), "getNo", new Class[] { String.class, String.class });
try {
ih.invoke(this, m, new Object[] { p0, p1 });
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static Method getMethod(Class clazz, String methodName, Class[] cs) {
Method method = null;
try {
method = clazz.getMethod(methodName, cs);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
return method;
}
}
代码运行结果:
This is UserService.query()
This is UserService.getNo(SunderLiu,SunderLiu@gmail.com)