代理模式(Proxy Pattern),为其他对象提供一个代理,并由代理对象控制原有对象的引用;也称为委托模式。
1.静态代理
2.动态代理
3.cglib代理:如果目标对象没有实现接口,用Cglib代理
静态代理总结:
1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.
2.缺点:
因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。
如何解决静态代理中的缺点呢?
答案是可以使用动态代理方式
spring的Proxy模式在aop中有体现,比如JdkDynamicAopProxy和Cglib2AopProxy。
静态代理
/**
* 创建Person接口
*/
public interface Person {
void giveMoney();//上交班费
}
/**
* Student类实现Person接口。Student可以具体实施上交班费的动作
*/
public class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void giveMoney() {
System.out.println(name + "上交班费50元");
}
}
/**
* 学生代理类,也实现了Person接口,保存一个学生实体,这样既可以代理学生产生行为
*/
public class StudentsProxy implements Person{
//被代理的学生
Student stu;
public StudentsProxy(Person stu) {
// 只代理学生对象
if(stu.getClass() == Student.class) {
this.stu = (Student)stu;
}
}
//代理上交班费,调用被代理学生的上交班费行为
public void giveMoney() {
stu.giveMoney();
}
}
/**
* 测试类
*/
public class StaticProxyTest {
public static void main(String[] args) {
//被代理的学生张三,他的班费上交有代理对象monitor(班长)完成
Person zhangsan = new Student("张三");
//生成代理对象,并将张三传给代理对象
Person monitor = new StudentsProxy(zhangsan);
//班长代理上交班费
monitor.giveMoney();
}
}
/**
* 学生代理类
*/
public class StudentsProxy implements Person{
//被代理的学生
Student stu;
public StudentsProxy(Person stu) {
// 只代理学生对象
if(stu.getClass() == Student.class) {
this.stu = (Student)stu;
}
}
//代理上交班费,调用被代理学生的上交班费行为
public void giveMoney() {
System.out.println("张三最近学习有进步!");
stu.giveMoney();
}
}
可以看到,只需要在代理类中帮张三上交班费之前,执行其他操作(方法)就可以了。
这种操作,也是使用代理模式的一个很大的优点。最直白的就是在Spring中的面向切面编程(AOP),我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。
这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。
动态代理
public void giveMoney() {
beforeMethod();//调用被代理方法前加入处理方法
stu.giveMoney();
}
/**
* 创建Person接口
*/
public interface Person {
void giveMoney(); //上交班费
}
public class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void giveMoney() {
try {
//假设数钱花了一秒时间
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "上交班费50元");
}
}
public class MonitorUtil {
private static ThreadLocal<Long> tl = new ThreadLocal<>();
public static void start() {
tl.set(System.currentTimeMillis());
}
//结束时打印耗时
public static void finish(String methodName) {
long finishTime = System.currentTimeMillis();
System.out.println(methodName + "方法耗时" + (finishTime - tl.get()) + "ms");
}
}
public class StuInvocationHandler<T> implements InvocationHandler {
T target; //invocationHandler持有的被代理对象
public StuInvocationHandler(T target) {
this.target = target;
}
/**
* proxy:代表动态代理对象 method:代表正在执行的方法 args:代表调用目标方法时传入的实参
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行" +method.getName() + "方法");
//代理过程中插入监测方法,计算该方法耗时
MonitorUtil.start();
Object result = method.invoke(target, args);
MonitorUtil.finish(method.getName());
return result;
}
}
测试类
public class ProxyTest {
public static void main(String[] args) {
//创建一个实例对象,这个对象是被代理的对象
Person zhangsan = new Student("张三");
//创建一个与代理对象相关联的InvocationHandler
InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);
//创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
//代理执行上交班费的方法
stuProxy.giveMoney();
}
}
总结一下: 上面的动态代理的例子,其实就是AOP的一个简单实现了,在目标对象的方法执行之前和执行之后进行了处理,对方法耗时统计。Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个东西的。