JavaSE进阶笔记:12(单元测试、反射、注解、动态代理)

146 阅读16分钟

一、单元测试

1.单元测试概述

  • Java程序最小功能单元是方法,单元测试是针对最小的功能单元(方法)编写测试代码,进而检查方法的正确性;
  • 实现自动化测试,获取测试结果报告

2. Junit单元测试框架

  • Junit是使用java编写的单元测试框架,开源;
  • 几乎所有IED工具都集成了JUnit,可以在IDE工具中直接编写并运行测试;

2.1 JUnit优点

  • 灵活的选择执行哪些测试方法,可以一键执行全部测试方法;
  • 可以生成全部方法的测试报告,绿色良好,红色失败;
  • 某个方法测试失败,不会影响其他方法;

3.单元测试实例

  • JUnit的jar包导入项目;
    • IDEA统称整合好了该框架
  • 编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法
  • 测试方法使用@Test注解:标注是测试方法
  • 测试方法中完成被测试方法的预期正确性测试;
  • 选中测试方法,选择“JUnit运行”,良好绿色,失败红色;
/**
 * 测试类
 */
public class TestUserService {
    /**
     * 测试方法注意:
     * 1.必须是公开,无参数、无返回值的方法
     * 2.上方必须有@Test注解
     */
    @Test
    public void testLoginName(){
        UserService userService = new UserService();
        String rs = userService.loginName("admin", "123456");

        /**
         * 进行预期结果正确性测试
         * assertEquals(String message, Object expected, Object actual)
         * (方法存在问题的提示信息,预期的结果  ,实际的方法返回结果)
         */
        Assert.assertEquals("该功能方法可能存在问题","登录成功",rs);
    }

    @Test
    public void testSelectName(){
        UserService userService = new UserService();
        userService.selectName();
    }

    @Before
    public void before(){
        System.out.println("@Before:在每个测试方法之前执行一次");
    }
    @After
    public void after(){
        System.out.println("@After:在每个测试方法之后执行一次");
    }

    @BeforeClass
    public static void beforeClass(){
        System.out.println("@BeforeClass:静态,在所有执行前,只执行一次");
        System.out.println("=====================");
    }

    @AfterClass
    public static void afterClass(){
        System.out.println("=====================");
        System.out.println("@AfterClass:静态,在所有执行后,只执行一次");
    }
    /**
     @BeforeClass:静态,在所有执行前,只执行一次
     =====================
     @Before:在每个测试方法之前执行一次
     登录成功
     @After:在每个测试方法之后执行一次
     @Before:在每个测试方法之前执行一次
     5
     @After:在每个测试方法之后执行一次
     =====================
     @AfterClass:静态,在所有执行后,只执行一次
     */
}

/**
 * 方法
 */
public class UserService {
    public String loginName(String loginName,String passWord){
        if ("admin".equals(loginName)){
            System.out.println("登录成功");
            return "登录成功";
        }else {
            return "out!";
        }
    }

    public void selectName(){
        System.out.println(10/2);
    }
}

4.单元测试常用注解

4.1 JUnit4

  • 开始执行方法:初始化资源;
  • 执行完后方法:释放资源;
注解说明
@Test测试方法
@Before修饰实例方法,每个测试方法前执行一次
@After修饰实例方法,每个测试方法后执行一次
@BeforeClass修饰静态方法,所有测试方法前,只执行一次
@AfterClass修饰静态方法,所有测试方法后,只执行一次

4.2 JUnit5

注解说明
@Test测试方法
@BeforeEach修饰实例方法,每个测试方法前执行一次
@AfterEach修饰实例方法,每个测试方法后执行一次
@BeforeAll修饰静态方法,所有测试方法前,只执行一次
@AfterAll修饰静态方法,所有测试方法后,只执行一次

二、反射

1.反射概述

  • 反射指对任何一个Class类,在“运行的时候”可以直接得到这个类全部成分;
  • 在运行时,可以直接得到该类的构造器对象Constructor、成员变量对象Field、成员方法对象Method;
  • 这种运行时动态获取类信息以及动态调用类中成分的能力称为java语言的反射机制;

2.反射关键

  • 反射第一步是先得到编译后的class类对象,然后得到class的全部成分;
  • 运行时获取类的字节码文件,解析类中全部成分

3.反射获取类对象的三种方法

/**
 * 目标:
 *  反射第一步,获取Class对象
 */
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.Class类中一个静态方法:forName(全限名:包名+类名)
        Class<?> c1 = Class.forName("d2_reflect_class/Student.java");
        System.out.println(c1);

        //2.类名.class
        Class<Student> c2 = Student.class;
        System.out.println(c2);

        //3.对象.getClass()获取对象对应类的Class类对象
        Student student = new Student();
        //Class<? extends Student> c3 = student.getClass();
        Class c3 = student.getClass();
        System.out.println(c3);
    }
}

4.反射获取构造器

4.1利用反射技术获取构造器的推荐方法

  • Constructor<?>[] getDeclaredConstructors()
  • Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
  • 二者都可获取任意修饰的构造器
/**
 * 获取构造器
 */
public class TestStudent01 {
    /**
     * 1.getConstructors()
     * 获取全部的构造器:只能获取public修饰的构造器
     * Constructor[] constructors
     */
    @Test
    public void getConstructors(){
        Class c = Student.class;
        //获取全部构造器(public修饰)
        Constructor[] constructors = c.getConstructors();
        for (Constructor constructor : constructors) {
            //获取构造器名====>参数个数
            System.out.println(constructor.getName()+"====>"+constructor.getParameterCount());
            //com.xx.d3_reflect_constructor.Student====>3
        }
    }
    /**
     * 2.getDeclaredConstructors()
     * 获取全部的构造器:所有构造器都能拿到
     * Constructor[] constructors
     */
    @Test
    public void getDeclaredConstructors(){
        Class c = Student.class;
        //获取全部构造器
        Constructor[] constructors = c.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            //获取构造器名====>参数个数
            System.out.println(constructor.getName()+"====>"+constructor.getParameterCount());
            //com.xx.d3_reflect_constructor.Student====>3
        }
    }
    /**
     * 3.Constructor<T> getConstructor(Class<?>... parameterTypes)
     * 获取全部的构造器:所有构造器都能拿到
     */
    @Test
    public void getConstructor() throws Exception {
        Class c = Student.class;
        //获取单个构造器(按照参数定位无参数构造器,只能public,否则报错)
        Constructor cons = c.getConstructor();
        //获取构造器名====>参数个数
        System.out.println(cons.getName()+"====>"+cons.getParameterCount());
    }
    /**
     * 4.getDeclaredConstructor()
     * 获取全部的构造器:所有构造器都能拿到
     * Constructor[] constructors
     */
    @Test
    public void getDeclaredConstructor() throws NoSuchMethodException {
        Class c = Student.class;
        //获取单个构造器(按照参数定位无参数构造器,所有修饰都可以)
        Constructor cons = c.getDeclaredConstructor();
        System.out.println(cons.getName()+"====>"+cons.getParameterCount());

        //获取单个构造器(有参数构造器,所有修饰都可以)
        Constructor cons2 = c.getDeclaredConstructor(String.class,int.class,double.class);
        System.out.println(cons2.getName()+"====>"+cons2.getParameterCount());
    }
}

4.2反射得到构造器可以做什么?

  • 创建对象:
    • public T newInstance(Object ... initargs)
  • 非public构造器,需要打开权限(暴力反射),再创建对象
    • setAccessible(boolean flag)
    • 反射会破坏封装性,私有的也可以访问
/**
 * 根据构造器创建对象
 */
public class TestStudent02 {
    /**
     * 4.getDeclaredConstructor()
     * 获取全部的构造器:所有构造器都能拿到
     * Constructor[] constructors
     */
    @Test
    public void getDeclaredConstructor() throws Exception {
        Class c = Student.class;
        //获取单个构造器(按照参数定位无参数构造器,所有修饰都可以)
        Constructor cons = c.getDeclaredConstructor();
        /**
         * can not access a member of class
         * com.d3_reflect_constructor.Student with modifiers "private"
         * 无法通过私有的构造器创建对象
         */
        //暴力反射,此次访问权限打开
        //反射会破坏封装性,私有的也可以访问
        cons.setAccessible(true);
        //使用无参构造器创建对象
        Student s = (Student) cons.newInstance();
        System.out.println(s);



        //获取单个构造器(有参数构造器,所有修饰都可以)
        Constructor cons2 = c.getDeclaredConstructor(String.class,int.class,double.class);
        System.out.println(cons2.getName()+"====>"+cons2.getParameterCount());
        Student ss = (Student) cons2.newInstance("悟空",6800,1.88);
        System.out.println(ss);
    }
}

5.反射获取成员变量

5.1利用反射获取成员变量

  • 获取所有成员变量Field[] getDeclaredFields()
  • 获取某个成员变量对象Field getDeclaredField(String name)
/**
 * 利用反射获取成员变量
 */
public class FiledDemo01 {
    /**
     * 获取所有成员变量
     * Field[] getDeclaredFields()
     */
    @Test
    public void getDeclaredFields(){
        Class<Student> c = Student.class;
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName()+"===>"+field.getType());
        }
        /**
         * name===>class java.lang.String
         * age===>int
         * height===>double
         * schoolName===>class java.lang.String
         * COUNTRY===>class java.lang.String
         */
    }

    /**
     * 获取某个成员变量对象
     * Field getDeclaredField(String name)
     */
    @Test
    public void getDeclaredField() throws NoSuchFieldException {
        Class<Student> c = Student.class;
        Field field = c.getDeclaredField("name");
        System.out.println(field.getName()+"===>"+field.getType());
        /**
         * name===>class java.lang.String
         */
    }
}

5.2对成员变量赋值和取值

  • 赋值和取值:
    • 赋值;set(Object obj, Object value)
    • //取值:Object get(Object obj)
  • 成员变量非public,需要打开权限暴力反射;
    • setAccessible(boolean flag)
/**
 * 对成员变量赋值和取值
 */
public class FiledDemo02 {
    @Test
    public void setFiled() throws Exception {
        Class<Student> studentClass = Student.class;
        Field ageF = studentClass.getDeclaredField("age");
        //age私有,暴力打开权限
        ageF.setAccessible(true);

        //赋值;set(Object obj, Object value)
        // 从对象设置值为?,变为值要求对象将自己修改为?;关系反转,称为反射
        Student s = new Student();
        ageF.set(s,18);//s.setAge(18);
        System.out.println(s);//Student{name='null', age=18, height=0.0}

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

6.反射获取成员变量方法并触发执行方法

/**
 * 利用反射获取成员对象方法,触发方法
 */
public class MethodDemo01 {
    /**
     * 获取所有方法
     *  Method[] getDeclaredMethods()
     */
    @Test
    public void getDeclaredMethods(){
        Class c = Student.class;
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName()+"返回值类型:"+method.getReturnType()+"  参数个数:  "+method.getParameterCount());
        }
        /**
         getName返回值类型:class java.lang.String  参数个数:  0
         setName返回值类型:void  参数个数:  1
         getAge返回值类型:int  参数个数:  0
         setAge返回值类型:void  参数个数:  1
         getHeight返回值类型:double  参数个数:  0
         setHeight返回值类型:void  参数个数:  1
         */
    }

    /**
     * 获取某个方法
     *  Method getDeclaredMethod(String name, Class<?>... parameterTypes)
     */
    @Test
    public void getDeclaredMethod() throws Exception {
        Class c = Student.class;
        //获取方法
        Method method = c.getDeclaredMethod("wKHeight");
        System.out.println(method.getName()+"返回值类型:"+method.getReturnType()+"  参数个数:  "+method.getParameterCount());
        Method method2 = c.getDeclaredMethod("wKHeight",String.class);
        System.out.println(method2.getName()+"返回值类型:"+method2.getReturnType()+"  参数个数:  "+method2.getParameterCount());
        /**
         wKHeight返回值类型:void  参数个数:  0
         wKHeight返回值类型:class java.lang.String  参数个数:  1
         */

        method.setAccessible(true);
        method2.setAccessible(true);
        //触发方法执行
        Student student = new Student();
        /**
         * Object invoke(Object obj, Object... args)
         *               (对象,       方法传递的参数(没有不写)
         * 注意:方法没有返回结果,返回null
         */
        Object invoke = method.invoke(student);
        System.out.println(invoke);//null

        Object invoke2 = method2.invoke(student, "悟空");
        System.out.println(invoke2);//悟空

    }
}

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

  • 反射是作用在运行时的技术,此时集合的泛型不能产生约束,此时集合可以存入任意类型元素;
  • 泛型只是在编译阶段可以约束集合只能操作某种类型,编译成Class文件进入运行阶段,真实类型都是ArrayList,泛型被擦除了;
    • JDK1.5之前没有泛型,为了兼容之前代码,需要泛型擦除
/**
 * 泛型擦除
 */
public class ReflectDemo01 {
    public static void main(String[] args) throws Exception {
        ArrayList<Integer> list1 = new ArrayList<>();
        ArrayList<String> list2 = new ArrayList<>();
        System.out.println(list1.getClass() == list2.getClass());//ture,二者类型都是ArrayList.class

        list1.add(5);
        list1.add(55);
        //list1.add("黑夜");

        Class c = list1.getClass();
        Method add = c.getDeclaredMethod("add", Object.class);
        Boolean rs = (Boolean) add.invoke(list1, "黑夜");
        System.out.println(rs);//true,添加成功
        System.out.println(list1);//[5, 55, 黑夜]

        //也可以突破约束
        ArrayList list3 = list1;
        list3.add(false);
        list3.add(56.55);
        System.out.println(list1);//[5, 55, 黑夜, false, 56.55]
    }
}

8.反射的作用:通用框架的底层原理

  • 反射的作用:
    • 运行时得到类的全部成分然后操作
    • 可以破坏封装(很突出)
    • 可以破坏泛型约束(很突出)
    • 更重要的用途:做java高级框架
/**
 * 通用存储工具
 */
public class MybatisUtil {
    public static void save(Object obj) {
        try (
                PrintStream ps = new PrintStream(new FileOutputStream("day12_junit_reflect_annotation_proxy_app/src/data.txt", true));
        ) {

            Class c = obj.getClass();
            //c.getSimpleName()获取当前类名, c.getName()全限名
            ps.println("============" + c.getSimpleName() + "=============");

            Field[] fields = c.getDeclaredFields();
            for (Field field : fields) {
                String name = field.getName();
                //提取本成员变量在obj对象中的值
                field.setAccessible(true);
                //通过拼接转换为字符型
                String value = field.get(obj) + "";
                ps.println(name + "===" + value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/**
 * 目标,提供一个通用模板,支持保存所有对象的具体信息
 */
public class ReflectFrameWorkDemo {
    public static void main(String[] args) {
        Student s = new Student("重云", 17, 1.70);
        MybatisUtil.save(s);
        Teacher t = new Teacher("退休老大爷", 10000, 0.0);
        MybatisUtil.save(t);
        /**
         * ============Student=============
         * name===重云
         * age===17
         * height===1.7
         * schoolName===null
         * COUNTRY===璃月
         * ============Teacher=============
         * name===退休老大爷
         * age===10000
         * salary===0.0
         */
    }
}

三、注解

1.注解概述、作用

  • java注解(Annotation)又称java标注,JDK5引入的注释机制;
  • java语言中的类、构造器、方法、成员变量、参数都可以被注解进行标注;
    • 标注后进行特殊处理,处理方式由业务需求决定
    • 如@Test,标记为测试方法

2.自定义注解

public @interface 注解名称{
	public 属性类型 属性名() default 默认值;
	//属性名()是规定写法,必须带;默认值看情况是否带
}
/**
 * 必须属性名是value,且没有其他属性或属性有默认值,才可以省略
 */
@Book("钢铁是怎样炼成的")
@MyBook(name = "斗破乾坤",authors = {"小火火","东东"},price = 98.8)
public class AnnotationDemo01 {

    @Book("钢铁是怎样炼成的")
    private AnnotationDemo01(){
        //构造器
    }

    @Book("钢铁是怎样炼成的")
    public static void main(String[] args) {
        @Book("钢铁是怎样炼成的")
        int age = 23;
    }
}

public @interface MyBook {
    String name();
    String[] authors();
    double price() default 9.99;
}

public @interface Book {
    String value();
    double price() default 9.99;
}

3.元注解

  • 概述:注解 注解的注解;
  • 常见的两个元注解:
    • @Target:约束自定义注解只能在哪些地方使用;
    • @Retention:申明注解的生命周期;
      • 注解默认在编译结束后消失

3.1元注解的常用参数

  • @Target中可使用值定义在ElementType枚举类
  • @Retention中可使用值定义在RetentionPolicy枚举类
@Target
TYPE类、接口
FIELD成员变量
METHOD成员方法
PARAMETER方法参数
CONSTRUCTOR构造器
LOCAL_VARIABLE局部变量
@Retention
SOURCE作用在源码阶段,
CLASS作用在源码阶段,字节码文件阶段,运行阶段不存在,默认设置
RUNTIME作用在源码阶段,字节码文件阶段,运行阶段(开发中常用)
/**
 * 元注解使用
 * 注解使用范围{方法,成员变量} 规定范围外使用报错
 */
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)//一直存在,运行阶段不消失
public @interface MyTest {
}

4.注解的解析

  • 注解的操作中经常需要解析,判断是否存在注解,存在,解析出内容。
  • 与注解解析相关的接口:
    • Annotation:注解顶级接口,注解都是Annotation类型对象;
    • AnnotatedElement:该接口定义了与注解解析相关的解析方法;
    • 所有的类成分都实现了AnnotatedElement接口他们都拥有解析注解的能力;
/**
 * 注解解析
 */
public class AnnotationDemo03 {
    @Test
    public void parseClass(){
        //1.得到类对象
        Class c = BookStore.class;
        // 2.判断类上是否存在Books注解
        if (c.isAnnotationPresent(Books.class)){
            // 3.获取该注解对象
            Books books = (Books) c.getDeclaredAnnotation(Books.class);
            System.out.println(books.value());
            System.out.println(books.price());
            System.out.println(Arrays.toString(books.authors()));
            /**
             * 《斗破大地》
             * 6.2
             * [王某, 某陆]
             */
        }
    }

    @Test
    public void parseMethod() throws NoSuchMethodException {
        //1.得到类对象,再得到方法
        Class c = BookStore.class;
        Method m = c.getDeclaredMethod("test");
        // 2.判断类上是否存在Books注解
        if (m.isAnnotationPresent(Books.class)){
            // 3.获取该注解对象
            Books books = (Books) m.getDeclaredAnnotation(Books.class);
            System.out.println(books.value());
            System.out.println(books.price());
            System.out.println(Arrays.toString(books.authors()));
            /**
             《斗破海洋》
             16.2
             [海某, 某洋]
             */
        }
    }

}

@Books(value = "《斗破大地》",price = 6.2,authors = {"王某","某陆"})
class BookStore{

    @Books(value = "《斗破海洋》",price = 16.2,authors = {"海某","某洋"})
    public void test(){
    }
}


@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Books {
    String value();
    double price() default 100;
    String[] authors();
}

5.注解场景:JUnit框架

/**
 * 模拟JUnit框架
 */
public class AnnotationDemo04 {
    @MyTest
    public void test01() {
        System.out.println("test01");
    }

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

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

    /**
     * 启动菜单,@MyTest注解的会被调用
     *
     * @param args
     */
    public static void main(String[] args) throws Exception {
        AnnotationDemo04 a = new AnnotationDemo04();
        Class c = AnnotationDemo04.class;
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(MyTest.class)) {
                //存在,运行
                method.invoke(a);
            }
        }
    }
}

/**
 * 元注解使用
 * 注解使用范围{方法,成员变量} 规定范围外使用报错
 */
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)//一直存在,运行阶段不消失
public @interface MyTest {
}

四、动态代理

1.代理概述

  • 代理:某些场景,对象会找一个代理对象,辅助完成工作;
  • 事务先找代理,先处理一些辅助工作,再由代理通知对象完成主要工作,代理处理首尾工作(如果需要);
    • 甲方->中介->乙方

2.创建代理对象

  • Java中代理的代表类:java.lang.reflect.Proxy;
  • Proxy提供一个静态方法,用于为对象产生一个代理对象返回;
/**
 * 动态代理测试
 */
public class ProxyTest {
    public static void main(String[] args) {
        Star s = new Star("iKun");
        Skill proxy = StarAgentProxy.getProxy(s);
        //每次通过代理进行业务,都单独结算,返回结果(没有返回null)
        String sing = proxy.sing();
        System.out.println("第一个返回值:"+sing);
        
        proxy.jump();
        proxy.rap();
        proxy.basketBall();
        /**
         * 收订金!!!!
         * iKun唱,叽叽叽叽。。。
         * 收尾款!!!正主回家
         * 第一个返回值:感谢粉丝
         
         * 收订金!!!!
         * iKun跳,jump,跳起来就是一脚!
         * 收尾款!!!正主回家
         */
    }
}

/**
 * 约束对象执行方法的接口
 */
public interface Skill {
    String sing();
    void jump();
    void rap();
    void basketBall();
}

/**
 * 对象实现接口
 */
public class Star implements Skill{
    private String name;

    public Star(String name) {
        this.name = name;
    }

    @Override
    public String sing() {
        System.out.println(name+"唱,叽叽叽叽。。。");
        return "感谢粉丝";
    }

    @Override
    public void jump() {
        System.out.println(name+"跳,jump,跳起来就是一脚!");
    }

    @Override
    public void rap() {
        System.out.println(name+"这个碗又大又圆。。。");
    }

    @Override
    public void basketBall() {
        System.out.println(name+"球。。。");
    }
}


/**
 * 代理对象
 */
public class StarAgentProxy {
    /**
     * 设计一个方法返回一个对象的代理对象
     * Skill:对象实现的接口,返回此类型
     */
    public static Skill getProxy(Star star){
        /**
         * public static Object newProxyInstance(
         *       ClassLoader loader,传入的对象的类加载器
         *       Class<?>[] interfaces,  对象实现的接口列表(得到列表,适应多接口情况star.getClass().getInterfaces())
         *       InvocationHandler h) 讲方法调用分配到的处理程序
         */
        //return (Skill) 返回类型强转为对应的接口类型
        return (Skill) Proxy.newProxyInstance(star.getClass().getClassLoader()
                , star.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("收订金!!!!");
                        //正主该干活了
                        //method正在调用的方法对象,args是方法内参数
                        Object rs = method.invoke(star, args);
                        System.out.println("收尾款!!!正主回家");
                        return rs;
                    }
                });
    }
}


3.动态代理应用案例:性能分析

3.1性能分析

/**
 * 功能接口
 */
public interface UserService {
    String login(String name,String passWord);
    void deleteUser();
    String selectUsers();
}

/**
 * 功能实现
 */
public class UserServiceImpl implements UserService {
    @Override
    public String login(String name, String passWord) {
        long startTime = System.currentTimeMillis();

        if (name.equals("admin") && passWord.equals("123")) {
            try {
                Thread.sleep(2 * 1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("登录成功");
            long endTime = System.currentTimeMillis();
            System.out.println("耗费时间:" + (endTime - startTime) / 1000);

            return "登录成功";
        }

        System.out.println("登录信息有误,请检查!");
        long endTime = System.currentTimeMillis();
        System.out.println("耗费时间:" + (endTime - startTime) / 1000);
        return "登录信息有误,请检查!";
    }

    @Override
    public void deleteUser() {
        long startTime = System.currentTimeMillis();

        try {
            System.out.println("删除用户信息中。。。。");
            Thread.sleep(3 * 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("耗费时间:" + (endTime - startTime) / 1000);
    }

    @Override
    public String selectUsers() {
        long startTime = System.currentTimeMillis();

        try {
            System.out.println("查询到100条用户信息。。。。");
            Thread.sleep(5 * 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("耗费时间:" + (endTime - startTime) / 1000);

        return "100条信息汇总";
    }
}


public class Test01 {
    @Test
    public void Test01(){
        UserService userService = new UserServiceImpl();
        System.out.println(userService.login("admin", "123"));
        System.out.println(userService.selectUsers());
        userService.deleteUser();
    }
    /**
     * 登录成功
     * 耗费时间:2
     * 登录成功
     * 查询到100条用户信息。。。。
     * 耗费时间:5
     * 100条信息汇总
     * 删除用户信息中。。。。
     * 耗费时间:3
     */
}

3.2动态代理实现性能分析,减少代码冗余

/**
 * 功能接口
 */
public interface UserService {
    String login(String name,String passWord);
    void deleteUser();
    String selectUsers();
}

/**
 * 动态代理功能实现
 * 冗余代码删除
 */
public class UserServiceImplProxy implements UserService {
    @Override
    public String login(String name, String passWord) {
        if (name.equals("admin") && passWord.equals("123")) {
            try {
                Thread.sleep(2 * 1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("登录成功");
            return "登录成功";
        }
        return "登录信息有误,请检查!";
    }

    @Override
    public void deleteUser() {
        try {
            System.out.println("删除用户信息中。。。。");
            Thread.sleep(3 * 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public String selectUsers() {
        try {
            System.out.println("查询到100条用户信息。。。。");
            Thread.sleep(5 * 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "100条信息汇总";
    }
}


/**
 * 动态代理工具
 */
public class ProxyUtil {
    public static UserService getProxy(UserService obj) {
        return (UserService) Proxy.newProxyInstance(obj.getClass().getClassLoader()
                , obj.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //记录性能
                        long startTime = System.currentTimeMillis();

                        Object rs = method.invoke(obj, args);

                        long endTime = System.currentTimeMillis();
                        System.out.println(method.getName() + "耗费时间:" + (endTime - startTime) / 1000);
                        return rs;
                    }
                });
    }
}


public class Test01 {
    @Test
    public void TestProxy(){
        UserService userService = ProxyUtil.getProxy(new UserServiceImplProxy());
        System.out.println(userService.login("admin", "123"));
        System.out.println(userService.selectUsers());
        userService.deleteUser();
    }
    /**
     * 登录成功
     * login耗费时间:2
     * 登录成功
     * 查询到100条用户信息。。。。
     * selectUsers耗费时间:5
     * 100条信息汇总
     * 删除用户信息中。。。。
     * deleteUser耗费时间:3
     */
}

4.动态代理优点

  • 不改变方法源码情况下,实现对方法功能增强,提高代码复用性;
  • 简化编程,调高软件系统可扩展性;
  • 可以为被代理对象所有方法做代理;
  • 灵活,支持任意接口类型的实现类对象做代理,也可以为接口本身做代理;
/**
 * 范用:动态代理工具
 **支持任意接口类型的实现类**
 */
public class ProxyUtil2 {
    //静态,为业务返回一个代理对象
    public static <T> T getProxy(T obj) {
        return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader()
                , obj.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //记录性能
                        long startTime = System.currentTimeMillis();

                        Object rs = method.invoke(obj, args);

                        long endTime = System.currentTimeMillis();
                        System.out.println(method.getName() + "耗费时间:" + (endTime - startTime) / 1000);
                        return rs;
                    }
                });
    }
}

RecordDate:2021/08/22