池州集团网站建设/甘肃新站优化
代理模式
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 提供了两种主要的动态代理机制:
- JDK 动态代理:基于接口的代理。
- 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 支持,主要通过以下几种方式实现:
- 基于代理的 AOP:使用 JDK 动态代理或 CGLIB 动态代理。
- 基于注解的 AOP:使用注解来定义切面和切点。
- 基于 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
解释
- 业务逻辑类:
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
方法:
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
注解的属性
value
和transactionManager
:指定要使用的事务管理器的名称。默认情况下,使用默认的事务管理器。propagation
:指定事务的传播行为。默认值为Propagation.REQUIRED
,表示当前方法必须在事务中运行。如果当前没有事务,则会启动一个新的事务。isolation
:指定事务的隔离级别。默认值为Isolation.DEFAULT
,表示使用底层数据库的默认隔离级别。timeout
:指定事务的超时时间(以秒为单位)。默认值为TransactionDefinition.TIMEOUT_DEFAULT
,表示使用底层事务管理器的默认超时时间。readOnly
:指定事务是否为只读。默认值为false
。只读事务可以优化性能,因为数据库可以跳过某些锁定操作。rollbackFor
和rollbackForClassName
:指定哪些异常会触发事务回滚。默认情况下,只有未捕获的运行时异常会触发回滚。noRollbackFor
和noRollbackForClassName
:指定哪些异常不会触发事务回滚。
使用示例
在类上使用 @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,可以实现更强大的代码生成和动态行为。
相关文章:

从代理模式到注解开发
代理模式 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 {Over…...

力扣刷题(自用)
哈希 128.最长连续序列 128. 最长连续序列 - 力扣(LeetCode) 这个题要求O(n)的时间复杂度,我一开始想的是双指针算法(因为我并不是很熟悉set容器的使用),但是双指针算法有小部分数据过不了。 题解给的哈…...

网站开发:使用VScode安装yarn包和运行前端项目
一、首先打开PowerShell-管理员身份运行ISE 输入命令: set-ExecutionPolicy RemoteSigned 选择“全是”,表示允许在本地计算机上运行由本地用户创建的脚本,没有报错就行了 二、接着打开VScode集成终端 输入 npm install -g yarn 再次输入以…...

Linux_线程的使用
目录 1、线程与进程的关系 2、线程的优缺点 3、创建线程 4、查看启动的线程 5、验证线程是共享地址空间的 6、pthread_create的重要形参 6.1 线程id 6.2 线程实参 7、线程等待 8、线程退出 9、线程取消 10、线程tcb 10.1 线程栈 11、创建多线程 12、__th…...

[word] word如何编写公式? #微信#知识分享
word如何编写公式? word如何编写公式?Word中数学公式是经常会使用到的,若是要在文档中录入一些复杂的公式,要怎么做呢?接下来小编就来给大家讲一讲具体操作,一起看过来吧! 方法一:…...

Pytest 框架快速入门
Pytest 框架常用功能介绍 一、简介 Pytest 是一个功能强大的 Python 测试框架,具有简单易用、测试用例清晰易读、支持参数化、可运行由 Nose 和 unittest 编写的测试用例、拥有丰富的第三方插件且可自定义扩展、支持重复执行失败的用例以及方便与持续集成工具集成…...

抖音视频素材去哪里找啊?视频素材网站库分享
在这个视觉盛宴的抖音平台上,高质量和有趣的视频素材常常是吸引观众的重要钥匙。如果你也正在寻找那些能让你的视频作品更加出色的资源,那么恭喜你,今天我将为你介绍10个超实用的视频素材网站,让你的抖音视频创作充满创意和效率。…...

win10 langchain-chatchat-0.3.1安装及测试
git clone https://github.com/chatchat-space/Langchain-Chatchat.git conda create -n langchain3 python3.11 conda activate langchain3 xinference安装用另一篇文章的内容处理。 pip install langchain-chatchat -U -i https://pypi.tuna.tsinghua.edu.cn/simple pip in…...

Redis 教程:从入门到入坑
目录 1. Redis 安装与启动1.1. 安装 Redis1.1.1. 在Linux上安装1.1.2. 在Windows上安装 1.2. 启动 Redis1.2.1. 在Linux上启动1.2.2. 在Windows上启动 1.3. 连接Redis1.3.1. 连接本地Redis1.3.2. 连接远程Redis1.3.2.1. 服务器开放端口1.3.2.2. 关闭防火墙1.3.2.3. 修改配置文件…...

计算机图形学入门31:动画与模拟的求解
1.前言 上一篇介绍了动画与模拟的很多方法、模拟各种运动、基本知识。定义一个物体的速度和加速度,算出物体在任何时刻的位置,但是没有介绍具体怎么实现。这篇文章就是从上一篇的概念出发,介绍怎么把一个物体或多个物体运动的位置、不同时间出…...

Jmeter-单用户单表查询千条以上数据,前端页面分页怎么做
这里写自定义目录标题 单用户单表查询千条以上数据 单用户单表查询千条以上数据 对于单用户查询千条以上数据,但是前端页面做了分页的情况下 可以直接把查询接口下的分页限制去掉,便可查询出当前页面查询条件下的全部数据 例如去掉如下内容࿱…...

夏日养猫攻略!你家猫咪缺水了吗?补水罐头秘籍大公开
炎炎夏日,高温来袭,这几天又有几只猫咪因为中暑被送到我们医院了,经过诊断,发现猫咪体温超过40C,而且严重缺水。 各位铲屎官真的得注意,酷暑炎热,给猫咪补水很重要。猫咪的汗腺数量远远不及人类…...

生成名片格式
/*** 生成名片* param array arr2 卡片素材* param array strs 素材文字 数组* param function successFn 回调函数* * */PosterCanvasCard: function(arr2, strs, successFn, errFun) {let that this;const ctx uni.createCanvasContext(myCanvas);ctx.clearRect(0, 0, 0, 0…...

Linux常用命令(简要总结)
Linux常用命令 Linux 是一个强大的操作系统,广泛应用于服务器、开发和嵌入式系统中。掌握一些常用的 Linux 命令对于高效地使用系统至关重要。以下是一些常用的 Linux 命令及其简要说明: 文件和目录操作 ls:列出目录内容 ls ls -l # …...

从挑战到实战!TDengine 新能源行业研讨会要点回顾
近年来,随着全球对可再生能源需求的不断增长,新能源行业迎来了前所未有的发展机遇。然而,伴随着行业的快速发展,海量数据的管理和高效利用成为了行业面临的重要挑战。如何通过先进的数据管理技术提升新能源系统的效率和可靠性&…...

Linux 之 设置环境变量
设置环境变量 启动帐号后自动执行的是 文件为 .bashrc,然后通过这个文件可设置自己的环境变量; 临时设置环境变量: 在终端中使用 export 命令可以临时设置环境变量,例如:export PATH$PATH:/your/custom/path这种方法设…...

postgresql删除用户
背景 **角色与用户**:在 PostgreSQL 中,用户和组的概念是通过“角色”来统一实现的。角色可以有登录权限(在这种情况下,它们通常被称为“用户”),也可以没有(在这种情况下,它们通常用…...

【java深入学习第5章】Spring Boot 统一功能的实现及处理方式
Spring Boot 统一功能处理 在开发 Web 应用程序时,为了提高代码的可维护性和可扩展性,我们通常会采用一些统一的功能处理方式。本文将介绍如何在 Spring Boot 中实现统一的数据返回格式、异常处理和功能处理,并通过一个图书管理系统的案例来…...

【常见开源库的二次开发】基于openssl的加密与解密——单向散列函数(四)
目录: 目录: 一、什么是单项散列函数? 1.1 如何验证文件是否被修改过 1.2 单项散列函数: 二、单向hash抗碰撞 2.1 弱抗碰撞(Weak Collision Resistance) 2.2 强抗碰撞(Strong Collision Resista…...

获取不重复流水号(java)
一:概述 很多业务场景都需要获取不重复的业务流水号,当微服务项目或服务多节点部署时,获取流水号场景使用分布式锁性能低下,可以基于数据库行锁实现获取不重复流水号。 二:创建流水号数据库 CREATE TABLE serial (i…...

【python虚拟环境管理】【mac m3】 使用pipx安装poetry
文章目录 一. 安装 pipx二. 安装Poetry1. 安装2. advanced 操作 官网文档:https://python-poetry.org/docs/ pipx介绍文档:https://blog.51cto.com/u_15064632/2570626 一. 安装 pipx pipx 用于全局安装 Python 命令行应用程序,同时在虚拟环…...

git使用以及理解
git练习网站 Learn Git Branching git操作大全Oh Shit, Git!?! git commit git branch name git merge bugFix 合并俩个分支 git rebase main git checkout headgit switch head 会导致HEAD分离 ,就是指head->HEAD->c1 相对引用 ------------------- …...

openlayers 3d 地图 非三维 立体地图 行政区划裁剪 地图背景
这是实践效果 如果没有任何基础 就看这个专栏:http://t.csdnimg.cn/qB4w0 这个专栏里有从最简单的地图到复杂地图的示例 最终效果: 线上示例代码: 想要做这个效果 如果你的行政区划编辑点较多 可能会有卡顿感 如果出现卡顿感需要将边界点相应…...

GEO数据挖掘从数据下载处理质控到差异分析全流程分析步骤指南
0. 综合的教学视频介绍 GEO数据库挖掘分析作图全流程每晚11点在线教学直播录屏回放视频: https://www.bilibili.com/video/BV1rm42157CT/ GEO数据从下载到各种挖掘分析全流程详解: https://www.bilibili.com/video/BV1nm42157ii/ 一篇今年近期发表的转…...

我想我大抵是疯了,我喜欢上了写单元测试
前言 大家好我是聪。相信有不少的小伙伴喜欢写代码,但是对于单元测试这些反而觉得多此一举,想着我都在接口文档测过了!还要写什么单元测试!写不了一点!! 由于本人也是一个小小程序猿🙉…...

【Visual Studio】Visual Studio使用技巧及报错解决合集
目录 目录 一.概述 二.Visual Studio报错问题及解决方法 三.Visual Studio操作过程中遇到的问题及解决方法 四.Visual Studio编译优化选项 五.Visual Studio快捷键 一.概述 持续更新Visual Studio报错及解决方法,包括Visual Studio报错问题及解决方法、Visua…...

服务器数据恢复—raid5阵列热备盘同步失败导致lun不可用的数据恢复案例
服务器存储数据恢复环境: 华为S5300存储中有一组由16块FC硬盘组建的RAID5磁盘阵列(包含一块热备盘)。 服务器存储故障: 该存储中的RAID5阵列1块硬盘由于未知原因离线,热备盘上线并开始同步数据,数据同步到…...

算法题目整合
文章目录 121. 小红的区间翻转142. 两个字符串的最小 ASCII 删除总和143. 最长同值路径139.完美数140. 可爱串141. 好二叉树 121. 小红的区间翻转 小红拿到了两个长度为 n 的数组 a 和 b,她仅可以执行一次以下翻转操作:选择a数组中的一个区间[i, j]&…...

万界星空科技AI低代码平台:重塑数字化创新边界
在这个日新月异的数字化时代,技术的飞速发展正以前所未有的力量重塑着每一个行业。企业如何在瞬息万变的市场环境中保持竞争力,实现业务的快速迭代与创新? 重塑开发模式,加速数字化转型 传统软件开发周期长、成本高、门槛高&…...

iredmail服务器安装步骤详解!如何做配置?
iredmail服务器安全性设置指南?怎么升级邮件服务器? iredmail是一个功能强大的邮件服务器解决方案,它集成了多个开源软件,使您能够快速部署和管理邮件服务。AokSend将逐步引导您完成安装过程,无需深入的编程知识即可轻…...