Java的AOP修改参数、Mybatis拦截器实现自动填充

Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充) 需求如下:我现在引入了一个第三方 jar 包,里面有一个 MyBatis-Plus 查询构造器,其中构造LIKE条件查询的条件是当前端传过来的参数带有逗号时,拼接为LIKE查询条件。关键代码:标识在对象的某个成员属性上​}​复制代码标识在Controller层上,以此来判断那些请求是需要被切入的。​​}复制代码一个非常简单的Javabean@Data​​​}​}}​复制代码Controller 类@Slf4j​}​}复制代码重点重点,切面类。 阅读详情

前言​

作为Java开发者,你认为反射这个知识点重要程度,在你心里是什么样的呢? 以前我也只觉得反射非常重要,但总归是听这个文章说,听那个朋友说,学是学了,但却没怎么应用。 当我正式进入到社会当 cv 仔的时候,需要考虑的问题多了,慢慢思考问题了,就觉得反射是个力大无穷的东西,更会感觉反射是个无所不能的东西,如各种各样的框架的底层,各种各样的拦截器的实现,反射都是其中少不了的一部分~ 如果平时着重于开发业务的话,那么确实可能会较少使用到反射机制,但并非是说反射它不重要,反射它是Java 框架的基础勒,可以说木有反射,Java的动态性是会受限的~

大致思路:

​

编写的过程中,即使书写完已阅读过,但难免可能会出现遗漏,如有发现问题请及时联系修正,非常感谢你的阅读,希望我们都能成为技术道路上的朋友。

一、反射是什么?

JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

不过要想解剖一个类,就要先获取到该类的字节码文件对应的Class类型的对象.

稍后就会讲到~

"反射之所以被称为框架的灵魂",主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力,这种能力也就是我们常说的动态性,利用这种性质可以使编写的程序更灵活更通用。

反射机制可以用来:

  • 在运行时分析类的能力,如可以构造任意一个类,可以获取任意一个类的全部信息,

  • 在运行时检查对象,如在运行时判断任意一个对象所属的类

  • 实现泛型数组操作代码,因为在运行时可以获取泛型信息

  • 利用Method对象,如我们经常使用的动态代理,就是使用Method.invoke()来实现方法的调用。

反射是一种功能强大且复杂的机制,在开发Java工具或框架方面,反射更是不可缺少的一部分。

二、Class 对象详解

之前说到了,如果要分析一个类,就必须要获取到该类的字节码文件对应 Class 类型对象。

另外如果有听到类模板对象,这个说的其实就是Class对象,大家不要误会了。

2.1、如何获取到Class对象呢?

得到Class的方式总共有四种:

  • 通过对象调用 getClass()方法来获取

  • 直接通过类名.class 的方式得到

  • 通过 Class对象的 forName() 静态方法来获取

  • Classloader,通过类加载器进行获取

/** * @description: * @author: Ning Zaichun * @date: 2022年09月15日 22:49 */ public class ClassDemo01 { ​ @Test public void test1() throws Exception { //1、通过对象调用 getClass() 方法来获取 // 类型的对象,而我不知道你具体是什么类,用这种方法 Student student = new Student(); Class studentClass1 = student.getClass(); System.out.println(studentClass1); // out: class com.nzc.Student ​ //2、直接通过`类名.class` 的方式得到 // 任何一个类都有一个隐含的静态成员变量 class // Class studentClass2 = Student.class; Class<?> studentClass2 = Student.class; System.out.println(studentClass2); // out: class com.nzc.Student System.out.println("studentClass1和studentClass2 是否相等==>" + (studentClass1 == studentClass2)); // studentClass1和studentClass2 是否相等==>true ​ //3、通过 Class 对象的 forName() 静态方法来获取,使用的最多 // 但需要抛出或捕获 ClassNotFoundException 异常 Class<?> studentClass3 = Class.forName("com.nzc.Student"); System.out.println(studentClass3); // out: class com.nzc.Student System.out.println("studentClass1和studentClass3 是否相等==>" + (studentClass1 == studentClass3)); //studentClass1和studentClass3 是否相等==>true ​ //4、 使用类的加载器:ClassLoader 来获取Class对象 ClassLoader classLoader = ClassDemo01.class.getClassLoader(); Class studentClass4 = classLoader.loadClass("com.nzc.Student"); System.out.println(studentClass4); System.out.println("studentClass1和studentClass4 是否相等==>" + (studentClass1 == studentClass4)); //studentClass1和studentClass4 是否相等==>true } } 复制代码

在这四种方式中,最常使用的是第三种方式,第一种都直接new对象啦,完全没有必要再使用反射了;第二种方式也已经明确了类的名称,相当于已经固定下来,失去了一种动态选择加载类的效果;而第三种方式,只要传入一个字符串,这个字符串可以是自己传入的,也可以是写在配置文件中的。

像在学 JDBC 连接的时候,大家肯定都使用过 Class对象的 forName() 静态方法来获取Class对象,再加载数据库连接对象,但可能那时候只是匆匆而过罢了。

注意:不知道大家有没有观察,我把各种方式所获取到的class对象,都进行了一番比较,并且结果都为true,这是因为一个类在 JVM 中只会有一个 Class 实例,为了解释此点,我把类加载过程也简单的做了一个陈述。

2.2、类的加载过程

当我们需要使用某个类时,如果该类还未被加载到内存中,则会经历下面的过程对类进行初始化。

​即类的加载 ---> 链接 ---> 初始化三个阶段。

在这里我只着眼于类的加载过程了,想要了解更为详细的,就需要大家去找找资料看看啦~

加载过程:

1、在我们进行编译后(javac.exe命令),会生成一个或多个字节码文件(就是项目中编译完会出现的 target 目录下的以.class结尾的文件)

2、接着当我们使用 java.exe 命令对某个字节码文件进行解释运行时。

3、加载过程

  • 就相当于将 class 文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构;

  • 并生成一个代表这个类的 java.lang.Class 对象,这个加载到内存中的类,我们称为运行时类,此运行时类,就是 Class的一个实例,所有需要访问和使用类数据只能通过这个 Class 对象。

  • 所谓Class对象,也称为类模板对象,其实就是 Java 类在 JVM 内存中的一个快照,JVM 将从字节码文件中解析出的常量池、 类字段、类方法等信息存储到模板中,这样 JVM 在运行期便能通过类模板而获 取 Java 类中的任意信息,能够对 Java 类的成员变量进行遍历,也能进行 Java 方法的调用。

  • 反射的机制即基于这一基础。如果 JVM 没有将 Java 类的声明信息存储起来,则 JVM 在运行期也无法进行反射。

4、这个加载的过程还需要类加载器参与,关于类加载器的类型大家可以去了解了解,还有双亲委派机制等,此处我便不再多言

2.3、为了更便于记忆的图

​(图片说明:为更好的描述JVM中只有一个Class对象,而画下此图,希望通过这张简图,让你记忆更为深刻)

2.4、Class 常用的API

通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等

  • getName():获得类的完整名字。

  • getFields():获得类的public类型的属性。

  • getDeclaredFields():获得类的所有属性。包括private声明的和继承类

  • getMethods():获得类的public类型的方法。

  • getDeclaredMethods():获得类的所有方法。包括private声明的和继承类

  • getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。

  • getConstructors():获得类的public类型的构造方法。

  • getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。

  • newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

另外就还有反射包下的几个常用的对象Constructor、Filed、Method等,分别表示类的构造器、字段属性、方法等等

这些都会在下文慢慢陈述出来~

三、获取运行时类完整信息并使用

所谓运行时类,就是程序运行时所创建出来的类,你直接理解为通过反射获取到的类也可。大体意思是如此。

在讲述这一小节时,先要理解Java中一切皆对象这句话。

我们平常编写一个类,我们会将它称为一个Java对象,但是在反射这里将此概念再次向上抽象了。

类的基本信息:构造方法、成员变量,方法,类上的注解,方法注解,成员变量注解等等,这些都是Java对象,也是证明了Java中一切皆对象这句话。

其中Constructor就表示构造方法的对象,他包含了构造方法的一切信息,

Field、Method等等都是如此。

不要太过于麻烦和重复书写,我将案例中操作的所有相关代码,都放在此处了,案例中的依赖全部基于此。

public interface TestService { } 复制代码
@Target({ElementType.TYPE,ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MarkAnnotation { ​ String value() default ""; ​ } 复制代码
public class Generic<T> { } ​ 复制代码
public interface GenericInterface<T> { } ​ 复制代码
// TYPE 表示可以标记在类上 // PARAMETER 表示可以标记在方法形式参数 // METHOD 方法 // FIELD 成员属性上 @Target({ElementType.TYPE,ElementType.METHOD, ElementType.FIELD,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) // 这里表明是运行时注解 @Documented public @interface LikeAnnotation { ​ String value() default ""; ​ ​ String[] params() default {}; } 复制代码
@Data @MarkAnnotation public class Person implements TestService{ private String sex; ​ public Integer age; ​ private void mySex(String sex){ System.out.println("我的性别"+sex); } ​ public void myAge(Integer age){ System.out.println("我的年龄"+age); } ​ ​ } 复制代码
@ToString(callSuper = true) // 增加这行是为了打印时将父类属性也打印出来,方便查看~ @Data @LikeAnnotation(value = "123") public class Student extends Person implements Serializable { @LikeAnnotation private String username; ​ @LikeAnnotation public String school; ​ private Double score; ​ public Student() { } ​ private Student(String username) { this.username = username; } ​ public Student(String username, String school) { this.username = username; this.school = school; } ​ @LikeAnnotation public void hello() { System.out.println("世界,你好"); } ​ public void say( String username) { System.out.println("你好,我叫" + username); } ​ private void myScore(Double score) { System.out.println("我的分数是一个私密东西," + score); } ​ public void annotationTest(@LikeAnnotation String username,@MarkAnnotation String str){ System.out.println( "测试获取方法参数中的注解信息"); } } 复制代码

3.1、反射获取运行时类构造方法并使用

class获取构造方法的相关API

// 获取所有的构造函数 Constructor<?>[] getConstructors() ​ // 获取 public或 private 修饰的狗赞函数,只要参数匹配即可 Constructor<?>[] getDeclaredConstructors() ​ // 获取所有 public 修饰的 构造函数 Constructor<T> getConstructor(Class<?>... parameterTypes) //调用此方法,创建对应的运行时类的对象。 public T newInstance(Object ... initargs) 复制代码

newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。

要想此方法正常的创建运行时类的对象,要求:

  • 运行时类必须提供空参的构造器;

  • 空参的构造器的访问权限得够。通常,设置为 public。

为什么要 javabean 中要求提供一个 public 的空参构造器?

原因: 1、便于通过反射,创建运行时类的对象;2、便于子类继承此运行时类时,默认调用 super() 时,保证父类有此构 造器。

想要更详细的了解,建议去看生成的字节码文件,在那里能够给出你答案。

测试:

/** * @description: * @author: Ning Zaichun * @date: 2022年09月17日 1:17 */ public class ConstructorTest { ​ /** * 获取公有、私有的构造方法 并调用创建对象 */ @Test public void test1() throws Exception { Class<?> aClass = Class.forName("com.nzc.Student"); ​ System.out.println("======获取全部public 修饰的构造方法========="); Constructor<?>[] constructors = aClass.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor); } System.out.println("======获取 public、private 修饰的构造方法,只要参数匹配即可========="); /** * 这里的参数 Class<?>... parameterTypes 填写的是参数的类型,而并非某个准确的值信息 */ Constructor<?> constructor = aClass.getDeclaredConstructor(String.class); System.out.println(constructor); // 因为此构造函数是 private 修饰,如果不设置暴力反射,则没有权限访问。 // 这里setAccessible(true) 是设置暴力反射,如果不设置,则会报错, constructor.setAccessible(true); // 这里调用的有参构造,所以在调用 newInstance 方法时,也需要填写参数 Object o1 = constructor.newInstance("宁在春"); constructor.setAccessible(false); System.out.println("o1===>"+o1); ​ /** * 如果需要获取有参构造,只需要填写对应的参数类型即可, * 获取无参构造,填null或不填都可。 */ Constructor<?> constructor1 = aClass.getConstructor(); Constructor<?> constructor2 = aClass.getConstructor(String.class,String.class); System.out.println("无参构造==>"+constructor1); System.out.println("有参构造==>"+constructor2); Object o2 = constructor1.newInstance(); Object o3 = constructor2.newInstance("宁在春2","xxxx社会"); System.out.println("o2===>"+o2); System.out.println("o3===>"+o3); } } 复制代码

既然能够获取到构造方法,那么也就是可以使用的,用Constructor.newInstance()方法来调用构造方法即可,在下列的打印信息中,也可以看出来确实如此,如果明确要获取为Student对象的话,进行强转即可。

======获取全部public 修饰的构造方法========= public com.nzc.Student(java.lang.String,java.lang.String) public com.nzc.Student() ======获取 public、private 修饰的构造方法,只要参数匹配即可========= private com.nzc.Student(java.lang.String) o1===>Student(username=宁在春, school=null, age=null) 无参构造==>public com.nzc.Student() 有参构造==>public com.nzc.Student(java.lang.String,java.lang.String) o2===>Student(username=null, school=null, age=null) o3===>Student(username=宁在春2, school=xxxx社会, age=null) 复制代码

3.2、反射获取运行时类成员变量信息

class对象中获取类成员信息使用到的API

Field[] getFields(); Field[] getDeclaredFields(); ​ Field getDeclaredField(String name); ​ public native Class<? super T> getSuperclass(); 复制代码

这里的 Field 类对象,其实就是表示类对象中的成员属性,不过还有多了很多其他在反射时需要用到的属性和API罢了~

3.2.1、获取私有公有类成员信息

为了有更好的对比,我先编写了一段不使用反射时的正常操作。

/** * 不使用反射的进行操作 */ @Test public void test1() { Student student = new Student(); student.setUsername("username"); student.setSchool("xxxx社会"); student.setScore(100.0); // 永远三岁的小伙子 哈哈 🤡 student.setAge(3); student.setSex("男"); ​ System.out.println("student信息===>" + student); // out:student信息===>Student(super=Person(sex=男, age=3), username=username, school=xxxx社会, score=100.0) } 复制代码

现在我再使用反射来实现上述操作~

在实现之前,还是先来看看如何获取公有、私有的成员变量吧

@Test public void test2() throws Exception { Class<?> stuClass = Class.forName("com.nzc.Student"); ​ System.out.println("========获取所有 public 修饰的成员属性(包括父类属性)====="); Field[] fields = stuClass.getFields(); for (Field field : fields) { System.out.println(field); } //public java.lang.String com.nzc.Student.school //public java.lang.Integer com.nzc.Person.age ​ System.out.println("========获取所有(public、private、protected等修饰的)属性成员(不包括父类成员属性)====="); Field[] fields1 = stuClass.getDeclaredFields(); for (Field field : fields1) { System.out.println(field); } //private java.lang.String com.nzc.Student.username //public java.lang.String com.nzc.Student.school //private java.lang.Double com.nzc.Student.score ​ System.out.println("========通过反射,获取对象指定的属性====="); Field username = stuClass.getDeclaredField("username"); System.out.println("username===>" + username); } 复制代码

但是你发现没有,这无法获取到父类的成员变量信息,父类的信息,我们该如何获取呢?

3.2.2、获取父类成员属性信息

其实一样的,我们也是要获取到父类的Class对象,在Class API中有一个getSuperClass()方法可以获取到父类的class对象,其他的操作都是一样的~

@Test public void test5() throws ClassNotFoundException { Class<?> stuClass = Class.forName("com.nzc.Student"); System.out.println("========获取所有属性成员(包括父类成员属性)====="); Class clazz = stuClass; List<Field> fieldList = new ArrayList<>(); while (clazz != null) { fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()))); clazz = clazz.getSuperclass(); } Field[] fields2 = new Field[fieldList.size()]; fieldList.toArray(fields2); for (Field field : fields2) { System.out.println(field); } //private java.lang.String com.nzc.Student.username //public java.lang.String com.nzc.Student.school //private java.lang.Double com.nzc.Student.score //private java.lang.String com.nzc.Person.sex //public java.lang.Integer com.nzc.Person.age } 复制代码

到这里我们已经知道如何获取到类的成员变量信息了,就可以来看看如何给它们设置值,或者通过反射的方式来获取到值了。

3.2.3、设置或修改类成员变量值

@Test public void test3() throws Exception { Class<?> stuClass = Class.forName("com.nzc.Student"); System.out.println("获取到 filed 后,设置值或修改值"); Constructor<?> constructor = stuClass.getConstructor(); Object o = constructor.newInstance(); ​ //操作pubilc 属性 Field school = stuClass.getDeclaredField("school"); school.set(o,"xxxx社会"); System.out.println(o); //Student(super=Person(sex=null, age=null), username=null, school=xxxx社会, score=null) // 另外既然可以设置,那么自然也是可以获取的 System.out.println(school.get(o)); //xxxx社会 ​ // 操作 private 修饰的成员属性 Field name = stuClass.getDeclaredField("username"); // setAccessible(true) 因为是获取private 修饰的成员变量 // 不设置暴力反射 是无法获取到的,会直接报 IllegalAccessException 异常 name.setAccessible(true); name.set(o,"Ningzaichun"); name.setAccessible(false); System.out.println(o); //Student(super=Person(sex=null, age=null), username=Ningzaichun, school=xxxx社会, score=null) } 复制代码

其实看到这里对于反射已经有个大概的印象了,并且这都是比较平常且实用的一些方法。

3.2.4、获取成员变量注解信息

@Test public void test4() throws Exception { Class<?> stuClass = Class.forName("com.nzc.Student"); Field school = stuClass.getDeclaredField("school"); ​ LikeAnnotation annotation = school.getAnnotation(LikeAnnotation.class); System.out.println(annotation); //@com.nzc.LikeAnnotation(params=[], value=) ​ Annotation[] annotations = school.getAnnotations(); for (Annotation annotation1 : annotations) { System.out.println(annotation1); } //@com.nzc.LikeAnnotation(params=[], value=) ​ ​ LikeAnnotation declaredAnnotation = school.getDeclaredAnnotation(LikeAnnotation.class); System.out.println(declaredAnnotation); //@com.nzc.LikeAnnotation(params=[], value=) ​ Annotation[] declaredAnnotations = school.getDeclaredAnnotations(); for (Annotation declaredAnnotation1 : declaredAnnotations) { System.out.println(declaredAnnotation1); } //@com.nzc.LikeAnnotation(params=[], value=) } 复制代码

关于getAnnotation、getDeclaredAnnotation 的分析大家可以看看下面这篇文章@Repeatable详解-getAnnotation、getDeclaredAnnotation获取不到对象

对于想要更详细的了解Java注解的朋友,大家可以继续找找相关资料。

3.3、反射获取运行时类对对象方法信息并调用

Class对象中使用的相关API

Method[] getMethods(); ​ Method getMethod(String name, Class<?>... parameterTypes); ​ Method[] getDeclaredMethods(); ​ Method getDeclaredMethod(String name, Class<?>... parameterTypes); ​ // 获取方法返回值类型 Class<?> getReturnType(); ​ // obj – 调用底层方法的对象 // args – 用于方法调用的参数 // return 使用参数args在obj上调度此对象表示的方法的结果 Object invoke(Object obj, Object... args) 复制代码

3.3.1、获取对象方法

获取公有、私有方法

@Test public void test1() throws Exception { Class<?> stuClass = Class.forName("com.nzc.Student"); ​ System.out.println("========获取所有public修饰的方法======"); Method[] methods = stuClass.getMethods(); for (Method method : methods) { System.out.println(method); } //public boolean com.nzc.Student.equals(java.lang.Object) //public java.lang.String com.nzc.Student.toString() //public int com.nzc.Student.hashCode() //public void com.nzc.Student.hello() //public void com.nzc.Student.say(java.lang.String) //public java.lang.Double com.nzc.Student.getScore() //public void com.nzc.Student.setScore(java.lang.Double) //public void com.nzc.Student.setSchool(java.lang.String) .... 还有一些没写出来了 ​ System.out.println("========获取对象【指定的public修饰的方法】======"); // 第一个参数为方法名,此处是获取public修饰的无参方法 Method hello = stuClass.getMethod("hello"); System.out.println("hello===>"+hello); // 带参方法,第二个参数填写【参数类型】 Method say = stuClass.getMethod("say", String.class); System.out.println("say===>"+say); //hello===>public void com.nzc.Student.hello() //say===>public void com.nzc.Student.say(java.lang.String) ​ ​ // 参数为 方法名,此处是获取 private 修饰的无参方法 // stuClass.getDeclaredMethod(""); Method myScore = stuClass.getDeclaredMethod("myScore", Double.class); System.out.println("myScore==>"+myScore); //myScore==>private void com.nzc.Student.myScore(java.lang.Double) ​ System.out.println("=======获取所有的方法======="); Method[] declaredMethods = stuClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); } //public void com.nzc.Student.say(java.lang.String) //private void com.nzc.Student.myScore(java.lang.Double) //public java.lang.Double com.nzc.Student.getScore() //public void com.nzc.Student.setScore(java.lang.Double) //protected boolean com.nzc.Student.canEqual(java.lang.Object) } 复制代码

既然能获取到,那么也是能够调用的啦~,

3.3.2、调用对象方法

还记得经常能看到的一个invoke()方法吗,这里就是调用method.invoke()方法来实现方法的调用。

@Test public void test2() throws Exception { Class<?> stuClass = Class.forName("com.nzc.Student"); Constructor<?> constructor = stuClass.getConstructor(); Object o = constructor.newInstance(); ​ Method myScore = stuClass.getDeclaredMethod("myScore", Double.class); myScore.setAccessible(true); // 调用方法 myScore.invoke(o, 99.0); // 相当于 Student student= new Student(); // student.myScore(99.0); myScore.setAccessible(false); } 复制代码

之前阐述了 Method它本身就记录了方法的一切信息,我们实现调用也就是第一步罢了,说它可以获取到当时定义方法的一切都不为过,接下来一步一步来说吧。

3.3.3、获取方法上的注解信息

@Test public void test2() throws Exception { Class<?> stuClass = Class.forName("com.nzc.Student"); System.out.println("==== 获取成员变量上指定的注解信息==="); ​ Field username = stuClass.getDeclaredField("username"); System.out.println(username); //private java.lang.String com.nzc.Student.username Annotation annotation = username.getAnnotation(LikeAnnotation.class); System.out.println(annotation); //@com.nzc.LikeAnnotation(params=[], value=) ​ Method hello = stuClass.getDeclaredMethod("hello"); LikeAnnotation annotation1 = hello.getAnnotation(LikeAnnotation.class); System.out.println(hello+"===="+annotation1); // public void com.nzc.Student.hello()====@com.nzc.LikeAnnotation(params=[], value=) } 复制代码

3.3.4、获取方法参数及参数注解信息

不过在写项目时,有可能还会要获取方法参数上的注解,那该如何获取方法参数呢?又该如何获取方法参数的注解信息呢?

@Test public void test3() throws Exception { Class<?> stuClass = Class.forName("com.nzc.Student"); System.out.println("==== 获取方法参数中的注解信息==="); ​ Method annotationTest = stuClass.getDeclaredMethod("annotationTest",String.class,String.class); ​ // 获取方法的返回值类型 Class<?> returnType = annotationTest.getReturnType(); // 获取权限修饰符 System.out.println(Modifier.toString(annotationTest.getModifiers()) ); // 获取到全部的方法参数 Parameter[] parameters = annotationTest.getParameters(); for (Parameter parameter : parameters) { Annotation annotation = parameter.getAnnotation(LikeAnnotation.class); if(annotation!=null){ // 参数类型 Class<?> type = parameter.getType(); // 参数名称 String name = parameter.getName(); System.out.println("参数类型"+type+" 参数名称==>"+name+" 参数上的注解信息"+annotation); //参数类型class java.lang.String 参数名称==>arg0 参数上的注解信息@com.nzc.LikeAnnotation(params=[], value=) } } // 获取参数上全部的注解信息 Annotation[][] parameterAnnotations = annotationTest.getParameterAnnotations(); for (int i = 0; i < parameterAnnotations.length; i++) { for (int i1 = 0; i1 < parameterAnnotations[i].length; i1++) { System.out.println(parameterAnnotations[i][i1]); } } // @com.nzc.LikeAnnotation(params=[], value=) //@com.nzc.MarkAnnotation(value=) int parameterCount = annotationTest.getParameterCount(); System.out.println("获取参数个数==>"+parameterCount); } ​ ​ 复制代码

3.3.5、获取方法返回参数和方法权限修饰符

@Test public void test33() throws Exception { Class<?> stuClass = Class.forName("com.nzc.Student"); ​ Method annotationTest = stuClass.getDeclaredMethod("annotationTest",String.class,String.class); // 获取方法的返回值类型 Class<?> returnType = annotationTest.getReturnType(); System.out.println(returnType); //void // 获取权限修饰符 System.out.println(Modifier.toString(annotationTest.getModifiers()) ); //public } 复制代码

3.4、反射获取运行时类信息、接口信息、包信息

3.4.1、获取运行时类的接口信息

/** * 获取运行时类实现的接口 */ @Test public void test5() { Class clazz = Student.class; Class[] interfaces = clazz.getInterfaces(); for (Class c : interfaces) { System.out.println(c); } System.out.println("===================="); // 获取运行时类的父类实现的接口 Class[] interfaces1 = clazz.getSuperclass().getInterfaces(); for (Class c : interfaces1) { System.out.println(c); } } 复制代码

3.4.2、获取类所在包的信息

/** * 获取运行时类所在的包 */ @Test public void test6() { Class clazz = Person.class; Package pack = clazz.getPackage(); System.out.println(pack); // out:package com.nzc } 复制代码

3.4.3、获取类上注解信息

@Test public void test1() throws Exception { Class<?> stuClass = Class.forName("com.nzc.Student"); System.out.println("==== 获取类上指定的注解信息==="); ​ LikeAnnotation annotation = stuClass.getAnnotation(LikeAnnotation.class); System.out.println(annotation); //@com.nzc.LikeAnnotation(params=[], value=123) System.out.println("获取注解上的值信息==>"+annotation.value()); //获取注解上的值信息==>123 //annotation.params(); 注解有几个属性,就可以获取几个属性 ​ System.out.println("==== 获取类上全部注解信息==="); ​ Annotation[] annotations = stuClass.getAnnotations(); for (Annotation annotation1 : annotations) { System.out.println(annotation1); //@com.nzc.LikeAnnotation(params=[], value=123) } ​ LikeAnnotation declaredAnnotation = stuClass.getDeclaredAnnotation(LikeAnnotation.class); System.out.println("declaredAnnotation==>"+declaredAnnotation); //declaredAnnotation==>@com.nzc.LikeAnnotation(params=[], value=123) Annotation[] declaredAnnotations = stuClass.getDeclaredAnnotations(); ​ } 复制代码

注意:lombok 相关的注解是无法获取到的,因为 lombok 注解为编译时注解,并非是运行时注解,在编译完成后,lombok 注解并不会保留于class文件中,因此是无法通过反射获取到的。

​

@Data 也标明了它的存在级别为源码级别,而运行时存在注解@Retention为@Retention(RetentionPolicy.RUNTIME) 。

3.5、反射获取运行时类的父类的泛型信息、接口泛型信息

@Test public void test8(){ Class clazz = Person.class; ​ // 获取泛型父类信息 Type genericSuperclass = clazz.getGenericSuperclass(); System.out.println(genericSuperclass); // com.nzc.Generic<java.lang.String> // 获取泛型接口信息 Type[] genericInterfaces = clazz.getGenericInterfaces(); ​ for (Type genericInterface : genericInterfaces) { System.out.println(genericInterface); //interface com.nzc.TestService //com.nzc.GenericInterface<java.lang.String> } } 复制代码

四、反射应用场景及实战案例

5.1、那到底什么时候会使用反射

一句话说它的应用场景就是:确定不下来到底使用哪个类的时候,比如你要开发一个通用工具类,为了达到通用性,传入的参数对象,一般都是无法限制的,这个时候就是要用到反射啦~。

反射的特征:动态性

5.2、AOP + 自定义注解 修改前端传递的参数信息

需求如下:我现在引入了一个第三方 jar 包,里面有一个 MyBatis-Plus 查询构造器,其中构造 LIKE条件查询的条件是当前端传过来的参数带有逗号时,拼接为LIKE查询条件。

关键代码:

标识在对象的某个成员属性上

@Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface LikeAnnotation { ​ String value() default ""; } ​ 复制代码

标识在Controller层上,以此来判断那些请求是需要被切入的。

@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MarkAnnotation { ​ String value() default ""; ​ } 复制代码

一个非常简单的Javabean

@Data public class Login { ​ @LikeAnnotation(value = "username") private String username; ​ private String password; ​ public Login() { } ​ public Login(String username, String password) { this.username = username; this.password = password; } } ​ 复制代码

Controller 类

@Slf4j @RestController public class HelloController { ​ @MarkAnnotation @GetMapping("/search") public Login getLikeLogin(Login login){ System.out.println(login); return login; } ​ } 复制代码

重点重点,切面类 LikeAnnotationAspect,处理逻辑全部在此处

@Aspect @Component @Slf4j public class LikeAnnotationAspect { ​ // 标记了 @MarkAnnotation 注解的才切入 降低性能消耗 @Pointcut("@annotation(com.nzc.annotation.MarkAnnotation)") public void pointCut() { ​ } ​ // 获取当前类及父类所有的成员属性 private static Field[] getAllFields(Object object) { Class<?> clazz = object.getClass(); List<Field> fieldList = new ArrayList<>(); while (clazz != null) { fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()))); clazz = clazz.getSuperclass(); } Field[] fields = new Field[fieldList.size()]; fieldList.toArray(fields); return fields; } ​ @Around("pointCut()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { long start = System.currentTimeMillis(); Object[] args = pjp.getArgs(); // 获取到第一个参数 Object parameter = args[0]; ​ log.info("parameter==>{}", parameter); if (parameter == null) { return pjp.proceed(); } ​ Field[] fields = getAllFields(parameter); ​ for (Field field : fields) { log.debug("------field.name------" + field.getName()); if (field.getAnnotation(LikeAnnotation.class) != null) { try { field.setAccessible(true); Object username = field.get(parameter); field.setAccessible(false); if (username != null && !username.equals("")) { field.setAccessible(true); field.set(parameter, "," + username + ","); field.setAccessible(false); } } catch (Exception e) { } } } // 调用方法 Object result = pjp.proceed(); long end = System.currentTimeMillis(); log.debug("修改耗时==>" + (end - start) + "ms"); return result; } ​ } 复制代码

实现效果:

​

​

5.3、Mybatis 拦截器实现自动填充创建人、修改人信息

看似好像写业务的我们,没有怎么接触Java反射,但实际上可能处处都隐含着反射。

使用过 Mybatis-Plus 的朋友,应该知道,可以设置自动填充数据(创建时间、更新时间、创建人、更新人等),不过那个是实现MetaObjectHandler接口进行处理的。

但是今天的话,我用Mybatis 原生的拦截器来进行一番实现,实现每次更新、添加时自动填充创建人、更新人等,表里没时间字段,就没演示时间了,但实现原理都一致。

import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.binding.MapperMethod.ParamMap; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.*; import org.springframework.stereotype.Component; ​ import java.lang.reflect.Field; import java.util.*; ​ /** * mybatis拦截器,自动注入创建人、创建时间、修改人、修改时间 * @author Ning Zaichun */ @Slf4j @Component @Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) }) public class MybatisInterceptor implements Interceptor { public static Field[] getAllFields(Object object) { Class<?> clazz = object.getClass(); List<Field> fieldList = new ArrayList<>(); while (clazz != null) { fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()))); clazz = clazz.getSuperclass(); } Field[] fields = new Field[fieldList.size()]; fieldList.toArray(fields); return fields; } ​ @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; String sqlId = mappedStatement.getId(); log.info("------sqlId------" + sqlId); //2022-09-17 17:16:50.714 INFO 14592 --- [nio-8080-exec-1] com.nzc.tree.commons.MybatisInterceptor : ------sqlId------com.nzc.tree.mapper.CategoryMapper.updateById SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); Object parameter = invocation.getArgs()[1]; log.info("------sqlCommandType------" + sqlCommandType); //2022-09-17 17:16:50.714 INFO 14592 --- [nio-8080-exec-1] com.nzc.tree.commons.MybatisInterceptor : ------sqlCommandType------UPDATE ​ if (parameter == null) { return invocation.proceed(); } if (SqlCommandType.INSERT == sqlCommandType) { Field[] fields = getAllFields(parameter); for (Field field : fields) { log.info("------field.name------" + field.getName()); try { if ("createBy".equals(field.getName())) { field.setAccessible(true); Object local_createBy = field.get(parameter); field.setAccessible(false); if (local_createBy == null || local_createBy.equals("")) { field.setAccessible(true); field.set(parameter, "nzc-create"); field.setAccessible(false); } } } catch (Exception e) { e.printStackTrace(); } } } if (SqlCommandType.UPDATE == sqlCommandType) { Field[] fields = null; if (parameter instanceof ParamMap) { ParamMap<?> p = (ParamMap<?>) parameter; if (p.containsKey("et")) { parameter = p.get("et"); } else { parameter = p.get("param1"); } if (parameter == null) { return invocation.proceed(); } ​ fields = getAllFields(parameter); } else { fields = getAllFields(parameter); } ​ for (Field field : fields) { log.info("------field.name------" + field.getName()); try { if ("updateBy".equals(field.getName())) { field.setAccessible(true); field.set(parameter,"nzc-update"); field.setAccessible(false); } } catch (Exception e) { e.printStackTrace(); } } } return invocation.proceed(); } ​ @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } ​ @Override public void setProperties(Properties properties) { // TODO Auto-generated method stub } } 复制代码

里面牵扯到的一些 Mybatis 中的一些对象,我没细说了,大家打印出来的时候都可以看到的。

测试:

​

执行的SQL语句打印信息

​

结果:

​反射的特性,看似和我们天天写业务没啥关系,但是它其实一直伴随着我们,这也是 Java 开发者的基础知识,基础不牢,地动山摇~

五、反射的优缺点

优点: 反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。 它允许程序创建和控制任何类的对象,无需提前硬编码目标类;对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;

缺点 :让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。

以下文字来自于 反射是否真的会让你的程序性能降低吗?

1.反射大概比直接调用慢50~100倍,但是需要你在执行100万遍的时候才会有所感觉

2.判断一个函数的性能,你需要把这个函数执行100万遍甚至1000万遍

3.如果你只是偶尔调用一下反射,请忘记反射带来的性能影响

4.如果你需要大量调用反射,请考虑缓存。

5.你的编程的思想才是限制你程序性能的最主要的因素

小结

仔细阅读下来你会发现,

正如文中所说,所谓Class对象,也称为类模板对象,其实就是 Java 类在 JVM 内存中的一个快照,JVM 将从字节码文件中解析出的常量池、 类字段、类方法等信息存储到模板中,这样 JVM 在运行期便能通过类模板而获取 Java 类中的任意信息,能够对 Java 类的成员变量进行遍历,也能进行 Java 方法的调用,获取到类的任意信息。

反射也就是这样啦,不知道你会使用啦吗,如果你还没有的话,我觉得可以再读上一遍,顺带自己验证一遍,希望你能有所收获。

若依框架避坑指南:自定义注解实现自动填充的5个常见问题 本文详细解析了在若依框架中使用自定义注解实现自动填充功能时常见的5个问题与解决方案。内容涵盖注解设计、AOP切面实现、与现有组件集成、性能优化及测试策略,重点解决了创建人、时间等字段自动填充的实践难点,帮助开发者构建健壮的自动填充机制。 阅读详情

相关推荐

MyBatis数据加密与模糊查询实战:TypeHandler与拦截器组合方案

数据安全是应用架构设计的核心考量,尤其在涉及用户敏感信息的场景下,数据库字段级加密成为合规性刚需。其原理是在数据持久化过程中,通过透明加解密技术,确保敏感数据以密文形式存储,同时业务代码无需感知加解密细节。这项技术的核心价值在于平衡安全性与开发效率,实现安全合规的低侵入性落地。在Java生态中,MyBatis作为主流的持久层框架,其TypeHandler机制和插件拦截器为数据透明加密提供了天然的技术支撑。通过自定义TypeHandler,可以无缝实现字段的自动加密存储与解密回显;而面对加密后数据难以进行模糊

weixin_30417487的博客 364

Java | 使用切面AOP拦截并修改Controller接口请求参数

在开发过程中,会有一些需求将controller层的一些方法入参进行全量转换,最容易想到的可能是在调用下层service方法时,调用公共的方法进行入参转换,这时带来的唯一问题就是代码不雅观,比较冗余。那还有什么方法可以更优雅的解决这个问题吗?答案是有的:切面。我们实现一个AOP切面程序,对入参中的需转换的参数进行专项转换,而无需在各个controller层的各个方法中进行转换处理。

Andya_net的博客 7364

java通过反射封装工具类,自动填充实体类,

最近项目上需要实现工具类,把公共属性填充到数据里面。

知识改变命运,ヾ(◍°∇°◍)ノ゙【求关注】 561

如何利用AOP/Aspect来修改方法的入参

通过AOP的思想,来改变接口请求的入参

歪桃的博客 7489

AOP修改方法的参数

AOP修改方法的参数,如何截取指定方法的参数并修改其参数值

SkyWalking跨线程Trace传递实战:手把手教你用RunnableWrapper解决异步链路中断问题

本文深入解析了SkyWalking在异步编程场景下链路追踪中断的根源,并提供了使用RunnableWrapper等官方包装类的实战解决方案。通过详细的原理解析和代码示例,手把手指导开发者如何实现跨线程的Trace传递,确保分布式系统监控链路的完整性,有效解决异步任务导致的链路中断问题。

sand8的博客 495

Java Spring 通过 AOP 实现方法参数的重新赋值、修改方法参数的取值

方式一:通过环绕通知实现 方式二:通过前置通知 + 反射实现

emmmm..... 6743

Python 驱动 CAD 二次开发:从零搭建自动化绘图环境

本文详细介绍了如何利用Python进行CAD二次开发,实现自动化绘图。通过win32com库建立Python与CAD软件的通信,文章从环境搭建、连接CAD、操作文档到核心绘图与批量处理,提供了完整的实战指南。该方法能显著提升机械设计、建筑绘图等领域的重复性工作效率,将创造力从繁琐劳动中解放出来。

weixin_29093169的博客 1237

Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)...

@charset "UTF-8"; .markdown-body { overflow: hidden; line-height: 1.75; font-size: 15px; background-image: linear-gradient(90deg, rgba(72, 42, 10, 0.05) 5%, rgba(72, 42, 10, 0) 0), linear-gradient(1t...

程序员小明1024 258

EDA环境下的LSF进阶调优:如何为Cadence/VCS等工具正确配置cgroups与daemon starter

本文深入探讨了在EDA环境中如何为Cadence/VCS等工具正确配置LSF的cgroups与daemon starter,解决资源分配与特殊应用兼容性问题。通过详细解析cgroups技术、daemon starter配置及混合资源管理策略,帮助提升LSF集群的效率和稳定性,特别适用于高性能计算和芯片设计场景。

weixin_33724046的博客 461

IFIX5.8在Win10系统下的安装避坑指南

本文提供了IFIX5.8在Windows 10系统下的详细安装教程与避坑指南。针对常见的驱动缺失、UAC拦截及兼容性报错等问题,文章从安装前准备、分步安装流程到安装后配置与驱动修复,给出了系统性的解决方案,帮助工控从业者顺利完成部署。

f6g7h8i9j的博客 364

SpringBoot中的两种字段自动填充方式

这个拦截器的作用是自动填充createdAt和updatedAt字段,以便在执行INSERT和UPDATE操作时自动记录创建和更新时间。主要拦截Executor的update方法,通过判断SQL类型来确定是INSERT还是UPDATE操作,从而设置相应字段。使用了MyBatis的MetaObject工具类来动态操作参数对象的字段值。通过这个拦截器,开发者不需要在业务代码中手动设置createdAt和updatedAt,大大减少了重复代码,也保证了这些公共字段的一致性和正确性。

2302_78571314的博客 1945

SAR成像专栏目录

为方便大家阅读,我做了一个专栏目录,蓝色字有超链接,可以直接打开。 本专栏默认读者已具备雷达系统的基本知识或者已阅读过雷达导论专栏中的文章。 1. 概述 SAR成像概述 星载SAR的各项指标解读(史上最全) SAR成像算法的实现途径与算力分析 2. SAR成像原理与SAR系统简介 雷达与分辨率 SAR成像原理与方法 SAR图像的形成和处理 SAR系统设计 3. 点目标仿真 点目标仿真(一)—— 数学模型 点目标仿真(二)—— Matlab仿真 4. 成像算法 更新中 雷达导论专

7475

用mybatis 拦截器 为insert update操作填充字段

用mybatis 拦截器 为insert update操作填充字段 import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.S

zqymby的博客 5013

风控域——决策引擎系统设计

本文详细介绍了风控决策引擎系统的设计与应用。决策引擎系统是一种智能化工具,可自动化、数据驱动地辅助或替代人工决策,广泛应用于金融、医疗、营销、风控等领域。文章阐述了决策引擎的核心功能,包括自动化决策、动态规则管理、实时处理和模型集成,以及系统组成,涵盖数据输入层、规则引擎和模型服务层。此外,还探讨了决策引擎的典型应用场景、技术架构、组件设计,以及风控决策引擎系统的技术方案、设计难点和重点。

庄小焱 3321

OpenClaw 超级 AI 实战专栏【实战案例】(五)邮件收发 + 日历日程 + 文件管理一站式搞定

OpenClaw 实现办公自动化核心是模块解耦:邮件收发、文件管理、日历日程各自封装,便于维护和扩展;开发中需重点解决编码兼容、权限配置、时间格式、重复处理四大类坑点,核心思路是 “提前校验 + 异常兜底 + 统一格式”;敏感信息(邮箱密码、API Token)务必通过.env管理,避免硬编码泄露。

qq_36130719的博客 746

java类向拦截器传值_MyBatis拦截器:给参数对象属性赋值的实例

该拦截器的作用:在进行增加、修改等操作时,给数据模型的一些通用操作属性(如:创建人、创建时间、修改人、修改时间等)自动赋值。该实现是在dao层拦截,即存入db前最后一层。后经分析,不是很合理,改为在service层拦截,用spring aop来实现了,该代码遂弃用。不过已经测试可用,记录备忘。package com.development;import java.lang.reflect.invo...

weixin_39763640的博客 745

MicroCap声学仿真介绍(三)-人耳的频率响应

这里需要注意的是,24mm我们会填为”24m”,这里的“m”是毫的意思,24m意思是24e-3,同理7.4mm我们可以填为7.4m,意思是7.4e-3。MicroCap声学库中,已内置“声源”模块和“管”模块,我们需要调用这两个模块,并用连接线将这两个模块连接到一起,完成仿真工作。上图中第一个图是在管的一端输入1Pa(94dBSPL)的信号,管的另一端的不同频率的声压大小的仿真结果。上述的结果跟我们的常识也是符合的。换言之,我们要仿真的目的是在管的一端加1pa的声压时,管的另一端不同频率下的声压分布。

山无忧的博客 1474

不使用binlog,canal,kafka等,只用java+mybatis拦截器来实现项目中的异步双写主从数据库,代码逻辑全整理

项目中因为要迁库,所以我要在原项目中接入我的双写逻辑,确保新旧两个库都有数据写入,假如新库写入失败,旧库数据也能写入,这就确保了重要数据不能丢失。 一开始考虑的方案是使用数据同步工具,像是canal或是DTS等,但是环境这块卡的比较死,没有其他花里胡哨的工具,只能纯靠java改写代码来实现了,期间排了不少坑,这里做个人踩坑记录实现效果,批量双写全部报200,自测下来还算成功......

Koikoi12的博客 4454

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

公共字段自动填充三种方法

步骤1:自定义注解/*** 自定义注解,用于标识某个方法需要进行功能字段自动填充*///数据库操作类型,UPDATE,INSERT步骤2:自定义切面类AutoFillAspect,统一拦截被AutoFill注解修饰的方法/*** 自定义切面,实现公共字段自动填充处理逻辑*/@Aspect@Component@Slf4j/*** 切入点*///加execution的目的是提升性能,以免Spring扫描了所有包/*** 前置通知,在通知中进行公共字段的赋值*/

heima_xzq的博客 358

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

MyBatis拦截器:给参数对象属性赋值

  该拦截器的作用:在进行增加、修改等操作时,给数据模型的一些通用操作属性(如:创建人、创建时间、修改人、修改时间等)自动赋值。   该实现是在DAO层拦截,即存入DB前最后一层。后经分析,不是很合理,改为在service层拦截,用spring AOP来实现了,该代码遂弃用。不过已经测试可用,记录备忘。 1 package com.development; 2 3 imp...

weixin_30449239的博客 696

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

通过拦截器给默认字段赋值,如何实现mybatis拦截器?

无需修改已有代码,如果使用AOP依然需要在每个位置添加注解,而Mybatis拦截器无需修改。

qq_53199639的博客 496

MyBatis-Plus自动填充公共字段:告别重复代码,提升开发效率

在数据持久化开发中,公共字段(如创建时间、创建人、更新时间、更新人)的管理是常见的工程需求。其核心原理是通过拦截数据操作生命周期,在SQL执行前自动注入预设值,从而将业务代码与基础设施逻辑解耦。这项技术的价值在于提升代码复用性、保证数据规范性,并减少因手动遗漏导致的数据不一致问题。它广泛应用于后台管理系统、电商平台等需要审计追溯和数据一致性保障的场景。本文以MyBatis-Plus的MetaObjectHandler为核心,结合ThreadLocal实现用户上下文传递,详细解析如何通过注解驱动的方式,实现公

349

从零手写公共字段自动填充:自定义注解 + AOP + 反射 + ThreadLocal 完整落地(苍穹外卖业务实践)

市面上普遍使用 Mybatis-Plus 提供的MetaObjectHandler实现自动填充,但面试中经常会被追问:不使用 Mybatis-Plus,原生 Mybatis 如何手动实现公共字段自动填充?面试话术:项目中如果使用 Mybatis-Plus 可以直接用框架自带填充,但为了应对原生 Mybatis 场景、吃透底层原理,我在苍穹外卖中手动实现了 AOP 通用填充方案。Mybatis-Plus 自动填充:底层封装黑盒,面试容易被深挖底层原理,且原生 Mybatis 项目无法使用;

m0_73470668的博客 203

基于MyBatis拦截器与注解实现数据动态加解密的工程实践

数据加密是保障企业应用安全的核心技术之一,其核心原理在于通过特定算法将明文转换为不可读的密文,以防止敏感信息泄露。在技术价值上,它不仅是合规要求,更是构建用户信任的基石。然而,在工程实践中,直接在业务代码中硬编码加解密逻辑会导致代码臃肿、难以维护,且容易遗漏造成安全漏洞。因此,寻求一种对业务代码无侵入的自动化方案成为普遍需求。应用场景广泛存在于涉及用户隐私(PII)如手机号、身份证号的用户中心、支付系统等。本文聚焦于利用MyBatis拦截器(Interceptor)与自定义注解,实现一种在数据持久化层自动、

weixin_34101229的博客 364
上一篇: 谈谈基于Kafka的六种微服务架构事件驱动模式
下一篇: JAVA缓存规范—JCache API与Spring Cache
Java_LingFeng
博客等级 码龄4年 505粉丝 259原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值