1.代理模式
代理模式是23中设计模式中的一种。简单来说就是我们使用代理对象来替代真实对象,这样就可以实现在不修改原目标对象的前提下,提供额外的功能操作,以达到扩展目标对象的功能。
代理模式的功能主要是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。
代理模式有静态代理和动态代理两种实现方式,我们先看静态代理的实现。
1.1 静态代理
学习动态代理之前,我们先掌握什么是静态代理。静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不灵活 比如接口一但新增方法,目标对象和代理对象都要进行修改。实际应用场景非常少。
静态代理在编译时就将接口、实现类、代理类都变成了一个个实际的class文件
静态代理实现步骤
1.定义接口及实现类
2.创建一个代理类同样实现这个接口
3.将目标对象注入进代理类,然后在代理类的对应方法调用目标类中对应方法。这样我们就可以通过代理类屏蔽对目标对象的访问,并可以在目标方法执行前后做一些自己想做的事情。
代码实现
1.定义购买门票的接口
public interface ITicketService{
boolean buyTicket();
}
2.实现购买门票的接口
public class TicketService implements ITicketService{
public boolean buyTicket(){
boolean flag = false;
System.out.println("购买ticket成功!");
flag = true;
return flag;
}
}
3.创建代理类并同样实现购买门票的接口
public class TicketServiceProxy implements ITicketService{
private final ITicketService ticketService;
public TicketServiceProxy(ITicketService ticketService){
this.ticketService = ticketService;
}
public boolean buyTicket(){
//调用方法之前,我们可以添加代理类自己的操作,前置操作
System.out.println("前置操作!");
ticketService.buyTicket();
//调用方法之后,我们可以添加代理类自己的操作,后置操作
System.out.println("后置操作!");
return true;
}
}
代码测试
public class Main{
public static void main(String args[]){
ITicketService ticketService = new TicketService();
TicketServiceProxy ticketServiceProxy = new TicketServiceProxy(ticketService);
ticketServiceProxy.buyTicket();
}
}
1.2 动态代理
相比静态代理,动态代理更加灵活。我们不需要针对每个目标对象都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类(CGLIB动态代理机制)
从JVM的角度来说,动态代理是在运行时动态生成类字节码,并加载到JVM中
Spring AOP、RPC框架都使用到了动态代理
动态代理在业务系统的开发中用的较少,但在框架级别的开发中是一门必备的技能。
Java动态代理实现方式有很多,比如JDK动态代理、CGLIB动态代理等等。
1.2.1 JDK动态代理机制
在Java动态代理机制中 InvocationHandler 接口和 Proxy类是核心
Proxy 类中使用频率最高的方法是:newProxyInstance(),这个方法主要用来生成一个代理对象。
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException{
}
这个方法一共有3个参数
1.loader:类加载器,用于加载代理对象
2.interfaces:被代理类实现的一些接口
3.h :实现了InvocationHandler接口的对象
要实现动态代理,要必须实现InvocationHandler来自定义处理逻辑。当我们的动态代理对象调用一个方法的时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用
public interface InvocationHandler{
/**
当你使用代理对象调用方法的时候实际会调用这个方法
**/
public Object invoke(Object proxy,Method method,Object[]args)throws Throwable;
}
invoke()方法有下面三个参数
1.proxy:动态生成的代理类
2.method:与代理对象调用的方法相对应
3.args:当前method方法的参数
也就是说你通过Proxy类的newProxyInstance()创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler接口的类的invoke方法。你可以在invoke方法中自定义处理逻辑,比如在方法执行前后做什么事情。
JDK动态代理的使用步骤
1.定义一个接口及实现类
public interface ITicketService {
/**
* 购买门票
* @return
*/
public boolean buyTicket();
}
public class TicketService implements ITicketService {
@Override
public boolean buyTicket() {
boolean flag = false;
System.out.println("购买门票成功");
flag = true;
return flag;
}
}
2.自定义InvocationHandler并重写invoke方法,在invoke方法中我们会调用原生方法(被代理类的方法)并自定义以下处理逻辑。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author beien
* @date 2023-09-04 15:51
* Copyright© 2023 beien
*/
public class ProxyInvocationHandler implements InvocationHandler {
private Object object = null;
public ProxyInvocationHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("前置操作");
Object obj = method.invoke(object,null);
System.out.println("后置操作");
return obj;
}
}
3.通过Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法创建代理对象
public class JdkDynamicProxyFactory{
public static Object getProxy(Object target){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new ProxyInvocationHandler(target));
}
}
代码测试
ITicketService ticketService =(ITicketService)JdkDynamicProxyFactory.getProxy(new TicketService());
ticketService.buyTicket();
1.2.2 CGLIB动态代理机制
JDK动态代理有一个缺点只能代理实现了接口的类,没有实现接口的类无法进行代理。
CGLIB框架则解决了该问题。
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。很多知名框架都是用CGLIB 例如Spring中的AOP模块,如果目标对象实现了接口,则默认采用JDK的动态代理,否则采用CGLIB动态代理。
在CGLIGB动态代理机制中,MethodInterceptor接口和Enhancer类是核心。
你需要自定义MethodInterceptor并重写intercept方法,intercept用于拦截增强被代理类的方法
public interface MethodInterceptor extends Callback{
//拦截被代理中的方法
public Object intercept(Object obj,java.lang.reflect.Method method,Object[]args,MethodProxy methodProxy)throws Throwable
}
1.obj:被代理的对象
2.metod:被拦截的方法
3.args:方法入参
4.methodProxy:用于调用原始方法
可以通过Enhancer类来获取被代理类,当代理类调用方法的时候,实际调用的是MethodInterceptor中的intercept方法
1.2.2.1 CGLIB动态代理使用步骤
1.定义一个类;
2.自定义MethodInterceptor并重写intercept方法,intercept用于拦截增强被代理类的方法,和JDK动态代理中的invoke方法类似;
3.通过Enhancer类的create()创建代理类。
CGLIB属于一个开源项目,我们在使用它之前必须引入jar
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
代码实现
1.实现一个票务系统购买门票功能
public class TicketService{
public boolean buyTicket(){
boolean flag = false;
System.out.println("购买ticket成功!");
flag = true;
return flag;
}
}
2.自定义MethodInterceptor(方法拦截器)
public class ProxyMethodInterceptor implements MethodInterceptor{
/**
*
* @param obj 被代理对象(需要增强的类)
* @param method 被拦截的方法(需要增强的方法)
* @param args 方法入参
* @param proxy 用于调用原始方法
* as many times as needed
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//调用方法之前,前置方法
System.out.println("前置操作:"+method.getName());
Object object = proxy.invokeSuper(obj,args);
//调用方法之后,后置方法
System.out.println("后置操作:"+method.getName());
return object;
}
}
3.获取代理类
public class CglibDynamicProxyFactory{
public static Object getProxy(Class<?> clazz){
//创建动态代理增强类
Enhancer enhancer = new Enhancer();//cglib
//设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
//设置被代理类
enhancer.setSuperclass(clazz);
//设置方法拦击
enhancer.setCallback(new ProxyMethodInterceptor());
//创建代理类
return enhancer.create();
}
}
4.代码测试
TicketService ticketService = (TicketService)CglibDynamicProxyFactory.getProxy(TicketService.class);
ticketService.buyTicket();