动态代理的概念
动态代理是一种在运行时生成代理对象的机制,无需手动编写代理类。
代理就类似于中介公司,为明星置办各种前期准备。例如歌声需要开演唱会唱歌,那么此时就需要代理对象进行置办场地,设备,然后明星只需要负责唱歌。同时,为了让中介公司知道自己要执行什么方法的代理(例如唱歌,跳舞),就需要一个接口来定义这些方法,让明星变为这个接口的实现类。
动态代理的应用场景
日志记录:在方法调用前后自动添加日志。
事务管理:统一管理数据库事务的开启和提交。
java中,可以使用Proxy.newProxyInstance(参数一,参数二,参数三)方法创建代理对象。
参数一:用于指定用哪个类加载器加载生成的代理类。
参数二:指定代理类需要实现的接口(明星类实现了哪些接口,代理类就需要实现哪些接口)
参数三:指定代理类需要如何去代理(代理要做的事)
参数一与参数二的写法固定,参数三需要使用匿名内部类进行编写代理需要实现的方法。
参数一使用getClassLoader()方法获取类的加载器,加载代理类。类名.class.getClassLoader(),这是参数一的固定写法。
参数二可以直接使用需要代理的对象实现的接口来写。
需要代理的对象.getClass.getInterface(),这是参数二通常的写法。
参数三则是用匿名内部类,重写InvocationHandler()接口的方法。
示例
要求:为歌手创建代理类对象,用于唱歌跳舞的前期准备
明星和代理类需要实现的方法
package ProxyDemo;
//明星服务接口
public interface StarService {
void sing(String name);
String dance();
}
明星对象
package ProxyDemo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
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 "谢谢!谢谢!";
}
}
代理对象
package ProxyDemo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理工具类,中介公司,专门负责创建代理对象并返回给别人使用
*/
public class ProxyUtil {
//创建一个明星对象的代理对象返回
public static StarService creatProxy(Star s) {
/**
* 参数一:用于指定用哪个类加载器加载生成的代理类。
* 参数二:指定代理类需要实现的接口(明星类实现了哪些接口,代理类就需要实现哪些接口)
* 参数三:指定代理类需要如何去代理(代理要做的事)
*/
StarService prox = (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 ("sing".equals(methodName)){
System.out.println("准备话筒,收钱20万");
}else if ("dance".equals(methodName)){
System.out.println("准备场地,收钱100万");
}
//真正干活(通知明星对象进行执行)
Object result = method.invoke(s, args);
return result;
}
});
return prox;
}
}
因为Proxy创建的类对象是Object型的,因此用强制转化将其转化为明星对象和代理对象需要实现的方法的接口类型,然后将代理对象返回给外部创建代理对象的对象。
匿名内部类内invoke()方法的三个参数分别为
参数一:proxy接收到代理对象本身
参数二:method代表正在被代理的方法
参数三:args代表正在被代理的方法的参数
可以利用参数一递归调用代理对象的类的其他方法
示例
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 通过 proxy 递归调用其他代理方法
if (method.getName().equals("methodA")) {
return proxy.getClass().getMethod("methodB").invoke(proxy);
}
return null;
}
创建代理对象的测试类(续上文)
package ProxyDemo;
public class Text {
public static void main(String[] args) {
//目标:创建代理对象
//1、准备一个明星对象:设计明星类
Star star = new Star("王梓钰");
//2、为明星创建一个专属于她的代理对象
StarService proxy = ProxyUtil.creatProxy(star);
proxy.sing("红昭愿");
System.out.println(proxy.dance());
}
}
执行结果
以上,就是动态代理的简单使用。