代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
静态代理
简单实例
Subject.java
public interface Subject {
public void Request();
}
RealSubject.java
public class RealSubject implements Subject{
@Override
public void Request() {
System.out.println("真实的请求");
}
}
Proxy.java
public class Proxy implements Subject{
RealSubject realSubject;
@Override
public void Request() {
if(realSubject==null){
realSubject=new RealSubject();
}
realSubject.Request();
}
}
通过代理实现realSubject的发送请求
Test.java
public class Test {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.Request();
}
}
具体实例(追求者通过代理赠送礼物)
SchoolGirl.java
public class SchoolGirl {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
GiveGift.java
public interface GiveGift {
void giveDolls();
void giveFlowers();
void giveChocolate();
}
Pursuit.java
public class Pursuit implements GiveGift{
public SchoolGirl mm;
public Pursuit(SchoolGirl mm){
this.mm=mm;
}
@Override
public void giveDolls() {
System.out.println(mm.getName()+"送你洋娃娃");
}
@Override
public void giveFlowers() {
System.out.println(mm.getName()+"送你鲜花");
}
@Override
public void giveChocolate() {
System.out.println(mm.getName()+"送你巧克力");
}
}
Proxy.java
public class Proxy implements GiveGift{
Pursuit gg;
public Proxy(SchoolGirl mm){
gg=new Pursuit(mm);
}
@Override
public void giveDolls() {
gg.giveDolls();
}
@Override
public void giveFlowers() {
gg.giveFlowers();
}
@Override
public void giveChocolate() {
gg.giveChocolate();
}
}
当Pursuit对象无法之间完成giveDolls()、giveFlowers()、giveChocolate()方法时,通过创建代理对象,由代理对象创建new pursuit(),并通过pursuit对象实现方法的调用。
分析:通过这里例子以及扩展我们来看一下静态代理模式的缺点吧:
- 如果出现上面的需求,那么势必会出现类爆炸的结果;
- 当然捕捉方法执行时间的代码都一样,我们每个方法都写,每个类都写,这也是代码的重复,没有达到代码复用的效果,这也完全违背了面向对象设计的原则。
思考:防止出现类爆炸,使代码能够得到复用。我们能不能用一个代理类,来代理所有需要的类。
动态代理模式
通过反射机制,利用JDK提供的Proxy类,在程序运行的时候在内存中根据目标对象来创建代理对象,避免了类爆炸的出现。
使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过代理类来调用目标方法,代理类会将所有的方法调用分派到目标对象上反射执行,还可以在分派过程中添加”前置通知”和后置处理(如在调用目标方法前校验权限,在调用完目标方法后打印日志等)等功能。
代理类
/**
* 此类需要实现InvocationHandler接口
* 调用处理器,当代理对象调用代理方法的时候,注册在调用处理器中的invoke方法会自动调用
* @author Carl_Hugo
* @date 2017年5月4日
*/
public class MyInvocationHandler implements InvocationHandler{
//目标对象,通过反射机制获得
private Object target;
//构造方法
public MyInvocationHandler(Object target) {
super();
this.target=target;
}
/**
* 执行目标对象的方法
* 参数:Object proxy 代理对象的引用,proxy变量中保存代理对象的内存地址
* Method method 目标对象的目标方法
* Object[] args:目标对象的目标方法执行的时候需要实参
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//开始时间
long begin = System.currentTimeMillis();
System.out.println("==============开始==============");
//执行目标对象中的方法
Object retValue = method.invoke(target, args);
//结束时间
long end = System.currentTimeMillis();
//计算时间
System.out.println("==============结束==============");
System.out.println("耗费时长:"+(end-begin)+"ms");
return retValue;
}
/**
* 获取目标对象的代理对象
* @return
*/
public Object getProxy(){
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
target.getClass().getInterfaces(), this);
}
}
测试类
/**
* 注意:JDK内置的动态代理Proxy只能代理接口
* (如果既想代理接口又想代理抽象类需要使用第三方组件:例如cglib)
* @author Carl_Hugo
* @date 2017年5月4日
*/
public class Test {
public static void main(String[] args) {
//实例化目标对象
UserService userService = new UserServiceImpl();
//实例化MyInvocationHandler
MyInvocationHandler invocationHandler = new MyInvocationHandler(userService);
//根据目标对象创建代理对象
UserService proxy = (UserService)invocationHandler.getProxy();
/*UserService proxy = (UserService)Proxy.newProxyInstance(UserService.class.getClassLoader(),
new Class[]{UserService.class}, invocationHandler);*/
//执行代理对象的方法
proxy.add();
}
}
UserService类
public interface UserService {
public abstract void add();
}
UserServiceImpl类
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("----------add-------------");
}
}
用起来是很简单吧,其实这里基本上就是AOP的一个简单实现了,在目标对象的方法执行之前和执行之后进行了增强。Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个东西的。