定义
java.lang.reflect
包中的类和接口来实现;反射机制主要包括
Class
类:类的实体Constructor
类:类的构造方法Method
类:类的方法Field
类:类的字段属性优缺点
package com.demo.fs;
public class DemoEntity {
public Integer id;
private String name;
public DemoEntity() {}
public DemoEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() { return id;}
public void setId(Integer id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
private void sayHello(){ System.out.println("hello"); }
public void sayHi() {System.out.println("hi");}
private void say(String param) {System.out.println(param);}
@Override
public String toString() { return "id:" + id + ",name:" + name; }
}
new Object().getClass()
.class
Class.forName("类的全路径")
Class<DemoEntity> aClass = DemoEntity.class;
Class<? extends DemoEntity> cClass = new DemoEntity().getClass();
Class<?> bClass = Class.forName("com.demo.fs.DemoEntity");
Class<DemoEntity> clazz = DemoEntity.class;
DemoEntity demoEntity = clazz.newInstance();
demoEntity.setId(1);
demoEntity.setName("张三");
System.out.println(demoEntity);
Class<DemoEntity> clazz = DemoEntity.class;
Constructor<DemoEntity> constructor = clazz.getConstructor(Integer.class, String.class);
DemoEntity demoEntity = constructor.newInstance(2, "李四");
System.out.println(demoEntity);
getConstructor() 参数为构造函数的参数对应的数据类型,如若不匹配则会抛出 java.lang.NoSuchMethodException 异常
getFields()
: 获取当前class类全部公共字段getField(String name)
:获取指定的公共字段getDeclaredField()
:获取类型声明的所有的字段,包括私有、受保护、公共和默认访问权限的字段getDeclaredField(String name)
:可以获取类中声明的指定字段,包括私有、受保护、公共和默认访问权限的字段上述实体类中id为public权限,name为private权限
Field filed = Class.getField("属性名");
filed.set("实体类",Object属性值);
Class<DemoEntity> clazz = DemoEntity.class;
Field idField = clazz.getField("id");
DemoEntity demoEntity = clazz.newInstance();
idField.set(demoEntity, 3);
System.out.println(demoEntity);
获取私有属性字段需要使用
getDeclaredField()
方法
Class<DemoEntity> clazz = DemoEntity.class;
Field idField = clazz.getField("id");
Field nameField = clazz.getDeclaredField("name");
DemoEntity demoEntity = clazz.newInstance();
idField.set(demoEntity, 4);
nameField.set(demoEntity,"小孙");
System.out.println(demoEntity);
上述代码执行中会抛出异常:java.lang.IllegalAccessException: Class com.demo.fs.AppRun can not access a member of class com.demo.fs.DemoEntity with modifiers "private"
是因为name
是私有属性
获取私有属性的字段需要暴力破除,通过设置nameField.setAccessible(true)
来设置允许访问私有属性
getMethods()
: 获取所有公有方法,包括父类(及Object)getMethod(String name)
: 获取指定公有方法,包括父类(及Object)getDeclaredMethods()
: 获取类中声明的所有方法,包括私有、受保护、公共和默认访问权限的方法getDeclaredMethod(String name)
: 获取类中指定方法,包括私有、受保护、公共和默认访问权限的方法invoke()
:执行方法Class<DemoEntity> clazz = DemoEntity.class;
DemoEntity demoEntity = clazz.newInstance();
Method sayHi = clazz.getMethod("sayHi");
sayHi.invoke(demoEntity);
此处调用也是一样,需要暴力破除 setAccessible(true);
Class<DemoEntity> clazz = DemoEntity.class;
DemoEntity demoEntity = clazz.newInstance();
Method sayHello = clazz.getDeclaredMethod("sayHello");
sayHello.setAccessible(true);
sayHello.invoke(demoEntity);
在获取方法时,需指定方法对应的参数类型,当指定的参数类型缺少或数据类型不匹配则会抛出异常,当有参方法为私有时,也需要在获取方法对象后,调用setAccessible(true);
Class<DemoEntity> clazz = DemoEntity.class;
DemoEntity demoEntity = clazz.newInstance();
Method say = clazz.getDeclaredMethod("say", String.class);
say.setAccessible(true);
say.invoke(demoEntity, "hello world");
Java注解(Annotation)是一种元数据(metadata),它可以用于在代码中添加注释和标记,使得程序可以通过反射机制获取到注解中的信息,从而达到动态修改程序行为的udit,提供了一种更加简洁,优雅,安全的编程方式。
注解可以适用于类,方法,字段,参数等多种程序元素上,比如常用注解@Override
,@Service
,@Bean
等
如何定义一个注解?
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Log {
// 注解属性
}
**@Target **
@Target用于指定被注解的适用范围,即可以标记在那些元素上面
**@Retention **
@Retention用于指定注解的生命周期,通常情况下,我自定义注解需要使用
RetentionPolicy.RUNTIME
,以便于运行时可以通过反射机制获取注解信息
**@Inherited **
@Inherited表示一个注解类型可以被继承,如果一个类使用了被@Inherited的注解,则它的子类也会自动继承该注解
**@Documented **
@Documented 是一个标记注解(meta-annotation),它用于指示被它所注解的注解将被 javadoc 工具记录。当使用 javadoc 命令生成 API 文档时,这些被记录的注解信息将被包含在生成的文档中。这样,在阅读 API 文档时,开发者可以方便地了解这些注解的含义和用法。
例如,如果一个注解被标记了 @Documented,那么在使用 javadoc 命令生成 API 文档时,该注解的说明信息将会被包含在生成的文档中。如果没有使用 @Documented 标记,则该注解的说明信息将不会被包含在生成的文档中。
一般来说,如果你开发的注解可以被用于文档化 API,则应该使用 @Documented 注解。
@Log(module = "demo",action = "test")
public class DemoEntity {
public static void main(String[] args) {
Class<DemoEntity> clazz = DemoEntity.class;
Log annotation = clazz.getAnnotation(Log.class);
if (annotation != null) {
System.out.println("module:" + annotation.module() + ",action:" + annotation.action());
}
}
getAnnotation(Class annotationClass)
:返回指定类型的注解Class<DemoEntity> clazz = DemoEntity.class;
Log annotation = clazz.getAnnotation(Log.class);
isAnnotationPresent(Class extends Annotation> annotationClass)
:判断目标对象是否有指定类型的注解,返回是一个布尔值if (DemoEntity.class.isAnnotationPresent(Log.class)) {
Log annotation = DemoEntity.class.getAnnotation(Log.class);
// 处理注解
}
getAnnotatedInterfaces()
:返回目标对象上所有注解类型为@Inherited的注解的接口类型,这个方法会遍历目标类的所有父类,并且如果注解的类型为@Inherited,也会返回这些父类的接口类型。getAnnotations()
:该方法返回一个包含此元素上所有注解的数组,包括继承的注解 Annotation[] annotations = DemoEntity.class.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof Log) {
// 处理注解
}
}
getDeclaredAnnotations()
:返回一个包含此元素上所有声明的注解的数组,不包括继承的注解和类上的注解。 Annotation[] annotations = DemoEntity.class.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof Log) {
// 处理注解
}
}
日志记录
注解&AOP实现日志信息记录
pom.xml
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.5.8version>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
dependencies>
自定义注解:
@Documented
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Log {
/**
* 当前模块
* @return 模块
*/
String module() default "" ;
/**
* 操作类型
* @return add新增,modify编辑,delete删除
*/
String action() default "" ;
}
aop切面
@Aspect
public class LogAop {
@Pointcut("execution(* com.*.controller.*.*(..))")
public void LogPointcut() {}
@Around("LogPointcut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Log annotation = methodSignature.getMethod().getAnnotation(Log.class);
// 日志记录
if (annotation != null) {
String module = annotation.module();
String action = annotation.action();
// TODO: 进行数据库存储
}
return point.proceed();
}
}