代理模式(Poxy)
概述
作用
拦截器、中介、黄牛,spring AOP,解耦,媒婆等等。
应用场景
为其他对象提供一种代理以控制对这个对象的访问。从结构上来看和 Decorator 模式类似, 但 Proxy 是控制,更像是一种对功能的限制,而 Decorator 是增加职责。
Spring 的 Proxy 模式在 AOP 中有体现,比如 JdkDynamicAopProxy 和 Cglib2AopProxy。
特点
在代理角色和被代理角色之间的代理过程由被代理对象决定,而代理对象会持有被代理对象的接口或者引用(方便代理角色在完成工作之前或之后来能找到代理对象并通知被代理对象)
分类
通常代理分为静态代理和动态代理
静态代理
实现静态代理实例
房子接口
public interface House {
//找房子
public void findRoom();
//付钱
public void payForRoom();
//买房子
public void sellRoom();
}
客户类
public class Customer implements House {
@Override
public void findRoom() {
System.out.println("找房子需要又大又便宜且地段好");
}
@Override
public void payForRoom() {
System.out.println("首付少,利息少");
}
@Override
public void sellRoom() {
System.out.println("能买个好价钱");
}
}
中介类
public class Intermediary {
private House house;
public Intermediary(House house){
this.house = house;
}
public void findRoom(){
System.out.println("根据客户买房的");
this.house.findRoom();
System.out.println("价格便宜");
}
}
测试类
public class StaticProxyTest1 {
public static void main(String[] args) {
Intermediary intermediary = new Intermediary(new Customer());
intermediary.findRoom();
}
}
测试结果
动态代理
jdk动态代理
Person 接口
public interface Person {
public void findLove();
public void zufangzi();
public void buy();
public void findJob();
//......
}
JDKPiTiaoKe类也就是代理类
public class JDKPiTaoKe implements InvocationHandler {
//被代理的对象,把引用给保存下来
private Person target;
public Object getInstance(Person target){
this.target =target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("这里是皮条客,专注拉皮条服务");
System.out.println("收集客户信息,开始为客户拉皮条");
method.invoke(target,args);
System.out.println("安排约会,如果合适就开始办");
return null;
}
}
ZhaoShenYao类也就是被代理类
public class ZhaoShenYao implements Person {
@Override
public void findLove() {
System.out.println("大眼睛水汪汪");
System.out.println("大长腿");
System.out.println("胸大,漂亮");
}
@Override
public void zufangzi() {
System.out.println("");
System.out.println("租房子");
}
@Override
public void buy() {
System.out.println("买面包");
}
@Override
public void findJob() {
System.out.println("月薪20K-50k");
System.out.println("找工作");
}
}
测试类
public class JDKProxyPlay {
public static void main(String[] args) {
// Person instance = (Person) new JDKKuYiSou().getInstance(new ZhaoShenYao());
Person instance = (Person) new JDKPiTaoKe().getInstance(new ZhaoShenYao());
System.out.println(instance.getClass());
instance.findLove();
System.out.println("-----------------------");
//通过反编译工具可以查看源代码
byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
FileOutputStream os = null;
try {
// os = new FileOutputStream("J://$Proxy01.class");
os = new FileOutputStream("J://$Proxy02.class");
os.write(bytes);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
最终测试结果
cglib 动态代理
zhaoshengyao类被代理类
public class ZhaoShengYao {
public void findLove(){
System.out.println("大眼睛水汪汪");
System.out.println("大长腿");
System.out.println("胸大,肤白,耐看");
}
}
CglibPiTiaoKe类即代理类
public class CglibPiTiaoKe implements MethodInterceptor {
public Object getInstance(Class<?> clazz){
Enhancer enhancer = new Enhancer();
//要把哪个设置为即将生成的新类父类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("这里是皮条客,专注拉皮条服务");
System.out.println("收集客户信息,开始为客户拉皮条");
methodProxy.invokeSuper(o,objects);
System.out.println("安排约会,如果合适就开始办");
return null;
}
}
测试类
public class CgilbProxyPlay {
public static void main(String[] args) {
System.out.println("-------------");
ZhaoShengYao instance = (ZhaoShengYao) new CglibPiTiaoKe().getInstance(ZhaoShengYao.class);
instance.findLove();
System.out.println(instance.getClass());
System.out.println("-------------");
}
}
静态代理和动态代理的区别
cglib proxy和jdk proxy之间的区别
cglib proxy
1)cglib需要实现方法拦截器。
2)cglib动态代理类和原先的类有业务继承关系,也就是父子类关系,而子类(动态代理类)会覆盖或者增强父类(被代理类)的非final修饰的方法,若果父类有final修饰的方法,则子类不能重写改动。
3)在目标对象没有实现了接口的情况下,需要采用cglib’代理,这意味着必须有CGLIB库做支撑。
jdk proxy
1)jdk依赖反射,无需第三方库。
2)jdk的代理类和被代理类则是兄弟关系,必须实现被代理类实现的接口。
3)jdk动态代理需要实现InvocationHandler接口,并重写其中的 public Object invoke(Object proxy, Method method, Object[] args)方法以便实现方法增强。
自定义动态代理类
房子接口类
public interface House {
//找房子
public void findRoom();
//付钱
public void payForRoom();
//买房子
public void sellRoom();
public void rentRoom();
public void rentalRoom();
}
shengyao接口类继承House
public class CustomerZhaoShengYao implements House {
@Override
public void findRoom() {
System.out.println("赵生耀开始找房子了");
}
@Override
public void payForRoom() {
System.out.println("赵生耀开始为找到的房子付钱了");
}
@Override
public void sellRoom() {
System.out.println("赵生耀开始卖房子了");
}
@Override
public void rentRoom() {
System.out.println("赵生耀开始租房子了");
}
@Override
public void rentalRoom() {
System.out.println("赵生耀开始出租房子了");
}
}
XSFT动态代理类
public class XSFTProxy {
public static final String ln="\r\n";
private static String generateSrc(Class<?> [] interfaces){
StringBuffer sb = new StringBuffer();
sb.append("package com.gupaoedu.vip.pattern.MyProxy;"+ln);
sb.append("import com.gupaoedu.vip.pattern.proxy.static1.House;"+ln);
sb.append("import java.lang.reflect.Method;"+ln);
sb.append("public class $Proxy0 implements "+interfaces[0].getName()+"{"+ln);
//定义变量
sb.append("XSFTInvocationHandler h;"+ln);
//定义构造方法
sb.append("public $Proxy0 (XSFTInvocationHandler h)"+"{"+ln);
sb.append("this.h = h;"+ln);
sb.append("}"+ln);
//写入其他方法
for(Method m:interfaces[0].getMethods()){
sb.append("public "+m.getReturnType().getName()+" "+m.getName()+"(){"+ln);
sb.append("try"+"{"+ln);
sb.append("Method m="+interfaces[0].getName()+".class.getMethod(\""+m.getName()+"\",new Class[]{});"+ln);
sb.append("this.h.invoke(this,m,null);"+ln);
sb.append("}catch(Exception e){"+ln);
sb.append("e.printStackTrace();");
sb.append("}"+ln);
sb.append("}");
}
sb.append("}"+ln);
return sb.toString();
}
public static Object newProxyInstance(XSFTClassloader classloader,Class<?> [] interfaces, XSFTInvocationHandler h ){
//1、编写源代码文件后动态生成java文件
String src = generateSrc(interfaces);
//2、将java文件写到磁盘当中
String filePath=XSFTProxy.class.getResource("").getPath();
File file = new File(filePath + "$Proxy0.java");
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter(file);
fileWriter.write(src);
fileWriter.flush();
fileWriter.close();
//3、把生成的.java文件编译成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> iterable = standardFileManager.getJavaFileObjects(file);
JavaCompiler.CompilationTask task = compiler.getTask(null,standardFileManager,null,null,null,iterable);
task.call();
standardFileManager.close();
//4、将编译生成的.class文件加载到jvm中
Class<?> proxyClass = classloader.findClass("$Proxy0");
Constructor<?> constructor = proxyClass.getConstructor(XSFTInvocationHandler.class);
file.delete();
//5、返回字节码重组以后的新代理对象
return constructor.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
XSFT类加载器
public class XSFTClassloader extends ClassLoader{
private File classPathFile;
public XSFTClassloader(){
String classPath = XSFTClassloader.class.getResource("").getPath();
this.classPathFile = new File(classPath);
}
@Override
protected Class<?> findClass(String name){
String className=XSFTClassloader.class.getPackage().getName()+"."+name;
if(classPathFile !=null){
File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
if(classFile.exists()){
FileInputStream fileInputStream = null;
ByteArrayOutputStream outputStream = null;
try {
fileInputStream = new FileInputStream(classFile);
outputStream = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while ((len=fileInputStream.read(buff))!=-1){
outputStream.write(buff,0,len);
}
return defineClass(className,outputStream.toByteArray(),0,outputStream.size());
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null != fileInputStream){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != outputStream ){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return null;
}
}
中介类
public class Intermediary implements XSFTInvocationHandler{
private House target;
public Object getInstance(House target){
this.target = target;
Class<?> clazz = target.getClass();
return XSFTProxy.newProxyInstance(new XSFTClassloader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
System.out.println("我是房屋中介,为客户提供租房、买卖房、以及出租房等服务");
System.out.println("现在开始收集客户信息");
method.invoke(this.target,args);
System.out.println("如对价格满意, 就可以开始交易");
return null;
}
}
测试类
public class NomalProxyTest {
public static void main(String[] args) {
House instance = null;
try {
instance = (House) new Intermediary().getInstance(new CustomerZhaoShengYao());
System.out.println(instance.getClass());
instance.sellRoom();
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试结果:
行笔至此,关于代理的相关基础知识已经学习完毕。