Proxy代理模式
2020-04-14 本文已影响0人
silence_J
静态代理
各种代理类可以组合起来使用
import java.util.Random;
/**
* 不改变源代码,记录坦克移动时间
* 使用代理模式
* 静态代理
*/
public class Tank implements Movable {
/**
* 模拟坦克移动了一段时间
*/
@Override
public void move() {
System.out.println("Tank is moving ......");
try {
// 模拟移动了0~10秒
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 任意组合使用
new TankLogProxy(
new TankTimeProxy(
new Tank()
)
).move();
}
}
// 代理人与代理对象实现共同的接口
interface Movable{
void move();
}
// 代理类
/**
* 计算时间
*/
class TankTimeProxy implements Movable {
Movable movable; // 聚合
public TankTimeProxy(Movable movable) {
this.movable = movable;
}
// 代理 实现了Movable接口类的 move()方法,并加入时间记录功能
@Override
public void move() {
long start = System.currentTimeMillis();
movable.move();
long end = System.currentTimeMillis();
System.out.println("[TankTimeProxy]--->"+(end-start));
}
}
/**
* 打印日志
*/
class TankLogProxy implements Movable {
Movable movable; // 聚合
public TankLogProxy(Movable movable) {
this.movable = movable;
}
// 代理 实现了Movable接口类的 move()方法,并加入打印日志功能
@Override
public void move() {
System.out.println("[TankLogProxy]---> start moving......");
movable.move();
long end = System.currentTimeMillis();
System.out.println("[TankLogProxy]---> stopped!");
}
}
动态代理
jdk实现
- 问题:如果有不止一个方法需要被代理?
- 若想让LogProxy可以重用,不仅可以代理Tank,还可以代理其它类型 ( 打印日志,时间计算是很多类都要做的事 ),这时怎么做?
- 分离代理行为与被代理对象
- 使用jdk的动态代理,内部是用ASM实现的
- jdk反射生成代理,被代理对象必须实现某个接口,这是由Proxy的内部实现决定的
/**
* 问题:如果有不止一个方法需要被代理?
* 若想让LogProxy可以重用,不仅可以代理Tank,还可以代理其它类型
* ( 打印日志,时间计算是很多类都要做的事 ),这时怎么做?
* 分离代理行为与被代理对象
* 使用jdk的动态代理
* jdk反射生成代理,被代理对象必须实现某个接口,这是由Proxy的内部实现决定的
*/
public class Tank implements Movable {
/**
* 模拟坦克移动了一段时间
*/
@Override
public void move() {
System.out.println("Tank is moving ......");
try {
// 模拟移动了0~10秒
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Tank tank = new Tank();
// 将jdk11 生成的代理类保存成文件
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
// 反射 通过二进制字节码分析类的属性和方法
Movable proxy = (Movable) Proxy.newProxyInstance(Tank.class.getClassLoader(),
new Class[]{Movable.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method:"+method.getName()+" start...");
Object o = method.invoke(tank, args);
System.out.println("method:"+method.getName()+" end!");
return o;
}
});
movable.move();
}
}
// 代理人与代理对象实现共同的接口
interface Movable{
void move();
}
jdk11下执行
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
可以将生成的代理类以文件形式保存。
以上代码中调用 Proxy.newProxyInstance方法会根据所传参数动态生成代理类。生成的代理类反编译后如下:
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 boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.jiangxb.proxy.v2.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());
}
}
}
该代理类默认继承Proxy类并实现了 之前第二个参数传入的Movable接口。代理类中除了有Object类的几个方法外还有 在Movable接口中定义的move()方法。
move()中:
super.h.invoke(this, m3, (Object[])null);
调用父类Proxy的成员变量h的invoke方法,看下Proxy中:
protected InvocationHandler h; // 所定义的成员变量是一个接口
这是一个接口,接口中定义了一个抽象方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
根据多态调用在匿名内部类中被重写的invoke方法:
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method:"+method.getName()+" start...");
Object o = method.invoke(tank, args); // 反射调用
System.out.println("method:"+method.getName()+" end!");
return o;
}
}
cglib实现
cglib实现动态代理不需要接口
需要导入依赖:
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
实现:
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 Tank {
/**
* 模拟坦克移动了一段时间
*/
public void move() {
System.out.println("Tank is moving ......");
try {
// 模拟移动了0~10秒
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class TimeMethodInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
Object result = null;
result = methodProxy.invokeSuper(o, objects);
System.out.println("after");
return result;
}
}
Spring AOP
IOC+AOP
bean工厂+灵活装配+动态行为拼接(织入)使Spring极为灵活
使用AOP代理,导入依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
Spring配置文件中进行aop配置:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 实体类 -->
<bean id="tank" class="com.jiangxb.proxy.v4.Tank"/>
<!-- 切面类 -->
<bean id="timeProxy" class="com.jiangxb.proxy.v4.TimeProxy"/>
<aop:config>
<aop:aspect id="time" ref="timeProxy">
<!-- 切点 -->
<aop:pointcut id="onmove" expression="execution(void com.jiangxb.proxy.v4.Tank.move())"/>
<aop:before method="before" pointcut-ref="onmove"/>
<aop:after method="after" pointcut-ref="onmove"/>
</aop:aspect>
</aop:config>
</beans>
Main:
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("app.xml");
Tank tank = (Tank) context.getBean("tank");
tank.move();
}
}
/**
* 输出为:
*
* before
* Tank is moving ......
* after
*/
-
除此外还可用更简单的注解配置AOP:
app.xml:开启自动代理
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启自动代理 -->
<aop:aspectj-autoproxy/>
<!-- 实体类 -->
<bean id="tank" class="com.jiangxb.proxy.v4.Tank"/>
<!-- 切面类 -->
<bean id="timeProxy" class="com.jiangxb.proxy.v4.TimeProxy"/>
</beans>
在切面类中添加注解及切入点表达式
@Aspect
public class TimeProxy {
@Before("execution(void com.jiangxb.proxy.v4.Tank.move())")
public void before () {
System.out.println("before");
}
@After("execution(void com.jiangxb.proxy.v4.Tank.move())")
public void after () {
System.out.println("after");
}
}