注解
注解在开发中常被我们利用到反射机制中,辅助反射机制做更多灵活的操作
注解在如今JAVA流行的框架中被大量的应用,简化了以前繁琐的配置工作。
注解可以在:
类上,属性上,方法上,构造器上,以及参数上使用,即在方法的大括号里面不能使用。
可以通过java内置的注解@Target来说明当前注解可以被应用的位置,对应的值被定义在ElementType上
例如:
@Target(ElementType.TYPE) 注解只能被用于类上
@Target({ElementType.TYPE,ElementType.METHOD}) 注解只能被用于类上或方法上
当可以用于多个位置时,需要定义成数组的方式包含所有ElementType的值,即"{}"包含
@Retention注解,用于标注当前注解的保留级别,有三个选项
RetentionPolicy.SOURCE 注解仅保留在源代码中
RetentionPolicy.CLASS 注解保留在字节码中,但是反射机制不能调用
RetentionPolicy.RUNTIME 注解保留在字节码文件中,并且可以被反射机制所使用
当不指定@Retention时,默认的保留级别为CLASS,因此我们通常都需要明确指出保留级别为RUNTIME
设置注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunClass {
}
/**
* 注解可以定义参数
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunMethod {
/*
定义参数的格式为:
格式:类型 参数名() [default 默认值]
注:default可选,用于为当前参数定义默认值。如果不指定,则使用注解时必须为此参数赋值。
使用注解传参时格式:
@注解名(参数名1=参数值1[,参数名2=参数值2,....])
如果注解@AutoRunMethod只有一个参数,且参数名为num时,那么使用时格式如下:
@AutoRunMethod(num=1)
=============重点=============
如果注解中只有一个参数,参数名建议选取value,这样的好处是,使用时可以不指定参数名,如:
@AutoRunMethod(1)
如果指定了默认值,则可以不指定参数,例如:
@AutoRunMethod() 此时注解中参数的使用default的默认值
*/
//为注解定义一个int型的参数
// int num() default 1;//一个参数时,参数名不建议选取value以外的名字。
int value() default 1;
}
将注释用在具体类里面:
@AutoRunClass
public class Person {
private String name = "张三";
private int age= 18;
public Person(){
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@AutoRunMethod(3)
public void sayHello(){
System.out.println(name+":Hello!");
}
@AutoRunMethod(5)
public void sayHi(){
System.out.println(name+":Hi");
}
@AutoRunMethod
public void watchTV(){
System.out.println(name+":看电视");
}
public void sing(){
System.out.println(name+"唱歌");
}
private void dosome(){
System.out.println("我是Person的私有方法dosome()");
}
public void doSomeThing(String something){
System.out.println(name+"正在做"+something);
}
public void doSomeThing(String something,int count){
for(int i=0;i<count;i++){
System.out.println(name+"正在做"+something+i+"次");
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
@AutoRunClass
public class Student {
@AutoRunMethod(7)
public void study(){
System.out.println("好好学习,天天向上");
}
public void playGame(){
System.out.println("Student:玩游戏!");
}
}
1、反射机制中查看注解:
public class ReflectDemo8 {
public static void main(String[] args) throws Exception {
Class cls = Class.forName("reflect.Person");
//判断当前类对象所表示的类是否被注解@AutoRunClass标注了?
boolean tf = cls.isAnnotationPresent(AutoRunClass.class);
if(tf){
System.out.println(cls.getName()+"被注解@AutoRunClass标注了!");
} else {
System.out.println(cls.getName()+"没有被注解@AutoRunClass标注了!");
}
}
}
2、方法上查看注解:
public class ReflectDemo9 {
public static void main(String[] args) throws Exception{
Class cls = Class.forName("reflect.Person");
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
/*
除了类对象Class之外,像方法对象Method,属性对象Field等都有
该方法,用于判断其表示的内容是否被某个注解标注了
*/
if(method.isAnnotationPresent(AutoRunMethod.class)){
System.out.println(method.getName()+":被注解@AutoRunMethod标注了");
} else {
System.out.println(method.getName()+":没有被注解@AutoRunMethod标注了");
}
}
}
}
3、实例1,自动实例化与当前类Test3在同一个包中被@AutoRunClass标注的类。
public class Test3 {
public static void main(String[] args) throws Exception {
File dir = new File(
Test3.class.getResource(".").toURI()
);
//通过当前类Test3的类对象获取所在的包名
String packageName = Test3.class.getPackage().getName();
//获取Test3.class文件所在的目录中所有.class文件
File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub : subs) {
//获取字节码文件的文件名
String fileName = sub.getName();
String className = fileName.substring(0, fileName.indexOf("."));
//加载该类的类对象
Class cls = Class.forName(packageName + "." + className);
if(cls.isAnnotationPresent(AutoRunClass.class)){
System.out.println("实例化:"+className);
Object o = cls.newInstance();
}
}
}
}
4、实例2,自动调用与Test4在同一个包中那些被@AutoRunClass标注的类中所有被@AutoRunMethod标注的方法。
public class Test4 {
public static void main(String[] args) throws Exception {
File dir = new File(
Test4.class.getResource(".").toURI()
);
//通过当前类Test3的类对象获取所在的包名
String packageName = Test4.class.getPackage().getName();
//获取Test3.class文件所在的目录中所有.class文件
File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub : subs) {
//获取字节码文件的文件名
String fileName = sub.getName();
String className = fileName.substring(0, fileName.indexOf("."));
//加载该类的类对象
Class cls = Class.forName(packageName + "." + className);
if(cls.isAnnotationPresent(AutoRunClass.class)){
System.out.println("实例化:"+className);
Object o = cls.newInstance();
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
if(method.isAnnotationPresent(AutoRunMethod.class)){
System.out.println("调用了"+cls.getName()+"的"+method.getName()+"方法");
method.invoke(o);
}
}
}
}
}
}
5、在反射机制中获取注解的参数
public class ReflectDemo10 {
public static void main(String[] args) throws Exception {
Class cls = Class.forName("reflect.Person");
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
//判断该方法是否被注解@AutoRunMethod标注了
if(method.isAnnotationPresent(AutoRunMethod.class)){
//通过方法对象获取该注解
AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);
int value = arm.value();
System.out.println(
"方法"+method.getName()+
"上的注解AutoRunMethod指定的参数值为:"+value
);
}
}
}
}
6、实例3, 自动调用与Test4在同一个包中那些被@AutoRunClass标注的类中所有被@AutoRunMethod标注的方法n次n对应的是注解@AutoRunMethod传入的参数值。
public class Test5 {
public static void main(String[] args) throws Exception {
File dir = new File(
Test4.class.getResource(".").toURI()
);
//通过当前类Test3的类对象获取所在的包名
String packageName = Test4.class.getPackage().getName();
//获取Test3.class文件所在的目录中所有.class文件
File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub : subs) {
//获取字节码文件的文件名
String fileName = sub.getName();
String className = fileName.substring(0, fileName.indexOf("."));
//加载该类的类对象
Class cls = Class.forName(packageName + "." + className);
if(cls.isAnnotationPresent(AutoRunClass.class)){
Object o = cls.newInstance();
//获取该类定义的所有方法
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
if(method.isAnnotationPresent(AutoRunMethod.class)){
AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);
int value = arm.value();
System.out.println("自动调用"+className+"类的方法:"+method.getName()+"()"+value+"次");
for(int i=0;i<value;i++) {
method.invoke(o);
}
}
}
}
}
}
}
7、getDeclaredMethods方法的有参形式怎么传:
File dir = new File(
DispatcherServlet.class.getClassLoader().getResource(
"./com/webserver/controller"
).toURI()
);
File[] subs = dir.listFiles(f -> f.getName().endsWith(".class"));
for (File sub : subs) {
String fileName = sub.getName();
String className = fileName.substring(0, fileName.indexOf("."));
Class cls = Class.forName("com.webserver.controller." + className);
//判断该类是否被@Controller标注了
if (cls.isAnnotationPresent(Controller.class)) {
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
//判断该方法是否被@RequestMapping标注了
if (method.isAnnotationPresent(RequestMapping.class)) {
//获取该注解
RequestMapping rm = method.getAnnotation(RequestMapping.class);
//获取该注解的参数(该方法处理的请求路径)
String value = rm.value();
if (path.equals(value)) {//判断当前请求是否为该方法处理的请求
//实例化该Controller
Object o = cls.newInstance();
//执行该方法
method.invoke(o, request, response);
return;
}
}
}
}
}