Java注解
一、定义:
注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。
二、作用:
①编写文档 :通过代码里标识的元数据生成文档【生成文档doc文档 @param @return @see @exception @version @author @since】
②编译检查 :通过代码里标识的元数据让编译器能够实现基本的编译检查【@Override @Deprecated [ˈdeprəkeɪtɪd] @SuppressWarnings】
③运行时分析:通过代码里标识的元数据对代码进行分析【使用反射解析注解】
三、注解与注释区别:
注释:是对代码的描述、说明, 编译器在编译生成字节码文件时一般会自动忽略注释,在运行时无法获取。
注解:可以存在于源文件、class文件、以及程序运行时,是代码的一部分。
四、Java内置三种标准注解:
① @Deprecated
注解为不建议使用,可以用在 方法和类上。
基本上这种方法和类都是因为升级或性能上面的一些原因废弃不建议使用,但是为了兼容或其他原因,还必须保留。
所以就打上这个注解。
在Java 本身的API中就有很多这样的例子, 方法打上了这个注解,进到Source code 会看到替代的新的方法是哪个。
在eclipse 中编写code时,添加此注解的方法在声明和调用的地方都会加上删除线。
② @Override
覆盖超类中的方法。
如果不小心拼写错误,或者方法签名对不上被覆盖的方法,编译器就会发出错误提示信息。
③ @SuppressWarnings
忽略警告。
如果你的code在转型或其他的部分有一些警告的话,但是你又想忽略这些警告,就可以使用这个注解了。
1) deprecation 使用了不赞成使用的类或方法时的警告
2) unchecked 执行了未检查的转换时警告
3) fallthrough 当使用switch操作时case后未加入break操作,而导致程序继续执行其他case语句时出现的警告
4) path 当设置一个错误的类路径、源文件路径时出现的警告
5) serial 当在可序列化的类上缺少serialVersionUID定义时的警告
6) fianally 任何finally子句不能正常完成时警告
7) all 关于以上所有情况的警告
五、Java内置四种元注解:
元注解的作用就是负责注解其他注解,在自定义注解的时候经常使用。
Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
1.@Target : 描述注解修饰对象范围
2.@Retention : 描述注解存在的生命周期
3.@Documented : 在JavaDoc中,包含此注解信息
4. @Inhretied : 允许子类继承父类中的注解
@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(); }
@Target:
@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
8.ANNOTATION_TYPE :用于描述注解
@Retention:
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)
@Documented:
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
@Inherited:
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。
当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
六、自定义注解:
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
定义注解格式:
public @interface 注解名 {定义体}
注解参数的可支持数据类型:
1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
2.String类型
3.Class类型
4.enum类型
5.Annotation类型
6.以上所有类型的数组
Annotation类型里面的参数该怎么设定:
第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;
第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;
第三,如果只有一个参数成员,最好把参数名称设为"value",后加小括号.
七、自定义注解小例子:
1. 定义英雄注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Hero { //战士、巫师、刺客 public enum HeroType{WARRIOR, WIZARD, ASSASSIN}; HeroType heroType() default HeroType.WARRIOR; }
2. 定义技能注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Skill { //普通技能、终极技能 public enum SkillType{NORNAL, FINAL}; String skillDesc() default ""; SkillType skillType() default SkillType.NORNAL; }
3. 定义抽象英雄类
public abstract class AbstractHero { //英雄名称 private String heroName; AbstractHero(String heroName){ this.heroName = heroName; } //一技能 public abstract void Q(); //二技能 public abstract void W(); //三技能 public abstract void E(); //终极技能 public abstract void R(); public String getHeroName() { return heroName; } public void setHeroName(String heroName) { this.heroName = heroName; } }
4. 定义两个英雄类
@Hero(heroType = HeroType.WARRIOR) public class Tiny extends AbstractHero{ public Tiny() { super("小小"); } @Skill(skillDesc = "造成1.2秒眩晕") @Override public void Q() { System.out.println("山崩"); } @Skill(skillDesc = "起飞") @Override public void W() { System.out.println(this.getHeroName() + "释放了:投掷"); } @Skill(skillDesc = "抗揍神技") @Override public void E() { System.out.println(this.getHeroName() + "释放了:崎岖外表"); } @Skill(skillType = SkillType.FINAL , skillDesc = "攻速减半") @Override public void R() { System.out.println(this.getHeroName() + "释放了:长大"); } }
@Hero(heroType = HeroType.WIZARD) public class Ezalor extends AbstractHero{ public Ezalor() { super("甘道夫"); } @Skill(skillDesc = "冲击波冲击波") @Override public void Q() { System.out.println(this.getHeroName() + "释放了:冲击波"); } @Skill(skillDesc = "法力流失") @Override public void W() { System.out.println(this.getHeroName() + "释放了:法力流失"); } @Skill(skillDesc = "查克拉魔法") @Override public void E() { System.out.println(this.getHeroName() + "释放了:查克拉魔法"); } @Skill(skillType = SkillType.FINAL , skillDesc = "光之守卫") @Override public void R() { System.out.println(this.getHeroName() + "释放了:光之守卫"); } }
5. 定义测试类
public class HeroResover { public static void main(String[] args){ createOnlyWarrior(); createOnlyWarriorFinalSkill(); } /** * 创建hero包中的所有战士,并释放Q技能 */ private static void createOnlyWarrior(){ String basePackage = "com.hand.jeb.class1.heros"; //获取hero包下所有类 List<String> clazzNameList = PackageUtil.getClassName(basePackage); for(String clazzName : clazzNameList){ System.out.println(clazzName); try { Class<?> heroClazz = Class.forName(clazzName); //获取Hero注解 Hero heroAnnotation = heroClazz.getAnnotation(Hero.class); //如果类具有 @Hero注解,并且HeroType为WARRIOR if(heroAnnotation != null && HeroType.WARRIOR.equals(heroAnnotation.heroType())){ AbstractHero warrior = (AbstractHero) heroClazz.newInstance(); warrior.Q(); } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } } /** * 创建hero包中的所有战士,并释放终极技能 */ private static void createOnlyWarriorFinalSkill(){ String basePackage = "com.hand.jeb.class1.heros"; //获取hero包下所有类 List<String> clazzNameList = PackageUtil.getClassName(basePackage); for(String clazzName : clazzNameList){ System.out.println(clazzName); try { Class<?> heroClazz = Class.forName(clazzName); //获取Hero注解 Hero heroAnnotation = heroClazz.getAnnotation(Hero.class); if(heroAnnotation != null && HeroType.WARRIOR.equals(heroAnnotation.heroType())){ AbstractHero warrior = (AbstractHero) heroClazz.newInstance(); Method[] methods = heroClazz.getMethods(); for(Method method : methods){ //System.out.println(method.getName()); Skill skillAnnotation = method.getAnnotation(Skill.class); //如果方法上有终极技能注解则释放 if(skillAnnotation != null && SkillType.FINAL.equals(skillAnnotation.skillType())){ method.invoke(warrior); } } } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } } } }
八、Spring、SpringMVC常用注解:
1、@Component 是所有受Spring 管理组件的通用形式,相当于一个JavaBean
将上面的小例子作出修改,在英雄类上增加@Compent注解,并在Spring配置文件中增加包扫描
<context:component-scan base-package="com.hand.jeb.class1.heros"/>
创建Spring测试类
public class HeroSpringResover { private static ApplicationContext ctx = null; private static ApplicationContext getContextInstance(){ if(ctx != null) return ctx; else return new GenericXmlApplicationContext("classpath:/spring/applicationContext.xml"); } public static void main(String[] args) { createOnlyWarrior(); } /** * 创建hero包中的所有战士,并释放Q技能 */ private static void createOnlyWarrior(){ String basePackage = "com.hand.jeb.class1.heros"; //获取hero包下所有类 List<String> clazzNameList = PackageUtil.getClassName(basePackage); for(String clazzName : clazzNameList){ System.out.println(clazzName); try { Class<?> heroClazz = Class.forName(clazzName); //获取Hero注解 Hero heroAnnotation = heroClazz.getAnnotation(Hero.class); //如果类具有 @Hero注解,并且HeroType为WARRIOR if(heroAnnotation != null && HeroType.WARRIOR.equals(heroAnnotation.heroType())){ //AbstractHero warrior = (AbstractHero) heroClazz.newInstance(); //使用Spring获取bean AbstractHero warrior = (AbstractHero) getContextInstance().getBean(heroClazz); warrior.Q(); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } }
2、@Controller、@Service、@Repository,均继承自@Component,区分控制层、业务层、数据访问层
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller { String value() default ""; }
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { String value() default ""; }
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Repository { String value() default ""; }
3、Spring自动装配注解 @Resource、@Autowired、@Qualifier
@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入
@Autowired默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合@Qualifier一起使用
@Resource注解是由J2EE提供,而@Autowired是由Spring提供,故减少系统对spring的依赖建议使用@Resource的方式
@Resource和@Autowired都可以书写标注在字段或者该字段的setter方法之上
<bean id="heroDao" class="com.hand.jeb.HeroImpl"></bean>
@Resource(name="heroDao") private IHero hero; @Autowired //如果有多个类实现了同一个接口,需要使用Qualifier区分 @Qualifier("heroDao") private IHero hero;
4、@Transactional,事务注解
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Transactional { // @AliasFor注解可用于声明一双别名属性,来给注解的属性起别名 @AliasFor("transactionManager") String value() default ""; @AliasFor("value") // 可选的限定描述符,指定使用的事务管理器,同value String transactionManager() default ""; //可选的事务传播行为设置 Propagation propagation() default Propagation.REQUIRED; //可选的事务隔离级别设置 Isolation isolation() default Isolation.DEFAULT; //事务超时时间 int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; //读写或只读事务,默认读写 boolean readOnly() default false; //导致事务回滚的异常类数组 Class<? extends Throwable>[] rollbackFor() default {}; //导致事务回滚的异常类名称数组 String[] rollbackForClassName() default {}; //不会导致事务回滚的异常类数组 Class<? extends Throwable>[] noRollbackFor() default {}; //不会导致事务回滚的异常类名称数组 String[] noRollbackForClassName() default {}; }
Propagation : 事务传播行为
事务传播行为类型 |
说明 |
PROPAGATION_REQUIRED |
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS |
支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY |
使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW |
新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED |
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER |
以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED |
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类 似的操作。 |
Isolation: 事务隔离级别
事务隔离级别 |
说明 |
READ_UNCOMMITTED |
读取未提交数据(会出现脏读, 不可重复读) 基本不使用。 |
READ_COMMITTED |
读取已提交数据(会出现不可重复读和幻读) |
REPEATABLE_READ |
可重复读(会出现幻读) |
SERIALIZABLE |
串行化 |
5、SpringMVC @RequestMapping,@RequestBody,@ResponseBody,@RequestParam,@PathVariable
@RequestMapping :SpringMVC分发请求映射,属性 method = RequestMethod.POST/ RequestMethod.GET可以限制请求类型
@RequestBody :
该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上;再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上。
使用时机:
A) GET、POST方式提时, 根据request header Content-Type的值来判断:
- application/x-www-form-urlencoded, 可选(即非必须,因为这种情况的数据@RequestParam, @ModelAttribute也可以处理,当然@RequestBody也能处理);
- multipart/form-data, 不能处理(即使用@RequestBody不能处理这种格式的数据);
- 其他格式, 必须(其他格式包括application/json, application/xml等。这些格式的数据,必须使用@RequestBody来处理);
B) PUT方式提交时, 根据request header Content-Type的值来判断:
- application/x-www-form-urlencoded, 必须;
- multipart/form-data, 不能处理;
- 其他格式, 必须;
说明:request的body部分的数据编码格式由header部分的Content-Type指定;
@ResponseBody
作用:
该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。
使用时机:
返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用
@RequestParam
作用:
A) 常用来处理简单类型的绑定,通过Request.getParameter() 获取的String可直接转换为简单类型的情况( String--> 简单类型的转换操作由ConversionService配置的转换器来完成);因为使用request.getParameter()方式获取参数,所以可以处理get 方式中queryString的值,也可以处理post方式中 body data的值;
B)用来处理Content-Type: 为 application/x-www-form-urlencoded
编码的内容,提交方式GET、POST;
C) 该注解有两个属性: value、required; value用来指定要传入值的id名称,required用来指示参数是否必须绑定
@PathVariable
作用:
当使用@RequestMapping URI template 样式映射时, 即 someUrl/{paramId}, 这时的paramId可通过 @Pathvariable注解绑定它传过来的值到方法的参数上。