【设计模式】代理模式

一、静态代理

    1、静态代理:代理和被代理对象在【代理之前】都是【确定】的。他们都实现【相同的接口或者继承相同的抽象类】

    2、代理实现方法: 

    (1)继承法:代理类直接【继承】被代理类,实现其原有方法,并添加一些额外功能

package com.imooc.proxy;
//先定义接口
public interface Moveable {
	void move();
}
package com.imooc.proxy;

import java.util.Random;

//被代理类
public class Car implements Moveable {

	@Override
	public void move() {
		//实现开车
		try {
			Thread.sleep(new Random().nextInt(1000));
			System.out.println("汽车行驶中....");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}
package com.imooc.proxy;

//静态代理(继承法)
public class Car2 extends Car {

	@Override
	public void move() {
		long starttime = System.currentTimeMillis();
		System.out.println("汽车开始行驶....");
		super.move();
		long endtime = System.currentTimeMillis();
		System.out.println("汽车结束行驶....  汽车行驶时间:" 
				+ (endtime - starttime) + "毫秒!");
	}

	
}

    (2)聚合方法:代理类实现【相同的功能接口:很重要,事项相同接口,不同代理也可以进

package com.imooc.proxy;

//静态代理(聚合方法)
public class Car3 implements Moveable {

	public Car3(Car car) {
		super();
		this.car = car;
	}

	private Car car;
	
	@Override
	public void move() {
		long starttime = System.currentTimeMillis();
		System.out.println("汽车开始行驶....");
		car.move();
		long endtime = System.currentTimeMillis();
		System.out.println("汽车结束行驶....  汽车行驶时间:" 
				+ (endtime - starttime) + "毫秒!");
	}

}
package com.imooc.proxy;

public class Client {

	/**
	 * 测试类
	 */
	public static void main(String[] args) {
        //1、不使用代理
//		Car car = new Car();
//		car.move();
		//2、使用继承方式实现
//		Moveable m = new Car2();
//		m.move();
		//3、使用聚合方式实现
		Car car = new Car();
		Moveable m = new Car3(car);
		m.move();
	}

}

3、总结:聚合比继承更适合静态代理模式

    如果使用继承的方式实现代理功能的叠加,代理类则会无限的膨胀。

例如:增加日志输出功能,则相应增加一个代理类。

    使用聚合的方式实现代理功能的叠加和组合比较方便实用。

例如:

package com.imooc.proxy;

//静态代理(聚合方法),记录日志
public class CarLogProxy implements Moveable {

	public CarLogProxy(Moveable m) {
		super();
		this.m = m;
	}

	private Moveable m;
	
	@Override
	public void move() {
		System.out.println("日志开始....");
		m.move();
		System.out.println("日志结束....");
	}

}
package com.imooc.proxy;
//静态代理(聚合方法),记录时间
public class CarTimeProxy implements Moveable {

	public CarTimeProxy(Moveable m) {
		super();
		this.m = m;
	}

	private Moveable m;
	
	@Override
	public void move() {
		long starttime = System.currentTimeMillis();
		System.out.println("汽车开始行驶....");
		m.move();
		long endtime = System.currentTimeMillis();
		System.out.println("汽车结束行驶....  汽车行驶时间:" 
				+ (endtime - starttime) + "毫秒!");
	}

}
package com.imooc.proxy;

public class Client {

	/**
	 * 测试类
	 */
	public static void main(String[] args) {
		Car car = new Car();
        //组合日志
		CarLogProxy clp = new CarLogProxy(car);
        //组合时间
		CarTimeProxy ctp = new CarTimeProxy(clp);
		ctp.move();
	}

}

二、动态代理

1、动态代理:动态产生代理,实现对【不同类】,【不同方法】的代理。

2、实现:

(1)JDK动态代理实现步骤:

  • 创建实现InvocationHandler的类,必须实现invoke方法
package com.imooc.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TimeHandler implements InvocationHandler {

	public TimeHandler(Object target) {
		super();
		this.target = target;
	}

	private Object target;
	
	/*
	 * 参数:
	 * proxy  被代理对象
	 * method  被代理对象的方法
	 * args 方法的参数
	 * 
	 * 返回值:
	 * Object  方法的返回值
	 * */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		long starttime = System.currentTimeMillis();
		System.out.println("汽车开始行驶....");
		method.invoke(target);
		long endtime = System.currentTimeMillis();
		System.out.println("汽车结束行驶....  汽车行驶时间:" 
				+ (endtime - starttime) + "毫秒!");
		return null;
	}

}

 

  • 创建被代理的类以及接口
同上述【静态代理】的【接口】和【被代理类】

 

  • 调用Proxy的静态方法,创建一个代理类:newProxyInstance
package com.imooc.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.imooc.proxy.Car;
import com.imooc.proxy.Moveable;

public class Test {

	/**
	 * JDK动态代理测试类
	 */
	public static void main(String[] args) {
		Car car = new Car();
		InvocationHandler h = new TimeHandler(car);
		Class<?> cls = car.getClass();
		/**
         * 创建代理类
		 * loader  类加载器
		 * interfaces  实现接口
		 * h InvocationHandler
		 */
		Moveable m = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(),
												cls.getInterfaces(), h);
		m.move();
	}

}

 

  • 通过代理调用方法

(2)cglib动态产生代理

  • 引入cglib-nodep-2.2.jar
  • 编写目标类(被代理类)
package com.imooc.cglibproxy;

//cglib代理,不需要实现接口
public class Train {

	public void move(){
		System.out.println("火车行驶中...");
	}
}
  • 对指定目标类产生一个子类(final修饰的类不能被继承,所以不能被代理。),通过方法拦截技术拦截所有父类方法的调用。
package com.imooc.cglibproxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {

	private Enhancer enhancer = new Enhancer();
	
	public Object getProxy(Class clazz){
		//设置创建子类的类
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		
		return enhancer.create();
	}
	
	/**
	 * 拦截所有目标类方法的调用
	 * obj  目标类的实例
	 * m   目标方法的反射对象
	 * args  方法的参数
	 * proxy代理类的实例
	 */
	@Override
	public Object intercept(Object obj, Method m, Object[] args,
			MethodProxy proxy) throws Throwable {
		System.out.println("日志开始...");
		//代理类调用父类的方法
		proxy.invokeSuper(obj, args);
		System.out.println("日志结束...");
		return null;
	}

}
  • 测试类
package com.imooc.cglibproxy;

public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		CglibProxy proxy = new CglibProxy();
		Train t = (Train)proxy.getProxy(Train.class);
		t.move();
	}

}

 

(3)两者的区别:

  • 只能代理实现某些接口的类,如果没有实现接口的类则不能使用JDK动态代理。
  • 针对类产生代理,原理就是为指定的目标类产生一个子类,子类通过方法拦截技术(覆盖父类的方法来实现功能的增强)拦截所有父类方法的调用(因为该种方式是使用继承方式,所以不能对final修饰的类进行代理)。

三、模拟JDK动态代理实现思路分析及简单实现(https://www.imooc.com/video/4903

  • 创建事务处理接口
package com.imooc.proxy;

import java.lang.reflect.Method;

public interface InvocationHandler {

	public void invoke(Object o,Method m);
}
  • 编写代理生成器
package com.imooc.proxy;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

import org.apache.commons.io.FileUtils;

public class Proxy {
	
	@SuppressWarnings("unchecked")
	public static Object newProxyInstance(Class infce,InvocationHandler h) throws Exception{
		String rt = "\r\n";
		String methodStr = "";
		for(Method m : infce.getMethods()){
			methodStr += "	@Override" + rt +
			"	public void " + m.getName() + "() {" + rt +
			"  try{" + rt +
			"  Method md = " + infce.getName() + ".class.getMethod(\"" 
										+ m.getName() + "\");" + rt +
			"  h.invoke(this,md);" +rt+ 
			"  }catch(Exception e){ e.printStackTrace();}" + rt +
			"	}" ;
		}
		//1、拼接代理类源码
		String str =
		"package com.imooc.proxy;" + rt +
		"import java.lang.reflect.Method;" + rt +
		"import com.imooc.proxy.InvocationHandler;" +  rt+
		"public class $Proxy0 implements " + infce.getName() + " {" + rt +
		"	public $Proxy0(InvocationHandler h) {" + rt +
		"		this.h = h;" + rt +
		"	}" + rt +
		"  private InvocationHandler h;" + rt+ 
		methodStr + rt +
		"}" ;
		//2、产生代理类的java文件到bin目录
		String filename = System.getProperty("user.dir") +"/bin/com/imooc/proxy/$Proxy0.java";
		File file = new File(filename);
		FileUtils.writeStringToFile(file, str);
		
		//3、编译
		//拿到编译器
		JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
		//文件管理者
		StandardJavaFileManager fileMgr = 
				complier.getStandardFileManager(null, null, null);
		//获取文件
		Iterable units = fileMgr.getJavaFileObjects(filename);
		//编译任务
		CompilationTask t = complier.getTask(null, fileMgr, null, null, null, units);
		//进行编译
		t.call();
		fileMgr.close();
		
		//4、load 到内存
		ClassLoader cl = ClassLoader.getSystemClassLoader();
		Class c = cl.loadClass("com.imooc.proxy.$Proxy0");
		//5、创建实例对象返回
		Constructor ctr = c.getConstructor(InvocationHandler.class);
		return ctr.newInstance(h);
	}

	
	
	
}

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值