代理模式(静态代理)
代理类为被代理类预处理消息、过滤消息并在此之后将消息转发给被代理类,之后还能进行消息的后置处理。代理类和被代理类通常会存在关联关系(代理类持有的被代理对象的引用 private RealImage realImage;),代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是 Real Object.
- 优点:可以很好的保护实际对象的业务逻辑对外暴露,从而提高安全性。
- 缺点:不同的接口要有不同的代理类实现,会很冗余
- 创建一个接口(Image),然后创建被代理的类(RealImage)实现该接口并且实现该接口中的抽象方法。
public interface Image {
String findImage(String name);
String display();
}
public class RealImage implements Image {
private String fileName;
public RealImage() {
}
public RealImage(String fileName){
this.fileName = fileName;
}
@Override
public String findImage(String name) {
System.out.println("原有的findImage方法");
return "find by " + name;
}
@Override
public String display() {
System.out.println("原有的display方法");
return "Displaying " + fileName;
}
}
- 创建一个代理类(ProxyImage),同时也实现这个接口。
- 在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。
public class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName){
this.fileName = fileName;
realImage = new RealImage(fileName);
}
@Override
public String findImage(String name) {
String image = realImage.findImage(name);
System.out.println("增强被代理的方法");
return image;
}
@Override
public String display() {
String display = realImage.display();
System.out.println("增强被代理的方法");
return display;
}
}
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");
String display = image.findImage("6666.avi");
System.out.println(display);
System.out.println("");
String display1 = image.display();
System.out.println(display1);
}
}
动态代理
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
- InvocationHandler 该接口中仅定义了一个方法invoke(Object obj,Method method, Object[] args)
- Proxy 动态代理类
动态代理步骤:
- 创建被代理的类以及接口
- 创建一个实现接口InvocationHandler的类,它必须实现invoke方法
public class InvocationHandlerImpl implements InvocationHandler {
// 要代理的对象
private Object target;
// 给要代理的真实对象赋初值
public InvocationHandlerImpl(Object target) {
this.target = target;
}
/* proxy: - 指代我们所代理的那个真实对象
method: - 指代的是我们所要调用真实对象的某个方法的Method对象
args: - 指代的是调用真实对象某个方法时接受的参数*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before..............");
System.out.println("要增强的方法:" + method.getName());
//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
Object value = method.invoke(target, args);
System.out.println("after..............");
return value;
}
}
- 通过Proxy的静态方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler handler)创建一个代理
- 通过代理调用方法
public class DynamicProxyDemonstration {
public static void main(String[] args) {
Image realImage = new RealImage("66666.img");
/**
* InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
* 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
* 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
*/
InvocationHandler handler = new InvocationHandlerImpl(realImage);
ClassLoader loader = realImage.getClass().getClassLoader();
Class[] interfaces = realImage.getClass().getInterfaces();
/**
* 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
*
* 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
* 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真
* 实对象,这样我就能调用这组接口中的方法了
* 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
*/
Image imageProxy = (Image) Proxy.newProxyInstance(loader, interfaces, handler);
String display = imageProxy.display();
System.out.println(display);
String name = imageProxy.findImage("789.avi");
System.out.println(name);
}
}
CGLIB 动态代理
CGLIB 创建动态代理类的模式是:
- 查找目标类上的所有非 final 的 public 类型的方法 (final 的不能被重写)
- 将这些方法的定义转成字节码
- 将组成的字节码转换成相应的代理的 Class 对象然后通过反射获得代理类的实例对象
- 实现 MethodInterceptor 接口, 用来处理对代理类上所有方法的请求
public class CglibInterceptor implements MethodInterceptor {
/**
* CGLIB 增强类对象,代理类对象是由 Enhancer 类创建的,
* Enhancer 是 CGLIB 的字节码增强器,可以很方便的对类进行拓展
*/
private Enhancer enhancer = new Enhancer();
/**
*
* @param object 被代理的对象
* @param method 代理的方法
* @param objects 方法的参数
* @param methodProxy CGLIB方法代理对象
* @return cglib生成用来代替Method对象的一个对象,使用MethodProxy比调用JDK自身的Method直接执行方法效率会有提升
* @throws Throwable
*/
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("方法增强前........");
Object o = methodProxy.invokeSuper(object, objects);
System.out.println("方法增强后........");
return o;
}
/**
* 创建一个代理对象
*/
public Object newProxyInstance(Class<?> c) {
/**
* 设置产生的代理对象的父类,增强类型
*/
enhancer.setSuperclass(c);
/**
* 定义代理逻辑对象为当前对象,要求当前对象实现 MethodInterceptor 接口
*/
enhancer.setCallback(this);
/**
* 使用默认无参数的构造函数创建目标对象,被代理的类要提供无参构造方法
* 有参构造 create(Class[] argumentTypes, Object[] arguments)
*/
//return enhancer.create();
return enhancer.create(new Class[]{String.class}, new Object[]{"daqiao.avi"});
}
}
// 测试类
public class CglibProxyDemo {
public static void main(String[] args) {
CglibInterceptor cglibInterceptor = new CglibInterceptor();
Image image = (Image )cglibInterceptor.newProxyInstance(RealImage.class);
String display = image.display();
System.out.println(display);
String image1 = image.findImage("123.avi");
System.out.println(image1);
}
}
JDK 动态代理和 CGLIB 动态代理的区别
- JDK 动态代理基于 Java 反射机制实现, 必须要实现了接口的业务类才能用这种方法生成代理对象,因为动态生成的类默认继承Proxy类。而 CGLIB 动态代理基于 ASM 框架通过生成业务类的子类来实现。
- JDK 动态代理的优势是最小化依赖关系,代码实现简单。因为是基于接口设计实现的,如果没有接口,会抛异常。而基于 CGLIB 框架的优势是无须实现接口。