从代理模式到注解开发

代理模式

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 提供了两种主要的动态代理机制:

  1. JDK 动态代理:基于接口的代理。
  2. 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 支持,主要通过以下几种方式实现:

  1. 基于代理的 AOP:使用 JDK 动态代理或 CGLIB 动态代理。
  2. 基于注解的 AOP:使用注解来定义切面和切点。
  3. 基于 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

解释

  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 方法:

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 注解的属性

  • valuetransactionManager:指定要使用的事务管理器的名称。默认情况下,使用默认的事务管理器。
  • propagation :指定事务的传播行为。默认值为 Propagation.REQUIRED,表示当前方法必须在事务中运行。如果当前没有事务,则会启动一个新的事务。
  • isolation :指定事务的隔离级别。默认值为 Isolation.DEFAULT,表示使用底层数据库的默认隔离级别。
  • timeout :指定事务的超时时间(以秒为单位)。默认值为 TransactionDefinition.TIMEOUT_DEFAULT,表示使用底层事务管理器的默认超时时间。
  • readOnly :指定事务是否为只读。默认值为 false。只读事务可以优化性能,因为数据库可以跳过某些锁定操作。
  • rollbackForrollbackForClassName:指定哪些异常会触发事务回滚。默认情况下,只有未捕获的运行时异常会触发回滚。
  • noRollbackFornoRollbackForClassName:指定哪些异常不会触发事务回滚。

使用示例

在类上使用 @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,可以实现更强大的代码生成和动态行为。

相关推荐
来自星星的坤5 分钟前
Spring Boot 邮件发送配置遇到的坑:解决 JavaMailSenderImpl 未找到的错误
java·开发语言·spring boot·后端·spring
梦想实现家_Z1 小时前
Spring Boot整合DeepSeek+MCP实践详解
spring boot·deepseek·mcp
缘友一世1 小时前
解决Spring Boot上传默认限制文件大小和完善超限异常(若依框架)
java·spring boot·后端
小杨4041 小时前
springboot框架项目实践应用十七(springcloud整合nacos)
spring boot·后端·spring cloud
亭台烟雨中2 小时前
【Springboot后端之间使用websocket长连接通信】
spring boot·后端·websocket
Foyo Designer6 小时前
【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 的未来:从微服务到云原生的演进
java·spring boot·redis·spring·微服务·云原生·restful
Yharim6 小时前
Spring框架 - SpringBoot
spring boot·面试
幽络源小助理6 小时前
SpringBoot学生成绩管理系统设计与实现
java·spring boot·后端
追光的独行者7 小时前
Springboot框架—单元测试操作
java·spring boot·单元测试
spe14378 小时前
【学习自用】配置文件中的配置项
java·spring boot·学习·mybatis