代理模式
1.背景:代理对象做事情
2.代码:https://github.com/gre2/wuleiDesignPatterns/tree/master/src/main/java/com/wl/prox
3.分类:jdk(静态,动态),cglib
4.jdk静态代理
逻辑:静态代理在编译前就需要代理类实现和被代理类相同的接口,并且直接在实现的方法中调用被代理类相应的方法
缺点:一个代理只能为一个被代理类服务,如果需要代理的类很多,需要写大量的代理类代码
//代理
public class HuangNiuTicket implements TicketInterface {
@Override
public void findTicket() {
System.out.println("我是黄牛,我可以帮买票");
new WuleiTicket().findTicket();
System.out.println("我是黄牛,票已到手");
}
}
//被代理
public class WuleiTicket implements TicketInterface {
@Override
public void findTicket() {
System.out.println("我是吴磊,我想买票");
}
}
public class MainStaticClass {
public static void main(String[] args) throws Exception {
new HuangNiuTicket().findTicket();
}
}
5.jdk动态代理
逻辑:也是基于接口实现,通过接口指向实现类实例的多态方式,将具体实现和调用解耦
优点:开始不知道针对哪个接口,哪个被代理类创建代理类,因为它是在运行时被创建的
核心:字节码重组(代理对象的类来源)
思路:第一块代码是代理类代码,第二块代码是测试类,第三块代码是生成代理类的字节码文件
第四块和第五块被第三块依赖的部分代码块
public class HuangNiuDynamicTicket implements InvocationHandler {
private Object wuleiTicket;
public HuangNiuDynamicTicket() {
}
public HuangNiuDynamicTicket(Object wuleiTicketRequest) {
this.wuleiTicket = wuleiTicketRequest;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是代理,我可以帮你弄");
//method:public abstract void com.wl.proxy.jdk.common.TicketInterface.findTicket()
//调用invoke方法会走到被代理对象的方法中
Object object = method.invoke(wuleiTicket, args);
System.out.println("我是代理,帮你弄完了");
return object;
}
public Object getInstance(Object request) {
this.wuleiTicket = request;
Class wuleiClazz = wuleiTicket.getClass();
System.out.println("被代理对象的实例是:" + wuleiClazz);
//被代理对象的实例是:class com.wl.prox.jdkproxy.common.WuleiTicket
//定义谁的什么方法被谁代理,之后返回一个proxy生成的对象
return Proxy.newProxyInstance(wuleiClazz.getClassLoader(), wuleiClazz.getInterfaces(), this);
}
}
public class MainDynamicClassBak {
public static void main(String[] args) throws Exception {
TicketInterface ticketInterfaceB = (TicketInterface) new HuangNiuDynamicTicket().getInstance(new WuleiTicket());
System.out.println("代理对象的class是:" + ticketInterfaceB.getClass());
//代理对象的class是:class com.sun.proxy.$Proxy0
//会走进代理者的invoke方法
ticketInterfaceB.findTicket();
//默认调用代理类的invoke方法
//获取代理类字节码内容
byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{TicketInterface.class});
FileOutputStream os = new FileOutputStream("$Proxy0.class");
os.write(data);
os.close();
}
}
public final class $Proxy1 extends Proxy implements TicketInterface {
private static Method m3;
public $Proxy1(InvocationHandler var1) throws {
super(var1);
}
public final void findTicket() throws {
try {
//super.h参数查看下一个代码块的Proxy类属性,invoke方法查看下两个代码块
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m3 = Class.forName("com.wl.prox.jdkproxy.common.TicketInterface").getMethod("findTicket");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
public class Proxy implements java.io.Serializable {
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
protected InvocationHandler h;
private Proxy() {
}
protected Proxy(InvocationHandler h) {
doNewInstanceCheck();
this.h = h;
}
}
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}
6.手写classLoader类,手写Proxy类,完成jdk动态代理——-类加载见别的文章
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h);
主要工作
1.生成源代码(用一个无参的接口举例)
2.保存为java文件
3.编译java文件,生成class(JavaCompiler)
4.加载到内存,ClassLoader.defineClass()方法
5.返回被代理的对象
6.代理类实现自己的WlInvocationHandler,ClassLoader也自己实现
public class WlProxy {
private static String ln = "\r\n";//回车换行
public static Object newProxyInstance(WlClassLoader loader, Class<?>[] interfaces, WlInvocationHandler h) throws Exception {
//1.生成源代码
String proxySrc = generateSrc(interfaces[0]);
//2.保存为java文件
String filePath = WlProxy.class.getResource("").getPath();
File f = new File(filePath + "$Proxy0.java");
FileWriter fw = new FileWriter(f);
fw.write(proxySrc);
fw.flush();
fw.close();
//3.编译java文件,生成class
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
Iterable iterable = manager.getJavaFileObjectsFromFiles(Arrays.asList(f));
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
CompilationTask task = compiler.getTask(null, manager, diagnostics, null, null, iterable);
boolean flag = task.call();//通过返回值知道是否编译class成功
//如果失败,会提示哪里失败了
//https://docs.oracle.com/javase/7/docs/api/javax/tools/JavaCompiler.html
// for (Diagnostic diagnostic : diagnostics.getDiagnostics())
// System.out.format("Error on line %d in %s%n",
// diagnostic.getLineNumber(),
// diagnostic.getSource().toString());
manager.close();
//4.加载到内存(jvm)
//5.返回被代理后的代理对象
Class proxyClass = loader.findClass("$Proxy0");
Constructor constructor = proxyClass.getConstructor(WlInvocationHandler.class);
return constructor.newInstance(h);
}
private static String generateSrc(Class<?> anInterface) {
StringBuffer src = new StringBuffer();
src.append("package com.wl.prox.jdkproxy.writeProxy;" + ln);
src.append("import java.lang.reflect.Method;" + ln);
src.append("public class $Proxy0 implements " + anInterface.getName() + "{" + ln);
src.append("public WlInvocationHandler h;" + ln);
src.append("public $Proxy0(WlInvocationHandler h) {" + ln);
src.append("this.h=h;" + ln);
src.append("}" + ln);
for (Method method : anInterface.getMethods()) {
src.append("public " + method.getReturnType() + " " + method.getName() + "(){" + ln);
src.append("try{" + ln);
src.append("Method m= " + anInterface.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + ln);
src.append("this.h.invoke(this,m,null);" + ln);
src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
src.append("}" + ln);
}
src.append("}");
return src.toString();
}
}
public class WlClassLoader extends ClassLoader {
private File baseDir;
public WlClassLoader() {
String basePath = WlClassLoader.class.getResource("").getPath();
this.baseDir = new File(basePath);
}
@Override
public Class findClass(String name) throws ClassNotFoundException {
String className = WlClassLoader.class.getPackage().getName() + "." + name;
if (baseDir != null) {
File classFile = new File(baseDir, name.replaceAll("\\.", "/") + ".class");
if (classFile.exists()) {
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1) {
out.write(buff, 0, len);
}
return defineClass(className, out.toByteArray(), 0, out.size());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != out) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return null;
}
}
7.cglib动态代理
区别:jdk动态代理的代理对象,强制转换成接口;
cglib的动态代理是通过生成一个被代理对象的子类,重写父类方法,可以强制转换成被代理对象(子类引用赋值给父类)
逻辑:被代理对象基于类实现,不依赖接口
原理:字节码重组
//CGLib底层采用ASM字节码生成框架
//多态
public class HuangNiuCglibTicket implements MethodInterceptor {
public Object getInstance(Class clazz) throws Exception {
//Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,Enhancer-增强器
Enhancer enhancer = new Enhancer();
//生成的子类,继承哪个父类
enhancer.setSuperclass(clazz);
//设置回调
enhancer.setCallback(this);
//生成源代码,编译class,加载到内存,返回被代理对象的子类
return enhancer.create();
}
@Override
public Object intercept(Object instance, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//instance为由CGLib动态生成的代理类实例,MethodProxy为生成的代理类对方法的代理引用
System.out.println("我是cglib黄牛,我可以帮买票");
methodProxy.invokeSuper(instance, args);
System.out.println("我是cglib黄牛,票已到手");
return null;
}
}