1.概述
代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
举个例子:
在租房的时候,有的人会通过房东直租,有的人会通过中介租房。中介一般是不是会提供一些额外的服务,这里的中介就相当于代理。
2.静态代理
2.1 具体案列
package com.demo.jtdl;
/**
* 抽象对象
*/
public interface Subject {
void getGloryCrystal();
}
package com.demo.jtdl;
/**
* 真实角色
*/
public class Player implements Subject{
@Override
public void getGloryCrystal() {
System.out.println("玩家获得一颗荣耀水晶");
}
}
package com.demo.jtdl;
public class TaoBaoProxy implements Subject {
private Subject subject;
public TaoBaoProxy(Subject subject) {
this.subject = subject;
}
@Override
public void getGloryCrystal() {
System.out.println("提醒玩家扫码上号");
subject.getGloryCrystal();
System.out.println("交货收钱");
}
}
package com.demo.jtdl;
public class Test {
public static void main(String[] args) {
TaoBaoProxy proxy=new TaoBaoProxy(new Player());
proxy.getGloryCrystal();
}
}
结果
2.2 静态代理的缺点
静态代理的缺点:一个代理类只能代理一个真实角色,一个系统中的真实角色有很多,此时就必须编写多个对应代理角色,此时就非常麻烦,而且会造成类的臃肿。
3.动态代理
3.1 动态代理实现方式
1.JDK实现:JDK Proxy基于反射
2.第三方类实现:CGLIB基于ASM(一个Java 字节码操作框架)
3.2基于接口的JDK动态代理
1.定义目标类,即被代理的类。
2.通过实现InvocationHandler接口来自定义自己的InvocationHandler;重写invoke方
法,在此方法中定义增强逻辑。
3.通过Proxy.newProxyInstance方法获得代理对象。
4.通过代理对象调用目标方法。
具体的代码实现如下
package com.demo.dtdl;
public interface UserService {
void read();
}
package com.ruoyi.web.cglib;
public class UserServiceImpl implements UserService{
@Override
public void read() {
System.out.println("去读书");
}
}
package com.demo.dtdl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 参数一Object proxy:代理的对象
* 参数二Method method:要运行的方法
* 参数三Object[] args:调用方法时,传递的实参
*
*/
System.out.println("准备书本");
Object invoke = method.invoke(target, args);
System.out.println("读书结束");
return invoke;
}
}
package com.demo.dtdl;
import java.lang.reflect.Proxy;
public class MyTest {
public static void main(String[] args) {
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(new UserServiceImpl());
/**
* 获取代理对象
* 参数一:用于指定用哪个类加载器,去加载生成的代理类
* 参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
* 参数三:用来指定生成的代理对象要干什么事情
*/
UserService proxyUserService = (UserService)Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
myInvocationHandler
);
proxyUserService.read();
}
}
结果:
3.3 基于类的CGLIB动态代理
1.引入CGLIB的相关依赖。
2.定义目标类,即被代理的类。
3.创建代理类实现CGLIB的MethodInterceptor接口,并重写intercept方法,在此方法中定义增强逻辑。
4.使用Enhancer类创建代理对象,设置目标类、回调对象等参数。
5.调用代理对象的方法,实现代理行为。
具体的代码实现如下
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
package com.ruoyi.web.cglib;
public interface UserService {
void read();
}
package com.ruoyi.web.cglib;
public class UserServiceImpl implements UserService{
@Override
public void read() {
System.out.println("去读书");
}
}
package com.ruoyi.web.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibInterceptor implements MethodInterceptor {
private UserService userService;
public CglibInterceptor(UserService userService) {
this.userService = userService;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("准备书本");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("读书结束");
return result;
}
}
@org.junit.Test
public void test(){
//得到方法拦截器
CglibInterceptor cglibInterceptor = new CglibInterceptor(new UserServiceImpl());
//使用cglib框架生成目标类的子类(代理类)实现增强
Enhancer enhancer = new Enhancer();
//设置父类字节码
enhancer.setSuperclass(UserServiceImpl.class);
//设置拦截操作
enhancer.setCallback(cglibInterceptor);
UserService userService = (UserService)enhancer.create();
userService.read();
}
3.4 JDK Proxy与CGLIB的区别:
基于接口 vs基于类
1.JDK Proxy 只能代理接口类型,它通过实现指定接口并生成代理对象来实现代理功能。
2.CGLIB 可以代理普通的类,它通过继承目标类,并在子类中重写方法来实现代理。
实现方式:
1.JDK Proxy 是基于反射机制实现的,它利用 Java 的反射 API动态生成代理对象。
2.CGLIB 使用了字节码生成库,直接操作字节码生成代理类。相比于JDK 代理的反射调用,CGLIB 的方法调用更快速。
性能:
1.由于 CGLIB 是直接对字节码进行操作,所以在创建和执行代理对象时通常比 JDK 代理更快速。
2.JDK Proxy 的性能略低,因为它涉及到反射调用的开销。JDK8 版本已经优化,性能与 CGLIB 差不多。
库依赖:
1.JDK Proxy 是 Java 标准库的一部分,无需额外的依赖。
2.CGLIB 需要引入相关的第三方库。