代理模式:有时,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理可以静默的解决一些业务无关的问题,比如远程、安全、事务、日志、资源关闭等,而开发者只关心他的业务实现。比如一个复杂的业务操作可能需要好几个方法来完成,但是我们可以用一个总的方法来依次调用这几个方法即可,这个方法代理了真正的实现。
void sum(){
int a = method1();
String b = method2();
...
}
int method1(){
// 执行业务1
return 10;
}
String method2(int a){
// 执行业务2
return 'AA;
}
一般形式为一个类代表另一个类的功能。
比如老板,秘书,员工。老板比较忙,平时员工是见不到的,就把审批等请求给秘书,然后秘书再找老板处理,处理完毕后交给员工。
如新员工要转正,需要老板批准,但是老板很忙,只负责查看评语然后做出批复。但是老板的秘书可以去找新员工的直接领导了解员工表现,整理直接领导对员工的评语。然后加上评语和新员工转正申请,一起给老板。老板同意之后,给予新员工答复,同时秘书会把转正申请归档。这就可以在老板批复的动作前后增加工作,增强这个事件。
实际工作中,真正的实现类,只负责处理核心业务逻辑,然后代理类就可以在核心方法调用前后,做增强工作。比如记录日志,监控性能,安全检查等。
优点:
- 职责清晰
- 代理对象作为中介,保护了目标对象
- 高扩展性
组成结构:
- 抽象角色:通过接口或抽象类声明真是角色实现的业务方法
- 代理角色:实现抽象接口,在自己的实现方法中调用真实角色对应的方法
- 真实角色:实现抽象接口,真正业务逻辑
代理模式又分为静态代理和动态代理
1)静态代理 :就是普通的编码,已经写好了。
2)动态代理:在运行阶段创建
在不改变原有实现的基础上,实现如下的功能
- 日志记录
- 权限控制
- 事务处理
静态代理
静态代理就是我们平时写的代码,比如有一个真正实现的类A,但是客户端不去直接调用A,而是通过B这个类,然后B再调用A。比如spring MVC中,要插入一条数据。首先是控制器调用服务层,然后服务层再去调用DAO层,数据在DAO层真正插入数据库。服务层就是一个代理,并且可以在服务层做一些特殊处理,比如数据转换等。
public interface Subject {
/**
* 简单测试
*/
void visit();
/**
* 求和
*
* @param a
* @param b
* @return 求和后的值
*/
int sum(int a, int b);
/**
* 返回当前名称
*
* @return
*/
String getName();
/***
* 问候
* @param name
*/
void greeting(String name);
}
public class RealSubject implements Subject {
private String name = "真正的实现类";
@Override
public void visit() {
System.out.println(name);
}
@Override
public int sum(int a, int b) {
return a + b;
}
@Override
public String getName() {
return "我是真正的实现";
}
@Override
public void greeting(String name) {
System.out.println("Hello " + name);
}
}
public class ProxySubject implements Subject {
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public void visit() {
this.subject.visit();
}
@Override
public int sum(int a, int b) {
return this.subject.sum(a, b);
}
@Override
public String getName() {
return this.subject.getName();
}
@Override
public void greeting(String name) {
this.subject.greeting(name);
}
}
public class Client {
public static void main(String[] args) {
ProxySubject proxySubject = new ProxySubject(new RealSubject());
proxySubject.visit();
System.out.println(proxySubject.getName());
proxySubject.greeting("詹姆斯");
int result = proxySubject.sum(1, 2);
System.out.println(result);
}
}
动态代理
为什么要有动态代理?
- 代理类需要有和真实实现类一样有所有的方法实现,当方法较多时,好多重复性的,机械的工作。代码相似冗余,不易维护
- 一般一个实现类需要关联一个代理类。
- 每当一个新的类实现接口,就需要创建对应的代理类
动态代理的本质就是在运行时 动态 的生成一个新类。前提是需要有一个接口。为什么呢?因为这个新生成的类要有和目标类一样的功能。通过接口可以保证目标类必须包含那些方法。
具体实现手段是反射,通过Method的调用和InvocationHandler
目前实现动态代理的方式有jdk Proxy和cglib,其中jdk自身实现有如下优点:
- 亲儿子,本身就支持,不存在依赖问题和第三方版本问题
- 代码实现简单
这二种的本质区别是,jdk需要一个接口,然后根据这个接口来在运行时-运行时-运行时动态-动态-动态的生成一个代理类出来,而cglib是创建一个目标类的子类,因此目标类的关键方法不能声明为final。
组成结构:
- 接口对象
- 具体实现类
- 实现InvocationHandler的通用类
public class AnimalJdkDynamicProxy implements InvocationHandler {
/**
* 01.我们不确定委托类是谁?委托类的类型 是Object
* 和委托类建立关联关系
*/
private Object target;
/**
* 02.给我一个委托类,我返回一个代理类对象
*/
public Object createProxy(Object target){
//根据传递的参数 进行对象的关联
this.target=target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
*
* @param proxy :代理对象
* @param method :方法名
* @param args : 参数列表
* @return
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("主人在召唤"); //系统级业务 开始事务
Object result= method.invoke(target,args); // 主业务
System.out.println("主人离开"); //系统级业务 日志处理 关闭事务
return result;
}
//创建测试方法
public static void main(String[] args) {
AnimalJdkDynamicProxy proxy=new AnimalJdkDynamicProxy();
Animal dog= (Animal) proxy.createProxy(new Dog());
dog.eat();
System.out.println("**************************");
dog.sleep();
}
}
动态代理一般用在什么地方?主要用于各种通用框架中,平时实际项目中用到的可能不多。