动态代理模式
实现原理
- 设计动态代理类(
DynamicProxy
)时,不需要显式实现与目标对象类(RealSubject
)相同的接口,而是将这种实现推迟到程序运行时由JVM
来实现
即:在使用时再创建动态代理类 & 实例静态代理则是在代理类实现时就指定与目标对象类(RealSubject)相同的接口
- 通过
Java
反射机制的method.invoke()
,通过调用动态代理类对象方法,从而自动调用目标对象的方法
//动态代理模式例子之一
//租房
public interface Rent {
public void rent();
}
//房东
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
//用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是使用反射机制来实现
seeHouse();
Object result = method.invoke(rent, args);
fare();
return result;
}
public void seeHouse(){
System.out.println("房东带去看房子");
}
public void fare(){
System.out.println("收中介费");
}
}
//用户类
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//动态代理类
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象
proxyInvocationHandler.setRent(host);
Rent proxy = (Rent) proxyInvocationHandler.getProxy();//这里的proxy动态生成
proxy.rent();
}
}
结果:
房东带去看房子
房东出租房子
收中介费
此程序不是常规的动态代理,只是给我们展示了大致动态代理的原理,通过反射机制调用
目标对象.
动态代理模式例子二
//功能接口
public interface UserService
{
public void add();
public void delete();
public void update();
public void query();
}
//实现类
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
//用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public void setRent(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是使用反射机制来实现
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String msg)
{
System.out.println("执行了"+msg+"方法");
}
}
//用户类
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//动态代理类
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setTarget(userService);
//动态生成代理类
UserService proxy = (UserService) proxyInvocationHandler.getProxy();
proxy.delete();
}
}
结果:
执行了delete方法
删除了一个用户
相比例子1,例子2的用法适用于所有情况,可以当做模板参考.
代理模式绝对不止于此
在这个场景中:我把账号托给代打平台,平台帮我找到代打者,然后把号给他,但同时,真实角色也要知道代理角色的信息等等,所以,代理模式运用很广泛,在框架里几乎无处不在,非常符合AOP切面编程.
在过去主打纵向编程的时代,切面编程在开发中也越来越重要.
优点
- 只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码
- 更强的灵活性
设计动态代理类(DynamicProxy)时,不需要显式实现与目标对象类(RealSubject)相同的接口,而是将这种实现推迟到程序运行时由 JVM来实现在使用时(调用目标对象方法时)才会动态创建动态代理类 & 实例,不需要事先实例化
缺点
- 效率低
相比静态代理中 直接调用目标对象方法,动态代理则需要先通过Java
反射机制 从而 间接调用目标对象方法 - 应用场景局限
因为 Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口 创建 代理类,不能针对类 创建代理类
即只能动态代理 实现了接口的类