动态代理
文章目录
- 动态代理
- JDK动态代理
- cglib动态代理
- jdk动态代理和cglib动态代理的区别
- 区别:
- CGlib动态代理示例:
JDK动态代理
1.我们需要定义一个接口,作为代理和目标对象共同实现的约束:
package com.kang.spring.service;
/**
* @Author Emperor Kang
* @ClassName UserService
* @Description 首先,我们需要定义一个接口,作为代理和目标对象共同实现的约束:
* @Date 2023/12/26 16:14
* @Version 1.0
* @Motto 让营地比你来时更干净
*/
public interface UserService {
void addUser(String name);
void deleteUser(String name);
}
2.我们定义一个目标对象,实现该接口
package com.kang.spring.service.impl;
import com.kang.spring.service.UserService;
/**
* @Author Emperor Kang
* @ClassName UserServiceImpl
* @Description TODO
* @Date 2023/12/26 16:14
* @Version 1.0
* @Motto 让营地比你来时更干净
*/
public class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("Add user: " + name);
}
@Override
public void deleteUser(String name) {
System.out.println("Delete user: " + name);
}
}
3.我们定义一个动态代理类
package com.kang.spring.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @Author Emperor Kang
* @ClassName JdkProxy
* @Description 我们定义一个动态代理类
* @Date 2023/12/26 16:16
* @Version 1.0
* @Motto 让营地比你来时更干净
*/
public class JdkProxy implements InvocationHandler {
/**
* 目标对象
*/
private Object target;
public JdkProxy(Object target) {
this.target = target;
}
/**
* 方法执行
* @param proxy 调用方法的代理实例(代理类)
* @param method 对应于在代理实例上调用的接口方法的方法实例。方法对象的声明类将是声明方法的接口,这个接口可能是代理接口的父接口,代理类通过代理接口继承方法。
* @param args 一个对象数组,包含在代理实例上的方法调用中传递的参数值;如果接口方法没有参数,则为null。基本类型的参数包装在适当的基本包装类的实例中,如java.lang.Integer或java.lang.Boolean。
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method " + method.getName());
return result;
}
}
在该代理类中,我们实现了InvocationHandler接口,并重写了invoke方法,该方法中实现了目标对象方法的前置和后置处理,即在目标对象方法执行前输出"Before method",在方法执行后输出"After method",并返回执行结果。
最后,在main方法中,我们首先创建了一个目标对象userService,然后创建了一个代理对象proxy,并将userService作为参数传递给代理对象的构造函数中。通过调用Proxy类的静态方法newProxyInstance,我们可以动态地创建代理对象。该方法的第一个参数是目标对象的类加载器,第二个参数是目标对象实现的接口,第三个参数是InvocationHandler对象。最后,我们调用代理对象的addUser和deleteUser方法,代理对象会在执行目标对象方法前后输出相应的信息。
Proxy.invoke
/**
* 方法执行
* @param proxy 调用方法的代理实例(代理类)
* @param method 对应于在代理实例上调用的接口方法的方法实例。方法对象的声明类将是声明方法的接口,这个接口可能是代理接口的父接口,代理类通过代理接口继承方法。
* @param args 一个对象数组,包含在代理实例上的方法调用中传递的参数值;如果接口方法没有参数,则为null。基本类型的参数包装在适当的基本包装类的实例中,如java.lang.Integer或java.lang.Boolean。
* @return
* @throws Throwable
*/
Proxy.newProxyInstance
/**
* ClassLoader loader:定义代理类的类加载器
* Class<?>[] interfaces:代理类要实现的接口列表
* InvocationHandler h:将方法调用分派给它的调用处理程序(就是调用目标对象方法本身)
*/
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(), // 类加载器
new Class[] { UserService.class }, // class类
handler); // 执行的方法
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{}
执行结果
Before method addUser
Add user: Alice
After method addUser
Before method deleteUser
Delete user: Bob
After method deleteUser
cglib动态代理
CGlib动态代理是一种基于字节码技术实现的动态代理方式,它的原理是通过生成被代理类的子类来实现代理的功能,因此,它不需要像JDK动态代理那样必须实现一个接口,可以代理任何类。
1.定义一个计算器类
package com.kang.spring.pojo;
/**
* @Author Emperor Kang
* @ClassName Calculator
* @Description 定义一个计算器类
* @Date 2023/12/27 9:47
* @Version 1.0
* @Motto 让营地比你来时更干净
*/
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int sub(int a, int b) {
return a - b;
}
}
2.CalculatorInterceptor是一个方法拦截器,用于在调用被代理对象的方法前后打印一些日志。
package com.kang.spring.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Author Emperor Kang
* @ClassName CalculatorInterceptor
* @Description 定义方法拦截器
* @Date 2023/12/27 10:03
* @Version 1.0
* @Motto 让营地比你来时更干净
*/
public class CalculatorInterceptor implements MethodInterceptor {
/**
* 被代理的目标对象
*/
private Object target;
public CalculatorInterceptor(Object target) {
this.target = target;
}
/**
* 拦截
* @param o 生成的代理对象
* @param method 要执行的方法对象
* @param args 执行方法的参数
* @param methodProxy 生成的代理方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("执行方法 " + method.getName() + " 前...");
Object result = methodProxy.invoke(target, args); // 通过代理对象调用方法
System.out.println("执行方法 " + method.getName() + " 后...");
return result;
}
}
3.main
package com.kang.spring;
import com.kang.spring.cglib.CalculatorInterceptor;
import com.kang.spring.pojo.Calculator;
import net.sf.cglib.proxy.Enhancer;
/**
* @Author Emperor Kang
* @ClassName CglibMain
* @Description TODO
* @Date 2023/12/27 10:07
* @Version 1.0
* @Motto 让营地比你来时更干净
*/
public class CglibMain {
public static void main(String[] args) {
Calculator calculator = new Calculator();
// 创建一个Enhancer对象,用于生成代理类 enhancer:增强器
Enhancer enhancer = new Enhancer();
/**
* 需要注意的是,由于CGlib动态代理是通过生成被代理类的子类来实现代理的功能,因此,如果被代理类是final类,那么就无法生成其子类,也就无法使用CGlib动态代理。
*/
enhancer.setSuperclass(Calculator.class); // 设置父类
enhancer.setCallback(new CalculatorInterceptor(calculator)); // 设置方法拦截器
// 生成代理类并创建代理对象
Calculator proxy = (Calculator) enhancer.create();
// 调用代理对象的方法
int result = proxy.add(1, 2);
System.out.println("result = " + result);
result = proxy.sub(3, 2);
System.out.println("result = " + result);
}
}
执行结果
执行方法 toString 前...
执行方法 toString 后...
执行方法 add 前...
执行方法 toString 前...
执行方法 toString 后...
执行方法 add 后...
result = 3
执行方法 sub 前...
执行方法 toString 前...
执行方法 toString 后...
执行方法 sub 后...
result = 1
jdk动态代理和cglib动态代理的区别
区别:
- JDK动态代理只能代理实现了接口的类,而不能代理普通的类。而CGlib动态代理可以代理普通的类。
- JDK动态代理使用Java的反射机制来生成代理类,而CGlib动态代理使用ASM框架来生成代理类,因此CGlib动态代理的效率比JDK动态代理要高。
- JDK动态代理生成的代理类是在内存中动态生成的类,而CGlib动态代理生成的代理类是在磁盘上写入文件,然后再加载到内存中的类。因此,CGlib动态代理的代理类在生成时需要更多的时间和空间。
CGlib动态代理示例:
首先,我们需要引入cglib的依赖,比如在Maven中可以添加以下依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
接下来,我们定义一个接口:
public interface UserService {
void addUser(String username, String password);
}
然后,我们实现这个接口:
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username, String password) {
System.out.println("Add user: " + username + " with password: " + password);
}
}
现在,我们要使用CGlib动态代理来代理这个实现类。首先,我们定义一个代理类:
public class UserServiceProxy implements MethodInterceptor {
private Object target;
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method " + method.getName());
return result;
}
}
在这个代理类中,我们实现了MethodInterceptor接口,这个接口有一个方法intercept,这个方法就是我们要在代理方法执行前后添加的逻辑。在这个方法中,我们使用了MethodProxy类来执行实际的方法调用。
在getInstance方法中,我们使用Enhancer类来创建代理对象。其中,setSuperclass方法用来设置代理对象的父类,setCallback方法用来设置代理对象的拦截器。
现在,我们可以使用代理对象来调用addUser方法了:
public class Main {
public static void main(String[] args) {
UserService userService = (UserService) new UserServiceProxy().getInstance(new UserServiceImpl());
userService.addUser("John", "123456");
}
}
这个示例中,我们通过代理类UserServiceProxy来代理实现类UserServiceImpl,并且在代理方法执行前后添加了逻辑,最终成功输出了方法调用前后的信息。