一、简介
代理模式
(Proxy Pattern) 是一种结构型模式
模式,一个类能代理另一个类做一些事情,相当于一个中介
。代理模式分为两种,一种是静态代理
,一种是动态代理
,他们的区别在于静态代理
是程序在运行前就已经存在代理类的字节码文件,代理类和原始类在运行前就已经确定。
动态代理
是在程序运行期间,通过JVM反射机制或者操作字节码方式动态生成代理类。代理类和委托类的关系是运行时在确定的。
下面将会详细进行分析
二、静态代理
静态代理分为两种,一种是基于继承
,一种是基于聚合
(也就是基于实现接口)。
2.1、基于聚合(实现接口)
聚合的特点是
1.目标对象必须实现接口
2.代理对象要实现与目标对象一样的接口
下面我们通过代码说明
我们创建一个用户接口还有她的实现类
public interface UserService {
public void login();
}
这个是目标对象,满足第一点。
public class UserServiceImpl implements UserService {
@Override
public void login() {
System.out.println("登录逻辑");
}
}
我们再创建一个代理类,代理用户接口里面的登录方法,做一些而外的事情,比如打印日志等
我们可以看到代理类也实现了被代理类相同的接口 UserService,满足第二点。
public class UserServiceProxy implements UserService {
//被代理对象
private UserService userService;
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
@Override
public void login() {
System.out.println("登录前");
userService.login();
System.out.println("登陆后");
}
}
我们在写个测试类
public class Main {
public static void main(String[] args) {
UserServiceProxy proxy = new UserServiceProxy(new UserServiceImpl());
proxy.login();
}
}
我们可以看到,登录方法完成了逻辑,而日志的打印由代理类完成
2.2、基于继承
基于继承的特点是
1、代理对象继承目标对象。
2、可以不需要实现接口
我们可以通过代码实现
目标对象
public class UserServiceImpl {
public void login() {
System.out.println("登录逻辑");
}
}
代理对象继承目标对象
public class UserServiceProxy extends UserServiceImpl {
@Override
public void login() {
System.out.println("登录前");
super.login();
System.out.println("登陆后");
}
}
测试类
public class Main {
public static void main(String[] args) {
UserServiceProxy proxy = new UserServiceProxy();
proxy.login();
}
}
三、动态代理
动态代理的源码在程序运行期间,通过JVM反射机制或者操作字节码动态生成代理类。代理类和委托类的关系是运行时在确定的。其中有两种,一种是 JDK 动态代理,一种是 CGLIB 动态代理。
3.1 JDK 动态代理
JDK 动态代理的特点
1、需要通过接口(父类)接收,必须实现接口
我们通过代码演示
首先创建用户接口和她的实现类
public interface UserService {
public void login();
}
public class UserServiceImpl implements UserService {
@Override
public void login() {
System.out.println("登录逻辑");
}
}
然后编写我们的 JDK 动态代理
public class JdkInvocationHandler implements InvocationHandler {
//被代理对象(目标代理对象)
private Object target;
public JdkInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("登录之前");
//反射执行目标对象的方法
Object result = method.invoke(target, args);
System.out.println("登录之后");
return result;
}
//拿到代理类
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
测试
public class Main {
public static void main(String[] args){
//打印动态生成的代理类
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//只能通过接口接收
UserService proxy = new JdkInvocationHandler(new UserServiceImpl()).getProxy();
proxy.login();
}
}
JDK 动态代理为什么需要实现接口,我们可以通过上面的代码打印出生成的代理类(生成的代理类在工程目录下),如下
public final class $Proxy0 extends Proxy implements UserService {
private static Method m1;
private static Method m2;
private static Method m3;
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 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 void login() throws {
try {
super.h.invoke(this, m3, (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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.javahly.proxy.jdk.service.UserService").getMethod("login");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
Proxy0 是生成的代理类,默认继承了 Proxy ,由于 Java 不支持多重继承,所以 JDK 动态代理必须实现接口。
被代理方法 login 里面的 h 就是我们的 JdkInvocationHandler
protected InvocationHandler h;
this 就是下面的代理类 proxy
UserService proxy = new JdkInvocationHandler(new UserServiceImpl()).getProxy();
3.2 CGLIB 动态代理
CGLIB 动态代理的特点是
1、基于操作字节码的方式实现
2、基于继承,不需要实现接口
3、运行时动态生成被代理类的子类,子类重写被代理类所有非final方法
4、final方法无法被代理,子类无法重写final函数
下面我们通过代码实现,首先我们需要引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
编写我们的被代理对象
public class UserServiceImpl {
public void login() {
System.out.println("登录逻辑");
}
}
基于CGLIB 实现的代理类
public class CglibMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("登录之前");
Object res = methodProxy.invokeSuper(obj, args);
System.out.println("登录之后");
return res;
}
}
测试类
public class Main {
public static void main(String[] args){
CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(cglibMethodInterceptor);
UserServiceImpl userService = (UserServiceImpl) enhancer.create();
userService.login();
}
}
四、总结
1、代理模式类型
1、静态代理
,静态代理里面又分为两种实现,基于继承
或基于实现
2、动态代理
动态代理有两种实现方法,一种是基于JDK 动态代理
,一种是基于CGLIB 动态代理
2、动态代理和静态代理有什么区别
1、程序在运行前就已经存在代理类的字节码文件,代理类和原始类在运行前就已经确定。
2、动态代理的源码在程序运行期间,通过JVM反射机制或者操作字节码动态生成代理类。代理类和委托类的关系是运行时在确定的。
3、基于接口和继承有什么区别
1、基于接口
(1)目标对象必须实现接口
(2)代理对象要实现与目标对象一样的接口
2、基于继承
(1)代理对象继承目标对象。
4、JDK 动态代理和 CGLIB 动态代理有什么区别
1、JDK 动态代理
(1) 代理类继承了Prox,Java不支持多重继承,所以生成的动态代理类需要实现接口。
(2) 代理类实现了接口,需要通过接口(父类)接收
2、CGLIB 动态代理
(1) 基于继承,不需要实现接口
(2) 运行时动态生成被代理类的子类,子类重写被代理类所有非final方法
(3) final方法无法被代理,子类无法重写final函数
—— 完
ABOUT
公众号:【星尘Pro】
github:https://github.com/huangliangyun