代理
为什么用代理?
- 有些类不能被修改的,因业务需要需要扩展这个类的功能。使用代理模式,可以基于被代理类不变的前提下,对被代理类的行为进行控制和扩展。
- 前提:
- 被代理类不变(或无法修改)
- 被代理类是基于接口的实现,因为代理类也需要实现这个接口。
静态代理
静态代理需要在程序运行之前,实现代理类并增加的业务需要的控制或扩展。
实现
- 提供一个被代理类的接口
- 被代理类实现这个接口
- 代理类实现这个接口
- 代理类中引用被代理类的对象
示例
例如:老师,他的功能是讲课
1. 定义接口
public interface ITeacher {
void teach();
}
2. 定义被代理类
public class Teacher implements ITeacher {
@Override
public void teach() {
System.out.println("开始讲课~~~~~~~~~");
}
}
3. 定义代理类
(1). 在讲课之前让老师说"上课起立!"、讲课之后说"下课!"
public class TeacherProxy implements ITeacher {
private ITeacher iTeacher;
public TeacherProxy(ITeacher iTeacher) {
this.iTeacher = iTeacher;
}
@Override
public void teach() {
System.out.println("上课起立!");
iTeacher.teach();
System.out.println("下课!");
}
}
(2). 也可以写一个稍复杂点的:假设有老师zhang,喜欢的说"上课起立!“后礼貌地说"请坐下!”
public class TeacherProxy implements ITeacher {
private ITeacher iTeacher;
private String name;
public TeacherProxy(String name) {
this.name= name;
}
@Override
public void teach() {
init();
System.out.println("上课起立!");
if(name.equals("zhang")){
System.out.println("请坐下!");
}
iTeacher.teach();
System.out.println("下课!");
}
private void init(){
if(iTeacher == null){
iTeacher = new Teacher();
}
}
}
4. 调用
private static void staticProxyTest() {
Teacher teacher = new Teacher();
TeacherProxy teacherProxy = new TeacherProxy(teacher);
teacherProxy.teach();
}
缺点
不够解耦
动态代理
重点还是在与动态代理的应用和理解上,java为提供了Proxy类帮助实现动态代理,实现起来也很简单。不需要创建代理类,只需要已知被代理类
和被代理类的实现接口
。
实例
1. 定义接口
扩展一下之前的接口,方便测试
public interface ITeacher {
void teach();
//老师也会唱歌
void sing();
//老师有自己的评分(年度评分评级之类的...)
int getScore();
}
2. 定义被代理类
扩展一下之前的被代理类,方便测试
public class Teacher implements ITeacher {
private int score;
@Override
public void teach() {
System.out.println("开始讲课~~~~~~~~~");
}
@Override
public void sing() {
System.out.println("音乐课,唱首歌~~~~~~~~~~~~~");
}
@Override
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
3. 调用
private static void jdkProxyTest() {
//被代理者
Teacher teacherDelegatee = new Teacher();
teacherDelegatee.setScore(88);
ITeacher teacherProxy = (ITeacher) Proxy.newProxyInstance(
teacherDelegatee.getClass().getClassLoader(),
teacherDelegatee.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//可以通过方法名来区分每次调用对应的新增处理代码
String methodName = method.getName();
if (methodName.equals("teach")) {
System.out.println("上课起立!");
method.invoke(teacherDelegatee, args);
System.out.println("下课!");
}
if (methodName.equals("sing")) {
System.out.println("上课起立!");
method.invoke(teacherDelegatee, args);
System.out.println("下课!");
}
if (methodName.equals("getScore")) {
Object returnValue = method.invoke(teacherDelegatee, args);
//假设做一个给老师加25分的操作
returnValue = (int) returnValue + 25;
//又返回值的情况
return returnValue;
}
//默认无返回值
return null;
}
});
//调用多个方法
System.out.println("------------------调用 teach()---------------");
teacherProxy.teach();
System.out.println("------------------调用 sing()-----------------");
teacherProxy.sing();
System.out.println("------------------调用 getScore()-------------");
//调用有返回值的getScore()
System.out.println("教师评分:" + teacherProxy.getScore());
}
其中
Proxy.newProxyInstance
创建的是ITeacher 对象,其中的参数解释如下:ClassLoader loader
,类加载器。被代理类的类加载器。Class<?>[] interfaces
,被代理类所实现的接口,这个接口也可以是多个。InvocationHandler h
,绑定代理类方法。代理类每调用一次任一方法,h(InvocationHandler)
的invoke
方法对应调用一次。
- 其它说明参见注释。
和装饰模式的比较
静态代理和装饰模式比较,发现实现方式几乎一模一样。那么区别在哪里?总结一下常规的说法:
- 静态代理主要做的是对被代理类增加控制,装饰模式主要是对原有对象的扩展。这主要体现在上面静态代理示例中,定义代理类的(2)种方式中。
其实是可以灵活使用的嘛,不需要那么纠结,在不增加耦合、不出bug的前提下,所有的设计模式都可以根据业务需求混合使用。