为一个对象提供一种代理,用来控制对这个对象的访问。
角色分类:
1、Subject 抽象主题角色 抽象主题类可以是抽象类也可以是接口, 是一个最普通的业务类型定义, 无特殊要求。
2、RealSubject 具体主题角色,被委托角色、 被代理角色。 实现Subject接口,是业务逻辑的具体执行者。
3、Proxy 代理主题角色。是委托类、 代理类。 它负责对真实角色的应用, 把所有抽象主题类定义的方法限制委托给真实主题角色实现, 并且在真实主题角色处理完毕前后做预处理和善后处理工作。
主要分三部分
1、静态代理
2、动态代理(讲JDK,CGLIB 以后说)
3、JDK动态代理原理
模拟场景:给明星打电话,一般都是助理接的。
先定义一个接电话接口和一个实现类
/**
* 接电话主题
*/
public interface AnswerSubject {
/**
* 接电话
* @param name 明星名字
*/
void answer(String name);
}
/**
* 实现类
*/
public class RealAnswerSubject implements AnswerSubject {
@Override
public void answer(String name) {
System.out.println("你好,我是"+name+"的助理。");
}
}
1、静态代理
说白了就是代理类 new 出来的,静态的、知道代理类是那一个类。
1.1 静态代理
/**
* 静态接电话代理
*/
public class AnswerProxy implements AnswerSubject {
/**
* 被代理对象
*/
private AnswerSubject answerSubject;
public AnswerProxy(AnswerSubject answerSubject) {
this.answerSubject = answerSubject;
}
@Override
public void answer(String name) {
/**
* 接电话的实现委托给被代理对象调用
*/
answerSubject.answer(name);
}
}
测试:直接 new 出已知的代理类对象,所有是静态代理。
public static void main(String[] args) {
//直接 new 出代理类对象
AnswerProxy answerProxy = new AnswerProxy(new RealAnswerSubject());
answerProxy.answer("张大头");
}
1.2 静态代理变种之普通代理
不能直接给明星打电话,得要先通过助理。
就是:被代理对象只能通过代理进行访问,不能直接访问被代理对象。
实现类改造一下:
public class RealAnswerSubject implements AnswerSubject {
public RealAnswerSubject(AnswerProxy proxy) {
//没有代理,就不能new,不能直接访问
if(proxy==null){
throw new RuntimeException("没有代理");
}
}
@Override
public void answer(String name) {
System.out.println("你好,我是"+name+"的助理。");
}
}
代理对象改造:
/**
* 接电话代理
*/
public class AnswerProxy implements AnswerSubject {
/**
* 被代理对象
*/
private AnswerSubject answerSubject;
public AnswerProxy() {
//创建被代理对象,代理对象为参数
this.answerSubject = new RealAnswerSubject(this);
}
@Override
public void answer(String name) {
/**
* 接电话的实现委托给被代理对象调用
*/
answerSubject.answer(name);
}
}
测试类:这时就直接使用代理对象,不用 new 被代理对象了,被代理对象不用对外暴露,不知道被代理的是谁。
public static void main(String[] args) {
//直接 new 出代理类对象
AnswerProxy answerProxy = new AnswerProxy();
answerProxy.answer("张大头");
}
1.3 静态代理变种之强制代理
可能和明星比较熟悉,给明星打电话,这时明星说你找下我助理吧,我助理知道。
就是:想绕过代理,直接访问被代理对象,但是呢被代理对象(明星)又返回一个代理对象(助理)给你。通过真实角色查找到代理角色, 否则你不能访问。
接口改造:
/**
* 接电话主题
*/
public interface AnswerSubject {
/**
* 接电话
* @param name 明星名字
*/
void answer(String name);
/**
* 每个明星都有一个自己的助理
* @return
*/
AnswerSubject getProxy();
}
实现类改造:
/**
* 实现类
*/
public class RealAnswerSubject implements AnswerSubject {
//代理
private AnswerProxy answerProxy;
@Override
public AnswerSubject getProxy() {
//创建代理对象,被代理的就是当前对象
answerProxy = new AnswerProxy(this);
return answerProxy;
}
@Override
public void answer(String name) {
//没有代理对象,不能访问
if(answerProxy==null){
throw new RuntimeException("不能直接访问");
}
System.out.println("你好,我是"+name+"的助理。");
}
}
代理类改造:
/**
* 接电话代理
*/
public class AnswerProxy implements AnswerSubject {
/**
* 被代理对象
*/
private AnswerSubject answerSubject;
/**
* @param answerSubject 被代理对象
*/
public AnswerProxy(AnswerSubject answerSubject) {
this.answerSubject = answerSubject;
}
@Override
public void answer(String name) {
answerSubject.answer(name);
}
@Override
public AnswerSubject getProxy() {
//代理对象返回的就是本身
return this;
}
}
测试:必须要先获取代理对象,通过代理对象访问。这种方法代理对象是在真实对象中控制的,对外只有通过真实对象获取真实对象的代理对象才能访问。
public static void main(String[] args) {
//直接 new 出代理类对象
AnswerSubject answerSubject = new RealAnswerSubject();
//不能直接访问
//answerSubject.answer("张大头");
//获取代理对象,只能通过代理对象访问
AnswerSubject proxy = answerSubject.getProxy();
proxy.answer("张小头");
}
1.4 静态代理扩展
给明星打电话,前几次是助理接的,说明星在忙。打第三次明星才接。这样就要一个扩展,来记录打电话的次数。在每次打电话后都要调用这个记录方法,并且代理要实现这个接口,在处理方法之前或者之后调用。
2、动态代理
/**
* 接电话主题
*/
public interface AnswerSubject {
/**
* 接电话
* @param name 明星名字
*/
void answer(String name);
}
/**
* 实现类
*/
public class RealAnswerSubject implements AnswerSubject {
@Override
public void answer(String name) {
System.out.println("你好,我是"+name+"的助理。");
}
}
JDK 的接口 InvocationHandler,实现此接口,被代理对象方法进行代理
/**
* 被代理类的方法进行代理
*/
public class AnswerHandler implements InvocationHandler {
/**
* 被代理对象
*/
private Object target;
public AnswerHandler(Object target) {
this.target = target;
}
/**
* 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//反射调用
Object result = method.invoke(target, args);
return result;
}
}
测试:
public static void main(String[] args) throws Exception{
//被代理对象
RealAnswerSubject answerSubject = new RealAnswerSubject();
//被代理对象handler
InvocationHandler handler = new AnswerHandler(answerSubject);
ClassLoader loader = answerSubject.getClass().getClassLoader();
//动态生成代理对象
AnswerSubject proxy = (AnswerSubject)Proxy.newProxyInstance(loader, new Class[]{AnswerSubject.class}, handler);
proxy.answer("张大头");
}
通过JDK Proxy.newProxyInstance() 方法生成代理对象,这个代理对象实现了被代理对象接口的所有方法。
代理对象就是通过InvocationHandler 接口,所有方法都由Handler 来处理,即所有的被代理方法都由InvocationHandler 接管实际的处理任务。
3、JDK动态代理原理
通过实现被代理类的所有接口,生成一个字节码文件后构造一个代理对象,通过持有反射构造被代理类的一个实例,再通过invoke反射调用被代理类实例的方法,来实现代理。
源码分析 https://blog.csdn.net/convict_eva/article/details/80239838