代理模式
一个类代表另外一个类的功能,表面上是这个类去执行,实际上它是一个类的代理,真正的只能类是被代理的那个类。
主要解决
在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层.
何时使用
想在访问一个类的时候做一些控制.
注意事项
1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 例如变压器
2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。例如:奶茶加奶霜
实现
jdk 自带的动态代理
- java.lang.reflect.Proxy:生成动态代理类和对象;
- java.lang.reflect.InvocationHandler(处理器接口):可以通过invoke方法实现
Subject 接口
public interface Subject {
public int sellBooks();
public String speak();
}
真实对象:
public class RealSubject implements Subject{
@Override
public int sellBooks() {
System.out.println("卖书");
return 1 ;
}
@Override
public String speak() {
System.out.println("说话");
return "张三";
}
}
处理器对象:
public class MyInvocationHandler implements InvocationHandler {
/**
* 因为需要处理真实角色,所以要把真实角色传进来
*/
Subject realSubject ;
//构造器
public MyInvocationHandler(Subject realSubject) {
this.realSubject = realSubject;
}
/**
*
* @param proxy 代理类
* @param method 正在调用的方法
* @param args 方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用代理类");
if(method.getName().equals("sellBooks")){
int invoke = (int)method.invoke(realSubject, args);
System.out.println("调用的是卖书的方法");
return invoke ;
}else {
String string = (String) method.invoke(realSubject,args) ;
System.out.println("调用的是说话的方法");
return string ;
}
}
}
调用端
public class Client {
public static void main(String[] args) {
//真实对象
Subject realSubject = new RealSubject();
//将RealSubject 传入 构造器
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(realSubject);
//代理对象,使用RealSubject构造出的 myInvocationHandler 实现方法的控制
Subject proxyClass = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{Subject.class}, myInvocationHandler);
proxyClass.sellBooks();
proxyClass.speak();
}
}
jdk动态代理主要步骤
- 构造器传入自己需要代理的对象
- 实现InvocationHandler 接口,写出自己需要的invoke方法。
- method.invoke(realSubject, args),调用真实对象。realSubject 为代理的对象
- 使用(Subject) Proxy.newProxyInstance()传入对应参数,即可获取到代理类
- 使用代理对象的方法
cglib代理
Cglib 动态代理是针对代理的类, 动态生成一个子类, 然后子类覆盖代理类中的方法, 如果是private或是final类修饰的方法,则不会被重写。
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要==代理的类没有实现接口==或者为了更好的性能,CGLIB是一个好的选择。
需要代理的类:
public class Engineer {
// 可以被代理
public void eat() {
System.out.println("工程师正在吃饭");
}
// final 方法不会被生成的字类覆盖
public final void work() {
System.out.println("工程师正在工作");
}
// private 方法不会被生成的字类覆盖
private void play() {
System.out.println("this engineer is playing game");
}
}
CGLIB代理类:
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("### before invocation");
Object result = method.invoke(target, objects);
System.out.println("### end invocation");
return result;
}
public static Object getProxy(Object target) {
Enhancer enhancer = new Enhancer();
// 设置需要代理的对象
enhancer.setSuperclass(target.getClass());
// 设置代理人
enhancer.setCallback(new CglibProxy(target));
return enhancer.create();
}
}
测试方法:
public class CglibMainTest {
public static void main(String[] args) {
// 生成 Cglib 代理类
Engineer engineerProxy = (Engineer) CglibProxy.getProxy(new Engineer());
// 调用相关方法
engineerProxy.eat();
}
}
CGLIB总结
- 代理类的final 方法与private方法不会被代理
- 实现cglib代理需要实现MethodInterceptor接口 ,重写intercept 方法.
- 在method.invoke(target, objects)进行方法补充增强.
- 由Enhancer的create() 方法返回代理人