07_注解与动态代理

注解

什么是注解
  • 注解(Annotation),就是 Java 代码里面的特殊标记,例如 @Override、@Test 等
  • 作用:让其他程序根据注解信息来决定怎么执行该程序
  • 注意:注解可以用在类上、构造器上、方法上、成员变量上、参数上…
  • 注解的本质是一个接口,Java 中所有注解都是继承了 Annotation 接口的
  • @注解(…) 其实就是一个实现类对象,实现了该注解以及 Annotation 接口
自定义注解
// 自定义注解
public @interface MyTest {
    String aaa();

    boolean bbb() default true;

    String[] ccc();
}
// 方法部分
@MyTest(aaa = "Jack", ccc = {"Java", "HTML"})
public void run() {}

特殊属性名:value——如果注解中只有一个 value 属性,使用注解时,value 名称可以不写!

public @interface MyTest {
    String value();  // 特殊属性
}
public @interface MyTest {
    String value();  // 特殊属性
    int age() default 23;  // 当有默认值时候,可以不删除此行
}
@MyTest("Jack")
public void run() {}
元注解
  • 元注解指的是:修饰注解的注解
@Target(ElemeElenntType.TYPE) 作用:声明被修饰的注解只能在哪些位置使用
TYPE 类,接口
FIELD 成员变量
METHOD 成员方法
PARAMETER 方法参数
CONSTRUCTOR 构造器
LOCAL_VARIABLE 局部变量
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElemeElenntType.TYPE,ElementType.METHOD})  // 当前被修饰的注解只能用在类和成员方法上
public @interface MyTest {
}
@Retention(RetentionPolicy.RUNTIME) 作用:声明注解的保留周期
SOURCE 只作用在源码阶段,字节码文件中不存在
CLASS(默认值) 保留到字节码文件阶段,运行阶段不存在
RUNTIME(开发常用) 一直保留到运行阶段
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})  // 当前被修饰的注解只能用在类和成员方法上
@Retention(RetentionPolicy.RUNTIME)  // 控制下面的注解一直保留到运行时
public @interface MyTest {
}
注解的解析

什么是注解的解析?

  • 就是判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来

如何解析注解?

  • 思想:要解析谁上面的注解,就应该先拿到谁
// 自定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
    String value();
    double aaa() default 100;
    String[] bbb();
}
// 在类和方法上使用注解

@MyTest(value = "Jack", aaa = 99.8, bbb = {"Java", "HTML"})
public class Demo {
    public static void main(String[] args) {}

    @MyTest(value = "Tony", aaa = 88.6, bbb = {"java", "html"})
    public void run() {}
}
// 解析类上的注解

import org.junit.Test;
import java.util.Arrays;
public class AnnotationTest {
    @Test
    public void parseClass() {
        // 1. 先得到 Class 对象
        Class c = Demo.class;
        
        // 2. 解析类上的注解
        // 判断这个类上是否包含了某个注解
        if (c.isAnnotationPresent(MyTest.class)) {
            MyTest myTest = (MyTest) c.getDeclaredAnnotation(MyTest.class);
            System.out.println(myTest.value());
            System.out.println(myTest.aaa());
            System.out.println(Arrays.toString(myTest.bbb()));
        }
    }
}
/*
运行结果:
    Jack
    99.8
    [Java, HTML]
 */
// 解析方法上的注解

import org.junit.Test;
import java.lang.reflect.Method;
import java.util.Arrays;

public class AnnotationTest {
    @Test
    public void parseMethod() throws Exception {
        // 1. 先得到 Class 对象,然后通过 Class 对象获取方法对象
        Class c = Demo.class;
        Method m = c.getDeclaredMethod("run");

        // 2. 解析方法上的注解
        // 判断这个方法上是否包含了某个注解
        if (m.isAnnotationPresent(MyTest.class)) {
            MyTest myTest = (MyTest) m.getDeclaredAnnotation(MyTest.class);
            System.out.println(myTest.value());
            System.out.println(myTest.aaa());
            System.out.println(Arrays.toString(myTest.bbb()));
        }
    }
}
/*
运行结果:
    Tony
    88.6
    [java, html]
 */
应用场景

案例:模拟 Junit 框架

// 需求:定义若干个方法,只要加了MyTest注解,就会触发该方法的执行
/*  分析:
	 1.定义一个自定义注解 MyTest,只能注解方法,存活范围是一直都在
	 2.定义若干个方法,部分方法加上 @MyTest 注解修饰,部分方法不加
	 3.模拟一个 Junit 程序,可以触发加了 @MyTest 注解的方法执行
*/
// 自定义注解部分

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)  // 注解只能注解方法
@Retention(RetentionPolicy.RUNTIME)  // 让当前注解可以一直存活着
public @interface MyTest {}
// 模拟框架部分

import java.lang.reflect.Method;

public class AnnotationTest {

    public void test1() {
        System.out.println("===test1===");
    }

    @MyTest
    public void test2() {
        System.out.println("===test2===");
    }

    public void test3() {
        System.out.println("===test3===");
    }

    @MyTest
    public void test4() {
        System.out.println("===test4===");
    }

    public static void main(String[] args) throws Exception {
        AnnotationTest a = new AnnotationTest();
        // 启动程序!
        // 1. 得到 Class 对象
        Class c = AnnotationTest.class;

        // 2. 提取这个类中的全部成员方法
        Method[] methods = c.getDeclaredMethods();

        // 3. 遍历,获取到每个方法对象,看看其方法上是否存在 @MyTest 注解
        // 如果存在,则触发该方法执行
        for (Method method : methods) {
            if (method.isAnnotationPresent(MyTest.class)) {
                method.invoke(a);  // 触发当前方法执行
            }
        }
    }
}
/*
运行结果:
    ===test2===
    ===test4===
 */

动态代理

代理:将一些通用部分的代码提取出来,交给代理去实现(类比 Python 的装饰器)
// 创建一个明星类(前置工作)

// 明星(类)
public class Star implements Agent {
    private String name;

    public Star(String name) {
        this.name = name;
    }
    
    // 唱歌方法
    public String sing(String title) {
        System.out.println(this.name + "正在唱" + title);
        return "Think everyone!";
    }
    
    // 跳舞方法
    public void dance() {
        System.out.println(this.name + "正在跳舞");
    }
}
// 创建一个代理人接口(前置工作)

// 代理人(接口)
public interface Agent {
    String sing(String name);
    void dance();
}
// 创建一个工具类,即:能制作出"代理人"的公司(前置工作)

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 中介公司(类)
public class ProxyUtil {
    // 创建代理对象的静态方法
    public static Agent createProxy(Star sss) {
        /*
        public static Object newProxyInstance(ClassLoader loader,
                                              Class[] interfaces,
                                              InvocationHandler h)
              参数一:用于指定一个类加载器
              参数二:指定生成的代理长什么样子,也就是有哪些方法
              参数三:用来指定生成的代理对象要干什么事情
        */
        Agent starProxy = (Agent) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(),  // 参数一
                new Class[]{Agent.class},  // 参数二
                new InvocationHandler() {  // 参数三
                    @Override  // 回调方法
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 代理对象要做的事情,请在这里写代码
                        if (method.getName().equals("sing")) {
                            System.out.println("准备话筒,收钱20W");
                        } else if (method.getName().equals("dance")) {
                            System.out.println("准备场地,收钱99W");
                        }
                        return method.invoke(sss, args);
                    }
                });
        return starProxy;  // 本静态方法的最后是————返回创建好的"代理对象"
    }
}
// 调用工具类,让公司为某个明星对象去定制一个专属代理人(正式部分)

public class Test {
    public static void main(String[] args) {
        // 创建一个明星,叫"迈克尔-杰克逊"
        Star star = new Star("Michael-Jackson");
        // 为"迈克尔-杰克逊"创建一个代理人
        Agent agent = ProxyUtil.createProxy(star);

        System.out.println(agent.sing("新年好"));  // 让代理人安排他唱歌
        System.out.println("-----------------");
        agent.dance();  // 让代理人安排他跳舞
    }
}

/* 运行结果:
    准备话筒,收钱20W
    Michael-Jackson正在唱新年好
    Think everyone!
    -----------------
    准备场地,收钱99W
    Michael-Jackson正在跳舞
 */

你可能感兴趣的:(Java高级,java)