反射使用
java.lang.reflect
获取Class对象的三种方式
public class Demo001 {
public static void main(String[] args) {
Class s = Student.class; //方式1
System.out.println(s.getName());
System.out.println(s.getSimpleName());
Class c2 = Class.forName("reflect.Student"); //方式2
System.out.println(c2.getName());
System.out.println(c1==c2); //返回true,说明是同一个对象
Student s = new Student();//方式3
Class c3 = s.getClass();
System.out.println(c3==c2); //返回true,也是同一个对象
}
}
为什么三种对象创建的方式创建出来的对象都是同一个呢?
Class 对象
在 JVM 中,每个类在整个生命周期中只有一个 Class 对象。只有一份字节码。
类加载器 (ClassLoader) → 加载类 → 创建唯一的 Class 对象 → 缓存到 JVM
堆内存 (Heap):
┌─────────────────────────┐
│ Class 对象 (唯一) │ ← c1, c2, c3 都指向这里
│ - 类名:reflect.Student│
│ - 字段信息 │
│ - 方法信息 │
│ - 构造函数信息 │
└─────────────────────────┘
↑
│ c1, c2, c3 都指向同一个地址
实例对象
Student s1 = new Student();
Student s2 = new Student();
Student s3 = new Student();
这3种都不是同一个对象。
类构造器(构造方法)
Class c1 = Student.class;
Constructor[] constructors = c1.getConstructors();//获取全部构造方法 只能拿public构造方法
Constructor[] declaredConstructors = c1.getDeclaredConstructors();//获取全部构造方法
Constructor ct1 = c1.getConstructor(String.class, int.class); //获取指定参数的构造方法
Constructor ct2 = c1.getDeclaredConstructor(String.class, int.class); //获取指定参数的构造方法
通过类构造器初始化对象
Constructor constructor = c1.getConstructor(String.class, int.class); //获取指定参数的构造方法
Constructor constructor2 = c1.getDeclaredConstructor(String.class, int.class); //获取指定参数的构造方法
Student s1 = (Student) constructor.newInstance("张三", 18);
Constructor cs4 = c1.getDeclaredConstructor();
cs4.setAccessible( true); //设置访问权限可以访问private构造方法
Student s2 = (Student) cs4.newInstance();
属性
Class c1 = Student.class;
Field[] fields = c1.getFields();//只能拿public字段
for (Field field : fields) {
System.out.println(field.getName()+"--"+field.getType());
}
Field[] declaredFields = c1.getDeclaredFields();//获取全部字段
for (Field field : declaredFields) {
System.out.println(field.getName()+"--"+field.getType());
}
//通过名字定位
// Field name = c1.getField("name"); //拿不到private
// System.out.println(name.getName()+"--"+name.getType());
Field age = c1.getDeclaredField("age");
System.out.println(age.getName()+"--"+age.getType());
//为实例对象赋值
Student s = new Student("11",1);
age.setAccessible(true);
age.set(s,18);
System.out.println(s.getAge());
//取值
int ageValue = (int) age.get(s);
System.out.println(ageValue);
方法
public static void testGetMethod() throws Exception {
Class c1 = Student.class;
Method[] methods = c1.getMethods();//获取全部方法 只能拿public方法
for (Method method : methods) {
System.out.println(method.getName()+"--"+method.getParameterCount() +"---"+method.getReturnType());
}
Method[] declaredMethods = c1.getDeclaredMethods();//获取全部方法
for (Method method : declaredMethods) {
System.out.println(method.getName()+"--"+method.getReturnType());
}
Method getAge1 = c1.getMethod("getAge"); //获取指定参数的构造方法,只能拿public方法
Method getAge2 = c1.getDeclaredMethod("getAge"); //获取指定参数的构造方法
Method setAge1 = c1.getDeclaredMethod("setAge", int.class);
//方法执行
Student s = new Student("11",1);
setAge1.invoke(s,18);
System.out.println(s.getAge());
Method show = c1.getDeclaredMethod("show");
show.setAccessible(true);
show.invoke(s);
}
反射作用
-
得到一个类的全部成分,然后操作。
-
破坏封装性,直接可以调用私有的方法。
-
主要是在java框架使用
注解使用
java里的特殊标记,让其他程序根据注解信息来决定怎么执行该程序。
注解本质是一个接口,继承于Annotation接口。
使用@注解的过程就是创建一个实现类对象。
元注解
修饰注解的注解
-
@Target
@Target(ElementType.TYOE) 1.TYPE 类,接口 2.FIELD 成员变量 3.METHOD 成员方法 4.PARAMETER 方法参数 5.CONSTRUCTOR 构造方法 6.LOCAL_VARIABLE 局部变量 -
@Retention 声明注解的保留周期
1.SOURCE 只在源码阶段,字节码文件不存在 2.CLASS 默认值,保留到字节码,运行阶段不存在 3.RUNTIME 常用,保留到运行阶段
注解解析
- 要解析哪个类上面的注解,就要先拿到他。
- 先拿到Class对象,再解析上面的注解
- 拿到方法Method对象,在解析上面的注解。
- 由于Class,Method,Field,Constructor都实现了AnnotatedElement接口,所以都有解析能力。
测试
创建4个注解
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ClassAnnotationTest {
/**
* 权限标识(如:system:user:list)
*/
String value();
/**
* 描述
*/
String description() default "";
}
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnnotationTest {
String value();
}
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodAnnotationTest {
String value();
}
import java.lang.annotation.*;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParameterAnnotationTest {
String value();
}
创建使用注解的类
@ClassAnnotationTest(value = "admin",description = "测试")
public class AnnotationTest {
@FieldAnnotationTest("x前缀-")
private String name;
private int age;
@MethodAnnotationTest("system:user:list")
public void login(){
System.out.println("登录成功");
}
public void test(@ParameterAnnotationTest("参数前缀-") String name){
System.out.println(name);
}
}
运行测试类
public class Demo01 {
//注解解析
public static void main(String[] args) throws Exception {
// getClassAnnotation();
// getMethodAnnotation();
// getFieldAnnotation();
getParameterAnnotation();
}
public static void getParameterAnnotation() throws Exception {
Class demo01Class = AnnotationTest.class;
Method method = demo01Class.getDeclaredMethod("test", String.class);
ParameterAnnotationTest annotation = method.getParameters()[0].getAnnotation(ParameterAnnotationTest.class);
System.out.println(annotation.value());
}
/**
* 获取属性注解
*/
public static void getFieldAnnotation() throws NoSuchFieldException {
Class demo01Class = AnnotationTest.class;
//拿到属性全部注解
Field[] fields = demo01Class.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
Field name = demo01Class.getDeclaredField("name");
//拿到属性上面全部注解
if (name.isAnnotationPresent(FieldAnnotationTest.class)) {
FieldAnnotationTest annotation = name.getAnnotation(FieldAnnotationTest.class);
System.out.println(annotation.value());
}
}
/**
* 获取方法注解
*/
public static void getMethodAnnotation() throws NoSuchMethodException {
Class demo01Class = AnnotationTest.class;
Method method = demo01Class.getDeclaredMethod("login");
//拿到方法上面全部注解
Annotation[] annotations = method.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
if(method.isAnnotationPresent(MethodAnnotationTest.class)){
MethodAnnotationTest annotation = method.getAnnotation(MethodAnnotationTest.class);
System.out.println(annotation.value());
}
}
public static void getClassAnnotation() {
Class demo01Class = AnnotationTest.class;
//拿到类上面全部注解
Annotation[] annotations = demo01Class.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//判断注解是否存在
boolean annotationPresent = demo01Class.isAnnotationPresent(ClassAnnotationTest.class);
System.out.println(annotationPresent);
if(annotationPresent){
//拿到指定注解
ClassAnnotationTest checkPermission = (ClassAnnotationTest) demo01Class.getDeclaredAnnotation(ClassAnnotationTest.class);
System.out.println(checkPermission.value());
System.out.println(checkPermission.description());
}
}
}
模仿Junit框架
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
String value() default "";
}
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class AnnotationTest {
@MyTest
public void test1(){
System.out.println("test1");
}
@MyTest
public void test2(){
System.out.println("test2");
}
// @MyTest
public void test3(){
System.out.println("test3");
}
public static void main(String[] args) throws Exception {
AnnotationTest annotationTest = new AnnotationTest();
Class demo01Class = AnnotationTest.class;
//拿到所有方法
Method[] methods = demo01Class.getDeclaredMethods();
for (Method method : methods) {
//判断方法是否有@MyTest注解
if(method.isAnnotationPresent(MyTest.class)){
//执行方法
method.invoke(annotationTest);
}
}
}
}
鉴权
结合前面学的反射和注解实现一个简单的鉴权框架
①注解
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Perms {
/**
* 权限标识
*/
String value();
}
②数据库
public class RolePermsDBService {
/*假设从数据库拿到角色权限*/
public String[] getRolePerms(){
String[] rolePerms = {"dept:add","dept:find"};
return rolePerms;
}
}
③使用注解的对象
public class DeptController {
@Perms("dept:add")
public void addDept(){
System.out.println("添加部门");
}
@Perms("dept:update")
public void updateDept(){
System.out.println("修改部门");
}
@Perms("dept:delete")
public void deleteDept(){
System.out.println("删除部门");
}
@Perms("dept:find")
public void findDept(){
System.out.println("查询部门");
}
}
④反射处理对象
public class DeptControllerBean {
DeptController deptController = new DeptController();
Class demo01Class = DeptController.class;
private List<String> rolePerms;
private DeptControllerBean(){
//初始化时获取数据库中的权限
RolePermsDBService rolePermsService = new RolePermsDBService();
rolePerms = Arrays.stream(rolePermsService.getRolePerms()).toList();
}
private static final DeptControllerBean instance = new DeptControllerBean();
public static DeptControllerBean getInstance(){
return instance;
}
private void invoke(String methodName) {
try{
Method addDept = demo01Class.getDeclaredMethod(methodName);
//判断权限
Perms annotation = addDept.getAnnotation(Perms.class);
if(annotation!=null){//存在注解
if (rolePerms.contains(annotation.value())) {
addDept.invoke(deptController);
return;
}else{
System.out.println("没有权限");
return;
}
}else{ //不存在注解
addDept.invoke(deptController);
}
addDept.invoke(deptController);
}catch (Exception e){
System.out.println("addDept方法不存在");
}
}
public void addDept(){
invoke("addDept");
}
public void updateDept(){
invoke("updateDept");
}
public void deleteDept(){
invoke("deleteDept");
}
public void findDept(){
invoke("findDept");
}
}
⑤启动类
public class RunApplication {
public static void main(String[] args) {
DeptControllerBean instance = DeptControllerBean.getInstance();
instance.addDept();
instance.updateDept();
instance.deleteDept();
instance.findDept();
}
}