Java动态代理
一、动态代理:介绍、准备
2.
3.
4.
二、动态代理:创建代理、工作流程演示
例子1:包含四个文件BigStar.java、Star.java、ProxyUtil.java、Test.java
BigStar.java代码:
package d4_proxy;
/*
* 大明星
*/
public class BigStar implements Star{
private String name;
public BigStar(String name) {
this.name = name;
}
public String sing(String name) {
System.out.println(this.name + "正在唱:" + name);
return "谢谢!谢谢!";
}
public void dance() {
System.out.println(this.name + "正在优美的跳舞~~");
}
}
Star.java代码:
package d4_proxy;
/*
* Star接口
*/
public interface Star {
String sing(String name);
void dance();
}
ProxyUtil.java代码:
package d4_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* 中介公司
*/
public class ProxyUtil {
public static Star createProxy(BigStar bigstar) {
/*newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数1:用于指定一个类加载器,用此加载器去加载生成的代理类
该参数写法一般是固定的,都是用当前类的类加载器,此处即为ProxyUtil.class.getClassLoader()
参数2:用于指定生成的代理长什么样子,也就是有哪些方法
该例中被代理类BigStar只有一个接口,即通过Star接口来告诉生成的代理类应该有哪些方法,
此处Class<?>[] interfaces为数组,因为被代理类有时有多个接口,
由于BigStar只有Star一个接口,因此只需要把这一个接口包装成数组就行了,此处即为new Class[]{Star.class}
参数3:用来指定生成的代理对象要干什么事情
InvocationHandler是一个接口,接口不能直接创建对象,
因此我们一般做法为new一个该接口命名的内部类对象(即该接口的匿名实例)来指定代理对象干什么事情,
此处即为
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
*/
Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
new Class[]{Star.class}, new InvocationHandler() {
/*
* 代理干什么事情实际是由invoke方法决定的
* invoke方法是一个回调方法
*
* 假设代理写好后,
* 我们在主程序(Test.java)会写这样的代码:
* Star starProxy = ProxyUtil.createProxy(s); //s为BigStar的实例
* 这样就得到了一个代理对象
* 然后我们会调用代理对象的sing方法和dance方法
* starProxy.sing("好日子")和starProxy.dance()
* 当我们调用代理对象的sing方法和dance方法时,sing和dance方法会调用此处的invoke方法
* invoke方法有三个参数 invoke(Object proxy, Method method, Object[] args)
* sing和dance方法调用invoke方法时也需要传入这三个参数
* 第一个参数:Java会把当前代理对象(starProxy)当作一个Object对象传给invoke方法的Object proxy参数
* 也就是说我们可以通过invoke方法的Object proxy参数获取当前的代理对象starProxy
* 第二个参数:它会把当前调用的方法当作一个method传进来,这个method具体是谁,由外面的调用者决定,如果
* 外面是sing是在调用invoke,那此时method就代表sing方法,如果外面dance在调用invoke,那
* 此时method就代表dance方法
* 第三个参数:Object[] args,它会把方法的参数通过Object[]数组传进来,比如starProxy.sing("好日子")中,
* 将"好日子"包装成Object[]数组通过Object[] args参数传进来,
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理对象要干的事情,会在这里写代码指定
//假设我们现在的业务需求是唱歌,要提前准备话筒,收钱20万;跳舞,要提前准备场地,收钱1000万
//根据业务需求,我们需要在invoke方法里,用if来询问当前method代表方法名是不是equals("sing"),
//如果是,说明当前正在代理唱歌方法,那我们就需要先"准备话筒,收钱20万",然后准备让BigStar对象实例来唱歌,
//调用method.invoke()方法,传入BigStar对象对应参数bigstar以及唱哪首歌对应的参数args,
//method.invoke(bigstar, args)即相当于bigstar.sing(args)
if(method.getName().equals("sing")){
System.out.println("准备话筒,收钱20万");
//method.invoke(bigstar, args);
//因为此时method.invoke(bigstar, args);方法有返回值,返回“谢谢!”“谢谢!”,因此要加return
return method.invoke(bigstar, args);
}
else if(method.getName().equals("dance")){
System.out.println("准备话筒,收钱1000万");
//把该方法结果也返回出去,虽然没有返回值
return method.invoke(bigstar, args);
}
else {
//假设其他方法不需要代理,直接找bigstar
return method.invoke(bigstar, args);
}
/*
*
* 抽出共同需要执行的语句,以上代码可以简化为
* if(method.getName().equals("sing")){
* System.out.println("准备话筒,收钱20万");
* }
* else if(method.getName().equals("dance")){
* System.out.println("准备话筒,收钱1000万");
* }
* return method.invoke(bigstar, args);
*
*
*/
}
});
return starProxy;
}
}
Test.java代码:
package d4_proxy;
/*
* 主程序
*/
public class Test {
public static void main(String[] args) {
BigStar s = new BigStar("杨超越"); //创建BigStar实例
Star starProxy = ProxyUtil.createProxy(s); //为s创建代理对象,并赋值给starProxy
//调用代理对象的sing方法唱一首歌
//starProxy.sing("好日子");
//由于该方法有返回值,因此需要定义一个变量(String rs)来接收返回值
String rs = starProxy.sing("好日子");
System.out.println(rs);
starProxy.dance();
}
}
例子2:包含一个文件Main.java
Main.java代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 传入ClassLoader
new Class[] { Hello.class }, // 传入要实现的接口
handler); // 传入处理调用方法的InvocationHandler
hello.morning("Bob");
}
}
interface Hello {
void morning(String name);
}
三、动态代理:实际应用、好处
将共同需要实现的功能放到代理,并根据不同需求使用代理实现
例子:包含四个文件UserServiceImpl.java、UserService.java、ProxyUtil.java、Test.java
UserServiceImpl.java代码:
package d5_proxy2;
/*
* 用户业务实现类(面向接口编程)
*/
public class UserServiceImpl implements UserService{
@Override
public void login(String loginName, String passWord) throws Exception {
//long startTime = System.currentTimeMillis();
//模拟登录用户
if("admin".equals(loginName) && "123456".equals(passWord)) {
System.out.println("您登录成功,欢迎光临本系统~");
} else {
System.out.println("您登录失败,用户名或密码错误~");
}
//模拟该业务耗时1000ms
Thread.sleep(1000);
//long endTime = System.currentTimeMillis();
//System.out.println("login方法执行耗时:" + (endTime - startTime)/1000.0 + "s");
}
@Override
public void deleteUsers() throws Exception {
//long startTime = System.currentTimeMillis();
//模拟删除用户
System.out.println("成功删除了一万个用户~");
//模拟该业务耗时1500ms
Thread.sleep(1500);
//long endTime = System.currentTimeMillis();
//System.out.println("deleteUsers方法执行耗时:" + (endTime - startTime)/1000.0 + "s");
}
@Override
public String[] selectUsers() throws Exception {
//long startTime = System.currentTimeMillis();
//模拟查询用户
System.out.println("查询出了3个用户");
String[] names = {"张全蛋", "李二狗", "牛爱花"};
//模拟该业务耗时500ms
Thread.sleep(500);
//long endTime = System.currentTimeMillis();
//System.out.println("selectUsers方法执行耗时:" + (endTime - startTime)/1000.0 + "s");
return names;
}
}
UserService.java代码:
package d5_proxy2;
/*
* 用户业务接口
*/
public interface UserService {
//登录功能
void login(String loginName,String password) throws Exception;
//删除用户
void deleteUsers() throws Exception;
//查询用户,返回数组的形式
String[] selectUsers() throws Exception;
}
ProxyUtil.java代码:
package d5_proxy2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* 代理类,将统计方法耗时的代码放到代理类中
*/
public class ProxyUtil {
public static UserService createProxy(UserService userservice) {
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
new Class[] {UserService.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
if(method.getName().equals("login") || method.getName().equals("deleteUsers") ||
method.getName().equals("selectUsers")) {
long startTime = System.currentTimeMillis();
Object rs = method.invoke(userservice, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "方法执行耗时:" + (endTime - startTime)/1000.0 + "s");
return rs;
} else {
//调用其他方法时则不统计时间,直接调用方法
Object rs = method.invoke(userservice, args);
//并把方法的结果返回出去
return rs;
}
}
});
return userServiceProxy;
}
}
Test.java代码:
package d5_proxy2;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws Exception{
//1.创建用户业务对象
//直接执行,没有耗时统计
//UserService userService = new UserServiceImpl();
//使用代理后,有耗时统计功能
UserService userService = ProxyUtil.createProxy(new UserServiceImpl());
//2.调用用户业务的功能
userService.login("admin", "123456");
System.out.println("-------------------------------");
userService.deleteUsers();
System.out.println("-------------------------------");
String[] names = userService.selectUsers();
System.out.println("查询到的用户是:" + Arrays.toString(names));
System.out.println("-------------------------------");
}
}
【黑马磊哥】Java动态代理深入剖析,真正搞懂Java核心设计模式:代理设计模式,
视频地址:https://www.bilibili.com/video/BV1ue411N7GX/?spm_id_from=333.337.search-card.all.click