代理模式是面向对象编程中比较常见的设计模式。在Java中分为静态代理、动态代理,其中动态代理又可以分为JDK动态代理、cglib动态代理。
一、静态代理
- 接口
- 目标对象
-
代理对象
- 优点: 代理对象与目标对象要实现相同的接口,可以做到在不修改目标对象的基础上,对程序功能进行扩展。
- 缺点: 一旦接口增加或者减少方法,那么目标对象和代理对象都需要维护。
接口:
public interface Movie {
void play();
}
目标类:
public class RealMovie implements Movie {
@Override
public void play() {
System.out.println("葫芦娃");
}
}
代理类:
public class ProxyMovie implements Movie {
private RealMovie realMovie;
public ProxyMovie(RealMovie realMovie) {
this.realMovie = realMovie;
}
@Override
public void play() {
// 在被代理对象前后增加业务
before();
realMovie.play();
after();
}
public void before() {
System.out.println("西游记");
}
public void after() {
System.out.println("大头儿子");
}
}
测试:
public class Test {
public static void main(String[] args) {
Movie movie = new ProxyMovie(new RealMovie());
movie.play();
}
}
二、动态代理
代理类在程序运行时创建的代理方式被称为动态代理。也就是说,代理类并不需要在Java代码中定义,而是在运行时动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
动态代理主要运用于框架中,例如在Spring的AOP中:
- 如果加入容器的目标对象有实现接口,使用JDK代理
- 如果目标对象没有实现接口,则使用Cglib代理
1、JDK动态代理
代理对象,不需要实现接口,目标对象一定要实现接口,利用JDK的API动态的在内存中构建代理对象。
动态代理的代理类需要实现InvocationHandler接口,通过reflect.Proxy的类的newProxyInstance方法可以得到这个接口的实例。
JDK代理类:
public class MyInvocationHandler implements InvocationHandler {
private Object object;
public MyInvocationHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object invoke = method.invoke(object, args);
after();
return invoke;
}
private void before() {
System.out.println("西游记");
}
private void after() {
System.out.println("大头儿子");
}
}
测试:
public class Test {
public static void main(String[] args) {
RealMovie realMovie = new RealMovie();
InvocationHandler handler = new MyInvocationHandler(realMovie);
Movie movie = (Movie) Proxy.newProxyInstance(RealMovie.class.getClassLoader(), RealMovie.class.getInterfaces(), handler);
movie.play();
}
}
2、cglib动态代理
静态代理和JDK动态代理都要求 目标对象 实现接口。但有时目标对象只是一个单独的对象,没有实现任何接口,这个时候就可以使用以目标对象子类 的方式实现代理,这种方法叫做Cglib代理。
Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。
- 用CGlib生成代理类是目标类的子类。
- 用CGlib生成代理类不需要接口。
- 用CGLib生成的代理类重写了父类的各个方法。
- 拦截器中的intercept方法内容就是代理类中的方法体。
目标类
public class MovieImpl {
public void play() {
System.out.println("葫芦娃");
}
}
引入cglib pom依赖
代理类:
public class CglibProxyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object object = methodProxy.invokeSuper(o, objects);
after();
return object;
}
private void before() {
System.out.println("西游记");
}
private void after() {
System.out.println("大头儿子");
}
}
测试:
public class Test {
public static void main(String[] args) {
// 创建Enhancer对象,类似于JDK动态代理的Proxy类
Enhancer enhancer = new Enhancer();
// 设置目标类的字节码文件
enhancer.setSuperclass(MovieImpl.class);
// 设置回调函数
enhancer.setCallback(new CglibProxyInterceptor());
// creat方法就是正式创建代理类
MovieImpl movie = (MovieImpl)enhancer.create();
// 调用代理类的play方法
movie.play();
}
}