Day13、单元测试-动态代理

Day13、单元测试-动态代理

课程安排

image-20220523194637800

单元测试

单元测试

  • 单元测试就是针对最小的功能单元编写测试代码, 归姹程序最小的功能单元是方法, 因此,单元测试就是针对归java方法的测试,进而检查方法的正确性。

目前测试方法是怎么进行的, 存在什么问题

  • 只有一个main 方法, 如果一个方法的测试失败了, 其他方法测试会受到影响。

  • 无法得到测试的结果报告, 需要程序员自己去观察测试是否成功。

  • 无法实现自动化测试。

Junit单元测试框架

  • JUnit 是使用归va 语言实现的单元测试框架, 它是开源的, 扫va 开发者都应当学习并使用Junit 编写单元测试。

  • 此外,几乎所有的IDE工具都集成了JUnit , 这样我们就可以直接在IDE 中编写并运行JUnit 测试,JUnit 目前最新版本是5 。

JUnit 优点

  • JUnit 可以灵活的选择执行哪些测试方法, 可以一键执行全部测试方法。

  • Junit 可以生成全部方法的测试报告。

  • 单元测试中的某个方法测试失败了, 不会影响其他测试方法的测试。

总结:

image-20220523195359427

单元测试快速入门

image-20220523195541296

Junit常用注解(Junit 4.xxxx版本)

注解 说明
@Test 测试方法
@Before 用来修饰实例方法, 该方法会在每一个测试方法执行之前执行一次。
@After 用来修饰实例方法, 该方法会在每一个测试方法执行之后执行一次。
@BeforeClass 用来静态修饰方法, 该方法会在所有测试方法之前只执行一次。
@AfterClass 用来静态修饰方法, 该方法会在所有测试方法之后只执行一次。
  • 开始执行的方法: 初始化资源。

  • 执行完之后的方法: 释放资源。

Junit常用注解(Junit 5.xxxx版本)

注解 说明
@Test 测试方法
@BeforeEach 用来修饰实例方法, 该方法会在每一个测试方法执行之前执行一次。
@AfterEach 用来修饰实例方法, 该方法会在每一个测试方法执行之后执行一次。
@BeforeAll 用来静态修饰方法, 该方法会在所有测试方法之前只执行一次。
@AfterAll 用来静态修饰方法, 该方法会在所有测试方法之后只执行一次。
  • 开始执行的方法: 初始化资源。

  • 执行完之后的方法: 释放资源。

反射

反射概述

  • 反射是指对于任何一个Class 类, 在" 运行的时候" 都可以直接得到这个类全部成分。

  • 在运行时, 可以直接得到这个类的构造器对象: Constructor

  • 在运行时, 可以直接得到这个类的成员变量对象: Field

  • 在运行时, 可以直接得到这个类的成员方法对象: Method

  • 这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。

反射的关键:

  • 反射的第一步都是先得到编译后的Class类对象, 然后就可以得到Class的全部成分。

HelloWorld.java 一> Javac 一> HelloWorld.class
Class c = HelloWorld.class;

总结:

image-20220523203040524

反射的第一步:获取Class类的对象

image-20220523204003543

总结:

image-20220523204027913

//方法一:class类中的一个静态方法。forname(全限名:包名+类名)
Class c1 = Class.forName("com.cafune.Day02_reflect_class.Student");
System.out.println(c1);

//方法二:类名.class
Class c2 = Student.class;
System.out.println(c2);

//方法三:对象.getClass(),获取对象对应类的Class对象
Student s = new Student();
Class c3 = s.getClass()
System.out.println(c3);

使用反射技术获取构造器对象并使用

image-20220523204125304

反射的第一步是先得到类对象, 然后从类对象中获取类的成分对象。

CIass 类中用于获取构造器的方法

方法 说明
Constructor <?> [] getConstructors() 返回所有构造器对象的数组( 只能拿public的)
Constructor<?> [] getDeclaredConstructors() 返回所有构造器对象的数组, 存在就能拿到
Constructor getConstructor(Class<?>...parameterTypes) 返回单个构造器对象( 只能拿public的)
Constructor getDeclaredConstructor(Class<?>...parameterTypes) 返回单个构造器对象, 存在就能拿到
@Test
public void getConstructors() throws Exception {
    Class c1 = Class.forName("com.cafune.Day03_reflect_constructor.Student");
        /*
        Class c2 = Student.class;
        Student s = new Student();
        Class c3 = s.getClass();
        */
    Constructor[] constructors = c1.getConstructors();//只能拿punlic的构造器
    for (Constructor constructor : constructors) {
        System.out.println(constructor.getName() + "===>"
                + constructor.getParameterCount());
    }
}

@Test
public void getDeclaredConstructors() throws Exception {
    Class c1 = Class.forName("com.cafune.Day03_reflect_constructor.Student");
        /*
        Class c2 = Student.class;
        Student s = new Student();
        Class c3 = s.getClass();
        */
    Constructor[] declaredConstructors = c1.getDeclaredConstructors();//我全都要
    for (Constructor declaredConstructor : declaredConstructors) {
        System.out.println(declaredConstructor.getName() + "===>"
                + declaredConstructor.getParameterCount());
    }
}
@Test
public void getConstructor() throws Exception{
    Class c1 = Class.forName("com.cafune.Day03_reflect_constructor.Student");
    Constructor con = c1.getConstructor();
    System.out.println(con.getName() + "===>"
            + con.getParameterCount());
}
@Test
public void getDeclaredConstructor() throws Exception{
    Class c1 = Class.forName("com.cafune.Day03_reflect_constructor.Student");
    Constructor dcons = c1.getDeclaredConstructor();
    System.out.println(dcons.getName() + "===>"
            + dcons.getParameterCount());
}
@Test
public void getDeclaredConstructor1() throws Exception{
    Class c1 = Class.forName("com.cafune.Day03_reflect_constructor.Student");
    Constructor dcons1 = c1.getDeclaredConstructor(String.class,int.class);
    System.out.println(dcons1.getName() + "===>"
            + dcons1.getParameterCount());
}

使用反射技术获取构造器对象并使用

  • 获取构造器的作用依然是初始化一个对象返回。

Constructor类中用于创建对象的方法

符号 说明
T newInstance(Object... lnitargs) 根据指定的构造器创建对象
public void setAccessible(boolean flag) 设置为true , 表示取消访问检查, 进行暴力反射
public class TestStudentDemo02 {
    //调用无参构造器得到一个类的对象返回
    @Test
    public void getDeclaredConstructor1() throws Exception{
        //a.获取类对象
        Class c1 = Class.forName("com.cafune.Day03_reflect_constructor.Student");
        //b.定位单个构造器对象(按照参数定位无参数构造器)
        Constructor cons = c1.getDeclaredConstructor();
        System.out.println(cons.getName() + "===>"
                + cons.getParameterCount());

        //如果遇到私有构造器可以暴力反射
        cons.setAccessible(true);//权限被打开
        Student s = (Student) cons.newInstance();
        System.out.println(s);

        System.out.println("-------------------");

        //定位某个有参构造器
        Constructor cons1 = c1.getDeclaredConstructor(String.class, int.class);
        System.out.println(cons1.getName() + "===>"
                + cons1.getParameterCount());
        Student s1 = (Student) 				 	cons1.newInstance("cafune", 22);
        System.out.println(s1);
    }
}

总结:

image-20220523214336632

使用反射技术获取成员变量对象并使用

image-20220523214459598

  • 反射的第一步是先得到类对象, 然后从类对象中获取类的成分对象。

  • Class 类中用于获取成员变量的方法

方法 说明
FieId[] getFields() 返回所有成员变量对象的数组( 只能拿public 的)
Field[] getDeclaredFieIds() 返回所有成员变量对象的数组, 存在就能拿到
Field getField(String name) 返回单个成员变量对象( 只能拿public的)
Field getDeclaredField(String name) 返回单个成员变量对象, 存在就能拿到

使用反射技术获取成员变量对象并使用

  • 获取成员变量的作用依然是在某个对象中取值、赋值

Field类中用于取值、赋值的方法

符号 说明
void set(Object obj, Object value) 赋值
Object get(Object obj) 取值
public void getDeclaredField() throws Exception {
    //a.定位class对象
    Class c = Student.class;
    //b.定位某个成员变量
    Field ageF = c.getDeclaredField("age");
    System.out.println(ageF.getName() + "==>" + ageF.getType());
    
    ageF.setAccessible(true);//暴力取值

    //c.赋值
    Student s = new Student();
    ageF.set(s, 18);
    System.out.println(s);

    //d.取值
    int age = (int) ageF.get(s);
    System.out.println(age);
}

使用反射技术获取方法对象并使用

image-20220524104041088

  • 反射的第一步是先得到类对象, 然后从类对象中获取类的成分对象。

  • Class类中用于获取成员方法的方法

方法 说明
Method[] getMethods() 返回所有成员方法对象的数组(只能拿public的)
Method[] getDeclaredMethods() 返回所有成员方法对象的数组,存在就能拿到
Method getMethod(String name, Class<?>... parameterTypes) 返回单个成员方法对象(只能拿public的)
Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回单个成员方法对象,存在就能拿到

Method类中用于触发执行的方法

符号 说明
Object invoke(Object obj,Object...args) 运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(没有就不写)
返回值:方法的返回值(没有就不写)
 //1.获取类中的所有成员方法对象
    @Test
    public void getDEclareMethods() {
        //a.获取类对象
        Class c = Dog.class;
        //b.获取全部方法,包括私有的
        Method[] methods = c.getDeclaredMethods();
        //c.遍历全部方法
        for (Method method : methods) {
            System.out.println(method.getName() + "返回值类型:" +
                    method.getReturnType() + "参数个数:" +
                    method.getParameterCount());

        }
    }
//2.获取类中的某个方法对象
@Test
public void getDEclareMethod() throws Exception {
    //a.获取类对象
    Class c = Dog.class;
    //b.获取特定方法,包括私有的
    Method m = c.getDeclaredMethod("eat");
    Method m1 = c.getDeclaredMethod("eat",String.class);

    m.setAccessible(true);
    m1.setAccessible(true);

    //c.触发方法的执行
    Dog d = new Dog();
    //如果方法没有结果回来,那么返回的是null
    Object result = m.invoke(d);
    System.out.println(result);

    Object result1 = m1.invoke(d,"🥩");
    System.out.println(result1);
}

总结:

1.利用反射技术获取成员方法对象的方法

获取类中成员方法对象
  • Method[] getDeclaredMethods()
  • Method getDeclaredMethod(String name, Class<?>... parameterTypes)

2.反射得到成员方法可以做什么?

依然是在某个对象中触发该方法执行
  • Object invoke(Object obj,Object...args)
如果某成员方法是非public的,需要打开权限(暴力反射),然后再触发执行
  • xx.setAccessible(boolean);

反射的作用:绕过编译阶段为集合添加数据

  • 反射是作用在运行时的技术, 此时集合的泛型将不能产生约束了, 此时是可以为集合存入其他任意类型的元素的。

ArrayList<Integer> list = new ArrayList<>();
list.add(100);
//list.add( "黑马" );//报错
list.add(99);
  • 泛型只是在编译阶段可以约束集合只能操作某种数据类型, 在编译成Class 文件进入运行阶段的时候, 其真实类型都是ArrayList 了, 泛型相当于被擦除了。

总结:

image-20220525104028476

案例:反射做通用框架

image-20220525104302903

image-20220525104356457

总结:

image-20220525150356426

注解

注解概述、作用

  • Java 注解(Annotation) 又称Java 标注, 是JDK5.0 引入的一种注释机制。

  • Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。

public class UserServiceTest {
@Test //->注解
public void testLogin(){
@Test //->注解
public void testChu(){
	}
}

注解的作用是什么呢?

  • 对JAVA中类、方法、成员变量做标记, 然后进行特殊处理, 至于到底做何种处理由业务需求来决定。

  • 例如: JUnit 框架中, 标记了注解@Test 的方法就可以被当成测试方法执行, 而没有标记的就不能当成测试方法执行。

自定义注解---格式

  • 自定义注解就是自己做一个注解来使用

public @interface 注解名称{
    public 属性名称 属性名() default 默认值;
}

特殊属性

  • value 属性, 如果只有一个value 属性的情况下, 使用value 属性的时候可以省略value 名称不写! !

  • 但是如果有多个属性, 且多个属性没有默认值, 那么value 名称是不能省略的。

元注解

元注解: 就是注解注解的注解。

元注解有两个:

@Target: 约束自定义注解只能在哪些地方使用,

@Retention : 申明注解的生命周期

image-20220525154042831

总结:

image-20220525155828237

注解的解析

  • 注解的操作中经常需要进行解析, 注解的解析就是判断是否存在注解, 存在注解就解析出内容。

与注解解析相关的接囗

Annotation: 注解的顶级接口, 注解都是Annotation 类型的对象

AnnotatedElement : 该接口定义了与注解解析相关的解析方法

方法 说明
Annotation[] getDeclaredAnnotations() 获得当前对象上使用的所有注解, 返回注解数组。
T getDecIaredAnnotation(CIass annotationClass) 根据注解类型获得对应注解对象
boolean isAnnotationPresent(Class annotationClass) 判断当前对象是否使用了指定的注解, 如果使用了则返回true, 否则false
  • 所有的类成分Class , Method Field ,Constructor, 都实现了AnnotatedE [ ement 接口他们都拥有解析注解的能力:

  • image-20220525160251523

案例:注解解析的案例

image-20220525160323666

public class AnnotationDemo04 {
    @Test
    public void parseMethod() throws Exception {
        //1.获取类对象
        Class c = BookStores.class;
        Method m = c.getDeclaredMethod("test");
        //2.判断这个类上是否存在这个注释
        if (m.isAnnotationPresent(BOOKS.class)){
            //3.直接获取该注解对象
            //获取m方法里面的注解  定为book
            BOOKS book = (BOOKS) m.getDeclaredAnnotation(BOOKS.class);
            System.out.println(book.value());
            System.out.println(book.price());
            System.out.println(Arrays.toString(book.authors()));
        }
    }
}
@BOOKS(value = "《情深深雨蒙蒙》",price = 99.9,authors = {"琼瑶","xiaoding"})
class BookStores{
    @BOOKS(value = "《三少爷的🗡》",price = 10000,authors = {"古龙","熊耀华"})
    public void test(){}

}

案例:模拟Junit框架

image-20220525162519632

public class AnnotationDemo05 {
    @Mytest
    public void test1(){
        System.out.println("======test1======");
    }
    public void test2(){
        System.out.println("======test2======");
    }
    @Mytest
    public void test3(){
        System.out.println("======test3======");
    }

    //启动菜单:有注解的才被调用
    public static void main(String[] args) throws Exception{
        AnnotationDemo05 a5 = new AnnotationDemo05();
        //a.获取类对象
        Class c = AnnotationDemo05.class;
        //b.提取全部方法
        Method[] methods = c.getDeclaredMethods();
        //c.遍历方法,看是否有mytest注解,有就跑它
        for (Method method : methods) {
            if (method.isAnnotationPresent(Mytest.class)){
                    method.invoke(a5);
            }
        }
    }
}

动态代理(重难点)

如何创建代理对象

  • java中代理的代表类是:Java.lang.reflect.Proxy

  • Proxy提供了一个静态方法,用于为对象产生一个代理对象返回

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
    /*为对象返回一个代理对象。
    参数一:定义代理类的类加载器
    参数二:代理类要实现的接口列表
    参数三:将方法调用分派到调用的处理程序(代理对象的核心处理程序)
    */

JAVA中如何生成代理,并指定代理干什么事

image-20220525205150846

image-20220525205302473

总结:

image-20220525205403487

image-20220525205416610

案例:模拟企业业务功能开发,并完成每个功能的性能统计

image-20220525205537302

image-20220525212318638

动态代理的优点

  1. 可以在不改变方法源码的情况下, 实现对方法功能的增强, 提高了代码的复用。

  2. 简化了编程工作、提高了开发效率,同时提高了软件系统的可扩展性,

  3. 可以为被代理对象的所有方法做代理。

  4. 非常的灵活,支持任意接口类型的实现类对象做代理/ 也可以直接为接本身做代理。

posted on 2022-06-01 08:57  Cafune-Ding  阅读(67)  评论(0)    收藏  举报