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) 与业务逻辑的分离。