设计模式之–代理模式
文章目录
代理可以简单理解为,B类托管A类的功能,并根据需求,对A类的访问作控制,这里的控制可以理解为对A类方法执行的流程的影响,包括但不限于:
在方法执行之前,先做其他事(前置通知)
在方法执行之后,再做某事(后置通知)
决定方法是否执行(环绕通知)
…
java中代理的主要应用体现在权限控制,日志管理,事务控制等方面
静态代理
静态代理的作用,可扩展性,可维护性相对较差。典型的静态代理可以通过
继承和包装器两种方式来实现,包装器方式比起继承方式稍方便。
继承方式的静态代理
/**
* @author:wangy
* @date: 2018/9/21 / 15:15
* @description: 通过继承的方式实现java静态代理, 主要在于理解所谓"代理"的概念
*/
public interface CellPhone {
void sendMessage();
}
/**
* 模拟被代理类对象
*/
public class Iphone5 implements CellPhone {
@Override
public void sendMessage() {
System.out.println("the sending message is in the air.");
}
}
/**
* 模拟代理类对象
*/
public class Iphone5S extends Iphone5 {
@Override
public void sendMessage() {
System.out.println("you need to unlock your phone first.");
super.sendMessage();
// 模拟短信发送
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("message sent successful.");
}
}
/**
*客户端
*/
public class Client {
public static void main(String[] args) {
CellPhone phone = new Iphone5S();
phone.sendMessage();
}
}
///:~
you need to unlock your phone first.
the sending message is in the air.
message sent successful.
上例中,Iphone5S 类继承了Iphone5,并对5的功能“加了点椰果”,这就是最简单的代理模式。但是,如果我还想“加点奶油”,那么就需要再创建一个类,给5“加奶油”,如果有很多手机,要加很多功能,只能通过硬编码 来完成,这样显然是不合适的。
包装器方式的静态代理
/**
* @author:wangy
* @date: 2018/9/21 / 15:15
* @description: 通过包装器的方式实现java静态代理
*/
public interface IceCream {
void iceCreamMaker();
}
/**
* 模拟被代理类对象
*/
public class BerryCream implements IceCream {
@Override
public void iceCreamMaker() {
System.out.println("this is a IceCream with blueberry juice.");
}
}
/**
* 模拟代理类对象
*/
public class Jelly implements IceCream {
// 代理对象中封装了被代理对象
private BerryCream berryCream;
public Jelly(BerryCream berryCream) {
this.berryCream = berryCream;
}
@Override
public void iceCreamMaker() {
System.out.println("add Grass jelly into Berry IceCream");
berryCream.iceCreamMaker();
System.out.println("now it's a Grass-jelly-Berry-IceCream");
}
}
/**
* 模拟客户端
*/
public class Client {
public static void main(String[] args) {
IceCream iceCream = new Jelly(new BerryCream());
iceCream.iceCreamMaker();
}
}
///:~
add Grass jelly into Berry IceCream
this is a IceCream with blueberry juice.
now it's a Grass-jelly-Berry-IceCream
在包装器模型中,被代理类对象被封装在代理类对象中。通过这种形式,如果我需要在“莓派冰激凌”上加上“烧仙草”和“珍珠果”,只需要将代码作一些改动,并且,其相对于继承模式的优势在于:继承是类似于一个链式的叠加,并且对于功能的改动比较麻烦(比如想先加珍珠果再加烧仙草,就需要改动继承关系)。而包装器设计就相对比较灵活了,只需要作简单改动:
// 将代理类对象1的封装类直接改为接口
public class Jelly implements IceCream {
private IceCream iceCream;
public Jelly(IceCream iceCream) {
this.iceCream = iceCream;
}
@Override
public void iceCreamMaker() {
System.out.println("add Grass jelly into Berry IceCream");
iceCream.iceCreamMaker();
System.out.println("now it's a Grass-jelly-Berry-IceCream");
}
}
// 添加代理对象2
public class Pearl implements IceCream {
private IceCream iceCream;
public Pearl(IceCream iceCream) {
this.iceCream = iceCream;
}
@Override
public void iceCreamMaker() {
System.out.println("add pearl fruit into Berry Ice Cream.");
iceCream.iceCreamMaker();
System.out.println("now it's a what a icecream!");
}
}
// 测试类
public class Client {
public static void main(String[] args) {
// 如果想改变“加料”的顺序,只需要改变对象初始化的顺序就ok
IceCream iceCream = new BerryCream();
Pearl pearl = new Pearl(iceCream);
Jelly jelly = new Jelly(pearl);
jelly.iceCreamMaker();
}
}
///:~
add Grass jelly into Berry IceCream
add pearl fruit into Berry Ice Cream.
this is a IceCream with blueberry juice.
now it's a what a icecream!
now it's a Grass-jelly-Berry-IceCream
动态代理
相较于静态代理,动态代理带来的好处是:一个代理工具(或者叫代理处理程序),能够有效地实现对不同委托类的代理,这样使代码更加灵活。代理模式一定程度上可以看作“功能增强”,而“功能增强”的需求本质上就是相对灵活的,这和动态代理的初衷相契合。
JDK 动态代理
JDK 动态代理是比较常见的代理模式。其实现方法可大致总结为:
- 1.创建一个实现接口
InvocationHandler的类,它必须实现invoke方法 - 2.创建被代理的类以及接口
- 3.通过
Proxy的静态方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理类实例 - 4.通过代理类实例调用委托类方法
1. JDK动态代理需要一个接口
这个接口供所有需要被代理的类实现
public interface Subject {
void sayHallo();
}
2. 和一个简单的实现类
public class RealSubject implements Subject {
@Override
public void sayHallo() {
System.out.println("大家好!");
}
}
3. 自定义代理处理程序,须实现 InvocationHandler 接口
public class MyProxyHandler implements InvocationHandler {
private Subject subject;
MyProxyHandler() {
}
MyProxyHandler(Subject subject) {
this.subject = subject;
}
/**
* 此方法用于生成一个指定接口的代理类实例, 该接口可以将方法调用指派到 [指定的调用处理程序],
* 这个所谓的 [调用处理程序] 就是 InvocationHandler的invoke()方法
*
* @param subject 需要被代理的类的实例对象(被代理接口的实现类)
* @return 一个指定接口的代理类实例
* 这里, 指定接口就是说的是 Subject 接口, 常说的JDK动态代理需要有一个接口,就是这个原因
*/
Object bind(Subject subject) {
this.subject = subject;
return Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), this);
// 此Proxy类的静态方法等价于
/*try {
// 获取实现指定接口的被代理类实例对象
Class<?> proxyClass = Proxy.getProxyClass(Subject.class.getClassLoader(), Subject.class.getInterfaces());
// 获取指定的 [调用处理程序对象] 的构造器
Constructor<?> proxyClassConstructor = proxyClass.getConstructor(MyProxyHandler.class);
// 通过指定的InvocationHandler实例创建实现指定接口的代理类实例对象
return proxyClassConstructor.newInstance(new MyProxyHandler(subject));
}catch(Exception e){
e.printStackTrace();
}*/
}
/* ********************************************
* java.lang.reflect.Proxy [extends Object implements Serializable] 类
* -- 该类提供用于创建动态代理类和实例的静态方法, 它是由这些静态方法创建的动态代理类的超类
* -- 动态代理类(以下称为代理类)是一个实现 [在创建类时在运行时指定的接口列表] 的类
* -- 代理接口(Subject)是代理类实现的一个接口
* -- 代理实例是代理类的一个实例, 每个代理实例都有一个关联的 [调用处理程序] 对象, 其实现接口 InvocationHandler
* -- 代理实例调用方法(sayHallo())时, 会被指派到 [调用处理程序] 对象的 invoke() 方法
* ********************************************/
/**
* @param proxy 代理类对象
* @param method 被代理类的方法实例
* @param args 被代理类对象(subject)的方法实例method的参数
* @return null
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
method.invoke(subject, args);
System.out.println("after...");
return null;
}
}
4. 客户端生成代理类对象并且调用委托类的方法
这个client介绍了3种创建动态代理类的方法
public class Client {
/**
* 被代理类对象(接口)
*/
private static Subject subject = new RealSubject();
/**
* 代理实例关联的调用处理程序对象实例
*/
private static InvocationHandler handler = new MyProxyHandler(subject);
/**
* 代理实例关联的调用处理程序对象, 一般是自定义的InvocationHandler类的实现类
*/
private static MyProxyHandler my_proxy = new MyProxyHandler();
public static void main(String[] args) {
m1();
m2();
m3();
}
/**
* 通过 [代理类实例] 调用 [被代理类] 实例的指定方法时, 会被[自定义代理类处理程序]指派给 [调用处理程序](即invoke()方法),并且执行invoke方法的增强代码
*/
private static void m3() {
Subject proxySubject = (Subject) my_proxy.bind(subject);
proxySubject.sayHallo();
}
private static void m2() {
Subject proxy_subject = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);
proxy_subject.sayHallo();
}
private static void m1() {
Subject proxySubject = null;
try {
Class<?> proxyClass = Proxy.getProxyClass(Subject.class.getClassLoader(), Subject.class);
Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
proxySubject = (Subject) constructor.newInstance(handler);
} catch (Exception e) {
e.printStackTrace();
}
proxySubject.sayHallo();
}
cglib 动态代理
与JDK动态代理不同的是,cglib动态代理不需要委托类实现某个接口,其生成的代理类是委托类的子类。当然,cglib也有其局限:
- final 类不能通过cglib代理
- final 修饰的方法不能通过cglib作增强处理
cglib实现动态代理可简单地归纳为:
- 1.创建委托类对象
- 2.创建[自定义代理类生成程序]类,该类须实现MethodInterceptor
- 3.通过Enhancer来创建代理类实例
- 4.通过代理类实例调用委托类方法
1.创建委托类
public class Flower {
public void bloom(){
System.out.println("the flower will bloom...");
}
}
2. 自定义代理类生成程序
public class ProxyInterceptor implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
Object getProxyClass(Class clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("after you fertilized...");
methodProxy.invokeSuper(o, objects);
System.out.println("and you will have a harvest.");
return null;
}
}
3. 客户端
public class Client {
private static ProxyInterceptor proxyInterceptor = new ProxyInterceptor();
public static void main(String[] args) {
Flower proxyClass = (Flower) proxyInterceptor.getProxyClass(Flower.class);
proxyClass.bloom();
}
}
JDK动态代理和cglib动态代理的区别
| 代理方式 | 优点 | 缺点 | 特点 |
|---|---|---|---|
| JDK | 代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理 | 不需要硬编码接口,代码复用率高 | 只能够代理实现了接口的委托类 |
| cglib | 代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理 | 可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口 | 不能对final类以及final方法进行代理 |
解析JDK动态代理
to be finished…
References:
- to be finished…