精华内容
下载资源
问答
  • 注解仅仅是元数据,和业务逻辑无关,所以当你查看注解类时,发现里面没有任何逻辑处理; <string name="app_name">AnnotationDemo</string> 这里的"app_name"就是描述数据"AnnotationDemo"的数据,这...

    注解@annotation?

    1.什么是注解?

    用一个词就可以描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据。注解仅仅是元数据,和业务逻辑无关,所以当你查看注解类时,发现里面没有任何逻辑处理;

    <string name="app_name">AnnotationDemo</string>
    

    这里的"app_name"就是描述数据"AnnotationDemo"的数据,这是在配置文件中写的,注解是在源码中写的,如下所示:

    @Override
    public String toString() {
        return "This is String Representation of current object.";
    }
    

    上面的代码中,我重写了toString()方法并使用了@Override注解。但是,即使我不使用@Override注解标记代码,程序也能够正常执行。

    @Override告诉编译器这个方法是一个重写方法(描述方法的元数据),如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。

    javadoc中的@author、@version、@param、@return、@deprecated、@hide、@throws、@exception、@see是标记,并不是注解;

    2. 注解如何使用

    使用注解很简单,根据注解类的@Target所修饰的对象范围,可以在类、方法、变量、参数、包中使用“@+注解类名+[属性值]”的方式使用注解。比如:

    @UiThread
    private void setTextInOtherThread(@StringRes int resId){
        TextView threadTxtView = (TextView)MainActivity.this.findViewById(R.id.threadTxtViewId);
        threadTxtView.setText(resId);
    }
    

    3. 注解的作用

    1. 格式检查:告诉编译器信息,比如被@Override标记的方法如果不是父类的某个方法,IDE会报错;

    2. 减少配置:运行时动态处理,得到注解信息,实现代替配置文件的功能;

    3. 减少重复工作:比如第三方框架xUtils,通过注解@ViewInject减少对findViewById的调用,类似的还有(JUnit、ActiveAndroid等);

    4. 为什么要引入注解

    1. XML配置复杂
    2. 与代码紧耦合

    注释的优缺点:

    ​ 1)保存在 class 文件中,降低维护成本。

    ​ 2)无需工具支持,无需解析。

    ​ 3)编译期即可验证正确性,查错变得容易。

    ​ 4)提升开发效率。

    5)若要对配置项进行修改,不得不修改 Java 文件,重新编译打包应用。

    6)配置项编码在 Java 文件中,可扩展性差。

    xml文件的优缺点:

    ​ 1)xml作为可扩展标记语言最大的优势在于开发者能够为软件量身定制适用的标记,使代码更加通俗易懂。

    ​ 2)利用xml配置能使软件更具扩展性。例如Spring将class间的依赖配置在xml中,最大限度地提升应用的可扩展性。

    ​ 3)具有成熟的验证机制确保程序正确性。利用Schema或DTD可以对xml的正确性进行验证,避免了非法的配置导致应用程序出错。

    ​ 4) 修改配置而无需变动现有程序。

    5)需要解析工具或类库的支持。

    6)解析xml势必会影响应用程序性能,占用系统资源。

    7)配置文件过多导致管理变得困难。

    8)编译期无法对其配置项的正确性进行验证,或要查错只能在运行期。

    9)IDE无法验证配置项的正确性无能为力。

    10)查错变得困难。往往配置的一个手误导致莫名其妙的错误。

    11)开发人员不得不同时维护代码和配置文件,开发效率变得低下。

    12)配置项与代码间存在潜规则。改变了任何一方都有可能影响另外一方。

    但也不是说引入注解就是好的,xml管理依赖项的注入是至关可见的,所以xml和annotation混合是比较好的方案

    https://stackoverflow.com/questions/182393/xml-configuration-versus-annotation-based-configuration

    https://cloud.tencent.com/developer/article/1448189

    假如你想为应用设置很多的常量或参数,这种情况下,XML是一个很好的选择,因为它不会同特定的代码相连。如果你想把某个方法声明为服务,那么使用Annotation会更好一些,因为这种情况下需要注解和方法紧密耦合起来,开发人员也必须认识到这点。

    另一个很重要的因素是Annotation定义了一种标准的描述元数据的方式。在这之前,开发人员通常使用他们自己的方式定义元数据。例如,使用标记interfaces,注释,transient关键字等等。每个程序员按照自己的方式定义元数据,而不像Annotation这种标准的方式。

    5. 常见注解

    @Override

    属于标记注解,不需要设置属性值;只能添加在方法的前面,用于标记该方法是复写的父类中的某个方法,如果在父类没有的方法前面加上@Override注解,编译器会报错:

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Override {
    }
    

    @Deprecated

    属于标记注解,不需要设置属性值;可以对构造方法、变量、方法、包、参数标记,告知用户和编译器被标记的内容已不建议被使用,如果被使用,编译器会报警告,但不会报错,程序也能正常运行:

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE})
    public @interface Deprecated {
    }
    

    @SuppressWarnings

    可以对构造方法、变量、方法、包、参数标记,用于告知编译器忽略指定的警告,不用再编译完成后出现警告信息:

    @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface SuppressWarnings {
        String[] value();
    }
    

    @TargetApi

    可以对接口、方法、构造方法标记,如果在应用中指定minSdkVersion为8,但有地方需要使用API 11中的方法,为了避免编译器报错,在调用API11中方法的接口、方法或者构造方法前面加上@Target(11),这样该方法就可以使用<=11的API接口了。虽然这样能够避免编译器报错,但在运行时需要注意,不能在API低于11的设备中使用该方法,否则会crash(可以获取程序运行设备的API版本来判断是否调用该方法):

    @Target({TYPE, METHOD, CONSTRUCTOR})
    @Retention(RetentionPolicy.CLASS)
    public @interface TargetApi {
        /**
         * This sets the target api level for the type..
         */
        int value();
    }
    

    @SuppressLint

    和@Target的功能差不多,但使用范围更广,主要用于避免在lint检查时报错:

    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    @Retention(RetentionPolicy.CLASS)
    public @interface SuppressLint {
        /**
         * The set of warnings (identified by the lint issue id) that should be
         * ignored by lint. It is not an error to specify an unrecognized name.
         */
        String[] value();
    }
    

    Android也有一些注解@UiThread、@MainThread、@WorkerThread、@BinderThread…

    6. 注解是如何实现的

    注解只是个标记,里面的属性就是键值对,通过反射可以获取值,然后根据值进行各种处理

    Java的annotation没有行为,只能有数据,实际上就是一组键值对而已。通过解析(parse)Class文件就能把一个annotation需要的键值对都找出来。

    • 有一个接口
    • 有一组键值对,它里面的数组能支持前面那个接口的功能

    注解是通过sun.reflect.annotation.AnnotationParser.annotationForMap()方法通过JDK的动态代理完成实例化的。

    public class AnnotationParser {
    
        public static Annotation annotationForMap(Class<? extends Annotation> type, Map<String, Object> memberValues) {
            return (Annotation) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new AnnotationInvocationHandler(type, memberValues));
        }
    }
    

    这个AnnotationInvocationHandler是一个实现了InvocationHandler的类,所以很明显这个其实就是动态代理了。
    嗯,注解本质是个接口,然后运行期间获取到的到底是什么,就是个接口的代理对象。然后说 @xxx 就能自动运行某个方法之类的具体实现,其实这类东西都依赖于一个容器或者工厂的东西

    class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    
        private static final long serialVersionUID = 6182022883658399397L;
    
        // 注解class
        private final Class<? extends Annotation> type;
        // 成员方法返回值集合
        private final Map<String, Object> memberValues;
    
        AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
            Class[] interfaces = type.getInterfaces();
            // 注解校验
            if (type.isAnnotation() && interfaces.length == 1 && interfaces[0] == Annotation.class) {
                this.type = type;
                this.memberValues = memberValues;
            } else {
                throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
            }
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 方法名
            String member = method.getName();
            // 方法参数
            Class<?>[] paramTypes = method.getParameterTypes();
            // equals()方法
            if (member.equals("equals") && paramTypes.length == 1 && paramTypes[0] == Object.class) {
                return equalsImpl(args[0]);
            }
            // 除equals()外, 注解的其它方法都不带参数
            if (paramTypes.length != 0) {
                throw new AssertionError("Too many parameters for an annotation method");
            }
            // toString()方法
            if (member.equals("toString")) {
                return toStringImpl();
            }
            // hashCode()方法
            if (member.equals("hashCode")) {
                return hashCodeImpl();
            }
            // annotationType()方法
            if (member.equals("annotationType")) {
                return type;
            }
            // 以下是注解自定义的方法
            // 注解方法的返回值
            Object result = memberValues.get(member);
            // 注解方法的返回值为null, 抛出异常
            if (result == null) {
                throw new IncompleteAnnotationException(type, member);
            }
            // 结果异常
            if (result instanceof ExceptionProxy) {
                throw ((ExceptionProxy) result).generateException();
            }
            // 克隆并返回数组结果
            if (result.getClass().isArray() && Array.getLength(result) != 0) {
                result = cloneArray(result);
            }
            // 返回结果
            return result;
        }
    
    }
    

    7. Java8新增类型注解

    在 Java8 之前的版本中,只能允许在声明式前使用注解。而在 Java8 版本中,注解可以被用在任何使用 Type 的地方,例如:初始化对象时 (new),对象类型转化时,使用 implements 表达式时,或者使用 throws 表达式时。

    //初始化对象时
    String myString = new @NotNull String();
    
    //对象类型转化时
    myString = (@NonNull String) str;
    
    //使用 implements 表达式时
    class MyList<T> implements @ReadOnly List<@ReadOnly T>{
        ...
    }
     //使用 throws 表达式时
    public void validateValues() throws @Critical ValidationFailedException{
        ...
    }
    

    定义一个类型的方法与普通的注解类似,只需要指定TargetElementType.TYPE_PARAMETER或者ElementType.TYPE_USE,或者同时指定这两个Target

    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
    public  @interface MyAnnotation {
        ...
    }
    

    Java8 通过引入类型,使得开发者可以在更多的地方使用注解,从而能够更全面地对代码进行分析以及进行更强的类型检查

    8. 四种元注解

    注解类会被@interface标记;

    注解类的顶部会被@Documented@Retention@Target@Inherited这四个注解标记(@Documented@Inherited可选,@Retention@Target必须要有)

    @Target:

    **作用:**用于描述注解的使用范围,即被描述的注解可以用在什么地方;

    取值:

    ​ 1)CONSTRUCTOR:构造器;

    ​ 2)FIELD:实例;

    ​ 3)LOCAL_VARIABLE:局部变量;

    ​ 4)METHOD:方法;

    ​ 5)PACKAGE:包;

    ​ 6)PARAMETER:参数;

    ​ 7)TYPE:类、接口(包括注解类型) 或enum声明。

    /**
     * 实体注解接口
     */
    @Target(value = {ElementType.TYPE})
    @Retention(value = RetentionPolicy.RUNTIME)
    public @interface Entity {
        /***
         * 实体默认firstLevelCache属性为false
         * @return boolean
         */
        boolean firstLevelCache() default false;
        /***
         * 实体默认secondLevelCache属性为false
         * @return boolean
         */
        boolean secondLevelCache() default true;
        /***
         * 表名默认为空
         * @return String
         */
        String tableName() default "";
        /***
         * 默认以""分割注解
         */
        String split() default "";
    }
    

    @Retention:

    **作用:**表示需要在什么级别保存该注解信息,用于描述注解的生命周期,即被描述的注解在什么范围内有效;

    取值:

    ​ 1)SOURCE:在源文件中有效,即源文件保留;

    ​ 2)CLASS:在class文件中有效,即class保留;

    ​ 3)RUNTIME:在运行时有效,即运行时保留;

    /***
     * 字段注解接口
     */
    @Target(value = {ElementType.FIELD})//注解可以被添加在实例上
    @Retention(value = RetentionPolicy.RUNTIME)//注解保存在JVM运行时刻,能够在运行时刻通过反射API来获取到注解的信息
    public @interface Column {
        String name();//注解的name属性
    }
    

    @Documented:

    **作用:**用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。

    **取值:**它属于标记注解,没有成员;

    @Documented
    @Retention(CLASS)
    @Target({METHOD,CONSTRUCTOR,TYPE})
    public @interface UiThread {
    }
    

    @Inherited:

    **作用:**用于描述某个被标注的类型是可被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

    **取值:**它属于标记注解,没有成员;

    示例:

    @Inherited  
    public @interface Greeting {  
        public enum FontColor{ BULE,RED,GREEN};  
        String name();  
        FontColor fontColor() default FontColor.GREEN;  
    } 
    

    9. 如何编写自定义注解

    元注解
    public @interface 注解名{
        定义体;
    }
    

    **ToDo.java:**注解类

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Todo {
        public enum Priority {LOW, MEDIUM, HIGH}
        public enum Status {STARTED, NOT_STARTED}    
        String author() default "Yash";
        Priority priority() default Priority.LOW;
        Status status() default Status.NOT_STARTED;
    }
    

    **BusinessLogic:**使用注解的类

    public class BusinessLogic {
        public BusinessLogic() {
            super();
        }
        
        public void compltedMethod() {
            System.out.println("This method is complete");
        }    
        
        @Todo(priority = Todo.Priority.HIGH)
        public void notYetStartedMethod() {
            // No Code Written yet
        }
        
        @Todo(priority = Todo.Priority.MEDIUM, author = "Uday", status = Todo.Status.STARTED)
        public void incompleteMethod1() {
            //Some business logic is written
            //But its not complete yet
        }
    
        @Todo(priority = Todo.Priority.LOW, status = Todo.Status.STARTED )
        public void incompleteMethod2() {
            //Some business logic is written
            //But its not complete yet
        }
    }
    

    **TodoReport.java:**解析注解信息

    public class TodoReport {
        public TodoReport() {
            super();
        }
    
        public static void main(String[] args) {
            getTodoReportForBusinessLogic();
        }
    
        /**
         * 解析使用注解的类,获取通过注解设置的属性
         */
        private static void getTodoReportForBusinessLogic() {
            Class businessLogicClass = BusinessLogic.class;
            for(Method method : businessLogicClass.getMethods()) {
                Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);
                if(todoAnnotation != null) {
                    System.out.println(" Method Name : " + method.getName());
                    System.out.println(" Author : " + todoAnnotation.author());
                    System.out.println(" Priority : " + todoAnnotation.priority());
                    System.out.println(" Status : " + todoAnnotation.status());
                    System.out.println(" --------------------------- ");
                }
            }
        }
    }
    

    10. 注解的应用

    10.1 SpringBoot组合注解
    @Api(tags = "自定义组合注解", description = "组合注解优化代码")
    @StandardResult
    @RequestMapping("/Ccww")
    @Controller
    @ResponseBody
    public class CombinationController{
    }
    

    @SpringBootApplication

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = {
    		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
    
    	@AliasFor(annotation = EnableAutoConfiguration.class)
    	Class<?>[] exclude() default {};
    	
    
    	@AliasFor(annotation = EnableAutoConfiguration.class)
    	String[] excludeName() default {};
    	
    
    	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    	String[] scanBasePackages() default {};
    	
    	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    	Class<?>[] scanBasePackageClasses() default {};
    
    }
    

    声明@Inherited注解,声明了它的类的子类是可以继承它的

    声明@SpringBootConfiguration注解,可为类标注为配置类

    声明@EnableAutoConfiguration注解,声明了它的类会默认开启自动配置

    声明@ComponentScan注解,同时是@ComponentScan注解的容器。我们发现scanBasePackagesscanBasePackageClasses两个注解属性上面同样声明了@AliasFor注解,分别指向了@ComponentScan注解的basePackages注解属性和basePackageClasses属性。

    声明了exclude()为排除特定的自动配置类以及excludeName()排除特定的自动配置类名称

    最后,@AliasFor的作用是什么呢?

    • 用到注解 属性上,表示两个属性互相为别名,互相为别名的属性值必须相同,若设置成不同,则会报错
    • 注解是可以继承的,但是注解是不能继承父注解的属性的,也就是说,我在类扫描的时候,拿到的注解的属性值,依然是父注解的属性值,而不是你定义的注解的属性值,所以此时可以在子注解对应的属性上加上@AliasFor
    package com.timwang.annotation;
    
    import io.swagger.annotations.Api;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.lang.annotation.*;
    
    /**
     * @author wangjun
     * @date 2019-12-13
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Controller
    @ResponseBody
    @RequestMapping
    @Api
    public @interface StandardResultRestControllerApi {
        /**
         * 定义映射路径URL
         */
        @AliasFor(annotation = RequestMapping.class, value = "path")
        String[] value() default {};
    
        /**
         *定义spring类名称
         */
        @AliasFor(annotation = Controller.class, value = "value")
        String name() default "";
        /**
         *定义Api类tags属性
         */
        @AliasFor(annotation = Api.class, attribute = "tags")
        String[] tags() default "";
    
        /**
         *定义Api类description属性
         */
        @AliasFor(annotation = Api.class, attribute = "description")
        String description() default "";
    }
    
    10.2 监控方法执行耗时

    假如,我们需要监控某些方法的执行,最原始的办法就是在方法执行的开头和结尾分别记录时间,最后计算前后的时间差即可,但是这些代码与核心业务无关,且大量重复、分散在各处,维护起来也困难。这时我们可以使用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.annotation.Target;
    
    /**
     * 自定义'统计方法耗时'并打印日志的注解.
     * @author tim.wang 
     * @date 2019-12-13
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    @Documented
    public @interface CostTime {
    
        /**
         * 执行超过某毫秒数时数则打印'warn'级别的日志,默认 0ms,即默认都打印.
         * @return 毫秒数
         */
        long value() default 0;
    
    }
    

    然后,书写监控所标注有@CostTime注解的方法代理类:

    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'注解的方法执行耗时的代理方法.
     * <p>实现了cglib中的`MethodInterceptor`的方法拦截接口.</p>
     */
    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.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[] 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();
    
            // 记录方法执行前后的耗时时间,并做差,判断是否需要打印方法执行耗时
            long startTime = System.currentTimeMillis();
            Object result = methodProxy.invokeSuper(o, args);
            long diffTime = System.currentTimeMillis() - startTime;
            if (limitTime <= 0 || (diffTime >= limitTime)) {
                String methodName = method.getName();
                // 打印耗时的信息
                log.warn("【CostTime监控】通过注解监控方法'{}'的执行耗时为:{}", methodName, diffTime);
            }
            return result;
        }
    
    }
    

    接着,可以写一些业务类及方法,这里就以A类为例:

    package com.timwang.annotation;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * @author wangjun
     * @date 2019-12-13
     */
    public class CostTimeClass {
        private static final Logger log = LoggerFactory.getLogger(CostTimeClass.class);
    
        /**
         * 始终打印方法执行耗时的方法.
         */
        @CostTime
        public void doSomeThing() {
            log.info("执行A类中doSomeThing()方法!");
        }
    
        /**
         * 当方法执行耗时大于等于'50ms'时打印出方法执行耗时.
         */
        @CostTime(50)
        public void doSomeThing2() {
            log.info("执行A类中doSomeThing2()方法!");
        }
    
    }
    
    

    最后,是用来测试CostTimeTest类某些业务方法执行耗时的测试类:

    package com.timwang.annotation;
    
    /**
     * @author wangjun
     * @date 2019-12-13
     */
    public class CostTimeTest {
        /** A类的全局实例. */
        private static CostTimeClass a;
    
        static {
            CostTimeProxy aproxy = new CostTimeProxy();
            a = (CostTimeClass) aproxy.getProxy(CostTimeClass.class);
        }
    
        /**
         * main 方法.
         *
         * @param args 数组参数
         */
        public static void main(String[] args) {
            a.doSomeThing();
            a.doSomeThing2();
        }
    }
    
    
    10.3 APT工具类
    /**是否存在对应 Annotation 对象*/
     public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
           return GenericDeclaration.super.isAnnotationPresent(annotationClass);
       }
     
    /**获取 Annotation 对象*/
       public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
           Objects.requireNonNull(annotationClass);
    
           return (A) annotationData().annotations.get(annotationClass);
       }
    /**获取所有 Annotation 对象数组*/   
    public Annotation[] getAnnotations() {
           return AnnotationParser.toArray(annotationData().annotations);
       }
    
    public class test {
       public static void main(String[] args) throws NoSuchMethodException {
    
            /**
             * 获取类注解属性
             */
            Class<Father> fatherClass = Father.class;
            boolean annotationPresent = fatherClass.isAnnotationPresent(MyTestAnnotation.class);
            if(annotationPresent){
                MyTestAnnotation annotation = fatherClass.getAnnotation(MyTestAnnotation.class);
                System.out.println(annotation.name());
                System.out.println(annotation.age());
            }
    
            /**
             * 获取方法注解属性
             */
            try {
                Field age = fatherClass.getDeclaredField("age");
                boolean annotationPresent1 = age.isAnnotationPresent(Age.class);
                if(annotationPresent1){
                    Age annotation = age.getAnnotation(Age.class);
                    System.out.println(annotation.value());
                }
    
                Method play = PlayGame.class.getDeclaredMethod("play");
                if (play!=null){
                    People annotation2 = play.getAnnotation(People.class);
                    Game[] value = annotation2.value();
                    for (Game game : value) {
                        System.out.println(game.value());
                    }
                }
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    }
    

    处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)。processAnnotationMoney方法就可以理解为APT工具类。

    /**定义限额注解*/
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface BankTransferMoney {
        double maxMoney() default 10000;
    }
    /**转账处理业务类*/
    public class BankService {
        /**
         * @param money 转账金额
         */
        @BankTransferMoney(maxMoney = 15000)
        public static void TransferMoney(double money){
            System.out.println(processAnnotationMoney(money));
    
        }
        private static String processAnnotationMoney(double money) {
            try {
                Method transferMoney = BankService.class.getDeclaredMethod("TransferMoney",double.class);
                boolean annotationPresent = transferMoney.isAnnotationPresent(BankTransferMoney.class);
                if(annotationPresent){
                    BankTransferMoney annotation = transferMoney.getAnnotation(BankTransferMoney.class);
                    double l = annotation.maxMoney();
                    if(money>l){
                       return "转账金额大于限额,转账失败";
                    }else {
                        return"转账金额为:"+money+",转账成功";
                    }
                }
            } catch ( NoSuchMethodException e) {
                e.printStackTrace();
            }
            return "转账处理失败";
        }
        public static void main(String[] args){
            TransferMoney(10000);
        }
    }
    
    
    10.4 框架层面的统一处理

    比如我们有的接口需要认证才能调用,有的不需要,简单的做法就是用配置的方式,将需要认证的接口配置好,然后进行拦截过滤,缺点是需要经常维护配置信息,用注解可以避免这个情况。 可以自定义一个注解,只要加了这个注解我们就对这个接口进行认证拦截操作,接下里详细的讲解下这个功能实现。

    定义开启认证的注解,作用在方法上,运行时可获取注解信息

    /**
     * 开启API权限认证
     * @author yinjihuan
     *
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface EnableAuth {
    }
    

    在需要认证的接口上增加注解

    @EnableAuth
    @GetMapping("/userCollectCityInfo")
    public Response getUserCollectCityInfos(HttpServletRequest request) {
        //..
    }
    

    在拦截器中进行拦截,拦截需要知道当前请求的接口是不是需要拦截的,我们可以在启动时将所有增加了@EnableAuth的接口信息保存起来,这样在拦截器中就知道哪个接口是需要认证。

    初始化需要认证的接口信息代码如下:

    package com.timwang.annotation;
    
    import com.google.common.collect.Lists;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import java.lang.reflect.Method;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author wangjun
     * @date 2019-12-13
     */
    @Component
    @Configuration
    public class ApiAuthDataInit implements ApplicationContextAware {
        public static List<String> checkApis = Lists.newArrayList();
        @Override
        public void setApplicationContext(ApplicationContext ctx) throws BeansException {
            Map<String, Object> beanMap = ctx.getBeansWithAnnotation(Controller.class);
            if (beanMap != null) {
                for (Object bean : beanMap.values()) {
                    Class<?> clz = bean.getClass();
                    Method[] methods = clz.getMethods();
                    for (Method method : methods) {
                        if (method.isAnnotationPresent(EnableAuth.class)) {
                            String uri = getApiUri(clz, method);
                            checkApis.add(uri);
                        }
                    }
                }
            }
        }
        private String getApiUri(Class<?> clz, Method method) {
            StringBuilder uri = new StringBuilder();
            uri.append(clz.getAnnotation(RequestMapping.class).value()[0]);
            if (method.isAnnotationPresent(RequestMapping.class)) {
                uri.append(method.getAnnotation(RequestMapping.class).value()[0]);
            }
            return uri.toString();
        }
    }
    

    实现ApplicationContextAware接口,然后通过getBeansWithAnnotation获取所有接口的bean信息,通过RestController注解来获取,也就是说只要class上增加了RestController注解,这边就都能获取到。

    然后通过反射获取bean中所有的方法,如果有增加EnableAuth的话就获取接口的uri存储到map中,这样过滤器中就可以根据map中的值来判断是不是需要进行权限认证了。

    http://www.importnew.com/15246.html【】

    http://www.yq1012.com/myweb/2230.html【注解是什么,为什么要使用注解】

    https://www.idlebrains.org/tutorials/java-tutorials/how-annotations-work-java/【HOW ANNOTATIONS WORK IN JAVA ?】

    https://cloud.tencent.com/developer/article/1448189【Java中的注解到底是如何工作的?】

    https://juejin.im/post/5a619f886fb9a01c9f5b7e4f【Java中的注解-自定义注解】

    https://juejin.im/entry/585fe4e61b69e600562147fa【从头到尾带你玩转注解】

    https://blog.csdn.net/lengxingxing_/article/details/65441337【java annotation(注解) 的优点缺点】

    https://www.jianshu.com/p/5cac4cb9be54【深入浅出Java注解】

    https://blinkfox.github.io/2018/11/08/hou-duan/java/java-zhu-jie-de-li-jie-he-ying-yong/【Java注解的理解和应用】

    https://www.cnblogs.com/deng-cc/p/7462577.html【注解的基本认识和元注解】

    https://www.zhihu.com/question/47449512/answer/106034220【怎样理解 Java 注解和运用注解编程?】

    https://www.race604.com/annotation-processing/【Java注解处理器】

    https://zhuanlan.zhihu.com/p/59337421【如何解决代码中if…else 过多的问题】

    https://www.maoqitian.com/2019/03/25/java-annotation/【Java注解完全解析回顾】

    https://github.com/maoqitian/JavaDemo/tree/master/annotationdemo【Java注解完全解析回顾-demo】

    https://zhuanlan.zhihu.com/p/60966151【注解(下)】

    https://cloud.tencent.com/developer/article/1101069【注解是什么?

    https://www.jianshu.com/p/869ed7037833【Spring中的@AliasFor标签】

    https://zhuanlan.zhihu.com/p/21410338【深入浅出Java注解】

    展开全文
  • JAVA上百实例源码以及开源项目

    千次下载 热门讨论 2016-01-03 17:37:40
     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...
  • Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥 Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、...
  • 源码分析 :ArrayList 源码+扩容机制分析 、LinkedList 源码 、HashMap(JDK1.8)源码+底层数据结构分析 、ConcurrentHashMap 源码+底层数据结构分析 并发 知识点/面试题: (必看 ) Java 并发基础常见面试题总结 ...
  • 源码分析 :ArrayList 源码+扩容机制分析 、LinkedList 源码 、HashMap(JDK1.8)源码+底层数据结构分析 、ConcurrentHashMap 源码+底层数据结构分析 并发 知识点/面试题: (必看 ) Java 并发基础常见面试题总结 ...
  • 如何阅读Android源码

    2020-12-28 15:28:01
    Java注解Java反射,这将列入虚拟机篇</li><li>Android对于Java集合的优化算法,这将列入Java基础篇</li></ul> 二、阅读源码的顺序和方式 <h4>2.1 阅读顺序 读源码是一个日积月累的过程,...
  • 集合源码分析 Noah-Java最佳实践与踩坑 查看详细文章,可以参考我的博客 代码篇 01 使用了并发工具类库,线程安全就高枕无忧了吗?: 02 代码加锁:不要让“锁”事成为烦心事: 03 线程池:业务代码最常用也最容易...
  • Erupt : 使用 Java 注解,快速开发 Admin 管理后台。零前端代码、零 CURD、不生成任何代码、自动建表、注解式 API,支持所有主流数据库,支持自定义页面,支持多数据源,提供二十几类业务组件,十几种展示形式,支持...
  • 替换 application.yml中 wstoken 的值(如何获取我的 token) 然后运行ChatbotApplication类main方法. 编写plugin 有两种plugin类型 ​ GroupBotEvent:群内非消息事件 非人为发送的消息:机器人入群,机器人退群,...
  • Spring mvc的注解如何工作的

    千次阅读 2017-11-19 23:43:41
    从前年开始使用spring和hibernate,mybatis等框架时,就转到注解来了。直到前些时,突然对注解开始...在IDE中,我们可以链接spring mvc中的@RequestMapping注解,发现以下源码[java] view plain copy print?@Target

    转自:http://blog.csdn.net/fly_sky520/article/details/21522903

    从前年开始使用spring和hibernate,mybatis等框架时,就转到注解来了。直到前些时,突然对注解开始好奇起来。为什么写注解就可以了?不需要大量配置文件呢?于是我查看了一些资料,对注解有了初步了解。

    引言:什么是注解?

    在IDE中,我们可以链接spring mvc中的@RequestMapping注解,发现以下源码

    1. @Target(value = {ElementType.METHOD, ElementType.TYPE})  
    2. @Retention(value = RetentionPolicy.RUNTIME)  
    3. @Documented  
    4. @Mapping  
    5. public @interface RequestMapping {  
    6.   
    7.     public String[] value() default {};  
    8.   
    9.     public RequestMethod[] method() default {};  
    10.   
    11.     public String[] params() default {};  
    12.   
    13.     public String[] headers() default {};  
    14.   
    15.     public String[] consumes() default {};  
    16.   
    17.     public String[] produces() default {};  
    18. }  
    @Target(value = {ElementType.METHOD, ElementType.TYPE})
    @Retention(value = RetentionPolicy.RUNTIME)
    @Documented
    @Mapping
    public @interface RequestMapping {
    
        public String[] value() default {};
    
        public RequestMethod[] method() default {};
    
        public String[] params() default {};
    
        public String[] headers() default {};
    
        public String[] consumes() default {};
    
        public String[] produces() default {};
    }
    这其实就是注解的写法。从这里我们可以发现,注解的写法比较简单,只要在intface前面加上@,就可以定义一个注解。但有几个其他的注解我们还不是很明白,同样spring是怎么通过这个注解进行运转的呢?

    首先:注解的作用是什么?

    1》生成文档,比如我们用的ide里面会自动加上比如@param,@return,@author等注解。

    2》编译时格式检查。这个最常见的是@override,@SuppressWarnings等等。

    3》跟踪代码依赖性,实现替代配置文件功能。上面的源码例子其实就是这个作用。

    其次:元注解

    在包 java.lang.annotation 中包含所有定义【自定义注解】所需用到的原注解和接口。如接口 java.lang.annotation.Annotation 是所有注解继承的接口,并且是自动继承,不需要定义时指定,类似于所有类都自动继承Object。查看Documented.class,可以看到这是个借口。它有三个注解(@Documented,@Retention,@Target),除此外,还有@Inherited,构成4个元注解。

    @Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。

    在doc文档中的内容会因为此注解的信息内容不同而不同。相当与@see,@param 等。

    @Retention 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括: 
              RetentionPolicy.SOURCE 注解将被编译器丢弃 
              RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃 
              RetentionPolicy.RUNTIME VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。

    @Target 表示该注解用于什么地方,可能的值在枚举类 ElemenetType 中,包括: 
              ElemenetType.CONSTRUCTOR 构造器声明 
              ElemenetType.FIELD 域声明(包括 enum 实例) 
              ElemenetType.LOCAL_VARIABLE 局部变量声明
              ElemenetType.ANNOTATION_TYPE 作用于注解量声明
              ElemenetType.METHOD 方法声明
              ElemenetType.PACKAGE 包声明 
              ElemenetType.PARAMETER 参数声明 
              ElemenetType.TYPE 类,接口(包括注解类型)或enum声明 

    @Inherited 允许子类继承父类中的注解。

    然后:我们来自己编写注解

    1. /** 
    2.  * 自定义注解 
    3.  * @author Fly 
    4.  */  
    5. @Documented  
    6. @Target({ElementType.METHOD, ElementType.TYPE})  
    7. @Retention(RetentionPolicy.RUNTIME)  
    8. public @interface AnnotationTest {  
    9.   
    10.     public String name() default “”;  
    11.   
    12.     public String sex() default “男”;  
    13. }  
    /**
     * 自定义注解
     * @author Fly
     */
    @Documented
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AnnotationTest {
    
        public String name() default "";
    
        public String sex() default "男";
    }
    1. /** 
    2.  * 注解测试 
    3.  * 
    4.  * @author Fly 
    5.  */  
    6. @AnnotationTest(sex = “男”, name = “张飞”)  
    7. public class MyAnnotationTest {  
    8.   
    9.   
    10.     @AnnotationTest(sex = “男”, name = “Fly”)  
    11.     public void setFly() {  
    12.     }  
    13.   
    14.   
    15.     @AnnotationTest(sex = “女”, name = “李明”)  
    16.     public void setLiMing() {  
    17.     }  
    18.   
    19.   
    20.     public static void main(String[] args) {  
    21.         //检查类MyAnnotationTest是否含有@AnnotationTest注解  
    22.         if (MyAnnotationTest.class.isAnnotationPresent(AnnotationTest.class)) {  
    23.             //若存在就获取注解  
    24.             AnnotationTest annotation = (AnnotationTest) MyAnnotationTest.class.getAnnotation(AnnotationTest.class);  
    25.             System.out.println(annotation);  
    26.             //获取注解属性  
    27.             System.out.println(annotation.sex());  
    28.             System.out.println(annotation.name());  
    29.             System.out.println(”///”);  
    30.             Method[] _methods = MyAnnotationTest.class.getDeclaredMethods();  
    31.             for (Method method : _methods) {  
    32.                 System.out.println(method);  
    33.                 if (method.isAnnotationPresent(AnnotationTest.class)) {  
    34.                     AnnotationTest test = method.getAnnotation(AnnotationTest.class);  
    35.                     System.out.println(”AnnotationTest(method=” + method.getName() + “,name=” + test.name() + “,sex=” + test.sex() + “)”);  
    36.   
    37.   
    38.                 }  
    39.             }  
    40.         }  
    41.     }  
    42. }  
    /**
     * 注解测试
     *
     * @author Fly
     */
    @AnnotationTest(sex = "男", name = "张飞")
    public class MyAnnotationTest {
    
    
        @AnnotationTest(sex = "男", name = "Fly")
        public void setFly() {
        }
    
    
        @AnnotationTest(sex = "女", name = "李明")
        public void setLiMing() {
        }
    
    
        public static void main(String[] args) {
            //检查类MyAnnotationTest是否含有@AnnotationTest注解
            if (MyAnnotationTest.class.isAnnotationPresent(AnnotationTest.class)) {
                //若存在就获取注解
                AnnotationTest annotation = (AnnotationTest) MyAnnotationTest.class.getAnnotation(AnnotationTest.class);
                System.out.println(annotation);
                //获取注解属性
                System.out.println(annotation.sex());
                System.out.println(annotation.name());
                System.out.println("///");
                Method[] _methods = MyAnnotationTest.class.getDeclaredMethods();
                for (Method method : _methods) {
                    System.out.println(method);
                    if (method.isAnnotationPresent(AnnotationTest.class)) {
                        AnnotationTest test = method.getAnnotation(AnnotationTest.class);
                        System.out.println("AnnotationTest(method=" + method.getName() + ",name=" + test.name() + ",sex=" + test.sex() + ")");
    
    
                    }
                }
            }
        }
    }
    测试结果如下:

    @test.AnnotationTest(sex=男, name=张飞)

    张飞
    ///
    public static void test.MyAnnotationTest.main(java.lang.String[])
    public void test.MyAnnotationTest.setLiMing()
    AnnotationTest(method=setLiMing,name=李明,sex=女)
    public void test.MyAnnotationTest.setFly()
    AnnotationTest(method=setFly,name=Fly,sex=男)

    到这里,我们对注解的基本有点了解了,注解的运用其实与反射式分不开的。我们可以利用代码中的注解间接控制程序代码的运行,它们通过Java反射机制读取注解的信息,并根据这些信息更改目标程序的逻辑。但是我们怎么使用注解呢?怎么让注解发挥作用,例如spring等框架时如何应用注解的呢?

    然后:注解理解的深入
    我们结合spring的控制反转和依赖注入来继续说明这个问题。

    看下面的代码,首先是一个IUser接口,包含一个login方法。然后又一个中文登录方法和英文登录方法都实现了Iuser接口。

    1. public interface IUser {  
    2.   
    3.     public void login();  
    4. }  
    public interface IUser {
    
        public void login();
    }
    
    1. public class ChineseUserImpl implements IUser {  
    2.     @Override  
    3.     public void login() {  
    4.         System.err.println(”用户登录!”);  
    5.     }  
    6. }  
    public class ChineseUserImpl implements IUser {
        @Override
        public void login() {
            System.err.println("用户登录!");
        }
    }
    1. public class EnglishUserImpl implements IUser {  
    2.     @Override  
    3.     public void login() {  
    4.         System.err.println(”User Login!”);  
    5.     }  
    6. }  
    public class EnglishUserImpl implements IUser {
        @Override
        public void login() {
            System.err.println("User Login!");
        }
    }
    然后有一个Test类,要注入IUser接口

    1. @AnnotationTest  
    2. public class Test {  
    3.   
    4.     private IUser userdao;  
    5.   
    6.     public IUser getUserdao() {  
    7.         return userdao;  
    8.     }  
    9.   
    10.     @AnnotationTest(nation = “ChineseUserImpl”)  
    11.     public void setUserdao(IUser userdao) {  
    12.         this.userdao = userdao;  
    13.     }  
    14.   
    15.     public void loginTest() {  
    16.         userdao.login();  
    17.     }  
    18. }  
    @AnnotationTest
    public class Test {
    
        private IUser userdao;
    
        public IUser getUserdao() {
            return userdao;
        }
    
        @AnnotationTest(nation = "ChineseUserImpl")
        public void setUserdao(IUser userdao) {
            this.userdao = userdao;
        }
    
        public void loginTest() {
            userdao.login();
        }
    }
    我们实现的是setter注入方式。为了配合这个例子,我把@AnnotationTest也稍作修改。

    1. @Documented  
    2. @Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})  
    3. @Retention(RetentionPolicy.RUNTIME)  
    4. public @interface AnnotationTest {  
    5.       
    6.     public String nation() default “”;  
    7. }  
    @Documented
    @Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AnnotationTest {
    
        public String nation() default "";
    }
    然后再引入一个类Container,类似spring容器的作用

    1. public class Container {  
    2.   
    3.     public static Test getBean() {  
    4.         Test test = new Test();  
    5.         if (Test.class.isAnnotationPresent(AnnotationTest.class)) {  
    6.             Method[] methods = Test.class.getDeclaredMethods();  
    7.             for (Method method : methods) {  
    8.                 System.out.println(method);  
    9.                 if (method.isAnnotationPresent(AnnotationTest.class)) {  
    10.                     AnnotationTest annotest = method.getAnnotation(AnnotationTest.class);  
    11.                     System.out.println(”AnnotationTest(field=” + method.getName()  
    12.                             + ”,nation=” + annotest.nation() + “)”);  
    13.                     IUser userdao;  
    14.                     try {  
    15.                         userdao = (IUser) Class.forName(”test.” + annotest.nation()).newInstance();  
    16.                         test.setUserdao(userdao);  
    17.                     } catch (Exception ex) {  
    18.                         Logger.getLogger(Container.class.getName()).log(Level.SEVERE, null, ex);  
    19.                     }  
    20.                 }  
    21.             }  
    22.         } else {  
    23.             System.out.println(”没有注解标记!”);  
    24.         }  
    25.         return test;  
    26.     }  
    27. }  
    public class Container {
    
        public static Test getBean() {
            Test test = new Test();
            if (Test.class.isAnnotationPresent(AnnotationTest.class)) {
                Method[] methods = Test.class.getDeclaredMethods();
                for (Method method : methods) {
                    System.out.println(method);
                    if (method.isAnnotationPresent(AnnotationTest.class)) {
                        AnnotationTest annotest = method.getAnnotation(AnnotationTest.class);
                        System.out.println("AnnotationTest(field=" + method.getName()
                                + ",nation=" + annotest.nation() + ")");
                        IUser userdao;
                        try {
                            userdao = (IUser) Class.forName("test." + annotest.nation()).newInstance();
                            test.setUserdao(userdao);
                        } catch (Exception ex) {
                            Logger.getLogger(Container.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }
            } else {
                System.out.println("没有注解标记!");
            }
            return test;
        }
    }
    在容器里面我使用反射获取注解属性nation所标注的内容,然后对Test类中的接口进行具体实现。这里的Container就是所谓的外部容器,可以对我们的注解或者是xml配置文件进行解析,以降低耦合性。

    最后我们再进行测试,代码如下

    1. /** 
    2.  * 注解测试 
    3.  * 
    4.  * @author Fly 
    5.  */  
    6. public class MyAnnotationTest {  
    7.   
    8.     public static void main(String[] args) {  
    9.         Test test = Container.getBean();  
    10.         test.loginTest();  
    11.     }  
    12. }  
    /**
     * 注解测试
     *
     * @author Fly
     */
    public class MyAnnotationTest {
    
        public static void main(String[] args) {
            Test test = Container.getBean();
            test.loginTest();
        }
    }
    测试结果如下:

    1. public void test.Test.loginTest()  
    2. public void test.Test.setUserdao(test.IUser)  
    3. AnnotationTest(field=setUserdao,nation=ChineseUserDaoImpl)  
    4. public test.IUser test.Test.getUserdao()  
    5. 用户登录!  
    public void test.Test.loginTest()
    public void test.Test.setUserdao(test.IUser)
    AnnotationTest(field=setUserdao,nation=ChineseUserDaoImpl)
    public test.IUser test.Test.getUserdao()
    用户登录!
    如果我把Test类中的
    1. @AnnotationTest(nation = “ChineseUserImpl”)  
    @AnnotationTest(nation = "ChineseUserImpl")

    修改成

    1. @AnnotationTest(nation = “EnglishUserImpl”)  
    @AnnotationTest(nation = "EnglishUserImpl")
    结构就变成

    1. public void test.Test.loginTest()  
    2. public test.IUser test.Test.getUserdao()  
    3. public void test.Test.setUserdao(test.IUser)  
    4. AnnotationTest(field=setUserdao,nation=EnglishUserImpl)  
    5. User Login!  
    public void test.Test.loginTest()
    public test.IUser test.Test.getUserdao()
    public void test.Test.setUserdao(test.IUser)
    AnnotationTest(field=setUserdao,nation=EnglishUserImpl)
    User Login!

    总结

    1、所有的注解类都隐式继承于 java.lang.annotation.Annotation,注解不允许显式继承于其他的接口。

    2、注解不能直接干扰程序代码的运行,无论增加或删除注解,代码都能够正常运行。Java语言解释器会忽略这些注解,而由第三方工具负责对注解进行处理。

    3、一个注解可以拥有多个成员,成员声明和接口方法声明类似,这里,我们仅定义了一个成员,成员的声明有以下几点限制:
    a)   成员以无入参无抛出异常的方式声明,如boolean value(String str)、boolean value() throws Exception等方式是非法的;
    b)   可以通过default为成员指定一个默认值,如String level() default “LOW_LEVEL”、int high() default 2是合法的,当然也可以不指定默认值;
    c)   成员类型是受限的,合法的类型包括原始类型及其封装类、String、Class、enums、注解类型,以及上述类型的数组类型。如ForumService value()、List foo()是非法的。
    d)   如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号(=),如@Description(“使用注解的实例”)。注解类拥有多个成员时,如果仅对value成员进行赋值则也可不使用赋值号,如果同时对多个成员进行赋值,则必须使用赋值号,如@DeclareParents (value = “NaiveWaiter”, defaultImpl = SmartSeller.class)。
    e)   注解类可以没有成员,没有成员的注解称为标识注解,解释程序以标识注解存在与否进行相应的处理;

    注:(1) 如果有多个annotationTest标注在不同方法上,那个结果本应该不同,但实际情况是,getdeclare方法的返回的methods的顺序很奇怪,每次结果的顺序都不一样,导致每次impl标识的结果一致,不知为什么?

    展开全文
  • 源码之ARouter

    2020-04-24 22:17:10
    理解之基础 Ioc(控制反转),涉及依赖注入和依赖查找 annotation(注解),包含JavaPoet库如何生成Java...一定要跑一下官方Demo APP,查看注解生成了哪些东西。到此,ARouter已经理解很深刻了。以下为个人理解,望指...

    理解之基础

    • Ioc(控制反转),涉及依赖注入和依赖查找,目的模块间解耦
    • annotation(注解),包含JavaPoet库如何生成Java文件
    • bootstrapping(自举)

    这里给出我自己学习的三个步骤:

    框架设计思想 视频&文章 --> 示例用法–> 源码分析

    回答一个问题:子Module如何跳转主Module,相互没有依赖的子Module如何跳转?

    一定要跑一下官方Demo APP,查看注解生成了哪些东西。到此,ARouter已经理解很深刻了。以下为个人理解,望指正。

    ARouter 节点关系

    在这里插入图片描述

    ARouter 映射关系

    在这里插入图片描述

    通过注解将RouteMeta路由元信息注册到路由表(生成的Java映射文件)中,跳转需求经过ARouter路由器查找分发,同时也能携带信息进行跳转。

    展开全文
  • Java虚拟机

    2018-01-20 13:22:29
    讲解了虚拟机的热点探测方法、HotSpot的即时编译器、编译触发条件,以及如何从虚拟机外部观察和分析JIT编译的数据和结果;第五部分探讨了Java实现高效并发的原理,包括JVM内存模型的结构和操作;原子性、可见性和...
  • 源码分析 :ArrayList 源码+扩容机制分析 、LinkedList 源码 、HashMap(JDK1.8)源码+底层数据结构分析 、ConcurrentHashMap 源码+底层数据结构分析 并发 知识点/面试题: (必看 ) Java 并发基础常见面试题总结 ...
  • simpleshop-axon-源码

    2021-06-05 23:32:11
    只使用注解和 Spring java 配置 Spring boot & 非常简单的休息服务。 jcabi 用于调试和日志。 看看它是如何工作的,下载而不是 mvn 测试。 如果您需要从 IDE 运行测试并查看方法如何工作 - 在 maven 运行配置 ...
  • 因为uiautomator是独占资源,所以当atx运行的时候uiautomatorviewer是不能用的,为了减少atx频繁的启停,我们开发了基于浏览器技术的weditor UI查看器。https://github.com/openatx/weditor 安装方法(备注: 目前最新...
  • 小伙伴问我:「冰河技术」公号如何查看文章分类? 新年新气象,2021年我们一起携手前行!! 元旦在家撸了两天Seata源码,你们是咋度过的呢? 完了,这货迟早要超过我!我该怎么办? 冰河去腾讯了? 满满的干货,我不...
  • APIAuto 一键自动接口回归测试,不需要写任何代码(注解、注释等全都不要) 一图胜千言 - APIJSON 部分基础功能概览 APIJSON App 演示 使用 APIJSON + ZBLibrary 开发的 Android 客户端 Demo (以下 Gif 图...
  • SSH后端框架,全注解开发 Maven插件,自动发布远程项目 数据库连接池,eachche缓存,前台图片优化 移动先行,响应式前台设计 更多特性,请自行下载查看 文理后院二手购物平台 一个不断升级中的JavaSSH购物平台网站 ...
  • 自动保存请求记录、自动生成接口文档,可添加常用请求、快捷查看一键恢复 一键自动接口回归测试,不需要写任何代码(注解、注释等全都不要) 一图胜千言 - 部分基础功能概览 [以下Gif图看起来比较卡,实际...
  • --3.0的junit是使用编程的方式来进行测试,而junit4是使用注解的方式来运行junit--> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version>...
  • JAVA使用Chimm.Excel通过“设置模板,填充数据”实现excel下载前言1....内容大多来自于作者,详情下载项目源码查看即可。 这里只给出了最基本的数据填充并下载功能,其他功能请看 Chimm.Excel 的 REA

    前言

    感谢 吃茫茫 大佬分享的开源项目 Chimm.Excel ,以下是个人的使用心得。内容大多来自于作者,详情下载项目源码查看即可。
    这里只给出了最基本的数据填充并下载功能,其他功能请看 Chimm.Excel 的 README.md 或项目源码。 Chimm.Excel 依赖于poi, Chimm.Excel 未提供的功能可以通过poi手动实现。

    1. 项目介绍(搬运)

    1.1 简介

    Chimm.Excel 是什么? 该程序是一个用 Java 写的 Excel 生成工具,基于模板操作,简单,快捷,易上手。

    1.2 特性

    数据组装方式大有不同

    和网上部分开源软件的区别是,这个程序是基于 excel 模板驱动的,需要在模版里填写变量名称,而并非在程序中添加注解。

    为什么不采用基于注解的方式?

    网上一些基于注解的 excel 导出,基本上只能导出简单的表格样式。该程序使用的是 excel 模板,所以可以导出一些比较复杂的表格样式。

    Antlr4

    Antlr (ANother Tool for Language Recognition) 是一个强大的跨语言语法解析器,可以用来读取、处理、执行或翻译结构化文本或二进制文件。它被广泛用来构建语言,工具和框架。Antlr可以从语法上来生成一个可以构建和遍历解析树的解析器。由于该程序是基于 Antlr4 进行开发的,所以在变量定义上面,非常的灵活,我们可以定义集合变量,甚至我们还可以在公式中定义变量。一个表格对应一个数据对象,开发人员只需查询数据、组装数据即可。

    excel 模板如何生成?

    模板生成非常的简单,我们定义变量的时候,只需要使用 $ + 大小括号 包围的形式即可,如:${school.name}。

    Chimm.Excel 功能简介

    • 导出excel二进制文件
    • 根据模板中的变量,将值写入
    • 支持公式
    • 支持带变量的公式,如:SUM(A1,A2,${demo.value})
    • 操作表格添加/减少行
    • ⭐️添加行会自动更新公式
    • 合并单元格(支持批量合并)
    • 更改单元格边框样式(加粗、虚线等)
    • 支持设置超链接(v1.2.0)

    2. 功能展示

    execl模板(测试表格.xlsx)如下:
    模板execl
    导出execl结果如下:
    结果execl

    3. 项目结构

    项目使用springboot快速搭建,如有不合理的地方… …就让他不合理吧,不合理你就自己改改,你看得懂就行。🙃
    项目结构
    maven位置:

    <!-- https://mvnrepository.com/artifact/com.github.chimmhuang/chimm.excel -->
    <dependency>
        <groupId>com.github.chimmhuang</groupId>
        <artifactId>chimm.excel</artifactId>
        <version>1.3.0</version>
    </dependency>
    

    ExeclController:

    @RestController
    public class ExcelController {
    
        @GetMapping(value = "getChimmExcel")
        public ResponseEntity<byte[]> getChimmExcel(HttpServletRequest request, HttpServletResponse response) throws Exception {
            // 获取文件的二进制
            File file = new File("src/main/resources/static/测试表格.xlsx");
            byte[] bytes = FileUtils.readFileToByteArray(file);
            // 通过 ExcelHelper 获取 excel 表格对象
            ExcelWorkbook excelWorkbook = ExcelHelper.createWorkbook(bytes);
    
            // 获取指定的 sheet 页(该对象即是我们设置好的表格模板)
            SheetTable table = excelWorkbook.getSheet(0);
            // 设置 自定义 sheet 页名称
            table.setSheetName("成绩表");
    
            // 获取封装的对象
            ClassRoom classRoom = ClassRoom.getClassRoomDemo();
    
            Row copy = table.getRow(3);
            // 将 rowNum 大于 13 的都删除,进行动态表格添加
            table.removeRowGE(3);
            for (int i = 0; i < classRoom.getStudents().size(); i++) {
                // 此处直接设置值也行。设置值或者设置变量,建议统一。我在此处设置为变量
                copy.getCell("A").setValue("${students[" + i + "].name}");
                copy.getCell("B").setValue("${students[" + i + "].sex}");
                copy.getCell("C").setValue("${students[" + i + "].age}");
                copy.getCell("D").setValue("${students[" + i + "].score}");
    //            copy.getCell("A").setValue(classRoom.getStudents().get(i).getName());
    //            copy.getCell("B").setValue(classRoom.getStudents().get(i).getSex());
    //            copy.getCell("C").setValue(classRoom.getStudents().get(i).getAge());
    //            copy.getCell("D").setValue(classRoom.getStudents().get(i).getScore());
                // 设置完毕后,添加进表格
                table.appendRow(copy);
            }
            
    		// 作者还提供了合并单元格、公式、更改样式等功能,这里不展示,需要的话下载 Chimm.Excel 源码看看吧
    
            // 将封装好的表格对象,填充到 excel 表格中
            ExcelHelper.fillInData(table, classRoom);
    
            // 将表格对象转换为二进制,resultBytes 即是最终想要的结果
            byte[] resultBytes = ExcelHelper.convert2Byte(table);
    
            // 设置http返回头
            HttpHeaders headers = new HttpHeaders();
            headers.add("contentType", "application/octet-stream");
            headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
            headers.add("Content-Disposition", "attachment; filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
            headers.add("Pragma", "no-cache");
            headers.add("Expires", "0");
            headers.add("Last-Modified", LocalDate.now().toString());
            headers.add("ETag", String.valueOf(System.currentTimeMillis()));
    
            return new ResponseEntity<>(resultBytes, headers, HttpStatus.CREATED);
        }
    }
    

    ClassRoom:

    @Data
    public class ClassRoom {
        private String className;
    
        private List<Student> students;
    
        public static ClassRoom getClassRoomDemo() {
            ClassRoom classRoom = new ClassRoom();
            List<Student> students = new ArrayList<>();
            classRoom.setClassName("华师大软件学院B班");
            for (int i = 0; i < 5; i++) {
                Student student = new Student();
                student.setName("学生" + i);
                student.setSex("男");
                student.setAge(23 + i);
                student.setScore(80 + i);
                students.add(student);
            }
            classRoom.setStudents(students);
            return classRoom;
        }
    }
    

    Student:

    @Data
    public class Student {
        private String name;
        private String sex;
        private Integer age;
        private Integer score;
    }
    

    3. 小结

    对表格样式有要求的情况下,使用 Chimm.Excel 可以节省大量的通过调整表格样式的时间(因为模板中样式已经设定好),大大的提高了实现导出excel功能的效率。

    但是,如果表格中插入动态的集合列表数据的操作还是略显麻烦,需要写循环插入。如果后面能实现只要一个${xxx}就能完成列表插入的话会更加方便。

    展开全文
  • Java的wait()、notify()学习三部曲之三:修改JVM源码控制抢锁顺序》 玩转Maven 《Ubuntu部署和体验Nexus3》 《没有JDK和Maven,用Docker也能构建Maven工程》 《maven构建docker镜像三部曲之一:准备环境》 ...
  • 这是我学习Java的知识总结。...怎么在Java中自定义注解? 深入理解String类 遇到的坑 List集合的坑 面经分享 记一次高级java开发的面试题总结 想加入技术群可加我个人微信备注"加群"即可。
  • 一些优秀的音视频博客推荐(见源码中的ReadMe.md) 视频编解码介绍 Camera和Camera2的使用 Android音视频录制 Android照片拍摄、自定义相机 直播原理 OpenGL介绍,OpenGL ES介绍和使用 显卡、GPU、显示器接口介绍 ...
  • 思维导图 和java相关的 更多干货 分布式实战(干货) spring cloud 实战(干货) mybatis 实战(干货) spring boot 实战(干货) React 入门实战(干货) 构建中小型互联网企业架构(干货) ...
  • [04] 如何手动描述java@RequestBodyMap<String,String>的示例输入? [05] SpringMVC-如何接收各种参数(普通参数,对象,JSON, URL) [06] swagger常用注解说明 [07] swagger ui swagger2 文件上传参数 input...
  • 使用注解替代枚举优化;glide加速优化;多渠道打包优化状态管理切换优化;TrimMemory和LowMemory优化;轮询操作优化;去除重复依赖库优化;合理运用软引用和弱引用优化;加载loading优化;网络请求异常拦截优化 具体...

空空如也

空空如也

1 2 3
收藏数 50
精华内容 20
关键字:

java如何查看注解源码

java 订阅