Java原生注解和Spring注解的说明
注解
- java 原生注解
- Spring 中的注解
一 Java原生注解
Java注解是在JDK1.5以后引入的新特性!
-
有利于代码的解藕、整洁,提升开发的效率!通俗来讲,注解就是标签,标签的内容可以变动!
-
注解也是java中的一种数据类型!注解的创建和接口的创建很类似!使用@Interface修饰!
-
JDK本身自带的几种原生注解!也就是我们常说的元注解,原生注解是用于修饰自定义注解!JVM规范规定自定义注解必须使用些原生注解修饰,否则不能正常使用。
目前为止JDK自带的原生注解有6个:@Retention , @Target , @Inherited , @Documented , @Repeatable , @Native
其中,@Native、@Repeabable是在JDK1.8之后推出的元注解!
注解1 @Retention
@Retention表示注解保留周期,用于提示注解被保留多长时间,
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
有三种取值:
- RetentionPolicy.SOURCE:保留在源码级别,被编译器抛弃(常见的@Override注解就是此类);
- RetentionPolicy.CLASS:被编译器保留在编译后的类文件级别,但是被虚拟机丢弃;
- RetentionPolicy.RUNTIME:保留至运行时,可以被反射读取。
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
注解2 @Target
@Target: 表示注解可以使用在什么地方
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
常见的取值有:
- ElementType.TYPE : 表示该注解可以被使用在 【类, 接口,注解,enum】上面
- ElementType.FIELD : 表示该注解可以被使用在 【属性域】上面
- ElementType.METHOD : 表示该注解可以被使用在 【方法】上面
- ElementType.PARAMETER : 表示该注解可以被使用在 【参数】上面
- ElementType.CONSTRUCTOR : 表示该注解可以被使用在 【构造函数】上面
- E lementType.LOCAL_VARIABLE* : 表示该注解可以被使用在 【局部变量】上面
- ElementType.ANNOTATION_TYPE : 表示该注解可以被使用在 【 注解类型】上面
- ElementType.PACKAGE : 表示该注解可以被使用在 【包】上面
- ElementType.TYPE_PARAMETER : 表示该注解可以被使用在 【输入参数】上面
- ElementType.TYPE_USE :
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
注解3 @Documented
注解写入文档 表示注解是否能被 javadoc 处理并保留在文档中。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
注解4 @Inherited
子类继承父类的注解(子类没有任何注解修饰)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
注解5 @Repeatable
@Repeatable : 表示注解的属性可以重复!同一种注解多次使用。可以用来表示某个对象存在多个身份。@Repeatable通俗来讲,就是注解容器!
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
/**
* Indicates the <em>containing annotation type</em> for the
* repeatable annotation type.
* @return the containing annotation type
*/
Class<? extends Annotation> value();
}
JDK提供的其他注解
@Deprecated:用于标志过时的类、方法和成员变量
@Override:用于修饰重写的方法
@SuppressWarnings:用于忽略@Deprecated标志过的警告
@SafeVarargs:参数安全类型注解,用于提示用户参数安全(JDK1.7)
@FunctionalInterface:函数式接口注解,用于定义函数式接口(JDK1.8)
范例
@Override
的作用是,提示编译器,使用了@Override
注解的方法必须override父类或者java.lang.Object中的一个同名方法。
表示 @Override 只能使用在方法上,保留在源码级别,被编译器处理,然后抛弃掉。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
二 Spring中的注解
注解1 @Autowired
Spring开发者对@Autowired注解必定是非常了解,其实就是 autowire=byType 就是根据类型的自动注入依赖(基于注解的依赖注入),可以被使用再属性域,方法,构造函数上。
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
注解2 @Qualifier
@Qualifier 就是 autowire=byName, 当@Autowired注解判断多个bean类型相同时,就需要使用 @Qualifier("xxBean") 来指定依赖的bean的id:
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default "";
}
注解3 @Resource
@Resource 属于JSR250标准,它并不是Spring提供的注解,但是Spring对它提供了支持。
-
@Resource
的作用相当于@Autowired,只不过@Autowired
按byType自动注入,而@Resource默认按 byName自动注入。 -
@Resource
注解的name属性解析为bean的名字,而type属性则解析为bean的类型。如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。 - 用于属性域和方法上。使用方式:
@Resource(name="xxBean")
. 不带参数的@Resource
默认值类名首字母小写。
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
String name() default "";
String lookup() default "";
Class<?> type() default java.lang.Object.class;
enum AuthenticationType {
CONTAINER,
APPLICATION
}
AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
boolean shareable() default true;
}
@Resource装配顺序:
- 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
- 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
- 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
- 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
注解4 @Component
@Repository
, @Service
, @Controller
@Component
是spring注解,注解后可以被spring框架所扫描并注入到spring容器来进行管理
@Controller
, @Service
, @Repository
, 这几个注解不同于上面的注解,上面的注解都是将被依赖的bean注入进入,而这几个注解的作用都是生产bean, 这些注解都是注解在类上,将类注解成spring的bean工厂中一个一个的bean。@Controller
, @Service
, @Repository
基本就是语义更加细化的@Component
。
- @Component 是通用注解,其他三个注解是这个注解的拓展,并且具有了特定的功能
- @Repository 注解在持久层中,具有将数据库操作抛出的原生异常翻译转化为spring的持久层异常的功能。注解类作为DAO对象(数据访问对象,Data Access Objects),这些类可以直接对数据库进行操作 有这些分层操作的话,代码之间就实现了松耦合,代码之间的调用也清晰明朗,便于项目的管理;
-
@Controller 层是spring-mvc的注解,bean会被spring-mvc框架所使用。具有将请求进行转发,重定向的功能。不能被其他注解所互换的。
@Controller
注解类进行前端请求的处理,转发,重定向。包括调用Service层的方法@Service
注解类处理业务逻辑。 -
@Service 层是业务逻辑层注解,这个注解只是标注该类处于业务逻辑层。
用这些注解对应用进行分层之后,就能将请求处理,义务逻辑处理,数据库操作处理分离出来,为代码解耦,也方便了以后项目的维护和开发.
注解5 # @RequestParam @RequestBody @PathVariable
用于参数绑定的注解
-
@PathVariable
: 处理requet uri 部分(这里指uri template中variable,不含queryString部分)。当使用@RequestMapping URI template 样式映射时, 即 someUrl/{paramId}, 这时的paramId可通过 @Pathvariable注解绑定它传过来的值到方法的参数上。 -
@RequestHeader
、@CookieValue
: @RequestHeader 注解,可以把Request请求header部分的值绑定到方法的参数上。
范例
@RestController
@RequestMapping("/head")
public class DemoController {
@RequestMapping("/info")
public String headInfo(@RequestHeader("Accept-Encoding") String encoding ,@RequestHeader("Keep-Alive")long keepAlive) {
return encoding + " : " + keepAlive;
}
}
@RequestParam
- 常用来处理简单类型的绑定,通过Request.getParameter() 获取的String可直接转换为简单类型的情况( String--> 简单类型的转换操作由ConversionService配置的转换器来完成);因为使用request.getParameter()方式获取参数,所以可以处理get 方式中queryString的值,也可以处理post方式中 body data的值;
- 用来处理Content-Type: 为
application/x-www-form-urlencoded
编码的内容,提交方式GET、POST; - 该注解有两个属性: value、required; value用来指定要传入值的id名称,required用来指示参数是否必须绑定;
@RequestBody
该注解常用来处理Content-Type: 不是application/x-www-form-urlencoded
编码的内容,例如application/json, application/xml等;
它是通过使用HandlerAdapter 配置的HttpMessageConverters
来解析post data body,然后绑定到相应的bean上的。
注解6 @PostConstruct 和 @PreDestroy
@PostConstruct 和 @PreDestroy 不是用于依赖注入,而是bean 的生命周期。类似于 init-method(InitializeingBean) destory-method(DisposableBean).
Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean销毁前执行特定的操作,您既可以通过实现 InitializingBean/DisposableBean 接口来定制初始化之后 /销毁之前的操作方法,也可以通过 <bean> 元素的 init-method/destroy-method 属性指定初始化之后 /销毁之前调用的操作方法
容器初始化 bean 和销毁前所做的操作定义方式有三种:
第一种:通过@PostConstruct 和 @PreDestroy 方法 实现初始化后和销毁bean之前进行的操作
第二种是:通过 在xml中定义init-method 和 destory-method方法
第三种是: 通过bean实现InitializingBean和 DisposableBean接口
范例
public class Boss {
@Resource
private Car car;
@Resource(name = "office")
private Office office;
@PostConstruct
public void postConstruct1(){
System.out.println("postConstruct1");
}
@PreDestroy
public void preDestroy1(){
System.out.println("preDestroy1");
}
}
public class AnnoIoCTest {
public static void main(String[] args) {
String[] locations = {"beans.xml"};
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext(locations);
Boss boss = (Boss) ctx.getBean("boss");
System.out.println(boss);
ctx.destroy();// 关闭 Spring 容器,以触发 Bean 销毁方法的执行
}
}
最后说明下Spring对注解的处理
三 spring中注解的处理
spring中注解的处理基本都是通过实现接口 BeanPostProcessor 来进行的:
public interface BeanPostProcessor {
@Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
相关的处理类有:
AutowiredAnnotationBeanPostProcessor,
CommonAnnotationBeanPostProcessor,
PersistenceAnnotationBeanPostProcessor,
RequiredAnnotationBeanPostProcessor
...
-
这些处理类,可以通过 context:annotation-config/ 配置隐式的配置进spring容器。这些都是依赖注入的处理,还有生产bean的注解(@Component, @Controller, @Service, @Repository)的处理:
-
这些都是通过指定扫描的基包路径来进行的<context:component-scan base-package="..." />,将他们扫描进spring的bean容器。
-
注意context:component-scan也会默认将 AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor 配置进来。所以context:annotation-config/是可以省略的。另外context:component-scan也可以扫描@Aspect风格的AOP注解,但是需要在配置文件中加入 aop:aspectj-autoproxy/ 进行配合。