一、静态代理
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);
}
}