注解的简单学习?

前言

当下的开发中都推荐使用注解,这样就可以免去写麻烦的xml配置,是十分便利的一项技术

什么是注解(Annotation)?

注解其实就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相对应的处理。

为什么要用到注解?

传统的方式中,我们通过配置文件(xml文件)类告诉文件类是怎么运行的,有了注解技术以后,我们可以通过注解告诉类如何运行

注解可以给方法、类注入信息

Java.lang包存在的五个基本的Annotation

1@override
该注解告诉编译器要检查该方法是实现父类的,比如当我们重写方法的时候,方法的结构)(参数返回值等)父类中并没有定义,则会给出清晰的报错以避免出现问题。

2@Deprecated
java设计的时候,可能会觉得某些方法设计的不好,但是为了兼容之前的程序,不能将其直接抛弃,则可以设置它为过时:当调用者对这个方法进行调用的时候,IDE上会出现一条删除线来提示调用者这个方法已经是过时的。

3@SuppressWarning
该注解的作用是可以使用它来让编译器不给予我们警告,一般不是很常用

4@SafeVarags
java7“堆污染”警告抑制(也不常用)

  • **堆污染:**当把一个不是泛型的集合赋值给一个泛型的集合的时候,这种情况下很容器发生堆污染

5@FunctionalInterface
用该注解来表示该接口是一个函数式接口


自定义注解基础

我们还可以自己来编写注解,给方法和类注入信息

1标记注解
没有任何成员变量的注解称为标记注解

//看起来它像一个接口定义
public @interface MyAnnotation{}

2元数据注解
我们自定义的注解是可以拥有成员变量的,拥有成员变量的注解叫做
元数据Annotation
定义变量的语法类似于声明方法?

public @interface MyAnnotation{
//定义了两个变量
String username();
int age();
}

在注解上定义的成员只能是String、数组、Class、枚举类、注解(对应xml的标签嵌套)

使用自定义注解

1常规使用

下面有个add方法,需要username和age参数
我们将通过注解来让方法拥有这两个变量!

//注解有什么属性,在修饰的时候就要给出对应的值
@MyAnnotation(username ="chenlele",age = "32")
public void add(String username,int age){
要想不赋值,可以在注解声明属性的时候给出默认值:
String username() default "chenlele";
}

注解的属性为value
如果注解上只有一个属性,并且它的属性名称为value,那么在使用的时候,我们可以不使用value,直接对它赋值就行。

public @interface MyAnnotation2{
	String value();
}

(使用注解,可以不指定value,直接赋值)

@MyAnnotation2 ("chenlele")
public void find(String id){

}

将自定义注解的基本信息注入到方法上

上面我们已经使用了注解,但目前为止的自定注解上的信息和方法上的信息是没有关联的。

将注解上的信息注入方法上,我们利用反射技术,步骤可以分为三步:

  • 反射出该类的方法
  • 通过方法得到注解上具体的信息
  • 将注解上的信息注入到方法上
//反射出该类的方法
Class aClass = Demo2.class;
Method method = aClass.getMethod("add",String.class,int.class);

//通过该方法得到注解上的具体信息
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
String username = annotation.username();
int age = annotation.age();

//将注解上的信息注入到方法上
Object o = aClass.newInstance();
method.invoke(o,username,age);

但是这样执行的时候我们发现会出现异常:
此时我们要在自定义注解上加上这样一句:

@Retention(RetentionPolicy.RUNTIME)

JDK的元Annotation

在java.lang.annotation下也有几个常用的元Annotation,他们都是用于修饰其他的Annotation定义。

@Retention
该注解只能用于修饰其他的Annotation,用于指定被修饰的Annotation被保留多长时间
它包含了一个RetentionPolicy类型的value变量,所以在使用的时候必须为这个成员变量赋值
只有三个值

  • SOURCE 编译
  • CLASS 字节码
  • RUNTIME 运行时
    默认情况下是class,前面我们的反射是在运行时期来动态获取信息的,则获取不到默认的Annotation的信息,于是就需要在自定义注解上修改它的存活时间

@Target
用来指定被修饰的Annotation用于修饰哪些程序单元

  • TYPE
  • FIELD
  • PARAMETER
  • CONSTRUCTOR
  • LOCAL_VARIABLE
  • ANNOTATION_TYPE(只能修饰annotation)
  • PACKGE

@Inherited
被修饰的注解将在类的实现类和继承类上进行传递


注入对象到方法/成员变量上

前面我们已经可以用注解将基本的信息注入到方法上了,现在我们想要将对象注入到方法上…
上面已经说过,注解中只能定义String,枚举类,Double之类的成员变量,那怎么把对象注入到方法上呢?

模拟场景:

Person类,拥有username和age属性和他们的访问器修改器

public class Person{
	private String username;
	private int age;

	public String getUsername(){
		return username;
	}
	public void setUsername(String username){
		this.username = username;
	}
	public int getAge(){
		return age;	
	}
	public void setAge(int age){
		this.age = age;
	}

}

PersonDao类,此类定义了Person对象,拥有person的getter/setter

public class PersonDao{
	private Person person;
	public Person getPerson(){
		return person;
	}
	public void setPerson(Person person){
		this.person = person;
	}
}

现在要做的事:使用注解将Person对象注入到setPerson()方法中,从而设置了PersonDao类的person属性。

public class PersonDao{
	private Person person;
	public Person getPerson(){
		return person;
	}

//将username改为chenlele,age为32的Person对象注入到setPerson
@InjectPerson (username="chenlele",age =32)
	public void setPerson(Person person){
		this.person = person;
	}
}

步骤:

(1)自定义一个注解,属性和javaBean一致

//注入工具是通过反射来获取到注解的信息的,所以保留域必须设置为RUNTIME
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectPerson{
	
	String username();
	int age();
}

(2)编写注入工具

//1使用内省【后面需要得到属性的写方法】得到想要的属性
PropertyDescriptor descriptor = new PropertyDescriptor("person",PersonDao.class);
//2得到想要注入属性的具体对象
Person person = (Person) descriptor.getPropertyType().newInstance();
//3得到该属性的写方法【setPerson()】
Method method = descriptor.getWriteMethod();
//4得到写方法的注解
Annotation annotation = method.getAnnotation(InjectPerson.class);
//5得到注解上的信息【注解的成员变量就是用方法来定义的】
Method【】 methods = annotation.getClass().getMethods();
//6将注解上的信息填充到person对象上
for(Method m :methods){

//得到注解上属性的名字【age或者name】
String name = m.getName();

//看看Person对象有没有与之对应的方法【setAge().setName()】
	try{
	//这里假设:写方法存在,获取到了写方法
	PropertyDescriptor descriptor1 = new PropertyDescriptor(name,Person.class);
	Method method1 = descriptor1.getWriteMethod();//setAge(),setName()
	//得到注解中的值
	Object o = m.invoke(annotation,null);
	//调用Person对象的setter方法,将注解上的值设置进去
	method1.invoke(person,o);
	
	}catch(Exception e){
		// Person对象没有与之对应的方法,跳到catch中,我们只需要让他继续遍历注解就好:
		continue;
	}
	}
	//当程序遍历完之后,person对象就已经填充完数据了
	//将person对象赋值给PersonDao【通过写方法】
	PersonDao personDao = new PersonDao();
	method.invoke(personDao,person);

实际的注入过程:

  • 得到想要类中注入的属性
  • 得到该属性的对象
  • 得到属性对应的写方法
  • 通过写方法得到注解
  • 获取注解详细的信息
  • 将注解的信息注入到对象上
  • 调用属性写方法,将已填充数据的对象注入到方法中

将对象注入到成员变量

上面已经说了如何将一个对象注入到方法上,那么注入到成员变量上也是非常简单:

步骤:
(1)在成员变量上使用注解

public class PersonDao{
	@InjectPerson(username = "chenlele",age=20) private Person person;

	public Person getPerson(){
		return person;
	}

	public void setPerson(Person person){

		this.person = person;
	}
}

(2)编写注入工具

//1得到想要注入的属性
Field field = PersonDao.class.getDeclaredField("person");
//2得到属性的具体对象
Person person = (Person)field.getType().newInstance();
//3得到属性上的注解
Annotation annotation = field.getAnnotation(InjectPerson.class);
//4得到注解的属性【注解上的属性是使用方法来表示的】
Method[] methods = annotation.getClass().getMethods();
//5将注入的属性填充到person对象上
for (Method method : methods){
	//5.1得到注解的属性名
	String name = method.getName();
	//查看Person有没有与之对应的写方法
	try{
		//如果有
		PropertyDescriptor.descriptor = new PropertyDesriptor(name,Person.class);
		//得到Person对象上的写方法
		Method method1 = descriptor.getWriteMethod();
		//得到注解上的值
		Object o = method.invoke(annotation,null);
		//填充person对象
		method1.invoke(person,o); 
	}catch(IntrospectionException e){
		//如果没有想要找的对应属性,继续循环
		continue;
	}
//循环完以后,person就即填充好数据了
// 6 把person对象设置到PersonDao中:
PersonDao personDao = new PersonDao();
field.setAccessible(true);
field.set(personDao,person);



}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值