JAVA自定义注解
<h2><strong>什么是注解?</strong></h2><p><span style="font-family: 宋体">注解是一种特殊的接口,用于为</span>Java<span style="font-family: 宋体">代码提供元数据。它们不会直接影响代码的执行,但可以被编译器、开发工具或运行时环境读取和使用。</span></p>
<p>Java<span style="font-family: 宋体">内置了一些常用的注解,如:</span></p>
<p>@Override - <span style="font-family: 宋体">表示方法重写父类方法</span></p>
<p>@Deprecated - <span style="font-family: 宋体">表示代码已过时</span></p>
<p>@SuppressWarnings - <span style="font-family: 宋体">抑制编译器警告</span></p>
<h2><strong>注解的基本语法</strong></h2>
<h3><strong>定义注解</strong></h3>
<p><span style="font-family: 宋体">使用</span>@interface<span style="font-family: 宋体">关键字来定义注解:</span></p>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> @<span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> AutoFill {
}</span></pre>
</div>
<p> </p>
<h3><strong>元注解</strong></h3>
<p><span style="font-family: 宋体">元注解是用来注解其他注解的注解,</span>Java<span style="font-family: 宋体">提供了以下几种元注解:</span></p>
<p>@Target - <span style="font-family: 宋体">指定注解可以应用的目标元素类型</span></p>
<p>@Retention - <span style="font-family: 宋体">指定注解的保留策略</span></p>
<p>@Documented - <span style="font-family: 宋体">表示注解应该被包含在</span><span style="font-family: Calibri">Javadoc</span><span style="font-family: 宋体">中</span></p>
<p>@Inherited - <span style="font-family: 宋体">表示注解可以被继承</span></p>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
</span><span style="color: rgba(0, 0, 255, 1)">public</span> @<span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> AutoFill {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 数据库操作类型:INSERT、UPDATE
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
OperationType value();
}</span></pre>
</div>
<p> </p>
<p><span style="font-family: 宋体">示例代码展示了一个用于公共字段自动填充的自定义注解,</span><span style="font-family: Calibri">@Target</span><span style="font-family: 宋体">明确注解可在方法上使用,</span><span style="font-family: Calibri">@Retention</span><span style="font-family: 宋体">明确在程序运行时可见。</span></p>
<p> </p>
<h3><strong>注解元素</strong></h3>
<p> </p>
<p><span style="font-family: 宋体">注解中可以定义元素,这些元素可以有默认值:</span></p>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">enum</span><span style="color: rgba(0, 0, 0, 1)"> OperationType {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 更新操作
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
UPDATE,
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 插入操作
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
INSERT
}</span></pre>
</div>
<p> </p>
<p><span style="font-family: 宋体">示例自定义注解中的</span><span style="font-family: Calibri">value</span><span style="font-family: 宋体">方法则用来返回上示枚举类型数据,明确 使用该注解的方法 的作用,使用方式如下:</span></p>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 更新员工信息
* </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> employee
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@AutoFill(OperationType.UPDATE)
</span><span style="color: rgba(0, 0, 255, 1)">void</span> updateById(Employee employee);</pre>
</div>
<p> </p>
<p><span style="font-family: 宋体">当注解只有一个方法且方法名为</span> value <span style="font-family: 宋体">时,使用时可以省略方法名</span>,<span style="font-family: 宋体">如果方法不叫</span> value<span style="font-family: 宋体">,就必须明确指定方法名</span>:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
</span><span style="color: rgba(0, 0, 255, 1)">public</span> @<span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> AutoFill {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 数据库操作类型:INSERT、UPDATE
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
OperationType type();
}<br><br></span></pre>
<pre>/**
* 更新员工信息
* @param employee
*/<span>
@AutoFill(type = OperationType.UPDATE)
void updateById(Employee employee);</span></pre>
</div>
<h2><strong>自定义注解</strong><strong>的使用</strong></h2>
<h3><strong>通过反射处理注解</strong></h3>
<p><span style="font-family: 宋体">我们可以使用反射机制在运行时读取和处理注解:</span></p>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Aspect
@Component
@Slf4j
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> AutoFillAspect {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 公共字段自动填充切入点
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Pointcut(</span>"execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> autoFillPointCut() {}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 公共字段自动填充
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Before(</span>"autoFillPointCut()"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> autoFill(JoinPoint joinPoint) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Throwable {
log.info(</span>"公共字段自动填充通知开始"<span style="color: rgba(0, 0, 0, 1)">);
MethodSignature signature </span>=<span style="color: rgba(0, 0, 0, 1)"> (MethodSignature)joinPoint.getSignature();
AutoFill autoFill</span>= signature.getMethod().getAnnotation(AutoFill.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取数据库操作类型</span>
Enum operationType =<span style="color: rgba(0, 0, 0, 1)"> autoFill.value();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从ThreadLocal中获取当前登录用户的id</span>
Long currentId =<span style="color: rgba(0, 0, 0, 1)"> BaseContext.getCurrentId();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取当前时间</span>
LocalDateTime now =<span style="color: rgba(0, 0, 0, 1)"> LocalDateTime.now();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从joinPoint中获取参数</span>
Object[] args =<span style="color: rgba(0, 0, 0, 1)"> joinPoint.getArgs();
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(args==<span style="color: rgba(0, 0, 255, 1)">null</span> || args.length==0<span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从参数中获取实体对象</span>
Object entity = args;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调用实体对象的方法,设置创建时间、创建人、更新时间、更新人</span>
<span style="color: rgba(0, 0, 255, 1)">if</span>(operationType==<span style="color: rgba(0, 0, 0, 1)">OperationType.INSERT){
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">{
Method setCreateTime </span>= entity.getClass().getDeclaredMethod("setCreateTime", LocalDateTime.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
Method setUpdateTime </span>= entity.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
Method setCreateUser </span>= entity.getClass().getDeclaredMethod("setCreateUser", Long.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
Method setUpdateUser </span>= entity.getClass().getDeclaredMethod("setUpdateUser", Long.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
setCreateTime.invoke(entity, now);
setUpdateTime.invoke(entity, now);
setCreateUser.invoke(entity, currentId);
setUpdateUser.invoke(entity, currentId);
}</span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e){
log.error(</span>"公共字段自动填充通知异常"<span style="color: rgba(0, 0, 0, 1)">, e);
}
}</span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span>(operationType==<span style="color: rgba(0, 0, 0, 1)">OperationType.UPDATE){
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">{
Method setUpdateTime </span>= entity.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
Method setUpdateUser </span>= entity.getClass().getDeclaredMethod("setUpdateUser", Long.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
}</span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e){
log.error(</span>"公共字段自动填充通知异常"<span style="color: rgba(0, 0, 0, 1)">, e);
}
}
}
}</span></pre>
</div>
<p> </p>
<p>上述示例展示的是@AutoFill注解进行公共字段自动填充必要的切面类。</p>
<div class="cnblogs_code">
<pre>@Aspect</pre>
</div>
<p>该注解用于声明切面类。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 公共字段自动填充切入点
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Pointcut(</span>"execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> autoFillPointCut() {}</pre>
</div>
<p>这一部分用于声明哪些部分需要拦截。(在com.sky.mapper包下的所有类及其所有方法 <strong>且</strong> 被自定义注解(@AutoFill)所标注)</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 公共字段自动填充
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Before(</span>"autoFillPointCut()"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> autoFill(JoinPoint joinPoint) <span style="color: rgba(0, 0, 255, 1)">throws</span> Throwable </pre>
</div>
<p>@Before: <span style="font-family: 宋体">表示这是一个</span><span style="font-family: 宋体">前置通知,在目标方法执行之前运行。</span></p>
<p>autoFillPointCut(): <span style="font-family: 宋体">引用一个切点表达式,定义了哪些方法需要被拦截。</span></p>
<p> </p>
<p>JoinPoint: AOP <span style="font-family: 宋体">连接点对象,可以获取被拦截方法的详细信息:</span><span style="font-family: 宋体">方法名、参数、目标对象等</span>。<span style="font-family: 宋体">通过</span> joinPoint.getArgs() <span style="font-family: 宋体">获取方法参数。</span></p>
<p> </p>
<div class="cnblogs_code">
<pre>MethodSignature signature =<span style="color: rgba(0, 0, 0, 1)"> (MethodSignature)joinPoint.getSignature();
AutoFill autoFill</span>= signature.getMethod().getAnnotation(AutoFill.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取数据库操作类型</span>
Enum operationType = autoFill.value();</pre>
</div>
<p> </p>
<p>joinPoint.getSignature(): <span style="font-family: 宋体">获取连接点的签名信息。</span></p>
<p>(MethodSignature): <span style="font-family: 宋体">强制转换为</span><span style="font-family: Calibri"> MethodSignature </span><span style="font-family: 宋体">类型,因为 </span><span style="font-family: Calibri">Spring AOP </span><span style="font-family: 宋体">只拦截方法。</span></p>
<p>signature.getMethod(): <span style="font-family: 宋体">获取被拦截的</span> Method <span style="font-family: 宋体">对象。</span></p>
<p>.getAnnotation(AutoFill.class): <span style="font-family: 宋体">从方法上获取</span><span style="font-family: Calibri"> @AutoFill </span><span style="font-family: 宋体">注解。</span></p>
<p>autoFill.value(): <span style="font-family: 宋体">调用注解的</span> value() <span style="font-family: 宋体">方法,获取注解中定义的枚举值。</span></p>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从joinPoint中获取参数</span>
Object[] args =<span style="color: rgba(0, 0, 0, 1)"> joinPoint.getArgs();
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(args==<span style="color: rgba(0, 0, 255, 1)">null</span> || args.length==0<span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从参数中获取实体对象</span>
Object entity = args;</pre>
</div>
<p> </p>
<p>利用从joinPoint中获取的参数获得原本方法中传递的实体对象。</p>
<div class="cnblogs_code">
<pre>Method setCreateTime = entity.getClass().getDeclaredMethod("setCreateTime", LocalDateTime.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);</span><span style="color: rgba(0, 0, 0, 1)">
Method setCreateUser </span>= entity.getClass().getDeclaredMethod("setCreateUser", Long.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);</span><span style="color: rgba(0, 0, 0, 1)">
setCreateTime.invoke(entity, now);
setCreateUser.invoke(entity, currentId);</span></pre>
</div>
<p>利用反射为对象设置公共字段。</p>
<p><strong><span style="font-size: 14pt">最终效果</span></strong></p>
<p> </p>
<p><img src="https://img2024.cnblogs.com/blog/3291169/202511/3291169-20251114200942358-2086750487.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/3291169/202511/3291169-20251114200946401-2000674547.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/3291169/202511/3291169-20251114200953896-1186014324.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/3291169/202511/3291169-20251114200959439-1357514389.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/3291169/202511/3291169-20251114201154520-130228613.png"></p>
<p>@AutoFill注解能有效替代原本冗长且重复的公共字段设置代码。</p>
<p> </p><br><br>
来源:https://www.cnblogs.com/syf0824/p/19223247
頁:
[1]