代理模式
Proxy
- 动态代理
- 静态代理
解释
类似于一个代理商的概念,代理主要的作用就是在不妨碍被代理方法执行的同时,添加一些逻辑。
比如:
- 加一个日志的打印
- 记录执行时间
- …
类图
生成&执行流程图
代码
- 静态代理
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 t = new Tank();
TankTimeProxy ttp = new TankTimeProxy(t);
TankLogProxy tlp = new TankLogProxy(ttp);
tlp.move();
}
}
class TankTimeProxy implements Movable {
Movable m;
public TankTimeProxy(Movable m) {
this.m = m;
}
@Override
public void move() {
long start = System.currentTimeMillis();
m.move();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
class TankLogProxy implements Movable {
Movable m;
public TankLogProxy(Movable m) {
this.m = m;
}
@Override
public void move() {
System.out.println("start moving...");
m.move();
long end = System.currentTimeMillis();
System.out.println("stopped!");
}
}
interface Movable {
void move();
}
- 动态代理之JDK代理
public static void main(String[] args) {
Tank tank = new Tank();
//reflection 通过二进制字节码分析类的属性和方法
Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
new Class[]{Movable.class}, //tank.class.getInterfaces()
new TimeProxy(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;
}
}
- JDK动态代理生成的类
// 1.修改配置,保存动态代理生成的类,1.8之后
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");
// 1.8以及1.8版本之间的配置方法
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 2.动态生成的代理类代码
final class $Proxy0 extends Proxy implements Movable {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final void move() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
//省略toString、hashCode、equals代码
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.test.testJdkProxy.Movable").getMethod("move");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
-
动态代理之Cglib
public class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Tank.class); enhancer.setCallback(new TimeMethodInterceptor()); 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(); } } }
- 如果类是final的则不可生成代理类
- 但是asm可以修改…
- cglib底层也是使用的asm
- 如果类是final的则不可生成代理类
-
动态代理之 Instrument
- class文件load到内存中拦截,实现逻辑修改class
知识点
代理方式
-
实现同样的接口,实现类中添加被代理的对象,在实际方法中添加自己的逻辑并执行原方法
class TankTimeProxy implements Movable { Tank tank; public TankTimeProxy(Tank tank) { this.tank = tank; } @Override public void move() { long start = System.currentTimeMillis(); tank.move(); long end = System.currentTimeMillis(); System.out.println(end - start); } }
但是这样的实现是有问题的。
- 如果想要多个代理嵌套怎么办?(传递的对象是类,代理类和原对象没有实际关系)
- 所以实际应用中,传递的是接口(代理类和原类都实现了接口)
- 如果想要多个代理嵌套怎么办?(传递的对象是类,代理类和原对象没有实际关系)
-
通过继承方式
- 扩展性不好。**cglib ** code generator library
-
通过类加载进内存前拦截,自定义class内容。
- 实现难度大。 Instrument
动态代理
如果需要一个代理可以代理Object并且不影响之前方法的使用,那么就需要引入动态代理。
JDK动态代理
通过实现相同接口来进行代理,运行期间动态生成代理类,通过invokeHandler接口的实现逻辑进行代理逻辑的执行。
参数问题
三个参数 第一个参数是代理对象( P r o x y Proxy Proxy),第二个参数是method,第三个参数是args。
代理类生成原理
asm 直接操作字节码(二进制)
Cglib动态代理
- 使用继承,继承代理类,调用父类方法实现代理。
- 不可代理final的类
- 底层原理也是asm
类加载器
BootstrapClassLoader
ExtClassLoader
AppClassLoader
Jdk动态代理时传递的第一个参数一般为被代理类的classLoader,防止不同classLoader不能加载到内存
父类加载器可以加载,同级别兄弟加载器不可以。
Spring的Aop就是使用的动态代理
- 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<!--
<bean id="d" class="com.mashibing.Driver"></bean>
<bean id="tank" class="com.mashibing.Tank">
<property name="driver" ref="d"></property>
</bean>
-->
<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>
</beans>
- 注解
@Aspect
public class TimeProxy {
@Before("execution (void com.mashibing.dp.spring.v2.Tank.move())")
public void before() {
System.out.println("method start.." + System.currentTimeMillis());
}
@After("execution (void com.mashibing.dp.spring.v2.Tank.move())")
public void after() {
System.out.println("method stop.." + System.currentTimeMillis());
}
}