装饰者模式&&动态代理
今日任务:
1.学习动态代理技术 使用动态技术完成无侵入式的增强事务逻辑
/
装饰者模式
装饰者设计模式 (静态代理)
目的: 增强原有对象的功能
应用点: IO流对象 (Writer Reader) 基础流
BufferedXXX,打印流,装饰类
装饰者设计模式 思想:
增强原有对象功能
Reader 原有功能,基础流
BufferedReader 装饰流
比喻:
地暖,取暖
地面,铺上一层地暖
地面(承重)
使用装饰对象,必须先存在被装饰对象
new BufferedReader(Reader r)
不改变原有功能,在原有的功能上进行增强
装饰者设计模式 || 静态代理:
无侵入式编程:不破坏上层代码
1.装饰者类跟原本实现类必须实现同一个接口(面向接口的特征 无侵入式编程)
2.装饰者类对象必须持有原本对象的 我不是替代你的 是补充增强
3.不需要增强的方法 调用原来对象
4.需要增强的 我们自己补充逻辑
代码实现1:
CarTest
public class CarTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Car car = new QQWrapper(new QQ());
car.run();
}
}
接口Car
public interface Car {
void run();
void stop();
int oilTank();
void driver(String driverName);
Car didi();
}
实现类QQ
public class QQ implements Car {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("qq完成百公里加速");
} catch (Exception e) {
}
}
后续方法..........
}
代理QQWrapper
public class QQWrapper implements Car {
private Car qq;
public QQWrapper(Car qq) {
super();
this.qq = qq;
}
@Override
public void run() {
long start = System.currentTimeMillis();
qq.run();
long end = System.currentTimeMillis();
System.out.println("百公里加速用时"+(end-start));
}
后续方法.....
}
代码实现2:
Test.java
public class Test {
public static void main(String[] args)throws IOException {
//创建原始流对象
FileReader fr = new FileReader("c:\\1.txt");
//创建自己定义的装饰类,构造方法,传递原始流
MyBufferedReader my = new MyBufferedReader(fr);
String line = null;
while ( (line = my.readLine())!=null){
System.out.println(line);
}
my.close();
}
}
MyBufferedReader.java
/**
* 装饰设计思想,自定义装饰类
* 模拟出 BufferedReader 的方法readLine()
*
* new MyBufferedReader(new FileReader(文件路径))
* 传递来的流FileReader,本身具有读取功能 read(),读取一个字符
* 利用原有的流方法read(),实现读取一行
*
* len=='\r' int和char计算,char提示为int计算
*/
public class MyBufferedReader extends Reader {
private Reader r ;//new FileReader(文件路径);
//构造方法,装饰哪个流,传递过来
public MyBufferedReader(Reader r){
this.r = r;
}
//创建方法,实现读取文本一行
public String readLine()throws IOException{
int len = 0;
//创建字符串缓冲区对象
StringBuilder sb = new StringBuilder();
//利用原始流 方法read()读取单个字符
//判断是不是读取到了换行符
while ( (len=r.read())!=-1 ){
//判断是不是 \r
if(len=='\r')
continue;
//判断是不是 \n
if(len == '\n')
//一行读取结束了,容器中的字符取出,返回
//字符串缓冲区中的内容变成String对象返回
return sb.toString();
//判断不是\r也不是\n 读取到是个有效字符
//字符追加缓冲区
sb.append((char)len);
}
//循环结束了,read()返回-1,文件读取完毕
//判断缓冲区是不是有字符
if(sb.length()>0)
return sb.toString();
return null;
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return 0;
}
@Override
public void close() throws IOException {
r.close();
}
}
动态代理
动态代理原理
Test
/**
* 实现动态代理程序
* 创建代理对象(经纪人)
* 被代理对象(明星) ArrayList
*
* 我是调用者,调用目标对象的方法 ArrayList
* 通过代理对象(经纪人)
* 方法: add remove size(运行) get
*
* Proxy.newInstance( 被代理对象的类加载器,被代理对象实现的接口, 代理对象执行代理方法的接口 ) 返回的是代理对象
* InvocationHandler 是代理对象实现代理功能接口
* 方法 Object invoke(被代理对象,被代理对象的方法,被代理对象方法的实际参数)
* 返回值,被代理对象执行方法后的结果
*
*
* Object Proxy.newInstance( ArrayList.class.getClassLoader,
* ArrayList.class.getInterfaces, 代理对象执行代理方法的接口 InvocationHandler ) 返回的是代理对象
*
* class A implements InvocationHandler{
* public Object invoke(ArrayList,被代理对象方法 get, 被代理方法的实际参数 1){
*
* 返回的是 get()方法结果
* }
*
* }
*/
public class Test {
public static void main(String[] args) {
List<String> arrayList = new ArrayList<String>();
arrayList.add("1");
arrayList.add("2");
//调用方法,传递被代理对象,返回代理后的对象
List<String> newList = (List<String>) getProxy(arrayList);
System.out.println(newList.get(0));
}
/**
* 定义方法,实现返回代理对象
*/
public static Object getProxy(List<String> list){
//调用工具类方法
// 代理对象类的加载器
// 被代理对象实现的接口
// InvocationHandler接口实现类
return Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), new InvocationHandler() {
@Override
/**
* proxy被代理对象 list
* method 调用对象的方法
* args 方法参数
* 返回: 执行方法后的结果
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//Method对象,可以获取到被代理对象调用的方法名
String methodName = method.getName();
if("size".equals(methodName)){
//运行执行
return method.invoke(proxy,args);
}else{
//不是 指定方法,不能运行
throw new RuntimeException("不能使用该方法");
// return null;
}
}
});
}
}
类的加载器
/**
* 获取一个类的加载器
* java.lang.ClassLoader 类的加载器
* 类的加载器,是个对象, 都是ClassLoader对象
* 任何一个类,静态属性class,获取该类的class文件对象
* 通过class文件对象的方法 getClassLoader()获取他的加载器
*/
public class Test {
public static void main(String[] args) {
ClassLoader classLoader = Test.class.getClassLoader();
System.out.println(classLoader);
classLoader = String.class.getClassLoader();
System.out.println(classLoader);
classLoader = DNSNameService.class.getClassLoader();
System.out.println(classLoader);
}
}
使用动态代理技术
保留 无侵入增强的特色
去除 写大量代码 写大量类
jdk动态代理
在java.lang.reflect 包 有一个类 Proxy 有一个方法 帮助实现
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) ;
使用这个方法 可以直接帮你产出一个已经被增强过的对象
三个参数:
ClassLoader:类加载器 随便一个类加载器(给一个除去特殊 引导类加载,和扩展类加载 使用自己引入第三方自己的类 得到那个类加载器)
interfaces:接口数组 传入原本对象实现所有接口数组
InvocationHandler:执行处理类对象(难点);
实现它InvocationHandler接口
Object invoke(Object proxy, Method method, Object[] args)
proxy:代表的是 即将返回的增强对象
method:代表的是增强对象调用方法的时候的方法本身
args:代表的是增强对象调用方法传入实参本身
Demo01.java
public class Demo01 {
public static void main(String[] args) {
//目的增强逻辑
QQ qq=new QQ();
//换动态代理 返回一个增强的对象
Car zq = (Car) Proxy.newProxyInstance(
Demo01.class.getClassLoader(),
qq.getClass().getInterfaces(),
new MyInvocationHandler(qq)
);
//zq.run();
//zq.stop();
//zq.driver("小明");
//int i = zq.oilTank();
//System.out.println(i);
zq.didi().didi().didi();
}
private static class MyInvocationHandler implements InvocationHandler{
private Car car;
public MyInvocationHandler(Car car) {
this.car = car;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//System.out.println(proxy);
//System.out.println(method.getName());
if ("run".equals(method.getName())){
long start = System.currentTimeMillis();
// 平常调用方法 对象.方法
// 方法.invoke(对象)
method.invoke(car);
long end = System.currentTimeMillis();
System.out.println("百公里加速用时"+(end-start));
}else if("driver".equals(method.getName())) {
System.out.println(Arrays.toString(args));
method.invoke(car,args[0]+"的媳妇儿");
}else if("oilTank".equals(method.getName())) {
Integer invoke = (Integer) method.invoke(car, args);
return invoke+100;
}else if("didi".equals(method.getName())) {
System.out.print("增强对象在调用:");
method.invoke(car);
return proxy;
}else {
//调用原来的方法
method.invoke(car);
}
return null;
}
}
}
invoke方法参数里输出proxy报错
解决方法
看源码:
直接输出proxy,调用toString方法,toString方法里是invoke,这样无限循环造成死递归,栈溢出,就会报错