注解:
【Java面试题】注解原理是什么?
注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。
注解整理
JDK :
Java定义了7套注解:
3个在Java.lang中:@Override、@Deprecated、@SuppressWarnings,
@Override:检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated:标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings:指示编译器去忽略注解中声明的警告。
☆ 4个在java.lang.annotation中:@Retention、@Documented、@Target、@Inherited
@Retention:标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问
@Documented:标记这些注解是否包含在用户文档中
@Target:标记这个注解应该是哪种 Java 成员
@Inherited:标记这个注解是继承于哪个注解类(默认注解并没有继承于任何子类)
Java7开始增加了3个注解:@SafeVarargs、@FunctionalInterface、@Repeatable
@SafeVarargs: Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface:Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable:Java 8 开始支持,标识某注解可以在同一个声明上使用多次
@Retention:指定标记注释的存储方式
RetentionPolicy.SOURCE - 标记的注释仅保留在源级别中,并由编译器忽略
RetentionPolicy.CLASS - 标记的注释在编译时由编译器保留,但Java虚拟机(JVM)会忽略
RetentionPolicy.RUNTIME - 标记的注释由JVM保留,因此运行时环境可以使用它
@Documented:注释表明,无论何时使用指定的注释,都应使用Javadoc工具记录这些元素。(默认情况下,注释不包含在Javadoc中)
@Target:以限制可以应用注释的Java元素类型。目标注释指定以下元素类型之一作为其值:
ElementType.TYPE 可以应用于类的任何元素
ElementType.FIELD 可以应用于字段或属性
ElementType.METHOD 可以应用于方法级注释
ElementType.PARAMETER 可以应用于方法的参数
ElementType.CONSTRUCTOR 可以应用于构造函数
ElementType.LOCAL_VARIABLE 可以应用于局部变量
ElementType.ANNOTATION_TYPE 可以应用于注释类型
ElementType.PACKAGE 可以应用于包声明
ElementType.TYPE_PARAMETER 可以应用于类型参数
ElementType.TYPE_USER 可以应用任何类型名称
@Inherited:表明注释类型可以从超类继承。当用户查询注释类型并且该类没有此类型的注释时,将查询类的超类以获取注释类型(默认情况下不是这样)。此注释仅适用于类声明。
@Repeatable: Java SE 8中引入的,@Repeatable注释表明标记的注释可以多次应用于相同的声明或类型使用(即可以重复在同一个类、方法、属性等上使用)
Spring注解
1.声明bean的注解
· @Component 组件,没有明确的角色
· @Service 在业务逻辑层使用(service层)
· @Repository 在数据访问层使用(dao层)
· @Controller 在展现层使用,控制器的声明(C)
2.注入bean的注解
· @Autowired:由Spring提供
· @Inject:由JSR-330提供
· @Resource:由JSR-250提供
都可以注解在set方法和属性上,推荐注解在属性上(一目了然,少写代码)。
3.java配置类相关注解
· @Configuration 声明当前类为配置类,相当于xml形式的Spring配置(类上)
· @Bean 注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)
· @Configuration 声明当前类为配置类,其中内部组合了@Component注解,表明这个类是一个bean(类上)
· @ComponentScan 用于对Component进行扫描,相当于xml中的(类上)
· @WishlyConfiguration 为@Configuration与@ComponentScan的组合注解,可以替代这两个注解
4.切面(AOP)相关注解
Spring支持AspectJ的注解式切面编程。
· @Aspect 声明一个切面(类上)
使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
· @After 在方法执行之后执行(方法上)
· @Before 在方法执行之前执行(方法上)
· @Around 在方法执行之前与之后执行(方法上)
· @PointCut 声明切点
在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)
5.@Bean的属性支持
· @Scope 设置Spring容器如何新建Bean实例(方法上,得有@Bean)
其设置类型包括:
Singleton (单例,一个Spring容器中只有一个bean实例,默认模式),
Protetype (每次调用新建一个bean),
Request (web项目中,给每个http request新建一个bean),
Session (web项目中,给每个http session新建一个bean),
GlobalSession(给每一个 global http session新建一个Bean实例)
· @StepScope 在Spring Batch中还有涉及
· @PostConstruct 由JSR-250提供,在构造函数执行完之后执行,等价于xml配置文件中bean的initMethod
· @PreDestory 由JSR-250提供,在Bean销毁之前执行,等价于xml配置文件中bean的destroyMethod
6.@Value注解
· @Value 为属性注入值(属性上)
7.环境切换
· @Profile 通过设定Environment的ActiveProfiles来设定当前context需要使用的配置环境。(类或方法上)
· @Conditional Spring4中可以使用此注解定义条件话的bean,通过实现Condition接口,并重写matches方法,从而决定该bean是否被实例化。(方法上)
8.异步相关
· @EnableAsync 配置类中,通过此注解开启对异步任务的支持,叙事性AsyncConfigurer接口(类上)
· @Async 在实际执行的bean方法使用该注解来申明其是一个异步任务(方法上或类上所有的方法都将异步,需要@EnableAsync开启异步任务)
9.定时任务相关
· @EnableScheduling 在配置类上使用,开启计划任务的支持(类上)
· @Scheduled 来申明这是一个任务,包括cron,fixDelay,fixRate等类型(方法上,需先开启计划任务的支持)
10.@Enable*注解说明
这些注解主要用来开启对xxx的支持。
· @EnableAspectJAutoProxy 开启对AspectJ自动代理的支持
· @EnableAsync 开启异步方法的支持
· @EnableScheduling 开启计划任务的支持
· @EnableWebMvc 开启Web MVC的配置支持
· @EnableConfigurationProperties 开启对@ConfigurationProperties注解配置Bean的支持
· @EnableJpaRepositories 开启对SpringData JPA Repository的支持
· @EnableTransactionManagement 开启注解式事务的支持
· @EnableTransactionManagement 开启注解式事务的支持
· @EnableCaching 开启注解式的缓存支持
11.测试相关注解
· @RunWith 运行器,Spring中通常用于对JUnit的支持
· @ContextConfiguration 用来加载配置ApplicationContext,其中classes属性用来加载配置类
SpringMVC注解
· @EnableWebMvc 在配置类中开启Web MVC的配置支持,如一些ViewResolver或者MessageConverter等,若无此句,重写WebMvcConfigurerAdapter方法(用于对SpringMVC的配置)。
· @Controller 声明该类为SpringMVC中的Controller
· @RequestMapping 用于映射Web请求,包括访问路径和参数(类或方法上)
· @ResponseBody 支持将返回值放在response内,而不是一个页面,通常用户返回json数据(返回值旁或方法上)
· @RequestBody 允许request的参数在request体中,而不是在直接连接在地址后面。(放在参数前)
· @PathVariable 用于接收路径参数,比如@RequestMapping(“/hello/{name}”)申明的路径,将注解放在参数中前,即可获取该值,通常作为Restful的接口实现方法。
· @requestParam 主要用于在SpringMVC后台控制层获取参数
· @RestController 该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。
· @ControllerAdvice 通过该注解,我们可以将对于控制器的全局配置放置在同一个位置,注解了@Controller的类的方法可使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上,这对所有注解了 @RequestMapping的控制器内的方法有效。
· @ExceptionHandler 用于全局处理控制器里的异常
· @InitBinder 用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中。
· @ModelAttribute 本来的作用是绑定键值对到Model里,在@ControllerAdvice中是让全局的@RequestMapping都能获得在此处设置的键值对。
· @SessionAttributes即将值放到session作用域中,写在class上面。
全局异常处理
· @ControllerAdvice:包含@Component。可以被扫描到。统一处理异常。
· @ExceptionHandler(Exception.class):用在方法上面表示遇到这个异常就执行以下方法。
SpringBoot注解
· @SpringBootApplication:申明让spring boot自动给程序进行必要的配置,这个配置等同于:@Configuration ,@EnableAutoConfiguration 和 @ComponentScan 三个配置。
· @ResponseBody:表示该方法的返回结果直接写入HTTP response body中,一般在异步获取数据时使用,用于构建RESTful的api。在使用@RequestMapping后,返回值通常解析为跳转路径,加上@Responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@Responsebody后,会直接返回json数据。该注解一般会配合@RequestMapping一起使用。
· @Controller:用于定义控制器类,在spring项目中由控制器负责将用户发来的URL请求转发到对应的服务接口(service层),一般这个注解在类中,通常方法需要配合注解@RequestMapping。
· @RestController:用于标注控制层组件(如struts中的action),@ResponseBody和@Controller的合集。
· @RequestMapping:提供路由信息,负责URL到Controller中的具体函数的映射。
· @EnableAutoConfiguration:SpringBoot自动配置(auto-configuration):尝试根据你添加的jar依赖自动配置你的Spring应用。例如,如果你的classpath下存在HSQLDB,并且你没有手动配置任何数据库连接beans,那么我们将自动配置一个内存型(in-memory)数据库”。你可以将@EnableAutoConfiguration或者@SpringBootApplication注解添加到一个@Configuration类上来选择自动配置。如果发现应用了你不想要的特定自动配置类,你可以使用@EnableAutoConfiguration注解的排除属性来禁用它们。
· @ComponentScan:表示将该类自动发现扫描组件。个人理解相当于,如果扫描到有@Component、@Controller、@Service等这些注解的类,并注册为Bean,可以自动收集所有的Spring组件,包括@Configuration类。我们经常使用@ComponentScan注解搜索beans,并结合@Autowired注解导入。可以自动收集所有的Spring组件,包括@Configuration类。我们经常使用@ComponentScan注解搜索beans,并结合@Autowired注解导入。如果没有配置的话,Spring Boot会扫描启动类所在包下以及子包下的使用了@Service,@Repository等注解的类。
· @Configuration:相当于传统的xml配置文件,如果有些第三方库需要用到xml文件,建议仍然通过@Configuration类作为项目的配置主类——可以使用@ImportResource注解加载xml配置文件。
· @Import:用来导入其他配置类。
· @ImportResource:用来加载xml配置文件。
· @Autowired:自动导入依赖的bean
· @Service:一般用于修饰service层的组件
· @Repository:使用@Repository注解可以确保DAO或者repositories提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不需要为它们提供XML配置项。
· @Bean:用@Bean标注方法等价于XML中配置的bean。
· @Value:注入Spring boot application.properties配置的属性的值。示例代码:
· @Inject:等价于默认的@Autowired,只是没有required属性;
· @Component:泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
· @Bean:相当于XML中的,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。
· @AutoWired:自动导入依赖的bean。byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。当加上(required=false)时,就算找不到bean也不报错。
· @Qualifier:当有多个同一类型的Bean时,可以用@Qualifier(“name”)来指定。与@Autowired配合使用。@Qualifier限定描述符除了能根据名字进行注入,但能进行更细粒度的控制如何选择候选者,具体使用方式如下:
· @Resource(name=”name”,type=”type”):没有括号内内容的话,默认byName。与@Autowired干类似的事。
Servlet3.0的注解
· @WebListener注解 : 监听器
· @WebFilter : 过滤器
· @WebServlet : 表示的就是一般的Servlet
· @WebInitParam : 表示的就是参数, 相当于
· @MultipartConfig : 辅助 Servlet 3.0 中 HttpServletRequest 提供的对上传文件的支持
lombok注解
· @Slf4j: 注解在类上,为类提供一个属性名为 log 的 log4j 的日志对象,避免重复代码。(基于lombok包)
· @Log:使用的是 java.util.logging.Logger ,直接使用变量 log,用法同上。
· @Data:用在pojo类,在类上面加这个注解。简化java代码编写,为该类提供读写属性,还提供了equals(),hashCode(),toString()方法。等价于 @ToString、@Getter、@Setter、@EqualsAndHashCode、@NoArgsConstructor
· @AllArgsConstructor:在pojo类上面加了这些注解以后,会生成一个包含所有变量的构造函数,同时如果变量使用了NotNull annotation , 会进行是否为空的校验,全部参数的构造函数的自动生成,该注解的作用域也是只有在实体类上,参数的顺序与属性定义的顺序一致
· @NoArgsConstructor:在pojo类上面加了这些注解以后,会生成一个无参构造函数
· @NonNull:该注解用在属性或构造器上,Lombok会生成一个非空的声明,可用于校验参数,能帮助避免空指针。
· @Cleanup:该注解能帮助我们自动调用close()方法,很大的简化了代码。
· @Builder:声明实体,表示可以进行Builder方式初始化
Swagger注解
· @Api : 协议集描述, 用于controller类上
· @ApiOperation : 协议描述, 用在controller的方法上
· @ApiImplicitParams : 非对象参数集, 用在controller的方法上
· @ApiImplicitParam : 非对象参数描述, 用在@ApiImplicitParams的方法里边
· @ApiParam : 对象参数描述, 用在@ApiImplicitParams的方法里边,定义接收的参数形式
· @ApiModel : 描述返回对象的意义, 用在返回对象类上
· @ApiModelProperty : 对象属性, 用在参数对象的字段上
· @ApiResponses : Response集, 用在controller的方法上
· @ApiResponse : Response, 用在 @ApiResponses里边
· @ResponseHeader : Response, 响应头设置
什么是注解(Annotation):
Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。 Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。
Annotation(注解)是JDK5.0及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。从某些方面看,annotation就像修饰符一样被使用,并应用于包、类 型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在Annotation的“name=value”结构对中。
Annotation的成员在Annotation类型中以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型。在此有一个特定的默认语法:允许声明任何Annotation成员的默认值:一个Annotation可以将name=value对作为没有定义默认值的Annotation成员的值,当然也可以使用name=value对来覆盖其它成员默认值。这一点有些近似类的继承特性,父类的构造函数可以作为子类的默认构造函数,但是也可以被子类覆盖。
Annotation能被用来为某个程序元素(类、方法、成员变量等)关联任何的信息。需要注意的是,这里存在着一个基本的规则:Annotation不能影响程序代码的执行,无论增加、删除 Annotation,代码都始终如一的执行。 另外,尽管一些annotation通过java的反射api方法在运行时被访问,而java语言解释器在工作时忽略了这些annotation。正是由于java虚拟机忽略了Annotation,导致了annotation类型在代码中是“不起作用”的; 只有通过某种配套的工具才会对annotation类型中的信息进行访问和处理。本文中将涵盖标准的Annotation和meta-annotation类型,陪伴这些annotation类型的工具是java编译器(当然要以某种特殊的方式处理它们)。
什么是metadata(元数据):
元数据从metadata一词译来,就是“关于数据的数据”的意思。
元数据的功能作用有很多,比如:你可能用过Javadoc的注释自动生成文档。这就是元数据功能的一种。总的来说,元数据可以用来创建文档,跟踪代码的依赖性,执行编译时格式检查,代替已有的配置文件。如果要对于元数据的作用进行分类,目前还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:
1. 编写文档:通过代码里标识的元数据生成文档
2. 代码分析:通过代码里标识的元数据对代码进行分析
3. 编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查
在Java中元数据以标签的形式存在于Java代码中,元数据标签的存在并不影响程序代码的编译和执行,它只是被用来生成其它的文件或针在运行时知道被运行代码的描述信息。
综上所述:
第一,元数据以标签的形式存在于Java代码中。
第二,元数据描述的信息是类型安全的,即元数据内部的字段都是有明确类型的。
第三,元数据需要编译器之外的工具额外的处理用来生成其它的程序部件。
第四,元数据可以只存在于Java源代码级别,也可以存在于编译之后的Class文件内部。
Annotation和Annotation类型:
Annotation:
Annotation使用了在java5.0所带来的新语法,它的行为十分类似public、final这样的修饰符。每个Annotation具有一个名字和成员个数>=0。每个Annotation的成员具有被称为name=value对的名字和值(就像javabean一样),name=value装载了Annotation的信息。
Annotation类型:
****Annotation类型定义了Annotation的名字、类型、成员默认值。一个Annotation类型可以说是一个特殊的java接口,它的成员变量是受限制的,而声明Annotation类型时需要使用新语法。当我们通过java反射api访问Annotation时,返回值将是一个实现了该 annotation类型接口的对象,通过访问这个对象我们能方便的访问到其Annotation成员。后面的章节将提到在java5.0的 java.lang包里包含的3个标准Annotation类型。
注解的分类:
根据注解参数的个数,我们可以将注解分为三类:
1.标记注解:一个没有成员定义的Annotation类型被称为标记注解。这种Annotation类型仅使用自身的存在与否来为我们提供信息。比如后面的系统注解@Override;
2.单值注解
3.完整注解
根据注解使用方法和用途,我们可以将Annotation分为三类:
1.JDK内置系统注解
2.元注解
3.自定义注解
以前,『XML』是各大框架的青睐者,它以松耦合的方式完成了框架中几乎所有的配置,但是随着项目越来越庞大,『XML』的内容也越来越复杂,维护成本变高。
于是就有人提出来一种标记式高耦合的配置方式,『注解』。方法上可以进行注解,类上也可以注解,字段属性上也可以注解,反正几乎需要配置的地方都可以进行注解。
关于『注解』和『XML』两种不同的配置模式,争论了好多年了,各有各的优劣,注解可以提供更大的便捷性,易于维护修改,但耦合度高,而 XML 相对于注解则是相反的。
追求低耦合就要抛弃高效率,追求效率必然会遇到耦合。本文意不再辨析两者谁优谁劣,而在于以最简单的语言介绍注解相关的基本内容。
注解的本质
「java.lang.annotation.Annotation」接口中有这么一句话,用来描述『注解』。
The common interface extended by all annotation types
所有的注解类型都继承自这个普通的接口(Annotation)
这句话有点抽象,但却说出了注解的本质。我们看一个 JDK 内置注解的定义:
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
这是注解 @Override 的定义,其实它本质上就是:
public interface Override extends Annotation{
}
没错,注解的本质就是一个继承了 Annotation 接口的接口。有关这一点,你可以去反编译任意一个注解类,你会得到结果的。
一个注解准确意义上来说,只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。
而解析一个类或者方法的注解往往有两种形式,一种是编译期直接的扫描,一种是运行期反射。反射的事情我们待会说,而编译器的扫描指的是编译器在对 java 代码编译字节码的过程中会检测到某个类或者方法被一些注解修饰,这时它就会对于这些注解进行某些处理。
典型的就是注解 @Override,一旦编译器检测到某个方法被修饰了 @Override 注解,编译器就会检查当前方法的方法签名是否真正重写了父类的某个方法,也就是比较父类中是否具有一个同样的方法签名。
这一种情况只适用于那些编译器已经熟知的注解类,比如 JDK 内置的几个注解,而你自定义的注解,编译器是不知道你这个注解的作用的,当然也不知道该如何处理,往往只是会根据该注解的作用范围来选择是否编译进字节码文件,仅此而已。
元注解
『元注解』是用于修饰注解的注解,通常用在注解的定义上,例如:
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
这是我们 @Override 注解的定义,你可以看到其中的 @Target,@Retention 两个注解就是我们所谓的『元注解』,『元注解』一般用于指定某个注解生命周期以及作用目标等信息。
JAVA 中有以下几个『元注解』:
· @Target:注解的作用目标
· @Retention:注解的生命周期
· @Documented:注解是否应当被包含在 JavaDoc 文档中
· @Inherited:是否允许子类继承该注解
其中,@Target 用于指明被修饰的注解最终可以作用的目标是谁,也就是指明,你的注解到底是用来修饰方法的?修饰类的?还是用来修饰字段属性的。
@Target 的定义如下:
我们可以通过以下的方式来为这个 value 传值:
@Target(value = {ElementType.FIELD})
被这个 @Target 注解修饰的注解将只能作用在成员字段上,不能用于修饰方法或者类。其中,ElementType 是一个枚举类型,有以下一些值:
· ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上
· ElementType.FIELD:允许作用在属性字段上
· ElementType.METHOD:允许作用在方法上
· ElementType.PARAMETER:允许作用在方法参数上
· ElementType.CONSTRUCTOR:允许作用在构造器上
· ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
· ElementType.ANNOTATION_TYPE:允许作用在注解上
· ElementType.PACKAGE:允许作用在包上
@Retention 用于指明当前注解的生命周期,它的基本定义如下:
同样的,它也有一个 value 属性:
@Retention(value = RetentionPolicy.RUNTIME
这里的 RetentionPolicy 依然是一个枚举类型,它有以下几个枚举值可取:
· RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件
· RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件
· RetentionPolicy.RUNTIME:永久保存,可以反射获取
@Retention 注解指定了被修饰的注解的生命周期,一种是只能在编译期可见,编译后会被丢弃,一种会被编译器编译进 class 文件中,无论是类或是方法,乃至字段,他们都是有属性表的,而 JAVA 虚拟机也定义了几种注解属性表用于存储注解信息,但是这种可见性不能带到方法区,类加载时会予以丢弃,最后一种则是永久存在的可见性。
剩下两种类型的注解我们日常用的不多,也比较简单,这里不再详细的进行介绍了,你只需要知道他们各自的作用即可。@Documented 注解修饰的注解,当我们执行 JavaDoc 文档打包时会被保存进 doc 文档,反之将在打包时丢弃。@Inherited 注解修饰的注解是具有可继承性的,也就说我们的注解修饰了一个类,而该类的子类将自动继承父类的该注解。
JAVA 的内置三大注解
除了上述四种元注解外,JDK 还为我们预定义了另外三种注解,它们是:
· @Override
· @Deprecated
· @SuppressWarnings
@Override 注解想必是大家很熟悉的了,它的定义如下:
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
它没有任何的属性,所以并不能存储任何其他信息。它只能作用于方法之上,编译结束后将被丢弃。
所以你看,它就是一种典型的『标记式注解』,仅被编译器可知,编译器在对 java 文件进行编译成字节码的过程中,一旦检测到某个方法上被修饰了该注解,就会去匹对父类中是否具有一个同样方法签名的函数,如果不是,自然不能通过编译。
@Deprecated 的基本定义如下:
依然是一种『标记式注解』,永久存在,可以修饰所有的类型,作用是,标记当前的类或者方法或者字段等已经不再被推荐使用了,可能下一次的 JDK 版本就会删除。
当然,编译器并不会强制要求你做什么,只是告诉你 JDK 已经不再推荐使用当前的方法或者类了,建议你使用某个替代者。
@SuppressWarnings 主要用来压制 java 的警告,它的基本定义如下:
它有一个 value 属性需要你主动的传值,这个 value 代表一个什么意思呢,这个 value 代表的就是需要被压制的警告类型。例如:
public static void main(String[] args) {
Date date = new Date(2018, 7, 11);
}
这么一段代码,程序启动时编译器会报一个警告。
Warning:(8, 21) java: java.util.Date 中的 Date(int,int,int) 已过时
而如果我们不希望程序启动时,编译器检查代码中过时的方法,就可以使用 @SuppressWarnings 注解并给它的 value 属性传入一个参数值来压制编译器的检查。
@SuppressWarning(value = "deprecated")public static void main(String[] args) {
Date date = new Date(2018, 7, 11);
}
这样你就会发现,编译器不再检查 main 方法下是否有过时的方法调用,也就压制了编译器对于这种警告的检查。
当然,JAVA 中还有很多的警告类型,他们都会对应一个字符串,通过设置 value 属性的值即可压制对于这一类警告类型的检查。
自定义注解的相关内容就不再赘述了,比较简单,通过类似以下的语法即可自定义一个注解。
public @interface InnotationName{
}
当然,自定义注解的时候也可以选择性的使用元注解进行修饰,这样你可以更加具体的指定你的注解的生命周期、作用范围等信息。
注解与反射
上述内容我们介绍了注解使用上的细节,也简单提到,「注解的本质就是一个继承了 Annotation 接口的接口」,现在我们就来从虚拟机的层面看看,注解的本质到底是什么。
首先,我们自定义一个注解类型:
这里我们指定了 Hello 这个注解只能修饰字段和方法,并且该注解永久存活,以便我们反射获取。
之前我们说过,虚拟机规范定义了一系列和注解相关的属性表,也就是说,无论是字段、方法或是类本身,如果被注解修饰了,就可以被写进字节码文件。属性表有以下几种:
· RuntimeVisibleAnnotations:运行时可见的注解
· RuntimeInVisibleAnnotations:运行时不可见的注解
· RuntimeVisibleParameterAnnotations:运行时可见的方法参数注解
· RuntimeInVisibleParameterAnnotations:运行时不可见的方法参数注解
· AnnotationDefault:注解类元素的默认值
给大家看虚拟机的这几个注解相关的属性表的目的在于,让大家从整体上构建一个基本的印象,注解在字节码文件中是如何存储的。
所以,对于一个类或者接口来说,Class 类中提供了以下一些方法用于反射注解。
· getAnnotation:返回指定的注解
· isAnnotationPresent:判定当前元素是否被指定注解修饰
· getAnnotations:返回所有的注解
· getDeclaredAnnotation:返回本元素的指定注解
· getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的
方法、字段中相关反射注解的方法基本是类似的,这里不再赘述,我们下面看一个完整的例子。
首先,设置一个虚拟机启动参数,用于捕获 JDK 动态代理类。
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
然后 main 函数。
我们说过,注解本质上是继承了 Annotation 接口的接口,而当你通过反射,也就是我们这里的 getAnnotation 方法去获取一个注解类实例的时候,其实 JDK 是通过动态代理机制生成一个实现我们注解(接口)的代理类。
我们运行程序后,会看到输出目录里有这么一个代理类,反编译之后是这样的:
代理类实现接口 Hello 并重写其所有方法,包括 value 方法以及接口 Hello 从 Annotation 接口继承而来的方法。
而这个关键的 InvocationHandler 实例是谁?
AnnotationInvocationHandler 是 JAVA 中专门用于处理注解的 Handler, 这个类的设计也非常有意思。
这里有一个 memberValues,它是一个 Map 键值对,键是我们注解属性名称,值就是该属性当初被赋上的值。
而这个 invoke 方法就很有意思了,大家注意看,我们的代理类代理了 Hello 接口中所有的方法,所以对于代理类中任何方法的调用都会被转到这里来。
var2 指向被调用的方法实例,而这里首先用变量 var4 获取该方法的简明名称,接着 switch 结构判断当前的调用方法是谁,如果是 Annotation 中的四大方法,将 var7 赋上特定的值。
如果当前调用的方法是 toString,equals,hashCode,annotationType 的话,AnnotationInvocationHandler 实例中已经预定义好了这些方法的实现,直接调用即可。
那么假如 var7 没有匹配上这四种方法,说明当前的方法调用的是自定义注解字节声明的方法,例如我们 Hello 注解的 value 方法。这种情况下,将从我们的注解 map 中获取这个注解属性对应的值。
其实,JAVA 中的注解设计个人觉得有点反人类,明明是属性的操作,非要用方法来实现。当然,如果你有不同的见解,欢迎留言探讨。
最后我们再总结一下整个反射注解的工作原理:
首先,我们通过键值对的形式可以为注解属性赋值,像这样:@Hello(value = "hello")。
接着,你用注解修饰某个元素,编译器将在编译期扫描每个类或者方法上的注解,会做一个基本的检查,你的这个注解是否允许作用在当前位置,最后会将注解信息写入元素的属性表。
然后,当你进行反射的时候,虚拟机将所有生命周期在 RUNTIME 的注解取出来放到一个 map 中,并创建一个 AnnotationInvocationHandler 实例,把这个 map 传递给它。
最后,虚拟机将采用 JDK 动态代理机制生成一个目标注解的代理类,并初始化好处理器。
那么这样,一个注解的实例就创建出来了,它本质上就是一个代理类,你应当去理解好 AnnotationInvocationHandler 中 invoke 方法的实现逻辑,这是核心。一句话概括就是,通过方法名返回注解属性值。
一、@Autowired 1、@Autowired是spring自带的注解,通过‘AutowiredAnnotationBeanPostProcessor’ 类实现的依赖注入; 2、@Autowired是根据类型进行自动装配的,如果找到多个类型相同的,会按照名称进行匹配,如果名称相同,会报错,如果需要按指定名称进行装配,则需要配合@Qualifier; 3、@Autowired有个属性为required,可以配置为false,如果配置为false之后,当没有找到相应bean的时候,系统不会抛错; 4、@Autowired可以作用在变量、setter方法、构造函数上。 a、将@autowored写在被注入的成员变量上,就不用再xml文件中配置了,在程序中去掉相应的setter和getter方法,
b、还可以写在构造方法上、setter方法上
c、@Qualifier @Qualifier("XXX") 中的 XX是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。 不过需要注意的是@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入参。
二、@Inject 1、@Inject是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject;实现注入。 2、@Inject是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named; 3、@Inject可以作用在变量、setter方法、构造函数上。 a、将@Inject可以作用在变量、setter方法、构造函数上,和@Autowired一样
b、@Named @Named("XXX") 中的 XX是 Bean 的名称,所以 @Inject和 @Named结合使用时,自动注入的策略就从 byType 转变成 byName 了。
三、@Resource 1、@Resource是JSR250规范的实现,需要导入javax.annotation实现注入。 2、@Resource是根据名称进行自动装配的,一般会指定一个name属性 3、@Resource可以作用在变量、setter方法上。 a、@Resource实例
@Autowired和@Resource的区别是什么?
总结: 1、@Autowired是spring自带的,@Inject是JSR330规范实现的,@Resource是JSR250规范实现的,需要导入不同的包 2、@Autowired、@Inject用法基本一样,不同的是@Autowired有一个request属性 3、@Autowired、@Inject是默认按照类型匹配的,@Resource是按照名称匹配的 4、@Autowired如果需要按照名称匹配需要和@Qualifier一起使用,@Inject和@Name一起使用
@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。 @Resource注解和@Autowired一样,也可以标注在字段或属性的setter方法上,但它默认按名称装配。名称可以通过@Resource的name属性指定,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。 @Resources按名字,是JDK的,@Autowired按类型,是Spring的。