代理模式
概述
- 当某对象要去访问目标对象时,不能或不适合直接访问目标对象时,可以创建一个代理对象去反问目标对象。
JAVA中的代理
- 静态代理:在编译时期生成。
- 动态代理:在运行时期动态生成,可分为JDK代理(接口代理)和CGLib代理(继承代理)。
角色
- 抽象对象:规范,需要实现的业务方法。
- 具体对象:实现规范的对象,实现了具体的业务。
- 代理对象:对具体对象进行代理,可增强或控制具体对象。
静态代理
实现
public class Test {
public static void main(String[] args) {
ProxyCattle proxyCattle = new ProxyCattle();
proxyCattle.sell();
}
}
// 抽象对象,票
interface Ticket {
void sell();
}
// 具体对象,演唱会
class VocalConcert implements Ticket {
@Override
public void sell() {
System.out.println("演唱会门票");
}
}
// 代理对象,黄牛
class ProxyCattle implements Ticket {
private VocalConcert vocalConcert = new VocalConcert();
@Override
public void sell() {
System.out.println("黄牛服务费");
vocalConcert.sell();
}
}
动态代理
JDK动态代理
实现
public class Test{
public static void main(String[] args) {
TicketProxyFactory ticketProxyFactory = new TicketProxyFactory(new VocalConcert());
Ticket proxyInstance = ticketProxyFactory.getProxyInstance();
proxyInstance.sell();
}
}
// 抽象对象,票
interface Ticket {
void sell();
}
// 具体对象,演唱会
class VocalConcert implements Ticket {
@Override
public void sell() {
System.out.println("演唱会门票");
}
}
// JDK 动态代理 (接口代理)
class TicketProxyFactory {
// 目标对象 (VocalConcert 可以改成Object,就是说可以代理所有的类。哈哈)
public VocalConcert vocalConcert;
public TicketProxyFactory(VocalConcert vocalConcert) {
this.vocalConcert = vocalConcert;
}
// 运行时,调用,动态生成代理类
public Ticket getProxyInstance() {
/**
* ClassLoader loader, 类加载器,JVM加载代理类使用
* Class<?>[] interfaces, 代理对象实现的接口(JDK 动态代理通过接口进行代理)
* InvocationHandler h 代理对象需要进行的处理
* @return
*/
Ticket ticketProxy = (Ticket) Proxy.newProxyInstance(
vocalConcert.getClass().getClassLoader(),
vocalConcert.getClass().getInterfaces(),
new InvocationHandler() {
/**
*
* @param proxy 代理对象 其实就是返回的 ticketProxy
* @param method 目标对象接口中的方法
* @param args 目标对象中接口方法的实际参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前处理");
// 通过目标对象和参数执行方法,返回invoke
Object invoke = method.invoke(vocalConcert, args);
System.out.println("后处理");
return invoke;
}
}
);
return ticketProxy;
}
JDK动态代理原理
- 可通过阿里巴巴提供的Java诊断工具Arthas(阿尔萨)查看内存中的代理类结构
- 当调用sell()方法时,调用的是代理类($Proxy)的sell()方法
- 代理类继承Proxy类,代理类sell()方法会去调用Proxy子实现类InvocationHandler()的invoke()方法
- 有接口,有方法,其实就可以通过反射来获取目标对象的 方法(Method method),方法参数(Objects[] args)
- 在invoke(Object proxy, Method method, Object[] args)方法中通过反射,可去调用目标对象的sell()方法(可进行前处理,后处理)
原理简化版
// JVM中的代理对象
public final class $Proxy0 extends Proxy implements Ticket {
private static Method m3;
public static $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
m3 = Class.forName("com.it.proxy.Ticket").getMethod("sell", new Class[0]);
}
public final void sell() {
this.h.invoke(this, m3, null); // 多态,子实现类重写了
return;
}
}
// Proxy类
class Proxy {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
}
CGLib动态代理
- 是第三方提供的,需引入jar包
依赖引入:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
实现
public class Test {
public static void main(String[] args) {
CGLibProxyFactory cgLibProxyFactory = new CGLibProxyFactory();
VocalConcert proxyInstance = cgLibProxyFactory.getProxyInstance();
proxyInstance.sell();
}
}
// 目标对象,演唱会
class VocalConcert {
public void sell() {
System.out.println("演唱会门票");
}
}
class CGLibProxyFactory implements MethodInterceptor {
public VocalConcert getProxyInstance() {
// 增强类
Enhancer enhancer = new Enhancer();
// 要增强的类(增强为父类)(同理,这里也可以改成Object.class 玩)
enhancer.setSuperclass(VocalConcert.class);
// 怎么增强,做些什么(回调函数,这里使用callback的子类:MethodInterceptor)
enhancer.setCallback(this);
// 返回增强类(代理类)
VocalConcert vocalConcert = (VocalConcert) enhancer.create();
return vocalConcert;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前处理");
// 调用的是代理对象父类的方法
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("后处理");
return invoke;
}
}
代理模式对比
- 静态代理使用较少,接口中的每个方法都要处理,如果接口扩展,实现类和代理类都需要重写新方法。
- 有接口的话,建议使用JDK动态代理,对所有方法进行代理。
- 没有接口,可使用CGLib动态代理,对所有方法进行代理(因为是继承代理,所有不能对final类型的类和方法进行代理)。
优缺点
优点:
- 可以对目标对象增强或隔绝保护。客户端访问代理对象,而不能直接访问目标对象。
- 让目标对象和客户端分离,减低耦合性。
缺点:
- 增加系统的复杂度。
使用场景
- 数据库事务
- 权限控制
改动测试-JDK动态代理
public class Test{
public static void main(String[] args) {
TicketProxyFactory ticketProxyFactory = new TicketProxyFactory(new VocalConcert());
TicketProxyFactory ticketProxyFactory2 = new TicketProxyFactory(new VocalConcert());
// 相同的目标对象,不同的代理对象 false
System.out.println(ticketProxyFactory.getProxyInstance() == ticketProxyFactory.getProxyInstance());
// 相同的目标对象,不同的代理对象 sell()返回的对象相同 ture (目标对象相同,sell()获取的对象也就相同)
System.out.println(ticketProxyFactory.getProxyInstance().sell() == ticketProxyFactory.getProxyInstance().sell());
// 不同的目标对象,不同的代理对象 sell()返回的对象不同 ture (目标对象不同,sell()获取的对象也就不同)
System.out.println(ticketProxyFactory.getProxyInstance().sell() == ticketProxyFactory2.getProxyInstance().sell());
}
}
// 抽象对象,票
interface Ticket {
Test2222 sell();
}
// 具体对象
class VocalConcert implements Ticket {
Test2222 test2222 = new Test2222();
@Override
public Test2222 sell() {
return test2222;
}
}
// JDK 动态代理 (接口代理)
class TicketProxyFactory {
// 目标对象
public VocalConcert vocalConcert;
public TicketProxyFactory(VocalConcert vocalConcert) {
this.vocalConcert = vocalConcert;
}
public Ticket getProxyInstance() {
Ticket ticketProxy = (Ticket) Proxy.newProxyInstance(
vocalConcert.getClass().getClassLoader(),
vocalConcert.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(vocalConcert, args);
return invoke;
}
}
);
return ticketProxy;
}
}
class Test2222{
}
改动测试-CGLib动态代理
public class Test {
public static void main(String[] args) {
CGLibProxyFactory cgLibProxyFactory = new CGLibProxyFactory();
// 生成不同的代理对象 false
System.out.println(cgLibProxyFactory.getProxyInstance() == cgLibProxyFactory.getProxyInstance());
// 生成不同的代理对象 sell()返回的也不同 false (因为是继承代码,调用的是父类的sell()方法,因此不同)
System.out.println(cgLibProxyFactory.getProxyInstance().sell() == cgLibProxyFactory.getProxyInstance().sell());
}
}
// 目标对象
class VocalConcert {
Test333 test333 = new Test333();
public Test333 sell() {
return test333;
}
}
class CGLibProxyFactory implements MethodInterceptor {
public VocalConcert getProxyInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(VocalConcert.class);
enhancer.setCallback(this);
VocalConcert vocalConcert = (VocalConcert) enhancer.create();
return vocalConcert;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object invoke = methodProxy.invokeSuper(o, objects);
return invoke;
}
}
class Test333{
}