Java反射與注解原理解析
Java反射基礎
了解Java反射的概念和原理,熟悉Class、Method、Field等反射API的使用方法,能夠通過反射來動態(tài)地創(chuàng)建對象、調用方法、設置字段等。
Java反射是Java語言中非常重要的一個特性,它允許程序在運行時獲取類的信息,并且可以通過反射調用類的方法、讀寫類的屬性等。下面是Java反射基礎概念和示例代碼:
1. 概念詳解
Java反射的核心是Class類,它代表了一個Java類的類型信息。通過Class可以獲取到類的各種信息,例如類的名稱、字段、方法、構造方法等。
獲取Class對象的三種方式:
- 使用對象.getClass()方法獲取對象的Class對象。
- 通過類名.class來獲取指定類的Class對象。
- 使用Class.forName(“類名”)方法獲取指定類的Class對象。
2. 示例代碼
以下是一個簡單的Java反射示例,它演示了如何通過反射創(chuàng)建對象、調用方法和設置字段:
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 {
// 創(chuàng)建Person類的Class對象
Class<Person> clazz = Person.class;
// 創(chuàng)建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()方法創(chuàng)建一個Person類的實例。接著,通過反射獲取到Person類的name和age字段,并分別為它們設置了值。最后,通過反射獲取到Person類的sayHello方法,并調用該方法輸出一句話。
Java反射進階
深入學習Java反射的應用場景,例如框架設計、動態(tài)代理、模板方法等;掌握Java反射中常用的技巧和注意事項,提高反射編程的效率和可維護性。
Java反射是指在運行時動態(tài)地獲取一個類的信息,對類的屬性和方法進行操作。它可以讓我們在運行時通過字符串來調用類的方法或創(chuàng)建對象,而不需要提前知道該類的名稱。Java反射廣泛應用于框架設計、動態(tài)代理、模板方法等場景。
下面分別介紹Java反射在框架設計、動態(tài)代理、模板方法等場景中的應用,并給出常用技巧和注意事項。
1. 框架設計
在框架設計中,Java反射被廣泛應用于插件機制和配置文件解析等場景。例如,Spring框架就使用了Java反射來實現(xiàn)依賴注入和AOP等功能。
常用技巧:
1)獲取Class對象:使用Class.forName()或Object.getClass()方法獲取Class對象。
2)創(chuàng)建對象:使用Class.newInstance()方法或Constructor.newInstance()方法創(chuàng)建對象。
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. 動態(tài)代理
在動態(tài)代理中,Java反射被用來動態(tài)地生成代理類。代理類可以攔截目標方法的調用并進行增強或修改。
常用技巧:
1)代理類的生成:使用Proxy.newProxyInstance()方法生成代理類。
2)InvocationHandler接口的實現(xiàn):通過實現(xiàn)InvocationHandler接口來攔截目標方法的調用并進行處理。
注意事項:
1)代理類必須實現(xiàn)至少一個接口。
2)動態(tài)代理只能代理接口類型的對象,無法代理普通類。
示例代碼:
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反射被用來實現(xiàn)框架的擴展性。通過Java反射,我們可以動態(tài)地調用擴展類中的方法,并將其與框架中的標準方法組合起來形成一個完整的流程。
常用技巧:
1)獲取方法:使用Class.getDeclaredMethod()或Class.getMethod()方法獲取方法。
2)設置方法可訪問性:使用Method.setAccessible()方法將方法設為可訪問。
3)調用方法:使用Method.invoke()方法調用方法。
注意事項:
1)擴展類必須實現(xiàn)一個標準的接口或繼承一個標準的抽象類,以便框架能夠調用擴展類中的方法。
2)需要確保擴展類中的方法與框架中的標準方法具有相同的名稱和參數(shù)列表。
示例代碼:
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反射在框架設計、動態(tài)代理和模板方法等場景中的應用,常用技巧和注意事項。希望可以幫助您深入學習Java反射。
Java注解基礎
了解Java注解的概念和基本語法,學習如何自定義注解,并且通過反射來獲取注解信息。
Java注解是一種元數(shù)據(jù),它可以在代碼中以聲明的形式出現(xiàn),但不直接參與編譯和執(zhí)行。Java注解可以用來提供程序中所需的任何基本信息,例如作者、版本、版權等。Java注解廣泛應用于框架設計、測試驅動開發(fā)、配置文件解析等場景。
下面介紹Java注解的概念、基本語法和自定義注解,并通過反射來獲取注解信息。
1. 概念
Java注解(Annotation)是一種附加在代碼中的修飾符,用于在源代碼中嵌入元數(shù)據(jù)。Java注解可以用來提供程序中所需的任何基本信息,例如作者、版本、版權等。Java注解可以在編譯時通過工具進行處理,或者在運行時由Java虛擬機進行處理。
2. 基本語法
Java注解的基本語法如下:
@AnnotationName
public class MyClass {
// ...
}
其中,@AnnotationName是注解的名稱,它可以在代碼中的類、方法、字段或參數(shù)上使用。注解可以帶有元素,元素以名字-值對的形式存在,用逗號分隔多個元素。例如:
@AnnotationName(name = "Alice", age = 18)
public class MyClass {
// ...
}
3. 自定義注解
我們可以通過Java的元注解來自定義注解。Java的元注解包括@Retention、@Target、@Inherited和@Documented。其中,@Retention用于指定注解的生命周期,@Target用于指定注解可以應用于哪些元素,@Inherited用于指定子類是否繼承注解,@Documented用于指定注解是否出現(xiàn)在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注解是一種元數(shù)據(jù),在Java語言中廣泛應用于框架設計、代碼生成、單元測試等場景。Java提供了很多內置注解,同時也有很多第三方注解庫可以使用。
下面介紹Java注解的進階應用場景,以及各種內置注解和第三方注解庫的使用方法。
1. 應用場景
1)框架設計:Java注解可以用來標記業(yè)務邏輯、數(shù)據(jù)訪問對象、服務等,以便框架能夠自動生成代碼或配置文件。
2)代碼生成:Java注解可以用來標記需要生成的類、方法、字段等信息,并根據(jù)注解生成對應的代碼。
3)單元測試:Java注解可以用來標記測試方法和測試類,以便測試框架能夠自動運行測試。
4)持久化:Java注解可以用來標記實體類和數(shù)據(jù)庫表之間的映射關系,以便ORM框架能夠自動處理持久化操作。
2. 內置注解
Java提供了很多內置注解,常用的有以下幾種:
1)@Override:表示方法覆蓋父類的方法。
2)@Deprecated:表示方法已經(jīng)過時,建議不再使用。
3)@SuppressWarnings:表示抑制編譯器警告。
4)@SafeVarargs:表示有參數(shù)數(shù)量可變的方法或構造函數(shù)是類型安全的。
5)@FunctionalInterface:表示接口是一個函數(shù)式接口。
示例代碼:
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:可以實現(xiàn)JSON序列化和反序列化的注解庫。
3)Hibernate Validator:可以實現(xiàn)數(shù)據(jù)校驗的注解庫。
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注解是一種元數(shù)據(jù),在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反射與注解實戰(zhàn)
在實際項目中應用Java反射和注解技術,例如使用反射實現(xiàn)IOC容器或AOP功能,使用注解進行數(shù)據(jù)校驗或權限控制等。
Java反射和注解是Java編程中非常重要的概念,可以通過反射獲取類、方法、字段等信息,并且可以使用注解來標記業(yè)務邏輯、數(shù)據(jù)訪問對象、服務等。下面給出一個Java反射和注解的實戰(zhàn)代碼詳解。
1. 實戰(zhàn)場景
假設我們正在開發(fā)一個簡單的社交應用,需要根據(jù)用戶輸入的API路徑動態(tài)調用對應的服務方法,并返回結果。例如,當用戶輸入/api/user/get時,我們需要調用UserService中的get()方法,當用戶輸入/api/post/add時,我們需要調用PostService中的add()方法。
2. 代碼實現(xiàn)
首先,我們定義一個@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...");
}
}接下來,我們實現(xiàn)一個API調度器DispatcherServlet,它根據(jù)用戶輸入的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 {
// 掃描服務類,并創(chuàng)建實例
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中,我們首先掃描服務類,并創(chuàng)建實例;然后,遍歷每個服務類的方法,如果有@Action注解,則將其注冊到HandlerMapping中;最后,根據(jù)用戶輸入的API路徑,調用對應的服務方法。
HandlerMapping是一個靜態(tài)類,它保存了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反射和注解實現(xiàn)了一個簡單的API調度器。通過@Service注解,我們標記了服務類;通過@Action注解,我們標記了服務方法。通過DispatcherServlet,我們掃描服務類并創(chuàng)建實例,然后注冊服務方法到HandlerMapping中。最后,在處理API請求時,我們根據(jù)API路徑獲取對應的ActionHandler,并通過反射調用服務方法。
到此這篇關于Java反射與注解原理解析的文章就介紹到這了,更多相關Java反射與注解內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot?替換?if?的參數(shù)校驗示例代碼
Spring?Validation是對hibernate?validation的二次封裝,用于支持spring?mvc參數(shù)自動校驗,接下來,我們以spring-boot項目為例,介紹Spring?Validation的使用,需要的朋友可以參考下2022-12-12
JSP頁面pageEncoding和contentType屬性
有關于JSP頁面中pageEncoding和contentType屬性。2013-04-04

