java——反射与注解
Java 反射基础
了解 Java 反射的概念和原理,熟悉 Class、Method、Field 等反射 API 的使用方法,能够通过反射来动态地创建对象、调用方法、设置字段等。 Java 反射是 Java 语言中非常重要的一个特性,它允许程序在运行时获取类的信息,并且可以通过反射调用类的方法、读写类的属性等。下面是 Java 反射基础概念和示例代码:
1. 概念详解
Java 反射的核心是 Class 类,它代表了一个 Java 类的类型信息。通过 Class 可以获取到类的各种信息,例如类的名称、字段、方法、构造方法等。
获取 Class 对象的三种方式:
使用对象.getClass()方法获取对象的 Class 对象。
通过类名.class 来获取指定类的 Class 对象。
使用 Class.forName("类名")方法获取指定类的 Class 对象。
2. 示例代码
以下是一个简单的 Java 反射示例,它演示了如何通过反射创建对象、调用方法和设置字段:
public class Person { private String name; private int age; public Person() {} public Person(String name, int age) { this.name = name; this.age = age; } public void sayHello() { System.out.println("Hello, I'm " + name + ", " + age + " years old."); } } public class ReflectionDemo { public static void main(String[] args) throws Exception { // 创建Person类的Class对象 Class<Person> clazz = Person.class; // 创建Person类的实例 Person person = clazz.newInstance(); // 获取Person类的name字段,并设置其值 Field nameField = clazz.getDeclaredField("name"); nameField.setAccessible(true); nameField.set(person, "Tom"); // 获取Person类的age字段,并设置其值 Field ageField = clazz.getDeclaredField("age"); ageField.setAccessible(true); ageField.setInt(person, 18); // 调用Person类的sayHello方法 Method sayHelloMethod = clazz.getDeclaredMethod("sayHello"); sayHelloMethod.invoke(person); // 输出:Hello, I'm Tom, 18 years old. } }
在上面的示例代码中,通过 Class 对象获取了 Person 类的信息,然后使用 newInstance()方法创建一个 Person 类的实例。接着,通过反射获取到 Person 类的 name 和 age 字段,并分别为它们设置了值。最后,通过反射获取到 Person 类的 sayHello 方法,并调用该方法输出一句话。
Java 反射进阶
深入学习 Java 反射的应用场景,例如框架设计、动态代理、模板方法等;掌握 Java 反射中常用的技巧和注意事项,提高反射编程的效率和可维护性。 Java 反射是指在运行时动态地获取一个类的信息,对类的属性和方法进行操作。它可以让我们在运行时通过字符串来调用类的方法或创建对象,而不需要提前知道该类的名称。Java 反射广泛应用于框架设计、动态代理、模板方法等场景。
下面分别介绍 Java 反射在框架设计、动态代理、模板方法等场景中的应用,并给出常用技巧和注意事项。
1. 框架设计
在框架设计中,Java 反射被广泛应用于插件机制和配置文件解析等场景。例如,Spring 框架就使用了 Java 反射来实现依赖注入和 AOP 等功能。
常用技巧:
1)获取 Class 对象:使用 Class.forName()或 Object.getClass()方法获取 Class 对象。
2)创建对象:使用 Class.newInstance()方法或 Constructor.newInstance()方法创建对象。
3)调用方法:使用 Method.invoke()方法调用方法。
4)读取字段:使用 Field.get()方法读取字段的值。
5)修改字段:使用 Field.set()方法修改字段的值。
注意事项:
1)性能问题:Java 反射的性能较低,因为它需要在运行时进行类型检查和方法调用。
2)安全问题:Java 反射可以访问私有成员和方法,因此可能会破坏封装性和安全性。
示例代码:
public class ReflectTest { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("com.example.User"); Object obj = clazz.newInstance(); Method setNameMethod = clazz.getMethod("setName", String.class); setNameMethod.invoke(obj, "Alice"); Method getNameMethod = clazz.getMethod("getName"); String name = (String) getNameMethod.invoke(obj); System.out.println(name); Field ageField = clazz.getDeclaredField("age"); ageField.setAccessible(true); ageField.set(obj, 18); int age = (int) ageField.get(obj); System.out.println(age); } }
2. 动态代理
在动态代理中,Java 反射被用来动态地生成代理类。代理类可以拦截目标方法的调用并进行增强或修改。
常用技巧:
1)代理类的生成:使用 Proxy.newProxyInstance()方法生成代理类。
2)InvocationHandler 接口的实现:通过实现 InvocationHandler 接口来拦截目标方法的调用并进行处理。
注意事项:
1)代理类必须实现至少一个接口。
2)动态代理只能代理接口类型的对象,无法代理普通类。
示例代码:
public interface IUserDao { void save(); } public class UserDaoImpl implements IUserDao { @Override public void save() { System.out.println("Save user..."); } } public class UserDaoProxy implements InvocationHandler { private Object target; public UserDaoProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Begin transaction..."); Object result = method.invoke(target, args); System.out.println("Commit transaction..."); return result; } } public class ProxyTest { public static void main(String[] args) { IUserDao userDao = new UserDaoImpl(); InvocationHandler handler = new UserDaoProxy(userDao); IUserDao proxy = (IUserDao) Proxy.newProxyInstance( userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), handler); proxy.save(); // Begin transaction...Save user...Commit transaction... } }
3. 模板方法
在模板方法中,Java 反射被用来实现框架的扩展性。通过 Java 反射,我们可以动态地调用扩展类中的方法,并将其与框架中的标准方法组合起来形成一个完整的流程。
常用技巧:
1)获取方法:使用 Class.getDeclaredMethod()或 Class.getMethod()方法获取方法。
2)设置方法可访问性:使用 Method.setAccessible()方法将方法设为可访问。
3)调用方法:使用 Method.invoke()方法调用方法。
注意事项:
1)扩展类必须实现一个标准的接口或继承一个标准的抽象类,以便框架能够调用扩展类中的方法。
2)需要确保扩展类中的方法与框架中的标准方法具有相同的名称和参数列表。
示例代码:
public interface ITemplate { void execute(); } public abstract class AbstractTemplate implements ITemplate { @Override public final void execute() { init(); doTask(); destroy(); } protected void init() { System.out.println("Initialize template..."); } protected abstract void doTask(); protected void destroy() { System.out.println("Destroy template..."); } } public class MyTemplate extends AbstractTemplate { @Override protected void doTask() { System.out.println("Do my task..."); } } public class TemplateTest { public static void main(String[] args) throws Exception { ITemplate template = new MyTemplate(); Method method = template.getClass().getMethod("doTask"); method.setAccessible(true); method.invoke(template); // Do my task... } }
以上是 Java 反射在框架设计、动态代理和模板方法等场景中的应用,常用技巧和注意事项。希望可以帮助您深入学习 Java 反射。
Java 注解基础
了解 Java 注解的概念和基本语法,学习如何自定义注解,并且通过反射来获取注解信息。 Java 注解是一种元数据,它可以在代码中以声明的形式出现,但不直接参与编译和执行。Java 注解可以用来提供程序中所需的任何基本信息,例如作者、版本、版权等。Java 注解广泛应用于框架设计、测试驱动开发、配置文件解析等场景。
下面介绍 Java 注解的概念、基本语法和自定义注解,并通过反射来获取注解信息。
1. 概念
Java 注解(Annotation)是一种附加在代码中的修饰符,用于在源代码中嵌入元数据。Java 注解可以用来提供程序中所需的任何基本信息,例如作者、版本、版权等。Java 注解可以在编译时通过工具进行处理,或者在运行时由 Java 虚拟机进行处理。
2. 基本语法
Java 注解的基本语法如下:
@AnnotationName public class MyClass { // ... }
其中,@AnnotationName 是注解的名称,它可以在代码中的类、方法、字段或参数上使用。注解可以带有元素,元素以名字-值对的形式存在,用逗号分隔多个元素。例如:
@AnnotationName(name = "Alice", age = 18) public class MyClass { // ... }
3. 自定义注解
我们可以通过 Java 的元注解来自定义注解。Java 的元注解包括 @Retention、@Target、@Inherited 和 @Documented。其中,@Retention 用于指定注解的生命周期,@Target 用于指定注解可以应用于哪些元素,@Inherited 用于指定子类是否继承注解,@Documented 用于指定注解是否出现在 Java 文档中。
示例代码:
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MyAnnotation { String name(); int age() default 18; }
上述代码定义了一个名为 MyAnnotation 的注解,它有两个属性:name 和 age。其中,@Retention(RetentionPolicy.RUNTIME)表示该注解可以在运行时保留,@Target(ElementType.TYPE)表示该注解只能应用于类。
4. 反射获取注解信息
我们可以使用 Java 反射来获取注解信息。通过 Class 对象的 getAnnotation()方法或 Method 对象的 getAnnotation()方法可以获取指定注解的实例。然后,我们可以通过访问注解的属性来获取信息。
示例代码:
@MyAnnotation(name = "Alice", age = 18) public class MyClass { // ... } public class ReflectTest { public static void main(String[] args) { Class<?> clazz = MyClass.class; MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class); if (annotation != null) { System.out.println(annotation.name()); // Alice System.out.println(annotation.age()); // 18 } } }
Java 注解进阶
深入学习 Java 注解的应用场景,例如框架设计、代码生成、单元测试等;掌握各种内置注解和第三方注解库的使用方法,提高注解编程的效率和可维护性。 Java 注解是一种元数据,在 Java 语言中广泛应用于框架设计、代码生成、单元测试等场景。Java 提供了很多内置注解,同时也有很多第三方注解库可以使用。
下面介绍 Java 注解的进阶应用场景,以及各种内置注解和第三方注解库的使用方法。
1. 应用场景
1)框架设计:Java 注解可以用来标记业务逻辑、数据访问对象、服务等,以便框架能够自动生成代码或配置文件。
2)代码生成:Java 注解可以用来标记需要生成的类、方法、字段等信息,并根据注解生成对应的代码。
3)单元测试:Java 注解可以用来标记测试方法和测试类,以便测试框架能够自动运行测试。
4)持久化:Java 注解可以用来标记实体类和数据库表之间的映射关系,以便 ORM 框架能够自动处理持久化操作。
2. 内置注解
Java 提供了很多内置注解,常用的有以下几种:
1)@Override:表示方法覆盖父类的方法。
2)@Deprecated:表示方法已经过时,建议不再使用。
3)@SuppressWarnings:表示抑制编译器警告。
4)@SafeVarargs:表示有参数数量可变的方法或构造函数是类型安全的。
5)@FunctionalInterface:表示接口是一个函数式接口。
示例代码:
public class MyClass { @Override public String toString() { return "MyClass"; } @Deprecated public void oldMethod() { } @SuppressWarnings("unchecked") public void test() { List list = new ArrayList(); list.add("Hello"); } @SafeVarargs public final <T> void print(T... args) { for (T arg : args) { System.out.print(arg); } } @FunctionalInterface interface MyInterface { void doSomething(); } }
3. 第三方注解库
除了内置注解,Java 还有很多第三方注解库可以使用,常用的有以下几种:
1)Lombok:可以自动生成 getter、setter、toString、equals、hashCode 等方法的注解库。
2)Jackson:可以实现 JSON 序列化和反序列化的注解库。
3)Hibernate Validator:可以实现数据校验的注解库。
4)Swagger:可以生成 API 文档的注解库。
示例代码:
@Data @AllArgsConstructor public class User { private String name; private int age; } public class JacksonTest { public static void main(String[] args) throws IOException { ObjectMapper mapper = new ObjectMapper(); User user = new User("Alice", 18); String json = mapper.writeValueAsString(user); System.out.println(json); // {"name":"Alice","age":18} User user2 = mapper.readValue(json, User.class); System.out.println(user2.getName()); // Alice } } public class ValidatorTest { @NotNull @Size(min = 3, max = 20) private String name; @Min(18) private int age; public ValidatorTest(String name, int age) { this.name = name; this.age = age; } public static void main(String[] args) { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator(); ValidatorTest test = new ValidatorTest("Alice", 16); Set<ConstraintViolation<ValidatorTest>> violations = validator.validate(test); for (ConstraintViolation<ValidatorTest> violation : violations) { System.out.println(violation.getMessage()); // must be greater than or equal to 18 } } } @RestController public class UserController { @GetMapping("/users") @ApiOperation(value = "Get all users") public List<User> getUsers() { ... } @PostMapping("/users") @ApiOperation(value = "Create a user") public void createUser(@Valid @RequestBody User user) { ... } }
以上是 Java 注解的进阶应用场景、各种内置注解和第三方注解库的使用方法。希望可以帮助您更好地理解和应用 Java 注解。
4. 总结
Java 注解是一种元数据,在 Java 语言中广泛应用于框架设计、代码生成、单元测试等场景。Java 提供了很多内置注解,同时也有很多第三方注解库可以使用。通过学习 Java 注解,我们可以提高注解编程的效率和可维护性。
示例代码:
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyAnnotation { String value(); } public class MyClass { @MyAnnotation("Hello") public void sayHello() { } } public class ReflectTest { public static void main(String[] args) throws Exception { MyClass obj = new MyClass(); Method method = obj.getClass().getMethod("sayHello"); MyAnnotation annotation = method.getAnnotation(MyAnnotation.class); if (annotation != null) { System.out.println(annotation.value()); // Hello } } }
Java 反射与注解实战
在实际项目中应用 Java 反射和注解技术,例如使用反射实现 IOC 容器或 AOP 功能,使用注解进行数据校验或权限控制等。 Java 反射和注解是 Java 编程中非常重要的概念,可以通过反射获取类、方法、字段等信息,并且可以使用注解来标记业务逻辑、数据访问对象、服务等。下面给出一个 Java 反射和注解的实战代码详解。
1. 实战场景
假设我们正在开发一个简单的社交应用,需要根据用户输入的 API 路径动态调用对应的服务方法,并返回结果。例如,当用户输入/api/user/get 时,我们需要调用 UserService 中的 get()方法,当用户输入/api/post/add 时,我们需要调用 PostService 中的 add()方法。
2. 代码实现
首先,我们定义一个 @Service 注解,用于标记服务类和服务方法:
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Service { String value(); } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Action { String value(); }
然后,我们定义两个服务类 UserService 和 PostService,并在类上添加 @Service 注解,表示这是一个服务类:
@Service("user") public class UserService { public void get() { System.out.println("Get user..."); } } @Service("post") public class PostService { public void add() { System.out.println("Add post..."); } }
接下来,我们实现一个 API 调度器 DispatcherServlet,它根据用户输入的 API 路径,调用对应的服务方法:
import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class DispatcherServlet { private Map<String, Object> beans = new HashMap<>(); public void init() throws Exception { // 扫描服务类,并创建实例 ClassPathScanUtil scanner = new ClassPathScanUtil("com.example.service"); for (String className : scanner.getFullyQualifiedClassNameList()) { Class<?> clazz = Class.forName(className); if (clazz.isAnnotationPresent(Service.class)) { Service service = clazz.getAnnotation(Service.class); String serviceName = service.value(); Object instance = clazz.newInstance(); beans.put(serviceName, instance); } } // 注册服务方法 for (Object instance : beans.values()) { Class<?> clazz = instance.getClass(); if (clazz.isAnnotationPresent(Service.class)) { Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Action.class)) { Action action = method.getAnnotation(Action.class); String actionName = action.value(); String serviceName = clazz.getAnnotation(Service.class).value(); String key = "/" + serviceName + "/" + actionName; ActionHandler handler = new ActionHandler(instance, method); HandlerMapping.register(key, handler); } } } } } public void dispatch(String apiPath) throws Exception { // 调用服务方法 ActionHandler handler = HandlerMapping.get(apiPath); if (handler != null) { handler.invoke(); } else { System.out.println("API path not found."); } } }
在 DispatcherServlet 中,我们首先扫描服务类,并创建实例;然后,遍历每个服务类的方法,如果有 @Action 注解,则将其注册到 HandlerMapping 中;最后,根据用户输入的 API 路径,调用对应的服务方法。
HandlerMapping 是一个静态类,它保存了 API 路径和对应的 ActionHandler。ActionHandler 封装了服务类实例和服务方法,可以通过 invoke()方法来调用服务方法。
import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class HandlerMapping { private static Map<String, ActionHandler> mappings = new HashMap<>(); public static void register(String key, ActionHandler handler) { mappings.put(key, handler); } public static ActionHandler get(String key) { return mappings.get(key); } } public class ActionHandler { private Object instance; private Method method; public ActionHandler(Object instance, Method method) { this.instance = instance; this.method = method; } public void invoke() throws Exception { method.invoke(instance); } }
最后,我们在 main()方法中使用 DispatcherServlet 来处理 API 请求:
public class Main { public static void main(String[] args) throws Exception{ DispatcherServlet servlet = new DispatcherServlet(); servlet.init(); servlet.dispatch("/user/get"); servlet.dispatch("/post/add"); servlet.dispatch("/comment/list"); } }
在这个例子中,我们使用 Java 反射和注解实现了一个简单的 API 调度器。通过 @Service 注解,我们标记了服务类;通过 @Action 注解,我们标记了服务方法。通过 DispatcherServlet,我们扫描服务类并创建实例,然后注册服务方法到 HandlerMapping 中。最后,在处理 API 请求时,我们根据 API 路径获取对应的 ActionHandler,并通过反射调用服务方法。
评论