简介
代理模式作用是:在不修改被代理对象功能的基础上,通过对代理类进行扩展,进行一些功能上的附加与增强。如Spring AOP就是用动态代理来实现的。下图可以表述代理模式的流程:
静态代理
我们在app上看电影的时候会看到很多广告,app厂家拿到电影的代理权限后,再通过穿插各种广告来增加自己的收益,下面通过代理模式来模拟一下
创建代表电影播放的接口
public interface Movie {
void play();
}
实现电影的播放接口
public class MovieProxy implements Movie {
private String movieName;
public MovieProxy(String movieName) {
this.movieName = movieName;
}
@Override
public void play() {
System.out.println("正在播放" + movieName);
}
}
创建电影的代理类,表示上述例子中的app
public class Player implements Movie {
private String movieName;
public Player(String movieName) {
this.movieName = movieName;
}
@Override
public void play() {
System.out.println("片头广告....");
MovieProxy movieProxy = new MovieProxy(movieName);
movieProxy.play();
System.out.println("片尾广告....");
}
}
测试类
public class Test {
public static void main(String[] args) {
Player player = new Player("功夫熊猫");
player.play();
}
}
结果
以上就是静态代理的实现,通过拓展代理类进行一些功能的拓展或增强 ,什么叫静态代理呢?就是增强的内容是事先定义好的,如果需要在运行时进行增强就需要用到动态代理。
动态代理
JDK动态代理
JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler
来处理,用JDK动态代理模拟看电影的例子
代码演示
创建接口,JDK代理中被代理的对象必须实现接口
public interface Movie {
void play();
}
接口实现类
public class MyMovie implements Movie {
@Override
public void play() {
System.out.println("正在播放电影");
}
}
测试类
public class Test {
public static void main(String[] args) {
// 保存动态代理生成的文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
ClassLoader classLoader = MyMovie.class.getClassLoader();
Class<?>[] interfaces = MyMovie.class.getInterfaces();
// proxy代理对象本身
// method被执行的方法
// args1方法参数
Movie movie = (Movie) Proxy.newProxyInstance(classLoader, interfaces, (proxy, method, args1) -> {
MyMovie myMovie = new MyMovie();
System.out.println("片头广告");
Object invoke = method.invoke(myMovie, args1);
System.out.println("片尾广告");
return invoke;
});
movie.play();
}
}
源码分析
newProxyInstance方法
在此方法中可以分为三个步骤
1)查找或生成指定的代理接口类,如果将sun.misc.ProxyGenerator.saveGeneratedFiles设置为true,则会在com.sun.proxy路径下保存生成的代理类
2)获取构造方法
3)利用反射实例化代理类
CGLIB代理
cglib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
代码演示
创建被代理类
public class Movie {
public void play() {
System.out.println("播放电影");
}
}
创建回调方法
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("片头广告");
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("片尾广告");
return o1;
}
}
测试类
public class Test {
public static void main(String[] args) {
// 生成的代理类存储路径
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,".\\code");
// 在创建Enhancer时会通过动态代理将EnhanceKey创建好,
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(Movie.class);
// 设置回调方法
enhancer.setCallback(new MyMethodInterceptor());
// 创建代理类
Movie movie = (Movie) enhancer.create();
movie.play();
}
}
总结
JDK和CGLIB动态代理的区别:
- JDK代理使用的是反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CGLIB代理使用字节码处理框架asm,对代理对象类的class文件加载进来,通过修改字节码生成子类。 - JDK创建代理对象效率较高,执行效率较低;
CGLIB创建代理对象效率较低,执行效率高。 - JDK动态代理机制是委托机制,只能对实现接口的类生成代理,通过反射动态实现接口类;
CGLIB则使用的继承机制,针对类实现代理,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,因为是继承机制,不能代理final修饰的类。