其实注解模块属于Java,不属于SpringBoot模块,但是为了方便,还是放在这个模块下
# 注解含义
注解其实就是 标注与解析,在我们的Java代码中,注解无处不再,我们也是无时不在使用,使用的太多,被忽略的理所当然。
无论是在JDK源码或者框架组件,都在使用注解能力完成各种识别和解析动作;在对系统功能封装时,也会依赖注解能力简化各种逻辑的重复实现;
在Annotation的源码注释中有说明:所有的注解类型都需要继承该公共接口,本质上看注解是接口,但是代码并没有显式声明继承关系,可以直接查看字节码文件;
import java.lang.annotation.*;
/**
* Documented:是否被javadoc或类似工具记录在文档中;
*/
@Documented
/**
* Inherited:标识注解是否可以被子类继承
*/
@Inherited
/**
* Target:作用目标,在ElementType枚举中可以看到取值包括类、方法、属性等;
*/
@Target(ElementType.METHOD)
/**
* Retention:保留策略,比如编译阶段是否丢弃,运行时保留;
*/
@Retention(RetentionPolicy.RUNTIME)
/**
* 此处声明一个SysLogInfo注解,作用范围是在方法上,并且在运行时保留,该注解通常用在服务运行时,结合AOP切面编程实现方法的日志采集;
*/
public @interface SysLogInfo {
}
method
是一个 Method
类型的对象,代表了一个方法。getAnnotation(Class annotationClass)
是 AnnotatedElement
接口中的一个方法,用于获取特定类型的注解
。SysLogInfo.class
是你要获取的注解的类型,这里是 SysLogInfo
注解类的类对象。从简单的入门开始
public class AnnotationDemo {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
// 定义一个带有自定义注解的方法
@SysLogInfo(name = "测试模块")
public static void main(String[] args) {
// 启用保存生成的代理文件,用于调试
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 使用反射获取AnnotationDemo类的所有方法
Method[] methods = AnnotationDemo.class.getMethods();
// 遍历每个方法
for (Method method : methods) {
// 检查方法是否有SysLogInfo注解
SysLogInfo annotation = method.getAnnotation(SysLogInfo.class);
if (annotation != null) {
// 记录带注解的方法信息
logger.error("方法名: {}", method.getName());
logger.error("模块: {}", annotation.name());
}
}
}
}
# 注意 这里回涉及两个概念 反射机制 动态代理(可自行百度学习)
反射:可以让程序在运行时获取类的完整结构信息
代理:给目标对象提供一个代理对象,让代理对象持有目标对象的引用
案例中通过反射机制,在程序运行时进行注解的获取和解析,值得关注的是SysLogInf对象的类名,输出的是代理类信息
案例执行完毕后,会在代码工程的目录下生成代理类,可以查看$Proxy2文件
// ********** 略 **********
public final class $Proxy2 extends Proxy implements SystemLog {
public final String model() throws {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
// ********** 略 **********
// 在对SystemLog解析的过程中,实际上是在使用注解的代理类,$Proxy2继承了Proxy类并实现了SysLogInf接口,并且重写了相关方法
// 值得一看是代理类中invoke方法调用,具体的处理逻辑在AnnotationInvocationHandler类的invoke方法中,会对注解原生方法和自定义方法做判断,并对原生方法提供实现,可自行阅读源码
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if (Object.class == method.getDeclaringClass()) {
if ("equals".equals(name)) {
Object obj = args[0];
return this.checkEquals(obj);
}
if ("toString".equals(name)) {
return this.annotation.toString();
}
if ("hashCode".equals(name)) {
return this.hashCode();
}
} else if ("annotationType".equals(name) && method.getParameterTypes().length == 0) {
return this.getAnnotationType();
}
MemberValue mv = this.annotation.getMemberValue(name);
return mv == null ? this.getDefault(name, method) : mv.getValue(this.classLoader, this.pool, method);
}
什么是反射?
Java反射机制在运行状态中,对于任何一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态的获取信息以及动态的调用对象的方法的功能我们称之为Java的反射机制
eg:类的class声明(class对象),变量(field),方法(method)等等信息,利用反射技术可以对一个类进行解剖,动态获取信息进行处理。
/**
* 学生类
* 基础方式不写注释了
*/
@Getter @Setter
class Student implements Serializable {
public Student(){
}
private String userName = "default_user_name";
private String age;
private String className;
public String eMail;
/**
* 获取学生信息 加密用户名称
* @return 学生信息
*/
public String encodingStudent(String user) {
return "大大怪-----" + user + " execute "+ "Student{" +
"userName='" + MD5Utils.getInstance().getMD5(userName) + '\'' +
", age='" + age + '\'' +
", className='" + className + '\'' +
", eMail='" + eMail + '\'' +
'}';
}
private String getStudentInfo() {
return "Student{" +
"userName='" + userName + '\'' +
", age='" + age + '\'' +
", className='" + className + '\'' +
", eMail='" + eMail + '\'' +
'}';
}
}
public class ReflectBaseDemo {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Test
public void testReflectBase() {
// new的时候会创建一个Student对象,一个Class对象.两个对象。
// 一个类对应一个Class对象,一个类只会被加载一次,一个类只会有一个Class对象;
Student student = new Student();
// 其中 ? extends Student 表示Student类及其子类
Class<? extends Student> studentClass = student.getClass();
logger.error("student.getClass() = {}", studentClass);
}
}
@Test
@DisplayName("反射基础练习-通过.class获取对象")
public void testReflectBase2() {
// new的时候会创建一个Student对象,一个Class对象.两个对象。
// 一个类对应一个Class对象,一个类只会被加载一次,一个类只会有一个Class对象;
Student student = new Student();
// 通过.class获取对象,这种方式是最安全可靠的,因为在编译时就会检查是否有这个类,如果没有就会报错,不会运行时报错,所以这种方式最安全可靠。
Class<Student> studentClass = Student.class;
logger.error("Student.class = {}", studentClass);
}
@Test
@DisplayName("通过Class.forName获取对象")
public void testReflectBase3() throws ClassNotFoundException {
// 通过Class.forName获取对象,这种方式是最不安全的,因为在编译时不会检查是否有这个类,如果没有就会运行时报错,所以这种方式最不安全。
// 但是这个方法使用最多,因为这个方法可以通过配置文件来获取类名。
// 这样就可以在不修改代码的情况下,通过修改配置文件来修改类名,这样就可以实现动态加载类。
try {
Class<?> studentClass = Class.forName("com.hrfan.java_se_base.base.reflect.base.Student");
logger.error("Student.class = {}", studentClass);
}catch (ClassNotFoundException e) {
throw new RuntimeException("class not found!",e);
}
}
field
是一个 Field
类型的对象,代表了一个字段(成员变量)。set()
是 Field
类中的一个方法,用于设置特定对象的字段的值。obj
是要操作的对象实例。"xxxxxx"
是要设置的字段值。 @Test
@DisplayName("获取类的属性以及成员变量")
public void testReflectBase4(){
try {
Class<?> studentClass = Class.forName("com.hrfan.java_se_base.base.reflect.base.Student");
logger.error("==================== 获取所有字段(包括private)====================");
// 获取所有的字段.注意 getFields私有字段是获取不到的,如果要获取私有字段,需要使用getDeclaredFields方法
for (Field field : studentClass.getDeclaredFields()) {
logger.error("field = {}", field);
}
logger.error("==================== 获取公共字段 ====================");
for (Field field : ) {
logger.error("field = {}", field);
}
logger.error("==================== 获取指定公共字段,并赋值 ====================");
try {
Field field = studentClass.getField("eMail");
logger.error("get field = {}", field);
// 通过反射构造对象
try {
// 通过反射获取构造方法,并创建对象(注意 getDeclaredConstructor 是获取所有的构造方法,包括私有的,如果要获取公共的构造方法,需要使用getConstructor方法)
Object obj = studentClass.getDeclaredConstructor().newInstance();
// 通过反射给字段赋值
field.set(obj,"安徽省阜阳市-xxxxxxxx");
Student student = (Student) obj;
logger.error("==================== 验证 ====================");
// 验证
logger.error("student = {}", student.getStudentInfo());
} catch (Exception e) {
throw new RuntimeException("constructor not found!",e);
}
} catch (NoSuchFieldException e) {
throw new RuntimeException("filed not found!",e);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException("class not found!",e);
}
}
}
studentClass
是一个 Class
对象的引用,代表了一个类的定义。getConstructor()
是 Class
类中的一个方法,它返回指定类中的公共构造方法,其中不需要传递任何参数。如果你的类中没有明确的定义无参构造方法,这个方法将抛出 NoSuchMethodException
异常。newInstance()
方法会使用该构造方法创建新的对象实例。Object
引用 obj
就是通过反射机制创建的新对象的引用。m
是一个 Method
类型的对象,代表了一个方法。invoke()
是 Method
类中的一个方法,用于调用特定对象的方法。object
是要调用方法的对象实例。"xxxxxx"
是作为参数传递给该方法的参数。@Test
@DisplayName("获取类的成员方法")
public void testReflectBase5(){
try {
Class<?> studentClass = Class.forName("com.hrfan.java_se_base.base.reflect.base.Student");
logger.error("==================== 获取所有方法(包括private)====================");
for (Method declaredMethod : studentClass.getDeclaredMethods()) {
logger.error("declaredMethod = {}", declaredMethod);
}
logger.error("==================== 获取公共方法 ====================");
// 此时会把Object类的方法也获取到,因为Student类继承了Object类,所以会把Object类的方法也获取到
for (Method method : studentClass.getMethods()) {
logger.error("method = {}", method);
}
logger.error("==================== 获取指定私有方法 ====================");
Method getStudentInfo = null;
try {
// 后面接的是方法参数类型,如果有多个参数,就写多个,如果没有参数,就不写
getStudentInfo = studentClass.getDeclaredMethod("getStudentInfo");
logger.error("getStudentInfo = {}", getStudentInfo);
} catch (NoSuchMethodException e) {
throw new RuntimeException("methods not found!",e);
}
logger.error("==================== 获取指定公共方法 ====================");
Method encodingStudent = null;
try {
// 后面接的是方法参数类型,如果有多个参数,就写多个,如果没有参数,就不写
encodingStudent = studentClass.getMethod("encodingStudent",String.class);
logger.error("encodingStudent = {}", encodingStudent);
try {
// 通过反射构造对象,注意这里是获取公共的构造方法,如果要获取私有的构造方法,需要使用getDeclaredConstructor方法
Object obj = studentClass.getConstructor().newInstance();
// 通过反射调用方法
String res = (String) encodingStudent.invoke(obj, "张三");
logger.error("==================== 验证(调用公共方法 ) ====================");
logger.error("res = {}", res);
} catch (Exception e) {
throw new RuntimeException("constructor not found!",e);
}
} catch (NoSuchMethodException e) {
throw new RuntimeException("methods not found!",e);
}
logger.error("==================== (暴力-解除私有限制) ====================");
// 通常情况下,使用 setAccessible(true) 是为了允许访问那些由于访问修饰符限制而无法直接访问的成员。然而,需要谨慎使用,因为它会绕过 Java 的安全检查。
getStudentInfo.setAccessible(true);
// 通过反射构造对象,注意这里是获取公共的构造方法,如果要获取私有的构造方法,需要使用getDeclaredConstructor方法
Object obj = null;
try {
obj = studentClass.getConstructor().newInstance();
// 通过反射调用方法
String res = (String) getStudentInfo.invoke(obj);
getStudentInfo.invoke(obj);
logger.error("res = {}", res);
} catch (Exception e) {
throw new RuntimeException("constructor not found!",e);
}
}catch (ClassNotFoundException e) {
throw new RuntimeException("class not found!",e);
}
}
优点
灵活性
和扩展性
,降低耦合性
,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类;缺点
性能问题
:使用反射基本上是一种解释操作,用于字段和方法接入时要慢于直接代码。使用反射会模糊程序内部逻辑
:程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射增加了安全问题
:比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。代理模式(Proxy Pattern)
是 23 种设计模式的一种,属于结构型模式。他指的是一个对象本身不做实际的操作,而是通过其他对象来得到自己想要的结果。这样做的好处是可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
代理的关键点就是代理对象和目标对象的关系
这里能体现出一个非常重要的编程思想:不要随意去改源码,如果需要修改,可以通过代理的方式来扩展该方法。
场景:(如在调用目标方法前校验权限,在调用完目标方法后打印日志等)等功能。
静态代理是一种设计模式,它在编译时就确定了代理类和被代理类的关系。在静态代理中,代理类是由程序员创建的,手动编写的。
public interface Calculator {
/**
* 加法
* @param a 第一个加数
* @param b 第二个加数
* @return 两个加数的和
*/
int add(int a, int b);
}
/**
* 加法实现类
* @author 13723
* @version 1.0
* 2024/2/1 20:15
*/
@Service
public class CalculatorImpl implements Calculator{
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* 加法
* @param a 第一个加数
* @param b 第二个加数
* @return 两个加数的和
*/
@Override
public int add(int a, int b) {
return a+b;
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.invoke.MethodHandles;
/**
* @author 13723
* @version 1.0
* 2024/2/1 20:17
*/
public class CalculatorProxy implements Calculator{
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* 被代理对象
*/
private CalculatorImpl calculatorImpl;
/**
* 无参构造函数
*/
public CalculatorProxy(){
}
/**
* 有参构造函数(这里提供是为例 注册代理对象到Spring中时,把被代理的对象传入进来)
* @param calculatorImpl 被代理对象
*/
public CalculatorProxy(CalculatorImpl calculatorImpl) {
this.calculatorImpl = calculatorImpl;
}
/**
* 加法
* @param a 第一个加数
* @param b 第二个加数
* @return 两个加数的和
*/
@Override
public int add(int a, int b) {
logger.error("================= 代理对象开始执行 校验 =================");
// 执行被代理对象的方法,代理对象可以在被代理对象的方法执行前后做一些事情
int add = calculatorImpl.add(a, b);
logger.error("计算结果:{}",add);
logger.error("================= 代理对象结束执行 校验 =================");
return add;
}
}
import com.hrfan.java_se_base.base.proxy.cglib.CalculatorImpl;
import com.hrfan.java_se_base.base.proxy.cglib.CalculatorProxy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ProxyCalculatorAutoConfiguration {
@Autowired
private CalculatorImpl calculator;
/**
* 代理类交给springboot进行管理,通过注入的方式,传入被代理类,生成代理类
* @return 代理类
*/
@Bean
public CalculatorProxy calculatorProxy() {
CalculatorProxy calculatorProxy = new CalculatorProxy(calculator);
return calculatorProxy;
}
}
import com.hrfan.java_se_base.base.proxy.cglib.CalculatorProxy;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
/**
* @author 13723
* @version 1.0
* 2024/2/1 20:19
*/
@SpringBootTest
public class CGLibProxyDemo {
@Resource
private CalculatorProxy proxy;
@Test
@DisplayName("测试静态代理")
public void test(){
proxy.add(1,2);
}
}
JDK动态代理是一种在运行时生成代理类的机制,它允许在代理类中动态地处理方法调用。这种代理机制通过
InvocationHandler
接口来实现,代理类在运行时通过实现指定接口生成,并且可以在代理类的方法中插入自定义的逻辑。在这个过程中,代理类并不是在编译时就确定的,而是在运行时动态生成的。
InvocationHandler
是一个接口,用于定义代理类的具体行为。它包含一个方法 invoke
,在代理对象的方法被调用时会被触发。通过实现这个接口,可以在方法调用前后执行自定义的逻辑。Proxy
类是 JDK 提供的用于创建动态代理类和实例的工具类。通过 newProxyInstance
方法,可以在运行时生成代理对象。这个方法接收一个类加载器、一组接口和一个 InvocationHandler
实现作为参数。Proxy
类在运行时动态生成的代理类。这个类实现了指定的接口,并将方法调用委托给 InvocationHandler
的 invoke
方法。JDK动态代理使用步骤:
InvocationHandler
接口的类,该类将负责在代理对象的方法调用时执行特定逻辑。Proxy.newProxyInstance
方法创建代理对象,该方法接收类加载器、接口数组和 InvocationHandler
实例作为参数。InvocationHandler
的逻辑。# 案例
每次保存用户前 对用户信息进行校验,保存完成后返回用户信息
/**
* 用户服务接口
* @author 13723
* @version 1.0
* 2024/2/1 22:19
*/
public interface UserServiceBi {
/**
* 添加用户
* @param name 用户名
*/
void addUser(String name);
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.lang.invoke.MethodHandles;
/**
* 用户服务实现类
* @author 13723
* @version 1.0
* 2024/2/1 22:30
*/
@Service
public class UserServiceBiImpl implements UserServiceBi {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* 添加用户
* @param name 用户名
*/
@Override
public void addUser(String name) {
logger.error("添加用户成功!用户名:{}",name);
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 用户服务代理类
* @author 13723
* @version 1.0
* 2024/2/1 22:31
*/
public class UserServiceProxy implements InvocationHandler {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* 被代理对象
*/
private UserServiceBiImpl userService;
/**
* 将代理对象注入到代理类中
* 多个线程可能会同时尝试创建代理对象,而代理对象的创建过程是不可变的,因此它本身是线程安全的。
* 但是,如果 userService 对象的状态会在代理对象创建之后被修改,那么就有可能出现线程安全问题
* 这里我们将 UserServiceProxy 的构造函数私有化,不允许外部直接创建实例。然后,通过 newInstance 工厂方法来创建代理对象。
* 在 invoke 方法中,我们使用了同步块来确保在多线程环境下的线程安全性。
* 这种方式保证了在实例化代理对象时的线程安全,避免了多个线程同时创建代理对象的问题。
* @param userService 被代理对象
*/
private UserServiceProxy(UserServiceBiImpl userService) {
this.userService = userService;
}
public static UserServiceBi newInstance(UserServiceBiImpl userService){
// 生成代理对象,这里的代理对象是在内存中生成的,不是在磁盘上生成的,所以不能直接通过反射获取,需要通过Proxy.newProxyInstance()方法获取
// 第一个参数:被代理对象的类加载器,这里使用被代理对象的类加载器,因为代理对象和被代理对象在同一个类加载器中,这样可以保证代理对象和被代理对象在同一个类加载器中
// 第二个参数:被代理对象的接口,这里使用被代理对象的接口,因为代理对象和被代理对象实现了同一个接口,这样可以保证代理对象和被代理对象实现了同一个接口
// 第三个参数:代理对象,这里使用代理对象,因为代理对象实现了InvocationHandler接口,这样可以保证代理对象实现了InvocationHandler接口
return (UserServiceBi) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
new UserServiceProxy(userService)
);
}
/**
* 在代理实例上处理方法调用并返回结果
* @param proxy the proxy instance that the method was invoked on
*
* @param method the {@code Method} instance corresponding to
* the interface method invoked on the proxy instance. The declaring
* class of the {@code Method} object will be the interface that
* the method was declared in, which may be a superinterface of the
* proxy interface that the proxy class inherits the method through.
*
* @param args an array of objects containing the values of the
* arguments passed in the method invocation on the proxy instance,
* or {@code null} if interface method takes no arguments.
* Arguments of primitive types are wrapped in instances of the
* appropriate primitive wrapper class, such as
* {@code java.lang.Integer} or {@code java.lang.Boolean}.
*
* @return the value to return from the method invocation on the
* @throws Throwable the exception to throw from the method
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
logger.error("执行保存用户前-校验用户数据!");
Object result = method.invoke(userService, args);
logger.error("执行保存用户后-返回用户数据!");
return result;
}
}
import com.hrfan.java_se_base.base.proxy.jdk_proxy.UserServiceBi;
import com.hrfan.java_se_base.base.proxy.jdk_proxy.UserServiceBiImpl;
import com.hrfan.java_se_base.base.proxy.jdk_proxy.UserServiceProxy;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.lang.invoke.MethodHandles;
/**
* @author 13723
* @version 1.0
* 2024/2/1 22:42
*/
@SpringBootTest
public class JDKProxyTest1 {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Autowired
private UserServiceBiImpl userService;
@Test
public void test1(){
UserServiceBi userProxy = UserServiceProxy.newInstance(userService);
userProxy.addUser("喜羊羊!");
}
}
JDK动态代理的优点:
JDK动态代理的缺点:
应用场景:
CGlib(Code Generation Library)是一个强大的,高性能的代码生成类库,它
通过动态生成字节码来创建代理对象
。相对于 JDK 动态代理而言,CGlib 提供了更多的功能和灵活性。
CGLib使用步骤可以包括
setSuperclass()
方法设置要代理的对象的类。CGLIB通过继承的方式创建代理对象,因此需要指定一个父类。setCallback()
方法设置代理对象的拦截器。拦截器是一个实现了MethodInterceptor接口的对象,它定义了在方法调用前后进行的额外操作,比如日志记录、性能监控等。create()
方法来创建代理对象。在这一步,CGLIB会动态生成一个代理类,并覆盖父类中的方法来添加拦截器的逻辑。# 保存用户前对信息进行校验,然后保存完数据,返回用户信息
/**
* 客户服务接口
* @author 13723
* @version 1.0
* 2024/2/2 7:40
*/
public interface ClientService {
/**
* 保存客户信息
* @param username 客户名称
*/
void saveClient(String username);
}
/**
* 客户服务实现类
* @author 13723
* @version 1.0
* 2024/2/2 7:42
*/
@Service
public class ClientServiceImpl implements ClientService{
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* 保存客户信息
* @param username 客户名称
*/
@Override
public void saveClient(String username) {
logger.error(" ----------------- 保存客户基础信息! ----------------- ");
}
}
/**
* 客户服务 拦截器类类,用于再方法调用前后 添加日志记录功能
* @author 13723
* @version 1.0
* 2024/2/2 7:43
*/
@Service
public class ClientServiceInterceptor implements MethodInterceptor {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* 在代理实例上处理方法调用并返回结果
* @param obj 代理对象
* @param method 被代理对象的方法
* @param args 方法参数
* @param methodProxy 代理对象的方法
* @return Object 返回值
* @throws Throwable 异常
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
logger.error("保存用户前-----------------------> 进行数据校验");
// 调用被代理对象的方法
Object result = methodProxy.invokeSuper(obj, args);
logger.error("保存用户后-----------------------> 返回用户信息");
return result;
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import java.lang.invoke.MethodHandles;
/**
* 客户服务 工厂类(用于创建代理对象)
* @author 13723
* @version 1.0
* 2024/2/2 7:47
*/
public class ClientServiceFactory {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* 创建代理对象
* @param clazz 要代理的对象
* @param interceptor 代理对象的拦截器
* @return 代理对象
* @param 泛型 T
*/
public static <T> T createProxy(T clazz, MethodInterceptor interceptor) {
// 创建一个增强器,用于创建代理对象,并设置要代理的对象的类,以及代理对象的拦截器,拦截器用于在方法调用前后添加日志记录功能
Enhancer enhancer = new Enhancer();
// 设置要代理的对象的类,以及代理对象的拦截器
enhancer.setSuperclass(clazz.getClass());
enhancer.setCallback(interceptor);
// 创建代理对象
return (T) enhancer.create();
}
}
import com.hrfan.java_se_base.spring_boot.proxy.cglib_proxy.ClientServiceFactory;
import com.hrfan.java_se_base.spring_boot.proxy.cglib_proxy.ClientServiceImpl;
import com.hrfan.java_se_base.spring_boot.proxy.cglib_proxy.ClientServiceInterceptor;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.lang.invoke.MethodHandles;
/**
* @author 13723
* @version 1.0
* 2024/2/1 22:42
*/
@SpringBootTest
public class CGlibProxyTest1 {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Autowired
private ClientServiceImpl clientService;
@Autowired
private ClientServiceInterceptor clientServiceInterceptor;
@Test
public void test1(){
// 创建代理对象
ClientServiceImpl proxy = ClientServiceFactory.createProxy(clientService, clientServiceInterceptor);
// 执行代理对象的方法
proxy.saveClient("美羊羊!");
}
}
Spring-CGLib代理.pdf
Proxy.newProxyInstance()
方法创建代理对象。