动态代理是一门java技术 是面向切面编程的重要技术,面向切面编程可以理解为面向方法编程,
比如如果要统计每个方法花费的时间,如果在每个方法中都都加入该测量方法,在一个工程中该工作量太大了,可以通过动态代理来实现。
对于动态代理黑马程序员给了一个很好的例子就是,你如果找一个明星唱歌你不可能直接找到她本人,你需要首先找到该明星的代理人然后把准备工作都做完 再找到明星唱歌。准备工作是代理人做的事情,唱歌这件事情是明星自己做的。回到第一段的例子,如果要测量每个方法的花费时间,可以让每个方法有一个代理人实现测量方法,然后方法体本身自己实现自身的方法。
动态代理的实现步骤
1.先创建一个接口 令被代理类实现
package com.itheima.ProxyDemo;
/*
* 实现动态代理的接口
* */
public interface Star {
//@Param("name") 唱的歌曲名称
void sing(String Sangname);
//返回值String
String dance();
}
2.创建一个实例类来实现接口 并重写接口的方法
package com.itheima.ProxyDemo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/*
* 明星类
* */
@Data@AllArgsConstructor
@NoArgsConstructor
public class StarImpl implements Star{
private String name;
@Override
public void sing(String Sangname) {
System.out.println(this.name+"正在唱歌"+Sangname);
}
@Override
public String dance() {
System.out.println(this.name+"正在跳舞");
return "thanks thanks ";
}
}
3.创建一个代理工具类(工具类代码解释都在注释中)
package com.itheima.ProxyDemo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* 实现动态代理的工具类
* */
public class ProxyUtils {
//方法定义为static 类可以直接调用
public static Object getProxyInstance(Object target) {
/*java.lang.reflect.Proxy中静态方法newProxyInstance的签名
public static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
第一个参数 ClassLoader loader 类加载器 用来加载代理类的加载 一般使用当前类加载器即可
第二个参数 Class<?>[] interfaces target实现的接口列表 (.getClass)
一般使用target反射来获得target的类加载器 然后获得该类实现的接口列表 (.getInterfaces)
第三个参数 InvocationHandler h 是一个接口 通过匿名内部类来实现 详细功能 下面聊 */
Object o = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
/*
* 该匿名类需要重写一个invoke方法
* invoke方法有三个参数Object proxy, Method method, Object[] args
* 第一个参数proxy是代理对象,一般不用
* 第二个参数method是目前正在执行的方法
* 一般使用method.getName()来获取目前正在执行方法的名称
* 一般使用method.invoke(target,args)来调用该方法的真正执行
* 第三个参数args是传入method的参数
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
/*
* 此处写出代理人需要做的工作 以代理人实现喊话功能为例
* */
System.out.println("代理人正在喊话" + name);
/*
利用反射的invoke来调用真正方法 该方法的返回值为一个Object
* 该方法真正执行
* 第一个参数 target是正在执行的方法所属的对象
* 第二个参数 args是正在执行的方法传入的参数
* 该
* */
Object invoke = method.invoke(target, args);
return invoke;
}
}
);
return o ; //将该代理人返回
}
}
4.在测试类中声明一个实体类,然后通过代理工具类来声明代理 最后通过代理调用方法
package com.itheima;
import com.itheima.ProxyDemo.ProxyUtils;
import com.itheima.ProxyDemo.Star;
import com.itheima.ProxyDemo.StarImpl;
import org.junit.jupiter.api.Test;
public class TestProxy {
@Test
public void testProxy(){
Star star = new StarImpl("张学友"); // 真实对象
Star proxyInstance = (Star)ProxyUtils.getProxyInstance(star);// 代理对象
proxyInstance.sing("冰雨");
System.out.println(proxyInstance.dance());
}
}