今天特意将静态代理、JDK动态代理、CGLIB动态代理 和 String AOP整理如下,与各位看客分享下。
1. 代理概念
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。
这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
2. 代理概述
主要优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
- 代理对象可以扩展目标对象的功能
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
主要缺点:
- 代理模式会造成系统设计中类的数量增加
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度;
以上缺点可以由动态代理来解决
3. 实例分析
屏幕快照 2021-01-19 下午3.42.13.png- 静态代理:
interface Movable {
void move();
}
/**
* 问题:我想记录坦克的移动时间
* 最简单的办法:修改代码,记录时间
* 问题2:如果无法改变方法源码呢?
* 用继承?
* v05:使用代理
*/
public class Tank implements Movable {
// 模拟坦克移动了一段儿时间
@Override
public void move() {
System.out.println("Tank moving claclacla...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class TankTimeProxy implements Movable
Movable movable;
public TankTimeProxy(Movable movable) {
this.movable = movable;
}
@Override
public void move() {
long start = System.currentTimeMillis();
movable.move();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
public static void main(String[] args) {
new TankTimeProxy(new Tank()).move();
}
静态代理分析:代理类A要代理的类B时,同样要实现B类实现的接口一样,通过代理类的构造方法将被代理的类对象注入进来,然后再通过实现接口对B类方法进行扩展。
如果要实现多种类型的代理,则类的数量会扩展。
- 动态代理:
interface Movable {
void move();
}
public class Tank implements Movable {
@Override
public void move() {
System.out.println("Tank moving claclacla...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Tank tank = new Tank();
Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(), // 代理类,这里使用被代理的类
new Class[]{Movable.class}, // 代理对象应该实现哪些接口
new LogHander(tank) // 被代理对象调用时的处理逻辑
);
m.move();
}
}
class TimeProxy implements InvocationHandler {
Movable m;
public TimeProxy(Movable m) {
this.m = m;
}
public void before() {
System.out.println("method start..");
}
public void after() {
System.out.println("method stop..");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object o = method.invoke(m, args); // 被代理对象的操作
after();
return o;
}
}
代理类是动态生成的,如下代码片段:
-
图1:
屏幕快照 2021-01-19 下午11.32.24.png -
图2:
屏幕快照 2021-01-19 下午11.32.37.png -
图3:
屏幕快照 2021-01-19 下午11.33.55.png -
图4:
屏幕快照 2021-01-19 下午11.51.43.png
1.根据所传入的参数生成一个代理类
2.代理类实现了相关接口(Movable),继承了Proxy,其构造函数是有参构造,传入一个InvocationHandler。见图1, 3
3.所以,其使用代理类,调用方法move(),都会先调用InvocationHandler中的invoke方法,见图2
4.至于要不要调用代理类的方法,需要看,在invoke中是否调用了method.invoke(tank, args)。见图4
- CGLIB动态代理:
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Tank.class);
enhancer.setCallback(new TimeMethodInterceptor()); // 相当于动态代理中的InvocationHandler
Tank tank = (Tank)enhancer.create();
tank.move();
}
}
class TimeMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(o.getClass().getSuperclass().getName());
System.out.println("before");
Object result = null;
result = methodProxy.invokeSuper(o, objects);
System.out.println("after");
return result;
}
}
class Tank {
public void move() {
System.out.println("Tank moving claclacla...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
-
pom.xml添加配置:
屏幕快照 2021-01-20 上午12.02.38.png -
String AOP:
public class Tank {
public void move() {
System.out.println("Tank moving claclacla...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
<bean id="tank" class="com.mashibing.dp.spring.v1.Tank"/>
<bean id="timeProxy" class="com.mashibing.dp.spring.v1.TimeProxy"/>
<aop:config>
<aop:aspect id="time" ref="timeProxy">
<aop:pointcut id="onmove" expression="execution(void com.mashibing.dp.spring.v1.Tank.move())"/>
<aop:before method="before" pointcut-ref="onmove"/>
<aop:after method="after" pointcut-ref="onmove"/>
</aop:aspect>
</aop:config>
注解的方式略,问度娘...
4. 总结
- 静态代理类型多的话,类的数量会膨胀。
- 动态代理要求被代理的对象必须实现接口,这是由proxy内部所决定的。
- CGLIB实现动态代理不需要接口,但final 类型的类无法实现动态(底层实现技术ASM)
- String AOP也是ASM实现的。
————————————————————
坐标帝都,白天上班族,晚上是知识的分享者
如果读完觉得有收获的话,欢迎点赞加关注
网友评论