当前位置:首页 > Java程序 > 正文内容

java 注解的实现(java注解实现方式)

abcsky883个月前 (02-10)Java程序75

今天给各位分享

目录:Java注解基础知识?Java内置注解?元注解?自定义注解?注解应用的三个真实业务?Java注解基础知识注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。

java 注解的实现(java注解实现方式)

它主要的作用有以下四方面:· 生成文档,通过代码里标识的元数据生成javadoc文档· 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证· 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。

· 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例这么来说是比较抽象的,我们具体看下注解的常见分类:· 橘子seo查询Java自带的标准注解,包括@Override、@Deprecated和@SuppressWarnings,分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告,用这些注解标明后编译器就会进行检查。

· 元注解,元注解是用于定义注解的注解,包括@Retention、@Target、@Inherited、@Documented,@Retention用于标明注解被保留的阶段@Target用来限制注解的使用范围

@Inherited该注解使父类的注解能被其子类继承@Documented该注解是一个标记注解,用于指示一个注解将被文档化@Repeatable 该注解是Java8新增的注解,用于开橘子seo查询发重复注解@类型注解(Type Annotation)该注解是Java8新增的注解,用于开发重复注解

· 自定义注解,可以根据自己的需求定义注解,并可用元注解对自定义注解进行注解。接下来我们通过这个分类角度来理解注解。Java内置注解JDK 中内置了以下注解:

内置注解 - @Override概念:检查该方法是否是重写方法如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误//这个extends 不要在意,我写上去只是为了更加方便直观的去理解,Object是万物之源,不写也会默认是其子类,不用解释过多吧?。

public class Annotation1 extends Object{@橘子seo查询Overridepublic String toString(){return"我是重新定义过的toString方法";

}}@Override(重写),这个大家应该很熟悉,重写父类的方法。我们可以看下Object类中toString()是什么样子的。

那么显而易见,使用了@Override(重写)注解,方法名、方法参数必须得和父类保持一致,否则会报错。如下图所示:

如果不加@Override(重写)注解,则正常编译。

内置注解 - @Deprecated概念:标记过时方法。如果使用该方法,会报编译警告。 在开发中,我们经常能遇到这样的情况,如下图:

在jdk中有大量这样的方法,我就不举例了,自己写一个橘子seo查询可能会更加方便理解public class Annotation1 extends Object{publicstaticvoidmain(String[] args){。

}@SuppressWarnings("all")publicstaticvoidtestSuppressWarnings(){System.out.println("测试+testSuppressWarnings忽略警告!");

}}

注意点: 这个不是报错,只是警告,提醒我们这个方法可能会有问题,可能有更好的方法来实现!内置注解 - @SuppressWarnings概念:指示编译器去忽略注解中声明的警告平时开发中,我们会遇橘子seo查询到这样的情况,如下图:。

这也不是错误,这是提醒我们,该方法没有使用到,警告提醒的作用加上@SuppressWarnings注解后public class Annotation1 extends Object { publicstaticvoidmain(String[] args) { } @SuppressWarnings("all") publicstaticvoidtestSuppressWarnings() { System.out.println("测试+testSuppressWarnings忽略警告!"); }}。

方法成功高亮起来,并且没有警告提示了!

我们可以点进去看下这个注解为橘子seo查询什么需要参数?

看这里,这个不是方法哦,这是参数。在注解中的参数格式:calss + 参数名 + ()!这个需要强行记忆哦,回头我们自定义注解时也需要用到。换一种写法加深理解!如下图:

注意点:当注解中只有一个参数时,我们无需加上参数名,注解会自动帮我们匹配的@SuppressWarnings 有常见的值,分别对应如下意思1.deprecation:使用了不赞成使用的类或方法时的警告(使用@Deprecated使得编译器产生的警告);。

2.unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 关闭编译器警告3.fallthroug橘子seo查询h:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;

4.path:在类路径、源文件路径等中有不存在的路径时的警告;5.serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;6.finally:任何 finally 子句不能正常完成时的警告;

7.rawtypes 泛型类型未指明8.unused 引用定义了,但是没有被使用9.all:关于以上所有情况的警告package annotation;import java.util.ArrayList;import java.util.List;public class Hero { String 橘子seo查询name; @SuppressWarnings({ "rawtypes", "unused" }) public static void main(String[] args) { List heros = new ArrayList(); }}。

内置注解 - @FunctionalInterface@FunctionalInterface这是Java1.8 新增的注解,用于约定函数式接口函数式接口概念: 如果接口中只有一个抽象方法(可以包含多个默认方法或多个static方法),该接口称为函数式接口。

函数式接口其存在的意义,主要是配合Lambda 表达式 来使用如例所示,AD接口只有一个adA橘子seo查询ttack方法,那么就可以被注解为@FunctionalInterface,而AP接口有两个方法apAttack()和apAttack2(),那么就不能被注解为函数式接口

AD :package annotation;@FunctionalInterfacepublic interface AD { public void adAttack();}AP:package annotation;@FunctionalInterfacepublic interface AP { public void adAttack(); public void apAttack2();}

元注解概念:顾名思义,元注橘子seo查询解就是给注解使用的注解!@Retention 作用域-(常用)概念:表示在什么级别保存该注解信息 在实际开发中,我们一般都写RUNTIME,除非项目有特殊需求!我们看下@Retention的源码。

可以看到,需要一个参数,进参数瞅瞅。

SOURCE: 源代码时有用CLASS: class文件中有用,但会被jvm丢弃RUNTIME: 运行时有用关系:RUNTIME>CLASS>SOURCE后面我们自定义注解时,每个都需要用该注解!@Documented 作用文档

概念:将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。老规矩看下源码:

无参的注解,作用域为Retent橘子seo查询ionPolicy.RUNTIME,运行时有用!这个只是用来作为标记,了解即可,在实际运行后会将该注解写入javadoc中,方便查看@Target 目标-(常用)概念:标记这个注解应该是使用在哪种 Java 成员上面!。

参数源码:

注意这里是数组格式的参数,证明可以传多个值@Target(ElementType.TYPE)——接口、类、枚举、注解@Target(ElementType.FIELD)——字段、枚举的常量@Target(ElementType.METHOD)——方法。

@Target(ElementType.PARAMETER)——方法参数@Target(ElementType.CON橘子seo查询STRUCTOR) ——构造函数@Target(ElementType.LOCAL_VARIABLE)——局部变量

@Target(ElementType.ANNOTATION_TYPE)——注解@Target(ElementType.PACKAGE)——包我们来试一下:

目标不对会报错的哦!我们将其改成方法上!编译即正常通过。

其他的作用域大家可以去自行尝试,篇幅问题,无法做到每个都去试一遍!@Inherited 继承概念:标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)。

这个很简单,就是当@InheritedAnno注解加在某个类A上时,假如类B继承了A,则B也会带上该注解新注解橘子seo查询-(了解即可)从 Java 7 开始,额外添加了 3 个注解:@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。

@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次三、自定义注解当我们理解了内置注解, 元注解和获取注解的反射接口后,我们便可以开始自定义注解了。

这个例子我把上述的知识点全部融入进来, 代码很简单:定义自己的注解package com.pdai.java.annotation;import橘子seo查询 java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface MyMethodAnnotation { public String title() default ""; public String 橘子seo查询description() default "";}

使用注解package com.pdai.java.annotation;import java.io.FileNotFoundException;import java.lang.annotation.Annotation;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.List;public class TestMethodAnnotation { @Override @MyMethodAnnotation(title = "toStr橘子seo查询ingMethod", description = "override toString method") public String toString() { return "Override toString method"; } @Deprecated @MyMethodAnnotation(title = "old static method", description = "deprecated old static method") public static void oldMethod() { System.out.println("old method, dont use i橘子seo查询t."); } @SuppressWarnings({"unchecked", "deprecation"}) @MyMethodAnnotation(title = "test method", description = "suppress warning static method") public static void genericsTest() throws FileNotFoundException { List l = new ArrayList(); l.add("abc"); oldMethod(); }}

用反射接口获取注解信息在TestMethodAnnotation中橘子seo查询添加Main方法进行测试:public static void main(String[] args) { try { // 获取所有methods Method[] methods = TestMethodAnnotation.class.getClassLoader() .loadClass(("com.pdai.java.annotation.TestMethodAnnotation")) .getMethods(); // 遍历 for (Method method : methods) { // 方法上是否有MyMethodAnnotation注解 if (method.isAnno橘子seo查询tationPresent(MyMethodAnnotation.class)) { try { // 获取并遍历方法上的所有注解 for (Annotation anno : method.getDeclaredAnnotations()) { System.out.println("Annotation in Method " + method + " : " + anno); } // 获取MyMethodAnnotation对象信息 MyMethodAnnotation methodAnno = method .getAnnotation(MyMethodAnnotation.clas橘子seo查询s); System.out.println(methodAnno.title()); } catch (Throwable ex) { ex.printStackTrace(); } } } } catch (SecurityException | ClassNotFoundException e) { e.printStackTrace(); }}

测试的输出Annotation in Method public static void com.pdai.java.annotation.TestMethodAnnotation.oldMethod() : @java.lang.Depreca橘子seo查询ted()Annotation in Method public static void com.pdai.java.annotation.TestMethodAnnotation.oldMethod() : @com.pdai.java.annotation.MyMethodAnnotation(title=old static method, description=deprecated old static method)old static methodAnnotation in Method public static void com.pdai.java.annotation.Te橘子seo查询stMethodAnnotation.genericsTest() throws java.io.FileNotFoundException : @com.pdai.java.annotation.MyMethodAnnotation(title=test method, description=suppress warning static method)test methodAnnotation in Method public java.lang.String com.pdai.java.annotation.TestMethodAnnotation.toString() : @com.橘子seo查询pdai.java.annotation.MyMethodAnnotation(title=toStringMethod, description=override toString method)toStringMethod

Java8提供了哪些新的注解?重复注解(ElementType.TYPE_USE)允许在同一声明类型(类,属性,或方法)上多次使用同一个注解Java8以前的版本使用注解有一个限制是相同的注解在同一位置只能使用一次,不能使用多次。

Java 8 引入了重复注解机制,这样相同的注解可以在同一地方使用多次重复注解机制本身必须用 @Repeatable 注解实际上,重复注解不是一个橘子seo查询语言上的改变,只是编译器层面的改动,技术层面仍然是一样的。

例如,我们可以使用如下示例来具体对比Java8之前的版本和Java8中的注解1)自定义一个包装类Hints注解用来放置一组具体的Hint注解@interface MyHints { Hint[] value();}@Repeatable(MyHints.class)@interface Hint { String value();}。

使用包装类当容器来存多个注解(旧版本方法)@MyHints({@Hint("hint1"), @Hint("hint2")})class Person {}使用多重注解(新方法)@Hint("hint1")橘子seo查询@Hint("hint2")class Person {}

2)完整类测试如下所示public class RepeatingAnnotations { @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Filters { Filter[] value(); } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Repeatable(Filters.class) public @interface Filter 橘子seo查询{ String value(); } @Filter("filter1") @Filter("filter2") public interface Filterable { } public static void main(String[] args) { for (Filter filter : Filterable.class.getAnnotationsByType(Filter.class)) { System.out.println(filter.value()); } }}。

输出结果:filter1filter2分析:注释Filter被@Repeatable( Filters.橘子seo查询class )注释Filters 只是一个容器,它持有Filter, 编译器尽力向程序员隐藏它的存在。

通过这样的方式,Filterable接口可以被Filter注释两次另外,反射的API提供一个新方法getAnnotationsByType() 来返回重复注释的类型(注意Filterable.class.getAnnotation( Filters.class )将会返回编译器注入的Filters实例。

3)java 8之前也有重复使用注解的解决方案,但可读性不好public @interface MyAnnotation { String role(); } public @interface橘子seo查询 Annotations { MyAnnotation[] value(); } public class RepeatAnnotationUseOldVersion { @Annotations({@MyAnnotation(role="Admin"),@MyAnnotation(role="Manager")}) public void doSomeThing(){ } }。

Java8的实现方式(由另一个注解来存储重复注解,在使用时候,用存储注解Authorities来扩展重复注解),可读性更强@Repeatable(Annotations.class) public @interface橘子seo查询 MyAnnotation { String role(); } public @interface Annotations { MyAnnotation[] value(); } public class RepeatAnnotationUseOldVersion { @MyAnnotation(role="Admin") @MyAnnotation(role="Manager") public void doSomeThing(){ } }。

类型注解(ElementType.TYPE_PARAMETER)1)Java 8 的类型注解扩展了注解使用的范围在java 8之前,注解只能是在声明的橘子seo查询地方所使用,java8开始,注解可以应用在任何地方例如:

创建类实例new @Interned MyObject();类型映射myString = (@NonNull String) str;implements 语句中class UnmodifiableList implements@Readonly List { ... }

throw exception声明void monitorTemperature() throws@Critical TemperatureException { ... }注意:在Java 8里面,当类型转化甚至分配新对象的时候,都可以在声明变量或者参数的时候使用注解。橘子seo查询

Java注解可以支持任意类型类型注解只是语法而不是语义,并不会影响java的编译时间,加载时间,以及运行时间,也就是说,编译成class文件的时候并不包含类型注解2)新增ElementType.TYPE_USE 和。

ElementType.TYPE_PARAMETER(在Target上)新增的两个注释的程序元素类型 ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER用来描述注解的新场合。

ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中ElementType.TYPE_USE 表示该注解能写在使用类型的任何语橘子seo查询句中(例如:声明语句、泛型和强制转换语句中的类型)。

例如,下面的示例@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})@interface MyAnnotation {}3)类型注解的作用。

类型注解被用来支持在Java的程序中做强类型检查配合第三方插件工具Checker Framework(注:此插件so easy,这里不介绍了),可以在编译的时候检测出runtime error(例如:UnsupportedOperationException;NumberFormatException;NullPointerExceptio橘子seo查询n异常等都是runtime error),以提高代码质量。

这就是类型注解的作用注意:使用Checker Framework可以找到类型注解出现的地方并检查例如下面的代码import checkers.nullness.quals.*;public class TestDemo{ void sample() { @NonNull Object my = new Object(); }}。

使用javac编译上面的类:(当然若下载了Checker Framework插件就不需要这么麻烦了)javac -processor checkers.nullness.NullnessChecker TestD橘子seo查询emo.java

上面编译是通过的,但若修改代码:@NonNull Object my = null;但若不想使用类型注解检测出来错误,则不需要processor,正常javac TestDemo.java是可以通过编译的,但是运行时会报 NullPointerException 异常。

为了能在编译期间就自动检查出这类异常,可以通过类型注解结合 Checker Framework 提前排查出来错误异常注意java 5,6,7版本是不支持注解@NonNull,但checker framework 有个向下兼容的解决方案,就是将类型注解@NonNull 用/**/注释起来。

import checke橘子seo查询rs.nullness.quals.*;public class TestDemo{ void sample() { /*@NonNull*/ Object my = null; }}

这样javac编译器就会忽略掉注释块,但用checker framework里面的javac编译器同样能够检测出@NonNull错误通过 类型注解 + checker framework 可以在编译时就找到runtime error。

注解支持继承吗?使用注解@Inherited可以让指定的注解在某个类上使用后,这个类的子类自动被该注解标记

结论:我们知道在编写自定义注解时,可以通过指定@Inherited注解,指明橘子seo查询白定义注解是否可以被继承编写类注解验证@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Inheritedpublic @interface Dengyi { String value() default "dengyi";}

@Dengyipublic class Person {}public class Student extends Person { public static void main(String[] args) { boolean res = Student.class.isAnnotationPre橘子seo查询sent(Dengyi.class); //true }}

注意注解继承的传递性:如果被标注为@Inherited某注解 用在一个父类上,则其子类,孙子类都可以通过反射获取该注解@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Inheritedpublic @interface Dengyi{ String value() default "";}

@Dengyipublic class A {}public class B extends A{}public class C ex橘子seo查询tends B{ public static void main(String[] args){ Class clz = C.class; Dengyi dengyi = clz.getAnnotation(Dengyi.class); System.out.println(dengyi); }}

重写或实现方法会覆盖掉原有方法的注解这个应当这样理解public interface Map{ @Dengyi void put();}public class HashMap implements Map{ @Override public void put(){ //这里相当于把接口的方法想覆盖掉橘子seo查询了,当然就没有原来方法的注解了 } public static void main (String[] args) throws NoSuchMethodException{ Class clz = HashMap.class; Method put = clz.getMethod("put"); Dengyi annotation = put.getAnnotation(Dengyi.class); System.out.println(annotation); //output: null Class clz2 = Map.class; Method put2 = clz2.getMeth橘子seo查询od("put") Dengyi dengyi = put2.getAnnotation(Dengyi.class); System.out.println(dengyi); //output: @com.jianglei3.bean.Dengyi(value=) }}

子类继承父类的方法会继承注解因为子类掉用父类方法时,会去父类寻找该方法的信息public class MMap{ @Dengyi public void put(){}}public class HashMap extend MMap{ public static void main(String[] args) throws 橘子seo查询NoSuchMethodException{ Method method = MMap.class.getMethod("put"); Annotation dengyi = method.getAnnotation(Dengyi.class); System.out.println(dengyi); //output: @com.jianglei3.bean.Dengyi(value=)}

注解实现的原理?设计到字节码知识,可以参考我的这篇文章:深入理解JVM虚拟机——java字节码技术及其命令剖析注解的应用场景1.log日志,特殊日志可以使用注解进行记录;环境搭建创建一个spring boo橘子seo查询t项目,并引入spring aop

项目中的pom.xml内容为 org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-web org.apache.commons commons-lang3 ${commons-lang3.version} com.baomidou mybatisplus-spring-boot-starter ${mybatisplus-spring-boot-starter.version} mysql mysql-connector-ja橘子seo查询va com.baomidou mybatis-plus ${mybatis-plus.version} com.baomidou mybatis-plus-generate ${mybatis-plus.version} com.alibaba druid-spring-boot-starter ${druid.version} com.alibaba fastjson ${fastjson.version} org.springframework.boot spring-boot-starter-test test

编写自定义日志注解在合适的包下创建自定义注解BussinessLog /**橘子seo查询 * 标记需要做业务日志的方法 * * @author earthchen * @date 2018/8/24 **/@Target({ElementType.PARAMETER, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface BussinessLog { /** * 日志类型 * * @return */ String type() default ""; /** * 业务的名称,例如:"修改菜单" */ String value() default "";}

如果还需要其橘子seo查询他的参数可以自定义其他方法编写日志逻辑创建一个日志切面import com.alibaba.fastjson.JSONObject;import com.earthchen.constant.BusinessStatus;import com.earthchen.domain.OperationLog;import com.earthchen.log.AsyncFactory;import com.earthchen.log.LogManager;import com.earthchen.log.annotation.BussinessLog;import com.earthchen.util橘子seo查询s.HttpUtil;import org.apache.commons.lang3.StringUtils;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.*;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.scheduling橘子seo查询.annotation.Async;import org.springframework.scheduling.annotation.EnableAsync;import org.springframework.stereotype.Component;import java.lang.reflect.Method;import java.util.Date;import java.util.Map;/** * 日志切面 * * @author earthchen * @date 2018/8/24 **/@Aspect@Component@EnableAsyncpublic class Lo橘子seo查询gAop { private Logger log = LoggerFactory.getLogger(this.getClass()); @Pointcut(value = "@annotation(com.earthchen.log.annotation.BussinessLog)") public void logPointCut() { } /** * 前置通知 用于拦截操作 * * @param joinPoint 切点 */ @AfterReturning(pointcut = "logPointCut()") public void doBefore(JoinPoint join橘子seo查询Point) { handleLog(joinPoint, null); } /** * 拦截异常操作 * * @param joinPoint * @param e */ @AfterThrowing(value = "logPointCut()", throwing = "e") public void doAfter(JoinPoint joinPoint, Exception e) { handleLog(joinPoint, e); } @Async protected void handleLog(final JoinPoint joinPoint, final Exception橘子seo查询 e) { try { // 获得注解 BussinessLog controllerLog = getAnnotationLog(joinPoint); if (controllerLog == null) { return; } // 获取当前的用户// User currentUser = ShiroUtils.getUser(); // *========数据库日志=========*// OperationLog operLog = new OperationLog(); operLog.setStatus(BusinessStatus.SUCCESS); operLog.setMe橘子seo查询ssage("操作成功"); operLog.setCreatetime(new Date()); // 请求的地址 operLog.setOperUrl(HttpUtil.getRequest().getRequestURI()); if (e != null) { operLog.setStatus(BusinessStatus.FAIL); operLog.setMessage(StringUtils.substring(e.getMessage(), 0, 2000)); } // 设置方法名称 String className = joinPoint.getTarget().getC橘子seo查询lass().getName(); String methodName = joinPoint.getSignature().getName(); operLog.setMethod(className + "." + methodName + "()"); // 处理设置注解上的参数 getControllerMethodDescription(controllerLog, operLog); // 保存数据库 LogManager.me().executeLog(AsyncFactory.bussinessLog(operLog)); } catch (Exception exp) { /橘子seo查询/ 记录本地异常日志 log.error("==前置通知异常=="); log.error("异常信息:{}", exp.getMessage()); exp.printStackTrace(); } } /** * 获取注解中对方法的描述信息 用于Controller层注解 * * @param log * @param operLog * @throws Exception */ public void getControllerMethodDescription(BussinessLog log, OperationLog operLog) throws Exception { // 设橘子seo查询置日志类型 operLog.setLogtype(log.type()); // 设置日志名字 operLog.setLogname(log.value()); // 获取参数的信息,传入到数据库中。

setRequestValue(operLog); } /** * 获取请求的参数,放到log中 * * @param operLog */ private void setRequestValue(OperationLog operLog) { Map map = HttpUtil.getRequest().getParameterMap(); String params = JSONObjec橘子seo查询t.toJSONString(map); operLog.setOperParams(params); } /** * 是否存在注解,如果存在就获取 * * @param joinPoint * @return * @throws Exception */ private BussinessLog getAnnotationLog(JoinPoint joinPoint) throws Exception { Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSign橘子seo查询ature) signature; Method method = methodSignature.getMethod(); if (method != null) { return method.getAnnotation(BussinessLog.class); } return null; }}。

这里定义的切点定义是所有被BussinessLog注解的方法上,如果有其他需求也可以自定义这里还是用了@EnableAsync和@Async注解,使其在打日志的时候是异步的由于异步交给线程池处理,在线程中不能直接获取spring中的bean,所以需要借助springUtil获取相关bean进行操橘子seo查询

import org.springframework.beans.BeansException;import org.springframework.beans.factory.NoSuchBeanDefinitionException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.橘子seo查询stereotype.Component;/** * spring工具类 方便在非spring管理环境中获取bean * * @author earthchen * @date 2018/8/24 **/@Componentpublic final class SpringUtils implements BeanFactoryPostProcessor { /** * Spring应用上下文环境 */ private static ConfigurableListableBeanFactory beanFactory; @Override public void postProcessBea橘子seo查询nFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { SpringUtils.beanFactory = beanFactory; } /** * 获取对象 * * @param name * @return Object 一个以所给名字注册的bean的实例 * @throws org.springframework.beans.BeansException */ @SuppressWarnings("unchecked") public static T getBean(String nam橘子seo查询e) throws BeansException { return (T) beanFactory.getBean(name); } /** * 获取类型为requiredType的对象 * * @param clz * @return * @throws org.springframework.beans.BeansException */ public static T getBean(Class clz) throws BeansException { T result = (T) beanFactory.getBean(clz); return result; } /** * 如果Be橘子seo查询anFactory包含一个与所给名称匹配的bean定义,则返回true * * @param name * @return boolean */ public static boolean containsBean(String name) { return beanFactory.containsBean(name); } /** * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。

如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) * * @param name * @return 橘子seo查询boolean * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException */ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { return beanFactory.isSingleton(name); } /** * @param name * @return Class 注册对象的类型 * @throws org.springframework.beans.factory.NoS橘子seo查询uchBeanDefinitionException */ public static Class getType(String name) throws NoSuchBeanDefinitionException { return beanFactory.getType(name); } /** * 如果给定的bean名字在bean定义中有别名,则返回这些别名 * * @param name * @return * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException */ public static 橘子seo查询String[] getAliases(String name) throws NoSuchBeanDefinitionException { return beanFactory.getAliases(name); }}。

编写controller进行测试import com.earthchen.log.annotation.BussinessLog;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController橘子seo查询;/** * @author earthchen * @date 2018/8/24 **/@RestControllerpublic class TestController { @BussinessLog(type = "操作",value = "进行测试") @RequestMapping("/test") public String testLog(){ return "test"; }}

运行项目,然后访问上述controller,然后查看控制台和数据库中相应的表是否有对应数2.注解的应用之监控方法执行耗时假如,我们需要监控某些方法的执行,最原始的办法就是在方法执行的开头和结尾分别记录橘子seo查询时间,最后计算前后的时间差即可,但是这些代码与核心业务无关,且大量重复、分散在各处,维护起来也困难。

这时我们可以使用Spring AOP来统计方法的执行耗时,同时我们也可以使用注解的方式来实现,更自由灵活首先,定义我们的执行耗时的方法上的注解:import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.橘子seo查询annotation.Target;/** * 自定义统计方法耗时并打印日志的注解. * * @author blinkfox on 2017-01-04. */@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})@Documentedpublic @interface CostTime { /** * 执行超过某毫秒数时数则打印warn级别的日志,默认 0ms,即默认都打印. * * @return 毫秒数 */ long value() default 0;}

然后,书写监控所标注有@CostTime注解的方法代理类橘子seo查询:import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 被标注为@CostTime注解的方法执行耗时的代理方法. *

实现了cglib中的`MethodInterceptor`的方法拦截接口.

* * @author blinkfox o橘子seo查询n 2017-01-04. */public class CostTimeProxy implements MethodInterceptor { private static final Logger log = LoggerFactory.getLogger(CostTimeProxy.class); private Enhancer enhancer = new Enhancer(); /** * 获取代理类. * * @param cls 代理类的class * @return 代理类实例 */ public Object getProxy(Class cls) { enhancer.橘子seo查询setSuperclass(cls); enhancer.setCallback(this); return enhancer.create(); } /** * 拦截方法,判断是否有@CostTime的注解,如果有则拦截执行. * * @param o 对象 * @param method 方法 * @param args 参数 * @param methodProxy 代理方法 * @return 对象 * @throws Throwable 问题 */ @Override public Object intercept(Object o, Method method, Object[] 橘子seo查询args, MethodProxy methodProxy) throws Throwable { // 判断该方法上是否有 CostTime 注解 if (!method.isAnnotationPresent(CostTime.class)) { return methodProxy.invokeSuper(o, args); } // 获取注解信息 CostTime costTime = method.getAnnotation(CostTime.class); long limitTime = costTime.value(); // 记录方法执行前后的耗时时间,并做差,判断是否需要打橘子seo查询印方法执行耗时 long startTime = System.currentTimeMillis(); Object result = methodProxy.invokeSuper(o, args); long diffTime = System.currentTimeMillis() - startTime; if (limitTime = limitTime)) { String methodName = method.getName(); // 打印耗时的信息 log.warn("【CostTime监控】通过注解监控方法{}的执行耗时为:{}", methodName, diffTi橘子seo查询me); } return result; }}

接着,可以写一些业务类及方法,这里就以A类为例:import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * A类. * * @author blinkfox on 2017/1/1. */public class A { private static final Logger log = LoggerFactory.getLogger(A.class); /** * 始终打印方法执行耗时的方法. */ @CostTime public void doSomeThing() { log.橘子seo查询info("执行A类中doSomeThing()方法!"); } /** * 当方法执行耗时大于等于50ms时打印出方法执行耗时. */ @CostTime(50) public void doSomeThing2() { log.info("执行A类中doSomeThing2()方法!"); }}

最后,是用来测试A类某些业务方法执行耗时的测试类:package com.blinkfox.test.reflect;/** * 耗时注解使用测试示例 * Created by blinkfox on 2017-01-04. */public class CostTimeTest { /** A类的橘子seo查询全局实例. */ private static A a; static { CostTimeProxy aproxy = new CostTimeProxy(); a = (A) aproxy.getProxy(A.class); } /** * main 方法. * * @param args 数组参数 */ public static void main(String[] args) { a.doSomeThing(); a.doSomeThing2(); }}

3.强一致业务场景下数据库查询强制走主库传统的数据库主从架构,主从同步是有一定的延迟的,在高并发强一致性业务场景下,这种延迟是不可橘子seo查询接受的,所以我们通过注解对一些重要的查询进行强制走主库的操作首先我们的数据库是分库分表的实现,中间件使用的是。

shardingsphere org.apache.shardingsphere sharding-jdbc-core ${sharding-sphere.version}

编写注解/** * @author: lekaijun * @date: 2021/5/24 * @description: */@Target(value = {ElementType.METHOD})@Retention(value = RetentionPolicy.RUNTIME)@Documentedpub橘子seo查询lic @interface MasterSelect {}

注解实现/** * 强制走主库拦截器 * * @author: * @description: */@Aspect@Componentpublic class MasterSelectAspect { @Value("${select.need.master:false}") private String needMaster; @Around("@annotation(com.ewt360.strategymarket.service.aop.MasterSelect)") public Object setMasterSelect橘子seo查询(ProceedingJoinPoint joinPoint) throws Throwable { //获取方法签名 MethodSignature methodSign = (MethodSignature) joinPoint.getSignature(); MasterSelect annotation = methodSign.getMethod().getAnnotation(MasterSelect.class); if (null == annotation || !"true".equals(needMaster)) { return joinPoint.proceed();橘子seo查询 } Object retVal = null; Throwable currentThrowable = null; try { if (!HintManager.isMasterRouteOnly()) { HintManager.getInstance().setMasterRouteOnly(); } retVal = joinPoint.proceed(); } catch (Throwable throwable) { currentThrowable = throwable; } finally { HintManager.clear(); if (currentThrowabl橘子seo查询e != null) { throw currentThrowable; } } return retVal; }}

使用实例@MasterSelect@Overridepublic List batchSubTaskInfo(BatchSubTaskInfoDTO batchSubTaskInfoDTO) { //业务代码}

举报/反馈

扫描二维码推送至手机访问。

版权声明:本文由海南拓宏网络科技工作室发布,如需转载请注明出处。

本文链接:http://www.4blc.com/post/16078.html

分享给朋友:

“java 注解的实现(java注解实现方式)” 的相关文章

修改录像机推荐小程序应该以数据反馈为基础(如何更改录像机通道名称)

今天给各位分享 原标题:EasyCVR更改录像存储路径,不生成录像文件如何解决?EasyCVR平台支持海量视频汇聚与管理,基于云边端一体化架构,具有强大的数据接入、处理及分发能力在功能上,平台可提供视频直播、云端录像、云存储、录像检索与回看、智能告警、平台级联、智能分析等。...

java培训班学校(学java的培训学校)

今天给各位分享 Java培训学校怎么选?如今,市面上的Java培训学校林林总总,让人眼花缭乱,想要选择一个最适合自己的学校并不容易,在选择Java培训学校时,应该注意哪些方面呢? Java培训学校怎么选?首先,应该了解清楚学校的培训方向和办学资质有着扎实的技术基础,并且有着...

java培训培训班(java的培训课程)

本篇文章给大家谈谈 原标题:Java培训机构的基础课程内容有哪些?对于零基础或者打算开始学习Java编程的同学,首先要去学习Java基础课程的内容,那么Java基础课程有哪些呢?1.Java概述,你要知道Java一些基本的知识,比如优点,什么时候诞生的,目前的发展状况如何。...

东方标准培训好吗安全吗(东方标准培训可不可以靠)

本篇文章给大家谈谈 原标题:东方标准|Web和Java的区别,如何选择这两个专业 现在网络发展迅速,很多人都开始选择计算机专业和软件工程专业,一开始都会选择Java作为他们的主修课程,但是在漫长的学习中有80%学生都放弃了Java选择web,那么对于没有学过计算机的朋友,小...

成都家博会时间(2022成都家博会地址在哪里)

今天给各位分享   现在市面上的培训机构很多,价格也是各不相同,主要是根据培训机构的教学质量和知名度决定的建议大家在选择Java培训机构,找一些口碑好的培训机构然后进行对比,这样很容易选择出靠谱的培训机构  成都Java培训的费用在1.5-2w之间,还要根据各个方面来选择合适的培训...

今年并购案例(2020年并购市场分析报告)

本篇文章给大家谈谈 炒股看京城投资家,关注我们收盘总结两市成交再上万亿!沪指涨2.06% 创业板大涨3.78%今日两市普涨,外资大幅回流,科技股、抗疫概念、券商等带头大涨,两市成交再上万亿;截至收盘,上证指数涨2.06%,深证成指涨3.12%,创业板指涨3.78%。 两市全...