從代理模式到注解開發

代理模式

package org.example.proxy;public class ProxyClient {public static void main(String[] args) {ProxyBuilder proxyBuilder = new ProxyBuilder();proxyBuilder.build();}
}interface BuildDream {void build();
}class CustomBuilder implements BuildDream {@Overridepublic void build() {System.out.println("自定義執行方法");}
}class ProxyBuilder implements BuildDream {private CustomBuilder customBuilder;@Overridepublic void build() {if (this.customBuilder == null) {this.customBuilder = new CustomBuilder();}System.out.println("代理執行前");customBuilder.build();System.out.println("代理執行后");}
}

執行結果:

代理執行前
自定義執行方法
代理執行后

動態代理

Java 動態代理是一種設計模式,允許在運行時創建代理對象,以攔截對目標對象的方法調用。動態代理通常用于橫切關注點(如日志記錄、事務管理、權限控制等)的實現。Java 提供了兩種主要的動態代理機制:

  1. JDK 動態代理:基于接口的代理。
  2. CGLIB 動態代理:基于類的代理。

JDK 動態代理

JDK 動態代理使用 java.lang.reflect.Proxy 類和 java.lang.reflect.InvocationHandler 接口來實現。它只能代理實現了接口的類。

示例代碼
package org.example.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** JDK動態代理實現*/
public class DynamicProxy {public static void main(String[] args) {MyService myService = (MyService) MyServiceProxy.newProxyInstance(new MyServiceImpl());myService.saveInfo();}
}// 定義接口
interface MyService {void saveInfo();
}// 實現接口的類
class MyServiceImpl implements MyService {@Overridepublic void saveInfo() {System.out.println("保存信息成功");}
}class MyServiceProxy implements InvocationHandler {private Object target;MyServiceProxy(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代理方法執行前");Object result = method.invoke(target, args);System.out.println("代理方法執行后");return result;}public static Object newProxyInstance(Object target) {return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new MyServiceProxy(target));}
}

執行結果:

代理方法執行前
保存信息成功
代理方法執行后

CGLIB 動態代理

CGLIB 動態代理使用字節碼生成庫來生成代理類,它可以代理沒有實現接口的類。

示例代碼
package org.example.proxy;import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** CGLIB實現動態代理*/
public class CGLibProxy {public static void main(String[] args) {HerService herService = (HerService) ProxyServiceInterceptor.newProxyInstance(new HerService());herService.saveInfo();herService.sayHello();}
}class HerService {public void saveInfo() {System.out.println("保存信息成功");}public void sayHello() {System.out.println("Hi");}
}class ProxyServiceInterceptor implements MethodInterceptor {private Object target;ProxyServiceInterceptor(Object target) {this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("代理方法執行前");Object result = methodProxy.invokeSuper(o, objects);System.out.println("代理方法執行后");return result;}public static Object newProxyInstance(Object target) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(new ProxyServiceInterceptor(target));return enhancer.create();}
}

運行結果:

代理方法執行前
保存信息成功
代理方法執行后
代理方法執行前
Hi
代理方法執行后

總結

  • JDK 動態代理:適用于代理實現了接口的類。
  • CGLIB 動態代理:適用于代理沒有實現接口的類。

這兩種動態代理機制都可以用于實現 AOP(面向切面編程),以便在不修改目標對象代碼的情況下添加額外的功能。

AOP

AOP(面向切面編程)是一種編程范式,它允許你在不修改業務邏輯代碼的情況下,添加橫切關注點(如日志記錄、事務管理、權限控制等)。Spring Framework 提供了強大的 AOP 支持,主要通過以下幾種方式實現:

  1. 基于代理的 AOP:使用 JDK 動態代理或 CGLIB 動態代理。
  2. 基于注解的 AOP:使用注解來定義切面和切點。
  3. 基于 XML 配置的 AOP:使用 XML 配置文件來定義切面和切點。

基于注解的 AOP 示例

下面是一個使用 Spring AOP 和注解的示例,展示了如何在 Spring 應用程序中使用 AOP。

1. 添加依賴

首先,確保你的項目中包含 Spring AOP 相關的依賴。如果你使用的是 Maven,可以在 pom.xml 中添加以下依賴:

<dependencies><!-- Spring AOP --><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.3.10</version></dependency><!-- AspectJ --><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.10</version></dependency>
</dependencies>
2. 定義業務邏輯類

定義一個簡單的業務邏輯類:

package com.example.service;import org.springframework.stereotype.Service;@Service
public class HelloService {public void sayHello() {System.out.println("Hello, World!");}
}
3. 定義切面類

定義一個切面類,使用注解來指定切點和通知:

package com.example.aspect;import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.service.HelloService.sayHello(..))")public void logBefore() {System.out.println("Before method call");}@After("execution(* com.example.service.HelloService.sayHello(..))")public void logAfter() {System.out.println("After method call");}
}
4. 配置 Spring 應用程序

創建一個 Spring 配置類,啟用 AOP 支持:

package com.example.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@ComponentScan(basePackages = "com.example")
@EnableAspectJAutoProxy
public class AppConfig {
}
5. 測試代碼

編寫一個測試類來驗證 AOP 的效果:

package com.example;import com.example.config.AppConfig;
import com.example.service.HelloService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);HelloService helloService = context.getBean(HelloService.class);helloService.sayHello();}
}

運行結果

當你運行測試代碼時,輸出將會是:

Before method call
Hello, World!
After method call

解釋

  1. 業務邏輯類HelloService 是一個簡單的業務邏輯類,包含一個 sayHello 方法。
  2. 切面類LoggingAspect 是一個切面類,包含兩個通知方法 logBeforelogAfter,分別在 sayHello 方法調用之前和之后執行。
  3. Spring 配置類AppConfig 是一個 Spring 配置類,啟用了 AOP 支持并掃描指定包中的組件。
  4. 測試代碼:在測試代碼中,通過 Spring 容器獲取 HelloService 的代理對象,并調用 sayHello 方法。

總結

通過使用 Spring AOP 和注解,你可以在不修改業務邏輯代碼的情況下,輕松地添加橫切關注點。Spring AOP 提供了強大的功能和靈活性,使得代碼更加模塊化和可維護。

@Before中的參數

在 Spring AOP 中,@Before 注解用于定義一個前置通知(Advice),它會在目標方法執行之前執行。@Before 注解的參數是一個切點表達式,用于指定哪些方法應該被攔截。切點表達式可以使用多種方式來匹配目標方法,包括方法簽名、注解、包名等。

常見的切點表達式

  1. 匹配方法簽名

    • execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
    • 例如:execution(* com.example.service.HelloService.sayHello(..))
  2. 匹配類上的注解

    • @within(annotationType)
    • 例如:@within(org.springframework.stereotype.Service)
  3. 匹配方法上的注解

    • @annotation(annotationType)
    • 例如:@annotation(org.springframework.transaction.annotation.Transactional)
  4. 匹配包名

    • within(package-name)
    • 例如:within(com.example.service..*)
  5. 匹配參數

    • args(argument-types)
    • 例如:args(String, ..)

示例代碼

以下是一些常見的 @Before 注解的使用示例:

1. 匹配特定方法

匹配 com.example.service.HelloService 類中的 sayHello 方法:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.service.HelloService.sayHello(..))")public void logBefore() {System.out.println("Before method call");}
}
2. 匹配特定包中的所有方法

匹配 com.example.service 包及其子包中的所有方法:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {@Before("within(com.example.service..*)")public void logBefore() {System.out.println("Before method call");}
}
3. 匹配帶有特定注解的方法

匹配帶有 @Transactional 注解的方法:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {@Before("@annotation(org.springframework.transaction.annotation.Transactional)")public void logBefore() {System.out.println("Before transactional method call");}
}
4. 匹配帶有特定參數的方法

匹配第一個參數為 String 類型的方法:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {@Before("args(java.lang.String, ..)")public void logBefore() {System.out.println("Before method call with String argument");}
}

組合切點表達式

你可以使用 &&||! 運算符來組合多個切點表達式。例如,匹配 com.example.service 包中的所有方法,并且這些方法帶有 @Transactional 注解:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {@Before("within(com.example.service..*) && @annotation(org.springframework.transaction.annotation.Transactional)")public void logBefore() {System.out.println("Before transactional method call in service package");}
}

總結

  • 匹配特定方法:使用 execution 表達式。
  • 匹配特定包中的所有方法:使用 within 表達式。
  • 匹配帶有特定注解的方法:使用 @annotation 表達式。
  • 匹配帶有特定參數的方法:使用 args 表達式。
  • 組合切點表達式:使用 &&||! 運算符。

通過合理使用這些切點表達式,你可以靈活地定義哪些方法應該被攔截,從而實現各種橫切關注點的功能。

反射

package org.example.reflect;import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class ReflectTest {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Class<?> aClass = Class.forName("org.example.reflect.Student");Class<Student> studentClass = Student.class;Constructor<Student> constructor = studentClass.getDeclaredConstructor(String.class, Integer.class);constructor.setAccessible(true);Student oh = constructor.newInstance("Oh", 66);//        Constructor<?>[] constructors = aClass.getDeclaredConstructors();
//        Iterator<Constructor<?>> constructorIterator = Arrays.stream(constructors).iterator();
//        while (constructorIterator.hasNext()) {
//            System.out.println(constructorIterator.next());
//        }Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class);// 為私有構造函數獲取訪問性declaredConstructor.setAccessible(true);Student instance = (Student) declaredConstructor.newInstance("Xin", 25);
//        instance.printInfo();Method declaredMethod = aClass.getDeclaredMethod("printInfo");declaredMethod.setAccessible(true);String result = (String) declaredMethod.invoke(instance);System.out.println(result);System.out.println(declaredMethod.invoke(oh).toString());}
}class Student {private String name;private Integer age;public Student() {System.out.println("無參構造函數");}private Student(String name, Integer age) {this.name = name;this.age = age;}private String printInfo() {System.out.println(this.name + " " + this.age);return "OK";}
}

Annotation

實現自定義注解

在Java中,自定義注解和使用AOP(面向切面編程)來掃描帶有該注解的類和方法,可以通過以下步驟實現。我們將使用Spring AOP來實現這一功能。

1. 創建自定義注解

首先,我們需要創建一個自定義注解。例如,我們創建一個名為 @MyAnnotation 的注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation {String value() default "";
}

2. 創建AOP切面

接下來,我們需要創建一個AOP切面類,用于攔截帶有 @MyAnnotation 注解的方法或類。我們可以使用Spring AOP來實現這一點。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class MyAnnotationAspect {@Before("@within(myAnnotation) || @annotation(myAnnotation)")public void beforeMethod(MyAnnotation myAnnotation) {// 在方法執行之前執行的邏輯System.out.println("Intercepted method with annotation: " + myAnnotation.value());}
}

3. 配置Spring AOP

為了使AOP切面生效,我們需要在Spring配置文件中啟用AOP支持。可以在Spring Boot應用程序的主類中添加 @EnableAspectJAutoProxy 注解:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@SpringBootApplication
@EnableAspectJAutoProxy
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}
}

4. 使用自定義注解

現在,我們可以在類或方法上使用自定義注解 @MyAnnotation,并且AOP切面會攔截這些帶有注解的方法或類。

import org.springframework.stereotype.Service;@Service
public class MyService {@MyAnnotation(value = "testMethod")public void testMethod() {System.out.println("Executing testMethod");}
}

5. 運行應用程序

啟動Spring Boot應用程序,并調用帶有 @MyAnnotation 注解的方法。你會看到AOP切面攔截了該方法,并執行了 beforeMethod 中的邏輯。

總結

通過上述步驟,我們實現了自定義注解,并使用Spring AOP掃描帶有該注解的類和方法,從而實現類似于Spring的操作。關鍵步驟包括創建自定義注解、創建AOP切面類、配置Spring AOP以及在類或方法上使用自定義注解。

@Retention@Target 注解

@Retention@Target 是Java注解的元注解(meta-annotations),它們用于定義自定義注解的行為和適用范圍。

@Retention

@Retention 注解指定了自定義注解的保留策略,即注解在什么階段仍然存在。RetentionPolicy 枚舉有以下幾種值:

  • RetentionPolicy.SOURCE:注解只在源代碼中存在,編譯器在編譯時會丟棄這種注解。
  • RetentionPolicy.CLASS:注解在編譯時被保留在類文件中,但在運行時不會被JVM保留。這是默認的保留策略。
  • RetentionPolicy.RUNTIME:注解在運行時也會被JVM保留,因此可以通過反射機制讀取這種注解。

在你的例子中,@Retention(RetentionPolicy.RUNTIME) 表示 @MyAnnotation 注解在運行時也會被保留,因此可以通過反射機制讀取。

@Target

@Target 注解指定了自定義注解可以應用的Java元素類型。ElementType 枚舉有以下幾種值:

  • ElementType.TYPE:可以應用于類、接口(包括注解類型)或枚舉聲明。
  • ElementType.FIELD:可以應用于字段或屬性。
  • ElementType.METHOD:可以應用于方法。
  • ElementType.PARAMETER:可以應用于方法參數。
  • ElementType.CONSTRUCTOR:可以應用于構造函數。
  • ElementType.LOCAL_VARIABLE:可以應用于局部變量。
  • ElementType.ANNOTATION_TYPE:可以應用于注解類型。
  • ElementType.PACKAGE:可以應用于包聲明。
  • ElementType.TYPE_PARAMETER:可以應用于類型參數(Java 8及以上)。
  • ElementType.TYPE_USE:可以應用于任何使用類型的地方(Java 8及以上)。

在你的例子中,@Target({ElementType.METHOD, ElementType.TYPE}) 表示 @MyAnnotation 注解可以應用于方法和類(包括接口和枚舉)。

總結

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation {String value() default "";
}
  • @Retention(RetentionPolicy.RUNTIME):表示 @MyAnnotation 注解在運行時也會被保留,可以通過反射機制讀取。
  • @Target({ElementType.METHOD, ElementType.TYPE}):表示 @MyAnnotation 注解可以應用于方法和類(包括接口和枚舉)。

通過這些元注解的配置,你可以控制自定義注解的行為和適用范圍。

重新理解@Transactional

@Transactional 是 Spring 提供的一個非常重要的注解,用于聲明式事務管理。它可以應用于類或方法上,以便在方法執行時自動開啟和管理事務。通過理解 @Transactional 的工作原理,我們可以更好地理解如何自定義注解并使用 AOP 來實現類似的功能。

@Transactional 注解的元注解

@Transactional 注解本身使用了一些元注解來定義其行為和適用范圍:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Transactional {// 其他屬性和方法
}
  • @Target({ElementType.METHOD, ElementType.TYPE}):表示 @Transactional 注解可以應用于方法和類(包括接口和枚舉)。
  • @Retention(RetentionPolicy.RUNTIME):表示 @Transactional 注解在運行時也會被保留,可以通過反射機制讀取。
  • @Documented:表示使用 @Transactional 注解的元素應當被 javadoc 或類似工具文檔化。

@Transactional 的屬性

@Transactional 注解有許多屬性,用于配置事務的行為,例如:

  • propagation:事務的傳播行為(例如,REQUIRED、REQUIRES_NEW 等)。
  • isolation:事務的隔離級別(例如,READ_COMMITTED、SERIALIZABLE 等)。
  • timeout:事務的超時時間。
  • readOnly:是否為只讀事務。
  • rollbackFor:指定哪些異常會觸發事務回滾。
  • noRollbackFor:指定哪些異常不會觸發事務回滾。

@Transactional 的工作原理

@Transactional 注解的工作原理主要依賴于 Spring AOP。Spring AOP 會攔截帶有 @Transactional 注解的方法或類,并在方法執行前后管理事務的開啟、提交和回滾。

自定義注解和 AOP 實現類似 @Transactional 的功能

我們可以通過自定義注解和 AOP 來實現類似 @Transactional 的功能。以下是一個示例,展示了如何創建一個自定義注解 @MyTransactional,并使用 AOP 來管理事務。

1. 創建自定義注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyTransactional {// 可以添加其他屬性,例如事務傳播行為、隔離級別等
}

@Transactional 注解是 Spring 框架中用于聲明式事務管理的核心注解。它可以應用于類或方法上,以聲明該類或方法需要事務支持。通過 @Transactional 注解,Spring 可以自動管理事務的開始、提交和回滾。

@Transactional 注解的定義

@Transactional 注解的定義如下:

package org.springframework.transaction.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.TransactionAttribute;@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {@AliasFor("transactionManager")String value() default "";@AliasFor("value")String transactionManager() default "";Propagation propagation() default Propagation.REQUIRED;Isolation isolation() default Isolation.DEFAULT;int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;boolean readOnly() default false;Class<? extends Throwable>[] rollbackFor() default {};String[] rollbackForClassName() default {};Class<? extends Throwable>[] noRollbackFor() default {};String[] noRollbackForClassName() default {};
}

元注解的解釋

@Retention
@Retention(RetentionPolicy.RUNTIME)
  • RetentionPolicy.RUNTIME:表示 @Transactional 注解在運行時也會被保留,可以通過反射機制讀取。這對于事務管理是必要的,因為 Spring 需要在運行時動態地管理事務。
@Target
@Target({ElementType.METHOD, ElementType.TYPE})
  • ElementType.METHOD:表示 @Transactional 注解可以應用于方法。
  • ElementType.TYPE:表示 @Transactional 注解可以應用于類(包括接口和枚舉)。當應用于類時,該類的所有方法都將具有事務支持,除非方法上另有聲明。

@Transactional 注解的屬性

  • valuetransactionManager:指定要使用的事務管理器的名稱。默認情況下,使用默認的事務管理器。
  • propagation:指定事務的傳播行為。默認值為 Propagation.REQUIRED,表示當前方法必須在事務中運行。如果當前沒有事務,則會啟動一個新的事務。
  • isolation:指定事務的隔離級別。默認值為 Isolation.DEFAULT,表示使用底層數據庫的默認隔離級別。
  • timeout:指定事務的超時時間(以秒為單位)。默認值為 TransactionDefinition.TIMEOUT_DEFAULT,表示使用底層事務管理器的默認超時時間。
  • readOnly:指定事務是否為只讀。默認值為 false。只讀事務可以優化性能,因為數據庫可以跳過某些鎖定操作。
  • rollbackForrollbackForClassName:指定哪些異常會觸發事務回滾。默認情況下,只有未捕獲的運行時異常會觸發回滾。
  • noRollbackFornoRollbackForClassName:指定哪些異常不會觸發事務回滾。

使用示例

在類上使用 @Transactional
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
@Transactional
public class MyService {public void performOperation() {// 該方法將在事務中運行}
}
在方法上使用 @Transactional
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class MyService {@Transactionalpublic void performOperation() {// 該方法將在事務中運行}
}

自定義注解與 @Transactional 的對比

通過前面的自定義注解示例,我們可以看到 @Transactional 注解的實現方式與自定義注解類似。它們都使用了 @Retention@Target 元注解來定義注解的保留策略和適用范圍。不同的是,@Transactional 注解還包含了多個屬性,用于配置事務的行為。

總結

  • @Retention(RetentionPolicy.RUNTIME):表示 @Transactional 注解在運行時也會被保留,可以通過反射機制讀取。
  • @Target({ElementType.METHOD, ElementType.TYPE}):表示 @Transactional 注解可以應用于方法和類(包括接口和枚舉)。
  • 屬性@Transactional 注解包含多個屬性,用于配置事務的傳播行為、隔離級別、超時時間、只讀屬性以及回滾規則。

通過理解 @Transactional 注解的定義和使用,我們可以更好地理解如何創建和使用自定義注解,并通過AOP實現類似的功能。

使用反射機制實現自定義注解

在Java中,自定義注解是一種強大的工具,用于提供額外的元數據信息。注解以@符號開頭,后面緊跟注解名。下面是如何創建和使用自定義注解的步驟:

1. 創建自定義注解

創建注解非常簡單,只需要定義一個Java接口,并使用@interface關鍵字。注解接口通常用于定義注解的元數據,如參數、默認值等。

// 創建一個自定義注解
@interface MyAnnotation {String value() default "";
}

在這個例子中,我們創建了一個名為MyAnnotation的注解,它有一個名為value的參數,參數默認值為""

2. 使用自定義注解

在類、方法、字段或參數上使用自定義注解,只需要在相應的位置使用@MyAnnotation

示例:使用自定義注解在方法上
public class ExampleClass {@MyAnnotation("example")public void myMethod() {// 方法體}
}
示例:使用自定義注解在字段上
public class ExampleClass {@MyAnnotation("example")private String myField;
}
示例:使用自定義注解在參數上
public class ExampleClass {public void myMethod(@MyAnnotation("example") String myParam) {// 方法體}
}
3. 反射操作

使用Java的反射API,可以讀取類上的注解信息。

import java.lang.reflect.Method;public class AnnotationReader {public static void main(String[] args) throws Exception {ExampleClass exampleClass = new ExampleClass();Method[] methods = exampleClass.getClass().getMethods();for (Method method : methods) {MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);if (myAnnotation != null) {System.out.println("Method: " + method.getName() + ", Value: " + myAnnotation.value());}}}
}
總結

自定義注解在Java中非常有用,可以幫助開發者添加額外的元數據信息,提高代碼的可讀性和可維護性。通過結合注解處理器(Annotation Processing Tool,APT)和反射API,可以實現更強大的代碼生成和動態行為。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/45940.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/45940.shtml
英文地址,請注明出處:http://en.pswp.cn/web/45940.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

visual studio開發C++項目遇到的坑

文章目錄 1.安裝的時候&#xff0c;順手安裝了C模板&#xff0c;導致新建項目執行出問題2.生成的exe&#xff0c;打開閃退問題3.項目里宏的路徑不對&#xff0c;導致后面編譯沒有輸出4. vs編譯ui&#xff0c;warning跳過&#xff0c;未成功5.vs編譯.h&#xff0c;warning跳過&a…

K8S 中的 CRI、OCI、CRI shim、containerd

K8S 如何創建容器&#xff1f; 下面這張圖&#xff0c;就是經典的 K8S 創建容器的步驟&#xff0c;可以說是冗長復雜&#xff0c;至于為什么設計成這樣的架構&#xff0c;繼續往下讀。 前半部分 CRI&#xff08;Container Runtime Interface&#xff0c;容器運行時接口&#xf…

避免海外業務中斷,TikTok養號注意事項

TikTok已成為企業和個人拓展海外業務的重要平臺。然而&#xff0c;由于平臺規則嚴格&#xff0c;賬號被封禁或限制訪問的風險始終存在。為了確保用戶在TikTok上的業務順利進行&#xff0c;著重說一些養號的注意事項。 文章分為三個部分&#xff0c;分別是遵守平臺規則、養號策略…

Qt判定鼠標是否在該多邊形的線條上

要判斷鼠標是否在由QPainterPath或一系列QPointF點定義的多邊形的線條上&#xff0c;你可以使用以下步驟&#xff1a; 獲取鼠標當前位置&#xff1a;在鼠標事件中&#xff0c;使用QMouseEvent的pos()方法獲取鼠標的當前位置。 檢查點與線段的距離&#xff1a;遍歷多邊形的每條…

面試高級 Java 工程師:2024 年的見聞與思考

面試高級 Java 工程師&#xff1a;2024 年的見聞與思考 由于公司業務拓展需要&#xff0c;公司招聘一名高級java工程研發工程師&#xff0c;主要負責新項目的研發及老項目的維護升級。我作為一名技術面試官&#xff0c;參與招聘高級 Java 工程師&#xff0c;我見證了技術領域的…

LATEX格式的高等數學題庫(導數和概率論與數理統計)

\documentclass{ctexart} \usepackage{amsmath,amssymb,amsfonts,hyperref} \usepackage{CJKutf8} \usepackage{enumitem} % 引入宏包 \usepackage [colorlinkstrue] {} \begin{document}\begin{CJK}{UTF8}{gkai}%正文放在此行下與\end{CJK}之間就行\tableofcontents\newpage\s…

F1-score(標準度量)

什么是F1-score&#xff1f; F1分數&#xff08;F1-score&#xff09;是分類問題的一個衡量指標。一些多分類問題的機器學習競賽&#xff0c;常常將F1-score作為最終測評的方法。它是精確率和召回率的調和平均數&#xff0c;最大為1&#xff0c;最小為0&#xff0c;如公式1所示…

高效轉換:CSV 轉 JSON 數組 API

在日常數據處理和分析中&#xff0c;CSV 和 JSON 是兩種常見的數據格式。無論是開發者還是數據科學家&#xff0c;經常需要在這兩種格式之間轉換。我們提供的 CSV 轉 JSON 數組 API 可以幫助您輕松完成這一任務。 功能特點&#xff1a; 多種輸入方式&#xff1a;支持直接粘貼…

使用GPT3.5,LangChain,FAISS和python構建一個本地知識庫

引言 介紹本地知識庫的概念和用途 在現代信息時代&#xff0c;我們面臨著海量的數據和信息&#xff0c;如何有效地管理和利用這些信息成為一項重要的任務。本地知識庫是一種基于本地存儲的知識管理系統&#xff0c;旨在幫助用戶收集、組織和檢索大量的知識和信息。它允許用戶…

C語言-->指針詳解

提示&#xff1a;本系列文章是C語言的重難點–>指針 C語言-->指針詳解 前言一、什么是指針&#xff1f;二、指針的聲明與初始化三、指針的解引用四、指針與數組五、指針與函數六、動態內存分配七、常見錯誤與注意事項總結我是將軍我一直都在&#xff0c;。&#xff01; 前…

Oracle或MySQL數據遷移到國產數據庫后的注意事項

一、人大金倉Kingbase 1、初始化后兼容 創建sysdate()方法兼容原生MySQL模式下不具備sysdate()的問題&#xff1a; create or replace function sysdate() returns timestamp with time zone as select current_timestamp; language sql; 2. 執行語句收集統計信息&#xff…

1.5-協程基礎與關鍵知識:連接線程的世界-回調型 API 協作

文章目錄 線程 API 轉換成掛起函數&#xff1a;suspendCoroutine支持取消的 suspendCoroutine&#xff1a;suspendCancellableCoroutine總結 線程 API 轉換成掛起函數&#xff1a;suspendCoroutine 在實際項目中即使已經使用協程了&#xff0c;可是要完全避免跟傳統的線程 API…

Excel 學習手冊 - 精進版(包括各類復雜函數及其嵌套使用)

作為程序員從未想過要去精進一下 Excel 辦公軟件的使用方法&#xff0c;以前用到某功能都是直接百度&#xff0c;最近這兩天跟著嗶哩嗶哩上的戴戴戴師兄把 Excel 由里到外學了一遍&#xff0c;收獲良多。程序員要想掌握這些內容可以說是手拿把掐&#xff0c;對后續 Excel 的運用…

linux的學習(七):讀取,函數,正則表達式,文本處理工具cut和awk

##簡介 shell編程中的讀取&#xff0c;函數&#xff0c;正則表達式&#xff0c;文本處理工具的簡單使用 read read&#xff1a;讀取控制臺的輸入 參數&#xff1a; -p&#xff1a;指定讀取時的提示符-t&#xff1a;等待讀取的時間 腳本例子 編寫i.sh腳本&#xff0c;enter…

算法實驗3:貪心算法的應用

實驗內容 &#xff08;1&#xff09;活動安排問題 設有n個活動的集合E{1, 2, …, n}&#xff0c;其中每個活動都要求使用同一資源&#xff0c;而在同一時間內只有一個活動能使用這一資源。每個活動i都有一個要求使用該資源的起始時間si和一個結束時間fi&#xff0c;且si <f…

JavaWeb-【2】CSS和JavaScript

筆記系列持續更新,真正做到詳細!!本次系列重點講解后端,那么第一階段先講解前端【續上篇HTML】 目錄 一、CSS 1、CSS介紹 2、CSS快速入門 3、CSS語法 4、字體顏色和邊框 5、背景顏色和字體樣式 6、div和文本居中 7、超鏈接去下劃線和表格細線 8、無序列表去掉樣式…

持續集成03--Jenkins的安裝與配置

前言 在持續集成/持續部署&#xff08;CI/CD&#xff09;的實踐中&#xff0c;Jenkins作為一個開源的自動化服務器&#xff0c;扮演著至關重要的角色。本篇“持續集成03--Jenkins的安裝配置”將帶您走進Jenkins的世界&#xff0c;深入了解如何在Linux環境中安裝并配置Jenkins。…

VUE:跨域配置代理服務器

//在vite.config。js中&#xff0c;同插件配置同級進行配置server:{proxy:{"/myrequest":{//代理域名&#xff0c;可自行修改target:"https://m.wzj.com/",//訪問服務器的目標域名changeOrigin:true,//允許跨域configure:(proxy,options) > {proxy.on(&…

人工智能與人類社會的共生共榮

隨著科技的飛速發展&#xff0c;人工智能&#xff08;AI&#xff09;已經不再是遙不可及的概念&#xff0c;而是深深地融入到了我們的日常生活中。從智能家居到智慧城市&#xff0c;從自動駕駛到醫療診斷&#xff0c;人工智能正以前所未有的方式改變著人類社會的每一個角落。然…

掌握Laravel控制器:構建強大應用的基石

掌握Laravel控制器&#xff1a;構建強大應用的基石 在Laravel框架中&#xff0c;控制器&#xff08;Controller&#xff09;是處理用戶請求和返回響應的核心組件。控制器充當了應用邏輯的中轉站&#xff0c;它接收來自路由的請求&#xff0c;處理這些請求&#xff0c;并返回視…