目录
相关视频:
java设计模式23种设计模式视频(第13~15章代理模式 复合模式 桥接模式)
相关文章:
第一推荐:轻松学,Java 中的代理模式及动态代理
第二推荐:10分钟看懂动态代理设计模式
1、代理模式基本概念及分类
1.1、代理模式定义:
为其他对象提供一种代理,以控制对这个对象的访问。代理对象起到中介作用,可以去掉功能服务或增加额外的服务。
举例:火车购票,可以去直接去火车站也可以去代售点,这个代售点就可以称之为火车站的代理。而代售点除了卖火车票外,可能会有自己额外的服务,比如电话预约。同时,代售点是不能退火车票的,要退的话只能去火车站。这个代售点,少了一些功能服务(退票),增加了一些额外服务(电话预约)。
1.2、几种常见的代理模式:
远程代理:为不同地理的对象,提供局域网代表对象。类似于客户端-服务器的模式。
举例:我有一家连锁店,我想实时了解各个门店的销售信息,我们可以通过远程代理来构造各个门店的监视器,来随时报告各个门店的销售和库存信息。
虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建。
举例:我们在浏览一个新闻的时候,会有文字和图片,如果网络情况不好,我们可以使用虚拟代理,用一个本地的图片暂时替代我们要加载的图片,当图片获取成功后再展示出来,如图二所示。这就是虚拟代理的一个简单应用。
图一:
图二:
保护代理:权限控制。例如,有些网站,只有注册登陆后才能发帖、评论。
智能引用代理:刚才举的火车票代售点的例子。
2、静态代理和动态代理
2.1、静态代理
2.1.1、定义
静态代理:代理和被代理对象在代理之前是确定的。他们都实现相同的接口或者继承相同的抽象类。
2.1.2、代码实战:
我们用代码来实现车行驶。
2.1.2.1、常规方式
Moveable.java
public interface Moveable {
void move();
}
Car.java
public class Car implements Moveable {
/**
* 如果不使用代理模式的话,我们如果想记录车行驶的时间,会采用如下方式
*/
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶...");
// 实现开车
try {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("汽车结束行驶... ");
System.out.println("汽车行驶时间为:" + time + "毫秒");
}
}
Client.java
/**
* 测试类
*/
public class Client {
public static void main(String[] args) {
// 不使用代理模式,常规方式
Car car = new Car();
car.move();
}
}
打印结果为:
2.1.2.2、通过继承的方式实现
Car.java
public class Car implements Moveable {
@Override
public void move() {
// 实现开车
try {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Car2.java
public class Car2 extends Car {
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶...");
// 调用父类 Car 的方法 move()
super.move();
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("汽车结束行驶... ");
System.out.println("汽车行驶时间为:" + time + "毫秒");
}
}
Client.java
public class Client {
public static void main(String[] args) {
// 使用继承的方式
Moveable moveable = new Car2();
moveable.move();
}
}
打印结果为:
2.1.2.3、通过聚合的方式实现
聚合:简单理解就是在一个类中调用另一个类的对象。
Car3.java
public class Car3 implements Moveable {
private Car car;
// 通过构造方法 把 Car 传进来
public Car3(Car car) {
this.car = car;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶...");
car.move();
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("汽车结束行驶... ");
System.out.println("汽车行驶时间为:" + time + "毫秒");
}
}
Clien.java
public class Client {
public static void main(String[] args) {
// 不使用代理模式,常规方式
// Car car = new Car();
// car.move();
// 使用继承的方式
// Moveable moveable = new Car2();
// moveable.move();
// 使用聚合的方式
Car car = new Car();
Car3 car3 = new Car3(car);
car3.move();
}
}
打印结果不变。
以上我们通过继承和聚合两种方式来实现静态代理,那么哪种方式更适合静态代理呢?
我们分析一下,先来说继承这种方式,现在如果需求发生变化,我们就得新建一个子类,如下图:
很显然,这种方式会随着功能的增加而变得越来越臃肿,因此这种方式应该被排除!
那么,聚合的方式呢?
CarTimeProxy.java
public class CarTimeProxy implements Moveable {
private Moveable moveable;
public CarTimeProxy(Moveable moveable) {
this.moveable = moveable;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶...");
moveable.move();
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("汽车结束行驶... ");
System.out.println("汽车行驶时间为:" + time + "毫秒");
}
}
CarLogProxy.java
public class CarLogProxy implements Moveable {
private Moveable moveable;
public CarLogProxy(Moveable moveable) {
this.moveable = moveable;
}
@Override
public void move() {
System.out.println("日志开始...");
moveable.move();
System.out.println("日志结束...");
}
}
Client.java
public class Client {
public static void main(String[] args) {
// 不使用代理模式,常规方式
// Car car = new Car();
// car.move();
// 使用继承的方式
// Moveable moveable = new Car2();
// moveable.move();
// 使用聚合的方式
// Car car = new Car();
// Car3 car3 = new Car3(car);
// car3.move();
//先记录日志,再记录时间
Car car = new Car();
CarTimeProxy carTimeProxy = new CarTimeProxy(car);
CarLogProxy carLogProxy = new CarLogProxy(carTimeProxy);
carLogProxy.move();
}
}
打印结果为:
如果我们想先记录时间再记录日志呢?很简单:
Client.java
public class Client {
public static void main(String[] args) {
// 不使用代理模式,常规方式
// Car car = new Car();
// car.move();
// 使用继承的方式
// Moveable moveable = new Car2();
// moveable.move();
// 使用聚合的方式
// Car car = new Car();
// Car3 car3 = new Car3(car);
// car3.move();
//先记录日志,再记录时间
// Car car = new Car();
// CarTimeProxy carTimeProxy = new CarTimeProxy(car);
// CarLogProxy carLogProxy = new CarLogProxy(carTimeProxy);
// carLogProxy.move();
//先记录时间,再记录日志
Car car = new Car();
CarLogProxy carLogProxy = new CarLogProxy(car);
CarTimeProxy carTimeProxy = new CarTimeProxy(carLogProxy);
carTimeProxy.move();
}
}
打印结果为:
可以看到,使用聚合的方式会比使用继承的方式灵活很多。
那么,接下来,如果我们需要给火车加上行驶时间和日志的记录呢?我们是否需要再新建两个代理类?有没有更简单的方法呢?
那么,有没有一种方法能够动态产生代理,实现对不同类、不同方法的代理呢?下面我们来了解一下动态代理。
2.2、动态代理
2.2.1、用JDK来实现动态代理
我们先来了解一下JDK的动态代理:
图一:
图二:
下面通过代码来了解一下:
TimeHandler.java
/**
* InvocationHandler 事务处理器
*/
public class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
this.target = target;
}
/**
* @param proxy 被代理的对象
* @param method 被代理的对象的方法
* @param args 被代理的对象的方法的参数
* @return 所调用方法的返回值
* @throws Throwable 异常
*/
@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();
long time = endTime - startTime;
System.out.println("汽车结束行驶... ");
System.out.println("汽车行驶时间为:" + time + "毫秒");
return null;
}
}
Test.java
public class Test {
public static void main(String[] args) {
Car car = new Car();
InvocationHandler h = new TimeHandler(car);
Class<?> cls = car.getClass();
/**
* newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
* loader: 被代理类的类加载器
* interfaces: 我们要实现的接口
* h:事务处理器
*/
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
m.move();
}
}
打印结果:
总结:
动态代理实现步骤:
作业,实现时间记录和日志记录:
public class Test {
public static void main(String[] args) {
// timeTest();
// logTest();
timeAndLogTest();
}
private static void timeAndLogTest() {
Car car = new Car();
InvocationHandler timeHandler = new TimeHandler(car);
Class<?> cls = car.getClass();
Moveable moveable = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), timeHandler);
InvocationHandler logHandler = new LogHandler(moveable);
Class<?> class2 = moveable.getClass();
Moveable moveable2 = (Moveable) Proxy.newProxyInstance(class2.getClassLoader(),
class2.getInterfaces(), logHandler);
moveable2.move();
}
private static void logTest() {
Car car = new Car();
InvocationHandler logHandler = new LogHandler(car);
Class<?> cls = car.getClass();
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), logHandler);
m.move();
}
private static void timeTest() {
Car car = new Car();
InvocationHandler timeHandler = new TimeHandler(car);
Class<?> cls = car.getClass();
/**
* newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
* loader: 被代理类的类加载器
* interfaces: 我们要实现的接口
* h:事务处理器
*/
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), timeHandler);
m.move();
}
}
2.2.2、JDK动态代理和CGLIB动态代理的区别
CglibProxy.java
package com.ph.proxy.cglibProxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer=new Enhancer();
public Object getProxy(Class clazz){
// 设置创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 拦截所有目标类方法的调用
* @param o 目标类的实例
* @param method 目标方法的反射对象
* @param objects 方法的参数
* @param methodProxy 代理类的实例
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
System.out.println("日志开始...");
//代理类调用父类的方法
methodProxy.invokeSuper(o,objects);
System.out.println("日志结束...");
return null;
}
}
Train.java
package com.ph.proxy.cglibProxy;
public class Train {
public void move(){
System.out.println("train running...");
}
}
Client.java
package com.ph.proxy.cglibProxy;
public class Client {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
Train train = (Train) proxy.getProxy(Train.class);
train.move();
}
}
3、模拟JDK动态代理实现思路分析及简单实现
动态代理实现思路
实现功能:通过Proxy的newProxyInstance返回代理对象
1、声明一段源码(动态产生代理)
2、编译源码(JDK Compiler API),产生新的类(代理类)
3、将这个类load到内存中,产生一个新的对象(代理对象)
4、return 代理对象
3.1、初步实现
3.1.1、Car.java
package com.test.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();
}
}
}
3.1.2、Moveable.java
package com.test.proxy;
public interface Moveable {
void move();
}
3.1.3、CarTimeProxy.java
package com.test.proxy;
public class CarTimeProxy implements Moveable {
private Moveable moveable;
public CarTimeProxy(Moveable moveable) {
this.moveable = moveable;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶...");
moveable.move();
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("汽车结束行驶... ");
System.out.println("汽车行驶时间为:" + time + "毫秒");
}
}
3.1.4、Proxy.java
package com.test.proxy;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
public class Proxy {
public static Object newInstance() {
String str = "package com.test.proxy;\n" +
"\n" +
"public class $Proxy0 implements Moveable {\n" +
"\n" +
" private Moveable moveable;\n" +
"\n" +
" public $Proxy0(Moveable moveable) {\n" +
" this.moveable = moveable; \n" +
" }\n" +
"\n" +
" @Override\n" +
" public void move() {\n" +
" long startTime = System.currentTimeMillis();\n" +
" System.out.println(\"汽车开始行驶...\");\n" +
"\n" +
" moveable.move();\n" +
"\n" +
" long endTime = System.currentTimeMillis();\n" +
" long time = endTime - startTime;\n" +
" System.out.println(\"汽车结束行驶... \");\n" +
" System.out.println(\"汽车行驶时间为:\" + time + \"毫秒\");\n" +
" }\n" +
"}";
/**
* System.getProperty("user.dir") -- D:\idea_ws\java\ProxyTest1
*
*/
String fileName = System.getProperty("user.dir") + "/bin/com/test/proxy/$Proxy0.java";
// System.out.println(fileName);
File file = new File(fileName);
try {
FileUtils.writeStringToFile(file, str);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
3.1.5、Client.java
package com.test.proxy;
/**
* 测试类
*/
public class Client {
public static void main(String[] args) {
Proxy.newInstance();
}
}
3.1.6、 Proxy.java
以上只是初步实现,但是我们的目标是对任意对象的任意方法产生任意代理,所以还需要继续改造。
package com.test.proxy;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
public class Proxy {
public static Object newInstance(Class interfa) {
String rt = "\r\n";
String methodStr = "";
for (Method m : interfa.getMethods()) {
methodStr += " @Override" + rt +
" public void " + m.getName() + "() {" + rt +
" long starttime = System.currentTimeMillis();" + rt +
" System.out.println(\"汽车开始行驶....\");" + rt +
" m." + m.getName() + "();" + rt +
" long endtime = System.currentTimeMillis();" + rt +
" System.out.println(\"汽车结束行驶.... 汽车行驶时间:\" " + rt +
" + (endtime - starttime) + \"毫秒!\");" + rt +
" }";
}
String str = "package com.test.proxy;\n" +
"\n" +
"public class $Proxy0 implements " + interfa.getName() + "{\n" +
"\n" +
" private " + interfa.getName() + " m;\n" +
"\n" +
" public $Proxy0(" + interfa.getName() + " m) {\n" +
" super();" + rt +
" this.m = m; \n" +
" }" + rt +
methodStr + rt +
"}";
/**
* System.getProperty("user.dir") -- D:\idea_ws\java\ProxyTest1
*
*/
String fileName = System.getProperty("user.dir") + "/bin/com/test/proxy/$Proxy0.java";
// System.out.println(fileName);
File file = new File(fileName);
try {
FileUtils.writeStringToFile(file, str);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
3.2、完善动态代理实现
3.2.1、Proxy.java
package com.test.proxy;
import org.apache.commons.io.FileUtils;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Proxy {
public static Object newInstance(Class interfa) throws Exception {
String rt = "\r\n";
String methodStr = "";
for (Method m : interfa.getMethods()) {
methodStr += " @Override" + rt +
" public void " + m.getName() + "() {" + rt +
" long starttime = System.currentTimeMillis();" + rt +
" System.out.println(\"汽车开始行驶....\");" + rt +
" m." + m.getName() + "();" + rt +
" long endtime = System.currentTimeMillis();" + rt +
" System.out.println(\"汽车结束行驶.... 汽车行驶时间:\" " + rt +
" + (endtime - starttime) + \"毫秒!\");" + rt +
" }";
}
String str = "package com.test.proxy;\n" +
"\n" +
"public class $Proxy0 implements " + interfa.getName() + "{\n" +
"\n" +
" private " + interfa.getName() + " m;\n" +
"\n" +
" public $Proxy0(" + interfa.getName() + " m) {\n" +
" super();" + rt +
" this.m = m; \n" +
" }" + rt +
methodStr + rt +
"}";
/**
* 1、产生代理类的Java文件
* System.getProperty("user.dir") -- D:\idea_ws\java\ProxyTest1
*/
String fileName = System.getProperty("user.dir") + "/src/com/test/proxy/$Proxy0.java";
File file = new File(fileName);
try {
FileUtils.writeStringToFile(file, str);
} catch (IOException e) {
e.printStackTrace();
}
/**
* 2、编译源码(JDK Compiler API),产生新的类(代理类)
*/
//编译
//拿到编译器
JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
//文件管理者
StandardJavaFileManager fileMgr =
complier.getStandardFileManager(null, null, null);
//获取文件
Iterable units = fileMgr.getJavaFileObjects(fileName);
//编译任务
JavaCompiler.CompilationTask t = complier.getTask(null, fileMgr,
null, null, null, units);
//进行编译
t.call();
fileMgr.close();
/**
* 3、将这个类load到内存中,产生一个新的对象(代理对象)
*/
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class c = cl.loadClass("com.test.proxy.$Proxy0");
Constructor ctr = c.getConstructor(interfa);
return ctr.newInstance(new Car());
}
}
3.2.2、Client.java
package com.test.proxy;
/**
* 测试类
*/
public class Client {
public static void main(String[] args) throws Exception {
Moveable moveable = (Moveable) Proxy.newInstance(Moveable.class);
moveable.move();
}
}
3.2.3、$Proxy0.java
package com.test.proxy;
public class $Proxy0 implements com.test.proxy.Moveable{
private com.test.proxy.Moveable m;
public $Proxy0(com.test.proxy.Moveable m) {
super();
this.m = m;
}
@Override
public void move() {
long starttime = System.currentTimeMillis();
System.out.println("汽车开始行驶....");
m.move();
long endtime = System.currentTimeMillis();
System.out.println("汽车结束行驶.... 汽车行驶时间:"
+ (endtime - starttime) + "毫秒!");
}
}
3.2.4、目录结构
3.3、动态代理实现添加InvocationHandler
3.3.1、InvocationHandler.java
package com.test.proxy;
import java.lang.reflect.Method;
public interface InvocationHandler {
public void invoke(Object o, Method m);
}
3.3.2、TimeHandler.java
package com.test.proxy;
import java.lang.reflect.Method;
public class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
@Override
public void invoke(Object o, Method m) {
try {
long starttime = System.currentTimeMillis();
System.out.println("汽车开始行驶....");
m.invoke(target);
long endtime = System.currentTimeMillis();
System.out.println("汽车开始行驶...."
+ (endtime - starttime) + "毫秒!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.3.3、Proxy.java
package com.test.proxy;
import org.apache.commons.io.FileUtils;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Proxy {
public static Object newInstance(Class interfa, InvocationHandler handler) throws Exception {
String rt = "\r\n";
String methodStr = "";
for (Method m : interfa.getMethods()) {
methodStr += " @Override" + rt +
" public void " + m.getName() + "() {" + rt +
" try{" + rt +
" Method md = " + interfa.getName() + ".class.getMethod(\""
+ m.getName() + "\");" + rt +
" h.invoke(this,md);" + rt +
" }catch(Exception e){ e.printStackTrace();}" + rt +
" }";
}
String str = "package com.test.proxy;\n" + rt +
"import java.lang.reflect.Method;" + rt +
"import com.test.proxy.InvocationHandler;" + rt +
"public class $Proxy0 implements " + interfa.getName() + "{\n" +
" public $Proxy0(InvocationHandler h) {" + rt +
" this.h = h;" + rt +
" }" + rt +
" private InvocationHandler h;" + rt +
methodStr + rt +
"}";
/**
* 1、产生代理类的Java文件
* System.getProperty("user.dir") -- D:\idea_ws\java\ProxyTest1
*/
String fileName = System.getProperty("user.dir") + "/src/com/test/proxy/$Proxy0.java";
File file = new File(fileName);
try {
FileUtils.writeStringToFile(file, str);
} catch (IOException e) {
e.printStackTrace();
}
/**
* 2、编译源码(JDK Compiler API),产生新的类(代理类)
*/
//编译
//拿到编译器
JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
//文件管理者
StandardJavaFileManager fileMgr =
complier.getStandardFileManager(null, null, null);
//获取文件
Iterable units = fileMgr.getJavaFileObjects(fileName);
//编译任务
JavaCompiler.CompilationTask t = complier.getTask(null, fileMgr,
null, null, null, units);
//进行编译
t.call();
fileMgr.close();
/**
* 3、将这个类load到内存中,产生一个新的对象(代理对象)
*/
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class c = cl.loadClass("com.test.proxy.$Proxy0");
Constructor ctr = c.getConstructor(InvocationHandler.class);
return ctr.newInstance(handler);
}
}
3.3.4、Client.java
package com.test.proxy;
/**
* 测试类
*/
public class Client {
public static void main(String[] args) throws Exception {
Car car = new Car();
InvocationHandler handler = new TimeHandler(car);
Moveable moveable = (Moveable) Proxy.newInstance(Moveable.class, handler);
moveable.move();
}
}