注解的好处:
1.能够读懂别人写的代码,特别是框架相关的代码。
2.本来可能需要很多配置文件,需要很多逻辑才能实现的内容,就可以使用一个或者多个注解来替代,这样就使得编程更加简洁,代码更加清晰。
3.(重点)刮目相看。
(但是怎么样才能让别人刮目相看呢?会用注解不是目的,最重要的是要使用自定义注解来解决问题。)
举个栗子:
如果面试的时候,你跟老板说你会使用注解,老板觉得你这个人还行;但是如果老板发现你会自定义注解解决问题,老板肯定就会眼前一亮。
注解这一概念是在java1.5版本提出的,说Java提供了一种原程序中的元素关联任何信息和任何元数据的途径的方法。
一、Java中的常见注解
1)JDK注解
JDK注解一共分为三类:
![](http://upload-images.jianshu.io/upload_images/4259662-a2124c2f3c861a5d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/684)
案例:
我们先新建一个接口people,如下:
public interface people {
public String name();
public int age();
public void work();
}
然后再建一个类Child实现类people这个接口,并实现该类的方法:
public class Child implements people {
@Override
public String name() {
return null;
}
@Override
public int age() {
return 0;
}
@Override
public void work() {
}
看到这里,我们发现这里的所有方法都会加上一个@Override标记,它告诉我们,同时也告诉编译器我们的这些方法肯定覆盖了类people里面的方法的。假如说,我现在把类people里面的某一个方法注释掉:
//public String name();
再看类Child里面的name方法就会报错。这样,以后大家看到@Override的时候就能想到这个方法是覆盖了某个接口的方法的。
然后,我们回过头来看类people里面有一个work的方法。这里我们可以理解为人是要工作的,但是并不是所有的人都在工作,那么怎么办呢?如果说这个接口正在用,我们不能删除这个方法,这个时候我们就可以这样:
@Deprecated
public void work();
@Deprecated标记就表明这个方法已经过时了,在实际中,它又有什么样的应用场景呢?我们在建一个测试类:
public class Test {
public void work() {
people people=new Child();
! people.work();
}
}
这个时候我们会发现myeclipse会给一个警告,并且在work中间出现一个破折号,意思就是这个方法已经过时了。那么问题来了,虽然这个方法过时了,但是我们就是那么傲娇,一定要用它,怎么办呢?只需要这样:
public class Test {
@SuppressWarnings("deprecation")
public void work() {
people people=new Child();
people.work();
}
}
这样我们就忽略了这个警告。@SuppressWarnings("deprecation")就表示我们忽略了deprecation这样的一个警告。
2)Java第三方注解
![](http://upload-images.jianshu.io/upload_images/4259662-f565063803be65c0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/675)
补充
1.在 service层有@Service 、在Controller有@Controller、在Dao层有@Repository
PS:@controller 控制器(注入服务)、@service 服务(注入dao)、@repository dao(实现dao访问)
2.@Autowired与@Resource的区别
a、@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。
b、@Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:
- @Autowired() @Qualifier("baseDao")
- private BaseDao baseDao;
如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找, 如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名 称进行装配。
- @Resource(name="baseDao")
- private BaseDao baseDao;
d、java代码中可以使用@Autowire或者@Resource注解方式进行装配,这两个注解的区别是:
@Autowire 默认按照类型装配,默认情况下它要求依赖对象必须存在如果允许为null,可以设置它required属性为false,如果我们想使用按照名称装配,可 以结合@Qualifier注解一起使用;
@Resource默认按照名称装配,当找不到与名称匹配的bean才会按照类型装配,可以通过name属性指定,如果没有指定name属 性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找 依赖对象.
3. @PreDestroy(JSR-250)注解 在方法上加上注解@PreDestroy,
这个方法就会在Bean初始化之后被Spring容器执行。其用法同@PostConstruct。和@PostConstruct 区别在于:@PostConstruct注释的方法将在类实例化后调用,而标注了 @PreDestroy 的方法将在类销毁之前调用。
4.@Scope注解
在使用XML定义Bean时,我们可能还需要通过bean的scope属性来定义一个Bean的作用范围,我们同样可以通过@Scope注解来完成这项工作:
@Scope("prototype")
@Controller
默认是单例模式,即scope="singleton"。另外scope还有prototype、request、session、global session作用域。scope="prototype"多例
@scope默认是单例模式(singleton)
如果需要设置的话@scope("prototype")
1.singleton单例模式,
全局有且仅有一个实例
2.prototype原型模式,
每次获取Bean的时候会有一个新的实例
3.request
1
2
3
4
5
6
7
|
<web-app>
...
<listener>
<listener-
class
>org.springframework.web.context.request.RequestContextListener</listener-
class
>
</listener>
...
</web-app>
|
4.session
session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效
5.global session
二、注解的分类
1)按照运行机制划分:
【源码注解→编译时注解→运行时注解】
源码注解:只在源码中存在,编译成.class文件就不存在了。
编译时注解:在源码和.class文件中都存在。像前面的@Override、@Deprecated、@SuppressWarnings,他们都属于编译时注解。
运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解。像@Autowired自动注入的这样一种注解就属于运行时注解,它会在程序运行的时候把你的成员变量自动的注入进来。
2)按照来源划分:
【来自JDK的注解——来自第三方的注解——自定义注解】
3)元注解:
元注解是给注解进行注解,可以理解为注解的注解就是元注解。
三、自定义注解
我们分四步来解析自定义注解:
自定义注解的语法要求:
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String desc();
String author();
int age() default 18;
}
首先我们要明确这不是一个接口,它是使用@interface关键字定义的一个注解。
然后我们看下面的几个方法,String desc();
虽然它很类似于接口里面的方法,其实它在注解里面只是一个成员变量(成员以无参无异常的方式声明),int age() default 18;
(成员变量可以用default指定一个默认值的)。
最后我们要知道:
①.成员类型是受限制的,合法的类型包括基本的数据类型以及String,Class,Annotation,Enumeration等。
②.如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号(=)。
③.注解类可以没有成员,没有成员的注解称为标识注解。
元注解:
有没有发现上面那段代码有一个没有说呢?没错,它们就是我们所说的元注解:
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
我们先看第一行:@Target是这个注解的作用域,ElementType.METHOD
是这个注解的作用域的列表,METHOD
是方法声明,除此之外,还有:CONSTRUCTOR(构造方法声明),FIELD(字段声明),LOCAL VARIABLE(局部变量声明),METHOD(方法声明),PACKAGE(包声明),PARAMETER(参数声明),TYPE(类接口)
第二行:@Retention是它的生命周期,前面不是说注解按照运行机制有一个分类嘛,RUNTIME
就是在运行时存在,可以通过反射读取。除此之外,还有:SOURCE(只在源码显示,编译时丢弃),CLASS(编译时记录到class中,运行时忽略),RUNTIME(运行时存在,可以通过反射读取)
第三行:@Inherited是一个标识性的元注解,它允许子注解继承它。
第四行:@Documented,生成javadoc时会包含注解。
使用自定义注解:
使用注解的语法:
@<注解名>(<成员名1>=<成员值1>,<成员名1>=<成员值1>,...)
案例:
@Description(desc="i am Color",author="boy",age=18)
public String Color() {
return "red";
}
这里的Description是我们刚才在自定义注解语法要求里面定义的注解噢,然后我们可以给它的每一个成员变量赋值,注意数据类型。值得注意的是,因为我们前面定义的作用域是在方法和类接口上,所以这个注解在Color()方法上使用是没问题的。
解析注解
概念:
通过反射获取类 、函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑。
准备工作:
Description类.png![](http://upload-images.jianshu.io/upload_images/4259662-15d79137fb666701.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/575)
接下来,我们就开始测试了:
public class ParseAnn {
public static void main(String[] args) {
try {
// 使用类加载器加载类
Class c = Class.forName("com.test.Child");
// 找到类上面的注解
boolean isExist = c.isAnnotationPresent(Description.class);
// 上面的这个方法是用这个类来判断这个类是否存在Description这样的一个注解
if (isExist) {
// 拿到注解实例,解析类上面的注解
Description d = (Description) c.getAnnotation(Description.class);
System.out.println(d.value());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出的结果:i am class annotation
可以看到,我们成功的解析了Child类上面的注解。
接下来,我们继续解析方法上的注解:
//获取所有的方法
Method[] ms = c.getMethods();
// 遍历所有的方法
for (Method m : ms) {
boolean isExist1 = m.isAnnotationPresent(Description.class);
if (isExist1) {
Description d1=m.getAnnotation(Description.class);
System.out.println(d1.value());
}
}
输出的结果:i am class annotation
i am method annotation
可以看到,我们成功的解析了方法上面的注解。
//另一种解析方法
for (Method m : ms) {
//拿到方法上的所有的注解
Annotation[] as=m.getAnnotations();
for (Annotation a : as) {
//用二元操作符判断a是否是Description的实例
if (a instanceof Description) {
Description d=(Description) a;
System.out.println(d.value());
}
}
}
也可以得到上面的效果。
此时,如果把Description类里面的元注解改一下,比如:
@Retention(RetentionPolicy.RUNTIME)→@Retention(RetentionPolicy.SOURCE),再运行程序,结果会成怎样呢?如果改成CLASS呢?读者们要不要试一试?
![](http://upload-images.jianshu.io/upload_images/4259662-81723683d33b9d3e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/529)