uacs2024

导航

Java基础7 junit-reflect-annotation-proxy

junit单元测试

StringUtil.java

//字符串工具类
public class StringUtil {
    public static void printNumber(String name){
        if(name == null){
            System.out.println("名字不能为空");
            return;
        }
        System.out.println("名字长度是:" + name.length());
    }

    //求字符串的最大索引
    public static int getMaxIndex(String data){
        if(data == null || "".equals(data)) {
            return -1;
        }
        return data.length()-1;
    }
}

StringUtilTest.java

import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * 测试类:junit单元测试框架,对业务类中的业务方法进行正确性测试。
 * 1.将Junit框架的jar包导入到项目中(注意:IDEA集成了Junit框架,不需要我们自己手工导入了)
 * 2.为需要测试的业务类,定义对应的测试类,并为每个业务方法,编写对应的测试方法(必须:公共、无参、无返回值)
 * 3.测试方法上必须声明@Test注解,然后在测试方法中,编写代码调用被测试的业务方法进行测试;
 * 4.开始测试:选中测试方法,右键选择“JUnit运行",如果测试通过则是绿色;如果测试失败,则是红色
 */
public class StringUtilTest {
    //测试方法:必须是公开public,无参,无返回值。
    //测试方法必须加上@Test注解(Junit框架的核心步骤)

    @Test
    public void testPrintNumber(){
        StringUtil.printNumber("张三123");
        StringUtil.printNumber("");
        StringUtil.printNumber(" ");
        StringUtil.printNumber(null);
    }

    @Test
    public void testGetMaxIndex(){
        int index1 = StringUtil.getMaxIndex("abc123");
        System.out.println(index1);
        int index2 = StringUtil.getMaxIndex(" ");
        System.out.println(index2);
        int index3 = StringUtil.getMaxIndex("");
        System.out.println(index3);
        int index4 = StringUtil.getMaxIndex(null);
        System.out.println(index4);

        //做断言:断言结果是否与预期结果一致
        Assert.assertEquals(index1,5, String.valueOf(index1));
        Assert.assertEquals(index2,0, String.valueOf(index2));
        Assert.assertEquals(index3,-1, String.valueOf(index3));
        Assert.assertEquals(index4,-1, String.valueOf(index4));
    }
}

testPrintNumber

testGetMaxIndex

 

 

reflect反射

反射获取类本身

Student.java

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private String name;
    private int age;
}

ReflectDemo1.java

public class ReflectDemo1 {
    public static void main(String[] args) throws Exception {
        //1. 获取类本身:类.class
        Class c1 = Student.class;
        System.out.println(c1);//com_itheima.demo2reflect.Student

        //2. 获取类本身:Class.forName("类全路径")
        Class c2 = Class.forName("com_itheima.demo2reflect.Student");
        System.out.println(c2);//com_itheima.demo2reflect.Student
        System.out.println(c1 == c2);//都是Student类本身  //true


     //3. 通过已创建的对象获取类 Student s = new Student("张三", 20); Class c3 = s.getClass(); System.out.println(c3);//com_itheima.demo2reflect.Student System.out.println(c1 == c3);//true } }

 

反射的API

Dog.java

public class Dog {
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }

    private String name;
    private int age;
    private String sex;

    private Dog(){ System.out.println("Dog()方法执行了"); }

    public Dog(String name) {
        this.name = name;System.out.println("Dog(String name)方法执行了");
    }

    public Dog(int age) {
        this.age = age;System.out.println("Dog(int age)方法执行了");
    }

    public Dog(String name, int age) {
        this.name = name;this.age = age;
        System.out.println("Dog(String name, int age)方法执行了");
    }

    private void eat(){ System.out.println("狗在吃东西"); }
    private void sleep(){ System.out.println("狗在睡觉"); }
    public String eat(String food){
        System.out.println("狗在吃" + food);return "狗吃饱了";
    }
}

 

ReflectDemo2.java

import org.testng.annotations.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectDemo2 {
    @Test
    public void getClassInfo(){
        Class c1 = Student.class;
        System.out.println(c1.getName());//com_itheima.demo2reflect.Student
        System.out.println(c1.getSimpleName());//Student
    }

    @Test
    public void getConstructorInfo() throws Exception {
        // 目标:获取类的构造器对象并对其进行操作。
        // 1、反射第一步:或者Class对象,代表拿到类
        Class c1 = Dog.class;

        // 2、获取构造器对象
        //Constructor[] con = c1.getConstructors();//获取所有公共构造器
        Constructor[] con1 = c1.getDeclaredConstructors();//获取所有构造器
        for (Constructor c : con1) {
            System.out.println(c);
            System.out.println(c.getName() + "构造器参数个数-->" + c.getParameterCount());
        }
        System.out.println("------------------------1------------------------");

        // 3、获取单个构造器对象
        Constructor con2 = c1.getDeclaredConstructor();//获取无参构造器
        System.out.println(con2.getName() + " 构造器参数个数-->" + con2.getParameterCount());//com_itheima.demo2reflect.Dog 构造器参数个数-->0

        Constructor con3 = c1.getDeclaredConstructor(String.class, int.class);
        System.out.println(con3.getName() + " 构造器参数个数-->" + con3.getParameterCount());//com_itheima.demo2reflect.Dog 构造器参数个数-->2

        // 4、获取构造器的作用依然是创建对象:创建对象// 暴力反射:暴力反射可以访问私有的构造器、方法、属性
        con2.setAccessible(true);//绕过访问权限检查,直接访问
        Dog d1 = (Dog) con2.newInstance();
        System.out.println(d1);//Dog{name='null', age=0, sex='null'}

        Dog d2 = (Dog) con3.newInstance("大黄", 3);
        System.out.println(d2);//Dog{name='大黄', age=3, sex='null'}
    }

    // 3、获取成员变量对象并对其进行操作。
    @Test
    public void getFieldInfo() throws Exception {
        // 目标:获取类的成员变量对象并对其进行操作。
        // 1、反射第一步:或者Class对象,代表拿到类
        Class c1 = Dog.class;
        // 2、获取成员变量对象
        Field[] fields = c1.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
            System.out.println(field.getName() + " 成员变量类型-->" + field.getType().getName());
        }
        System.out.println("------------------------2------------------------");

        // 3、获取单个成员变量对象
        Field field = c1.getDeclaredField("name");
        System.out.println(field.getName() + " 成员变量类型-->" + field.getType().getName());//name 成员变量类型-->java.lang.String

        Field field2 = c1.getDeclaredField("sex");

        // 4、获取成员变量的作用依然是创建对象。
        Dog d1 = new Dog("小黄",4);
        field2.setAccessible(true);
        field2.set(d1,"male");
        System.out.println(d1);//Dog{name='大黄', age=4, sex='male'}

        String sex = (String)field2.get(d1);
        System.out.println(sex);//male
    }

    // 4、获取成员方法对象并对其进行操作。
    @Test
    public void getMethodInfo() throws Exception {
        // 目标:获取类的成员方法对象并对其进行操作。
        // 1、反射第一步:或者Class对象,代表拿到类。
        Class c1 = Dog.class;
        // 2、获取成员方法对象
        Method[] methods = c1.getDeclaredMethods();
        for(Method method : methods){
            System.out.println(method);
            System.out.println(method.getName() + " 成员方法返回值类型-->" + method.getReturnType().getName());
            System.out.println(method.getName() + " 构造器参数个数-->" + method.getParameterCount());
        }
        System.out.println("------------------------3------------------------");

        // 3、获取单个成员方法对象
        Method eat1Method = c1.getDeclaredMethod("eat");//获取无参成员方法
        System.out.println(eat1Method.getName() + " 成员方法返回值类型-->" + eat1Method.getReturnType().getName());//eat 成员方法返回值类型-->void
        System.out.println(eat1Method.getName() + " 构造器参数个数-->" + eat1Method.getParameterCount());//eat 构造器参数个数-->0
        Method eat2Method = c1.getDeclaredMethod("eat", String.class);
        System.out.println(eat2Method.getName() + " 成员方法返回值类型-->" + eat2Method.getReturnType().getName());//eat 成员方法返回值类型-->java.lang.String
        System.out.println(eat2Method.getName() + " 构造器参数个数-->" + eat2Method.getParameterCount());//eat 构造器参数个数-->1
        System.out.println("------------------------4------------------------");

        // 4、获取成员方法的作用依然是调用方法。
        Dog d1 = new Dog("大黄",3);
        eat1Method.setAccessible(true);
        Object res1 = eat1Method.invoke(d1);
        System.out.println(res1);//null

        Object res2 = eat2Method.invoke(d1,"骨头");
        System.out.println(res2);//狗吃饱了

    }
}

getClassInfo

com_itheima.demo2reflect.Student
Student

getConstructorInfo

public com_itheima.demo2reflect.Dog(int)
com_itheima.demo2reflect.Dog构造器参数个数-->1
public com_itheima.demo2reflect.Dog(java.lang.String)
com_itheima.demo2reflect.Dog构造器参数个数-->1
private com_itheima.demo2reflect.Dog()
com_itheima.demo2reflect.Dog构造器参数个数-->0
public com_itheima.demo2reflect.Dog(java.lang.String,int)
com_itheima.demo2reflect.Dog构造器参数个数-->2
------------------------1------------------------
com_itheima.demo2reflect.Dog 构造器参数个数-->0
com_itheima.demo2reflect.Dog 构造器参数个数-->2
Dog()方法执行了
Dog{name='null', age=0, sex='null'}
Dog(String name, int age)方法执行了
Dog{name='大黄', age=3, sex='null'}

getFieldInfo

private java.lang.String com_itheima.demo2reflect.Dog.name
name 成员变量类型-->java.lang.String
private int com_itheima.demo2reflect.Dog.age
age 成员变量类型-->int
private java.lang.String com_itheima.demo2reflect.Dog.sex
sex 成员变量类型-->java.lang.String
------------------------2------------------------
name 成员变量类型-->java.lang.String
Dog(String name, int age)方法执行了
Dog{name='小黄', age=4, sex='male'}
male

getMethodInfo

public java.lang.String com_itheima.demo2reflect.Dog.toString()
toString 成员方法返回值类型-->java.lang.String
toString 构造器参数个数-->0
private void com_itheima.demo2reflect.Dog.sleep()
sleep 成员方法返回值类型-->void
sleep 构造器参数个数-->0
private void com_itheima.demo2reflect.Dog.eat()
eat 成员方法返回值类型-->void
eat 构造器参数个数-->0
public java.lang.String com_itheima.demo2reflect.Dog.eat(java.lang.String)
eat 成员方法返回值类型-->java.lang.String
eat 构造器参数个数-->1
------------------------3------------------------
eat 成员方法返回值类型-->void
eat 构造器参数个数-->0
eat 成员方法返回值类型-->java.lang.String
eat 构造器参数个数-->1
------------------------4------------------------
Dog(String name, int age)方法执行了
狗在吃东西
null
狗在吃骨头
狗吃饱了

 

ReflectDemo3.java

import java.lang.reflect.Method;
import java.util.ArrayList;

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        // 目标:反射的基本作用。
        // 1、类的全部成分的获取  2、可以破坏封装性  3、可以绕过泛型的约束
        ArrayList<String> list = new ArrayList<>();
        list.add("C++");list.add("Java");list.add("Python");
        //list.add(6); //报错

        Class c1 = list.getClass();//c1 == ArrayList.class
        System.out.println(c1);//class java.util.ArrayList
        System.out.println(c1.getName());//java.util.ArrayList
        System.out.println(c1.getSimpleName());//ArrayList
        System.out.println(c1.getPackage().getName());//java.util
        System.out.println(c1.getSuperclass().getName());//java.util.AbstractList
        System.out.println(c1.getInterfaces().length);//4

        //获取 ArrayList类的add方法
        Method addMethod = c1.getDeclaredMethod("add", Object.class);
        //触发list集合对象的add方法执行
        addMethod.invoke(list,6);
        addMethod.invoke(list,true);
        addMethod.invoke(list,114.514);
        addMethod.invoke(list,'a');

        System.out.println(list);//[C++, Java, Python, 6, true, 114.514, a]

    }
}

 

 

Student.java

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private String name;
    private int age;
}

Teacher.java

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {
    private String name;
    private int age;
    private double salary;
    private Dog dog;
    private String address;
}

 

使用Java反射机制来保存任意对象的信息到文件中

SaveObjectFramework.java

import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;

public class SaveObjectFramework {
    //保存任意对象的静态方法
    public static void saveObject(Object obj) throws Exception{
        PrintStream ps = new PrintStream(new FileOutputStream("D:\\IdeaProjects\\day5-oop\\day06-junit-reflect-annotation-proxy" +
                "\\src\\com_itheima\\demo2reflect\\obj.txt",true));

        // 只有反射可以直到对象有多少个字段
        // 1.获取Class对象
        Class c1 = obj.getClass();
        String simpleName = c1.getSimpleName();//  获取类名
        ps.println("===========================" + simpleName + "===========================");
        // 2.获取Class对象所有字段
        Field[] fields = c1.getDeclaredFields();
        for (Field field : fields) {
            String fieldName = field.getName();// 获取字段名
            field.setAccessible(true);
            Object fieldValue = field.get(obj) + "";// 获取字段值
            ps.println(fieldName + "=" + fieldValue);
        }
        ps.close();
    }
}

ReflectDemo4.java

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception{
        Dog d1 = new Dog("小花", 5);
        SaveObjectFramework.saveObject(d1);

        Student s1 = new Student("张三", 18);
        SaveObjectFramework.saveObject(s1);

        Teacher t1 = new Teacher();
        SaveObjectFramework.saveObject(t1);

        Teacher t2 = new Teacher("李四", 18, 9999, d1, "北京");
        SaveObjectFramework.saveObject(t2);
    }
}

obj.txt

===========================Dog===========================
name=小花
age=5
sex=null
===========================Student===========================
name=张三
age=18
===========================Teacher===========================
name=null
age=0
salary=0.0
dog=null
address=null
===========================Teacher===========================
name=李四
age=18
salary=9999.0
dog=Dog{name='小花', age=5, sex='null'}
address=北京

 

 

Annotation注解

A.java

public @interface A {
    String value();
    int age() default 18;
    String[] names() default {"张三", "李四"};
}

MyBook.java

//对Java中类、方法、成员变量做标记,然后进行特殊处理
public @interface MyBook {
    String name();
    double price() default 9.9;
    String[] author();
}

/*
本质上是一个接口,接口中定义了3个抽象方法
public interface MyBook extends Annotation {
    public abstract String name();

    public abstract double bbb(9.9);

    public abstract String[] author();
}
*/

AnnotationDemo1.java

@MyBook(name = "java", price = 99.9, author = {"张三", "李四"})
@A("hello")  //特殊属性value。如果注解只有一个value属性,或者除了value属性其他都有默认值,value名称可以不写
public class AnnotationDemo1 {
    public static void main(String[] args) {
        //对Java中类、方法、成员变量做标记,然后进行特殊处理
    }
}

 

 

MyTest2.java

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

@Target({ElementType.METHOD,ElementType.TYPE})//表示该注解只能用于方法和类上
@Retention(RetentionPolicy.RUNTIME)//表示该注解在运行时存在
public @interface MyTest2 {
    String value();
    double height() default 100;
    String[] address();
}

Demo.java

@MyTest2(value = "小王", height = 179.0, address = {"北京", "上海"})
public class Demo {
    @MyTest2(value = "小叶", height = 165.0, address = {"北京", "哈尔滨"})
    public void go() { System.out.println("go()方法执行了..."); }
}

AnnotationDemo3.java

import org.testng.annotations.Test;
import java.lang.reflect.Method;
import java.util.Arrays;

public class AnnotationDemo3 {

    @Test
    public void parseClass() throws Exception{
        //1.获取Class对象
        Class c1 = Demo.class;
        //2.使用isAnnotationPresent判断这个类上是否陈列了注解MyTest2
        if(c1.isAnnotationPresent(MyTest2.class)){
            //3.获取注解对象
            MyTest2 annotation1 = (MyTest2) c1.getDeclaredAnnotation(MyTest2.class);
            //4.获取注解的属性值
            String[] address = annotation1.address();
            double height = annotation1.height();
            String value = annotation1.value();

            System.out.println(Arrays.toString(address));//[北京, 上海]
            System.out.println(height);//179.0
            System.out.println(value);//小王
        }
    }

    @Test
    public void parseMethod() throws Exception{
        //1.获取Class对象
        Class c1 = Demo.class;
        //2.获取方法对象
        Method method = c1.getMethod("go");
        //3.使用isAnnotationPresent判断该方法是否有MyTest2注解
        if(method.isAnnotationPresent(MyTest2.class)){
            //4.使用getAnnotation方法获取该方法上的MyTest2注解
            MyTest2 annotation = method.getDeclaredAnnotation(MyTest2.class);

            String[] address = annotation.address();
            double height = annotation.height();
            String value = annotation.value();

            System.out.println("address:"+Arrays.toString(address)+" , height:"+height+" , value:"+value);//address:[北京, 哈尔滨] , height:165.0 , value:小叶
        }
    }
}

 

 

MyTest.java

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 {
    int count() default 2;
}

AnnotationDemo4.java

import java.lang.reflect.Method;

public class AnnotationDemo4 {
    public static void main(String[] args) throws Exception{
        AnnotationDemo4 ad = new AnnotationDemo4();
        //1.  获取类对象
        Class c = AnnotationDemo4.class;
        //2. 获取类中所有方法
        Method[] methods = c.getMethods();
        for(Method method : methods){
            if(method.isAnnotationPresent(MyTest.class)){
                //3. 获取这个方法的注解
                MyTest methodAnnotation = method.getDeclaredAnnotation(MyTest.class);
                int count = methodAnnotation.count();
                for(int i = 0; i < count; i++){
                    method.invoke(ad);
                }
            }
        }
    }


    @MyTest
    public void show1(){ System.out.println("show1方法执行了..."); }

    public void show2(){ System.out.println("show2方法执行了..."); }

    @MyTest(count = 3)
    public void show3(){ System.out.println("show3方法执行了..."); }

    public void show4(){ System.out.println("show4方法执行了..."); }
}

res

show1方法执行了...
show1方法执行了...
show3方法执行了...
show3方法执行了...
show3方法执行了...

 

 

Proxy代理

proxy1

这几个程序共同实现了一个 Java 动态代理 的经典案例,模拟明星(Star)与经纪人(Proxy)的工作模式。

Star.java

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Star implements StarService{
    private String name;

    @Override
    public void sing(String name) { System.out.println(this.name + "表演唱歌:" + name); }

    @Override
    public String dance() {
        System.out.println(this.name + "表演跳舞"); return "谢谢";
    }
}

StarService.java

定义明星的行为规范:sing(String) 和 dance()
作用:统一代理类和真实对象的接口,确保代理行为与真实对象行为一致。

public interface StarService {
    void sing(String name);
    String dance();
}

ProxyUtil.java

通过 Proxy.newProxyInstance() 动态生成代理对象
代理逻辑(InvocationHandler):
      前置处理:根据方法名(sing 或 dance)准备道具(话筒/场地)
      调用真实对象:通过 method.invoke(s, args) 让明星执行实际表演
      返回结果:将明星方法的返回值(如 dance() 的 "谢谢")透传给调用方
关键点:
      代理类无需显式定义,运行时动态生成
      通过反射调用真实对象的方法,实现无侵入增强

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

/**
 * 对象工具类:中介公司,专门负责创建代理对象并返回给别人使用
 */
public class ProxyUtil {
    // 创建一个明星对象的代理对象返回
    public static StarService createProxy(Star s) {
        System.out.println("public static StarService createProxy(Star s)第一行代码");
        /**
         * 参数一:用于执行用哪个类加载器去加载生成的代理类
         * 参数二:用于指定代理类需要实现的接口:明星类实现了哪些接口,代理类就实现哪些接口
         * 参数三:用于指定代理类需要如何去代理(代理要做的事情)
         */
        StarService proxy = (StarService) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                s.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /**
                         * 用来声明代理对象要干的事情。
                         * 参数一:proxy接收到代理对象本身(暂时用处不大)
                         * 参数二:method代表正在被代理的方法
                         * 参数三:args代表正在被代理的方法的参数
                         */
                        String methodName = method.getName();
                        if(methodName.equals("sing")){
                            System.out.println("经纪人代理sing方法,准备话筒");
                        }else if(methodName.equals("dance")){
                            System.out.println("经纪人代理dance方法,准备场地");
                        }
                        // 真正干活(叫明星对象过来干活)
                        Object result = method.invoke(s, args);
                        return result;
                    }
                });
        System.out.println("public static StarService createProxy(Star s)最后一行代码");
        return proxy;
    }
}

Test.java

public class Test {
    public static void main(String[] args) {
        Star star = new Star("章若楠");
        // 2.为明星对象创建一个专属的代理对象
        StarService proxy = ProxyUtil.createProxy(star);//
        System.out.println("=============================================");
        proxy.sing("冰雨");
        proxy.dance();
    }
}

res

public static StarService createProxy(Star s)第一行代码
public static StarService createProxy(Star s)最后一行代码
=============================================
经纪人代理sing方法,准备话筒
章若楠表演唱歌:冰雨
经纪人代理dance方法,准备场地
章若楠表演跳舞

设计模式总结
        动态代理模式:
              解耦了核心逻辑(明星表演)与辅助逻辑(经纪人准备道具)
              适用于日志记录、权限校验、事务管理等横切关注点(AOP场景)
        对比静态代理:无需为每个类手动编写代理类,更加灵活。
通过这个案例,可以直观理解 Java 动态代理的核心思想:在运行时动态增强对象行为。

 

 

proxy2

这几个程序展示了 动态代理 在业务系统中的实际应用,核心是通过代理模式 无侵入式地为方法调用添加性能监控功能(耗时统计)。

UserService.java

/**
 *  用户业务接口
 */
public interface UserService {
    // 登录功能
    void login(String loginName,String passWord) throws Exception;
    // 删除用户
    void deleteUsers() throws Exception;
    // 查询用户,返回数组的形式。
    String[] selectUsers() throws Exception;
    String[] selectUsers2() throws Exception;
}

UserServiceImpl.java

/**
 * 用户业务实现类(面向接口编程)
 */
public class UserServiceImpl implements UserService{
    @Override
    public void login(String loginName, String passWord) throws Exception {
        if("admin".equals(loginName) && "123456".equals(passWord)){
            System.out.println("您登录成功,欢迎光临本系统~");
        }else {
            System.out.println("您登录失败,用户名或密码错误~");
        }
        Thread.sleep(1000);
    }

    @Override
    public void deleteUsers() throws Exception{
        System.out.println("成功删除了1万个用户~");
        Thread.sleep(1500);
    }

    @Override
    public String[] selectUsers() throws Exception{
        System.out.println("查询出了3个用户");
        String[] names = {"张全蛋", "李二狗", "牛爱花"};
        Thread.sleep(500);

        return names;
    }

    @Override
    public String[] selectUsers2() throws Exception {
        System.out.println("查询出了3000个用户");
        String[] names = {"张全蛋2", "李二狗2", "牛爱花2"};
        Thread.sleep(2500);

        return names;
    }
}

ProxyUtil.java

ProxyUtil(代理工具类)
     动态代理实现:
         通过 Proxy.newProxyInstance() 生成代理对象
         增强逻辑(InvocationHandler):
             记录开始时间(System.currentTimeMillis())
             调用真实方法(method.invoke(obj, args))
             计算并打印耗时(结束时间 - 开始时间)
          泛型支持:<T> T 设计可复用代理逻辑(不限于 UserService)

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

public class ProxyUtil {
    public static <T> T createProxy(T obj) {
        T proxy = (T) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                obj.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        long start = System.currentTimeMillis(); // 记录开始时间 1970年1月1日0时0分0秒 走到此刻的总毫秒值
                        // 真正调用业务对象被代理的方法
                        Object result = method.invoke(obj, args);
                        long end = System.currentTimeMillis();
                        System.out.println(method.getName() + "方法耗时:"+(end-start)/1000.0+"秒");
                        return result;
                    }
                });
        return proxy;
    }
}

Test.java

import java.util.Arrays;

/**
 * 目标:使用动态代理解决实际问题,并掌握使用代理的好处。
 */
public class Test {
    public static void main(String[] args) throws Exception{
        // 1、创建用户业务对象。
        UserService userService = ProxyUtil.createProxy(new UserServiceImpl());

        // 2、调用用户业务的功能。
        userService.login("admin", "123456");

        userService.deleteUsers();

        String[] names = userService.selectUsers();
        System.out.println("查询到的用户是:" + Arrays.toString(names));

        String[] names2 = userService.selectUsers2();
        System.out.println("查询到的用户是:" + Arrays.toString(names2));
    }
}

res

您登录成功,欢迎光临本系统~
login方法耗时:1.003秒
成功删除了1万个用户~
deleteUsers方法耗时:1.51秒
查询出了3个用户
selectUsers方法耗时:0.513秒
查询到的用户是:[张全蛋, 李二狗, 牛爱花]
查询出了3000个用户
selectUsers2方法耗时:2.503秒
查询到的用户是:[张全蛋2, 李二狗2, 牛爱花2]

技术亮点
  无侵入式监控:
    不修改 UserServiceImpl 源码,通过代理自动为所有方法添加耗时统计。
  通用性设计:
    ProxyUtil 可代理任何接口的实现类(如未来扩展 ProductService)。
  性能分析场景:
    快速定位耗时方法(如发现 selectUsers2 性能瓶颈)。

应用场景扩展
  这种模式常用于以下需求:
    日志记录:自动记录方法调用参数和结果
    事务管理:方法调用前后自动启/ commit事务
    权限校验:在方法执行前检查权限
    缓存代理:优先从缓存返回结果

对比静态代理的优势
       减少重复代码:无需为每个类手动编写代理类
    动态适配:代理逻辑可灵活调整(如后续改为记录日志)
    解耦核心逻辑:业务类只需关注自身功能,增强逻辑由代理统一处理
通过这个案例,可以清晰理解动态代理如何优雅地实现 横切关注点(Cross-Cutting Concerns) 与业务逻辑的分离。

 

posted on 2025-06-07 21:48  ᶜʸᵃⁿ  阅读(7)  评论(0)    收藏  举报