代理模式
java
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 {
@Override
public void build() {
System.out.println("自定义执行方法");
}
}
class ProxyBuilder implements BuildDream {
private CustomBuilder customBuilder;
@Override
public void build() {
if (this.customBuilder == null) {
this.customBuilder = new CustomBuilder();
}
System.out.println("代理执行前");
customBuilder.build();
System.out.println("代理执行后");
}
}
执行结果:
java
代理执行前
自定义执行方法
代理执行后
动态代理
Java 动态代理是一种设计模式,允许在运行时创建代理对象,以拦截对目标对象的方法调用。动态代理通常用于横切关注点(如日志记录、事务管理、权限控制等)的实现。Java 提供了两种主要的动态代理机制:
- JDK 动态代理:基于接口的代理。
- CGLIB 动态代理:基于类的代理。
JDK 动态代理
JDK 动态代理使用 java.lang.reflect.Proxy
类和 java.lang.reflect.InvocationHandler
接口来实现。它只能代理实现了接口的类。
示例代码
java
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 {
@Override
public void saveInfo() {
System.out.println("保存信息成功");
}
}
class MyServiceProxy implements InvocationHandler {
private Object target;
MyServiceProxy(Object target) {
this.target = target;
}
@Override
public 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));
}
}
执行结果:
java
代理方法执行前
保存信息成功
代理方法执行后
CGLIB 动态代理
CGLIB 动态代理使用字节码生成库来生成代理类,它可以代理没有实现接口的类。
示例代码
java
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;
}
@Override
public 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();
}
}
运行结果:
java
代理方法执行前
保存信息成功
代理方法执行后
代理方法执行前
Hi
代理方法执行后
总结
- JDK 动态代理:适用于代理实现了接口的类。
- CGLIB 动态代理:适用于代理没有实现接口的类。
这两种动态代理机制都可以用于实现 AOP(面向切面编程),以便在不修改目标对象代码的情况下添加额外的功能。
AOP
AOP(面向切面编程)是一种编程范式,它允许你在不修改业务逻辑代码的情况下,添加横切关注点(如日志记录、事务管理、权限控制等)。Spring Framework 提供了强大的 AOP 支持,主要通过以下几种方式实现:
- 基于代理的 AOP:使用 JDK 动态代理或 CGLIB 动态代理。
- 基于注解的 AOP:使用注解来定义切面和切点。
- 基于 XML 配置的 AOP:使用 XML 配置文件来定义切面和切点。
基于注解的 AOP 示例
下面是一个使用 Spring AOP 和注解的示例,展示了如何在 Spring 应用程序中使用 AOP。
1. 添加依赖
首先,确保你的项目中包含 Spring AOP 相关的依赖。如果你使用的是 Maven,可以在 pom.xml
中添加以下依赖:
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. 定义业务逻辑类
定义一个简单的业务逻辑类:
java
package com.example.service;
import org.springframework.stereotype.Service;
@Service
public class HelloService {
public void sayHello() {
System.out.println("Hello, World!");
}
}
3. 定义切面类
定义一个切面类,使用注解来指定切点和通知:
java
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 支持:
java
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 的效果:
java
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
解释
- 业务逻辑类 :
HelloService
是一个简单的业务逻辑类,包含一个sayHello
方法。 - 切面类 :
LoggingAspect
是一个切面类,包含两个通知方法logBefore
和logAfter
,分别在sayHello
方法调用之前和之后执行。 - Spring 配置类 :
AppConfig
是一个 Spring 配置类,启用了 AOP 支持并扫描指定包中的组件。 - 测试代码 :在测试代码中,通过 Spring 容器获取
HelloService
的代理对象,并调用sayHello
方法。
总结
通过使用 Spring AOP 和注解,你可以在不修改业务逻辑代码的情况下,轻松地添加横切关注点。Spring AOP 提供了强大的功能和灵活性,使得代码更加模块化和可维护。
@Before
中的参数
在 Spring AOP 中,@Before
注解用于定义一个前置通知(Advice),它会在目标方法执行之前执行。@Before
注解的参数是一个切点表达式,用于指定哪些方法应该被拦截。切点表达式可以使用多种方式来匹配目标方法,包括方法签名、注解、包名等。
常见的切点表达式
-
匹配方法签名:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
- 例如:
execution(* com.example.service.HelloService.sayHello(..))
-
匹配类上的注解:
@within(annotationType)
- 例如:
@within(org.springframework.stereotype.Service)
-
匹配方法上的注解:
@annotation(annotationType)
- 例如:
@annotation(org.springframework.transaction.annotation.Transactional)
-
匹配包名:
within(package-name)
- 例如:
within(com.example.service..*)
-
匹配参数:
args(argument-types)
- 例如:
args(String, ..)
示例代码
以下是一些常见的 @Before
注解的使用示例:
1. 匹配特定方法
匹配 com.example.service.HelloService
类中的 sayHello
方法:
java
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
包及其子包中的所有方法:
java
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
注解的方法:
java
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
类型的方法:
java
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
注解:
java
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
表达式。 - 组合切点表达式 :使用
&&
、||
和!
运算符。
通过合理使用这些切点表达式,你可以灵活地定义哪些方法应该被拦截,从而实现各种横切关注点的功能。
反射
java
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
的注解:
java
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来实现这一点。
java
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
注解:
java
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切面会拦截这些带有注解的方法或类。
java
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
注解可以应用于方法和类(包括接口和枚举)。
总结
java
@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
注解本身使用了一些元注解来定义其行为和适用范围:
java
@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. 创建自定义注解
java
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
注解的定义如下:
java
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
java
@Retention(RetentionPolicy.RUNTIME)
RetentionPolicy.RUNTIME
:表示@Transactional
注解在运行时也会被保留,可以通过反射机制读取。这对于事务管理是必要的,因为 Spring 需要在运行时动态地管理事务。
@Target
java
@Target({ElementType.METHOD, ElementType.TYPE})
ElementType.METHOD
:表示@Transactional
注解可以应用于方法。ElementType.TYPE
:表示@Transactional
注解可以应用于类(包括接口和枚举)。当应用于类时,该类的所有方法都将具有事务支持,除非方法上另有声明。
@Transactional
注解的属性
value
和transactionManager
:指定要使用的事务管理器的名称。默认情况下,使用默认的事务管理器。propagation
:指定事务的传播行为。默认值为Propagation.REQUIRED
,表示当前方法必须在事务中运行。如果当前没有事务,则会启动一个新的事务。isolation
:指定事务的隔离级别。默认值为Isolation.DEFAULT
,表示使用底层数据库的默认隔离级别。timeout
:指定事务的超时时间(以秒为单位)。默认值为TransactionDefinition.TIMEOUT_DEFAULT
,表示使用底层事务管理器的默认超时时间。readOnly
:指定事务是否为只读。默认值为false
。只读事务可以优化性能,因为数据库可以跳过某些锁定操作。rollbackFor
和rollbackForClassName
:指定哪些异常会触发事务回滚。默认情况下,只有未捕获的运行时异常会触发回滚。noRollbackFor
和noRollbackForClassName
:指定哪些异常不会触发事务回滚。
使用示例
在类上使用 @Transactional
java
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class MyService {
public void performOperation() {
// 该方法将在事务中运行
}
}
在方法上使用 @Transactional
java
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class MyService {
@Transactional
public void performOperation() {
// 该方法将在事务中运行
}
}
自定义注解与 @Transactional
的对比
通过前面的自定义注解示例,我们可以看到 @Transactional
注解的实现方式与自定义注解类似。它们都使用了 @Retention
和 @Target
元注解来定义注解的保留策略和适用范围。不同的是,@Transactional
注解还包含了多个属性,用于配置事务的行为。
总结
@Retention(RetentionPolicy.RUNTIME)
:表示@Transactional
注解在运行时也会被保留,可以通过反射机制读取。@Target({ElementType.METHOD, ElementType.TYPE})
:表示@Transactional
注解可以应用于方法和类(包括接口和枚举)。- 属性 :
@Transactional
注解包含多个属性,用于配置事务的传播行为、隔离级别、超时时间、只读属性以及回滚规则。
通过理解 @Transactional
注解的定义和使用,我们可以更好地理解如何创建和使用自定义注解,并通过AOP实现类似的功能。
使用反射机制实现自定义注解
在Java中,自定义注解是一种强大的工具,用于提供额外的元数据信息。注解以@
符号开头,后面紧跟注解名。下面是如何创建和使用自定义注解的步骤:
1. 创建自定义注解
创建注解非常简单,只需要定义一个Java接口,并使用@interface
关键字。注解接口通常用于定义注解的元数据,如参数、默认值等。
java
// 创建一个自定义注解
@interface MyAnnotation {
String value() default "";
}
在这个例子中,我们创建了一个名为MyAnnotation
的注解,它有一个名为value
的参数,参数默认值为""
。
2. 使用自定义注解
在类、方法、字段或参数上使用自定义注解,只需要在相应的位置使用@MyAnnotation
。
示例:使用自定义注解在方法上
java
public class ExampleClass {
@MyAnnotation("example")
public void myMethod() {
// 方法体
}
}
示例:使用自定义注解在字段上
java
public class ExampleClass {
@MyAnnotation("example")
private String myField;
}
示例:使用自定义注解在参数上
java
public class ExampleClass {
public void myMethod(@MyAnnotation("example") String myParam) {
// 方法体
}
}
3. 反射操作
使用Java的反射API,可以读取类上的注解信息。
java
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,可以实现更强大的代码生成和动态行为。