动态代理详解
大约 5 分钟Spring全家桶Spring入门
动态代理角色
动态代理和静态代理角色一样
- 接口(抽象角色)
- 真实角色
- 代理角色
- 客户端访问代理角色
动态代理的代理类是动态生成的,不是我们直接写好的!
动态代理分类
基于接口的动态代理:JDK动态代理
基于类的动态代理:cglib
Java字节码:javasist
需要了解两个类
- Proxy 代理
- InvocationHandle 调用处理程序
接口(抽象角色)
Rent.java
package com.kuang.demo03;
/**
* @author Administrator
*/
public interface Rent {
/**
* 出租房屋
*/
public void rent();
}
真实角色
Host.java
package com.kuang.demo03;
/**
* @author Administrator
* 房东
*/
public class Host implements Rent {
public void rent() {
System.out.println("房东要出租房子!");
}
}
自动生成代理类
ProxyInvocationHandler.java
package com.kuang.demo03;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author Administrator
* 用这个类,自动生成代理类
*/
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);
}
/**
* 处理代理实例,并返回结果
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
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("收中介费");
}
}
客户端通过自动生成代理类实现
Client.java
package com.kuang.demo03;
/**
* @author Administrator
*/
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色:现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象
pih.setRent(host);
/**
* 这里的proxy就是动态生成的,我们并没有写
*/
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
把自动生成代理的类参数化,形成一个工具类
ProxyInvocationHandler.java
package com.kuang.demo04;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author Administrator
* 用这个类,自动生成代理类
*/
public class ProxyInvocationHandler implements InvocationHandler {
/**
* 被代理的接口
*/
private Object target;
public void setTarget(Object target) {
this.target = target;
}
/**
* 生成代理对象
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
/**
* 处理代理实例,并返回结果
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
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("[debug]***执行了" + msg + "方法***");
}
}
使用
package com.kuang.demo04;
import com.kuang.demo02.UserService;
import com.kuang.demo02.UserServiceImpl;
/**
* @author Administrator
*/
public class Client {
public static void main(String[] args) {
//这是真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色,不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//设置要代理的对象
pih.setTarget(userService);
//动态生成代理类
UserService proxy = (UserService) pih.getProxy();
proxy.add();
proxy.delete();
proxy.update();
proxy.query();
}
}
动态代理的好处
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务!
- 公共业务也就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是接口,一般就是对应的一类任务!
- 一个动态代理可以代理多个类,只要是实现了同一个接口即可!
InvocationHandler接口
public interface InvocationHandlerInvocationHandler是由代理实例的调用处理程序实现的接口 。
每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。
Object invoke(Object proxy,
方法 method,
Object[] args)
throws Throwable处理代理实例上的方法调用并返回结果。 当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。
参数
proxy - 调用该方法的代理实例
method -所述方法对应于调用代理实例上的接口方法的实例。 方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。 原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
结果
从代理实例上的方法调用返回的值。 如果接口方法的声明返回类型是原始类型,则此方法返回的值必须是对应的基本包装类的实例; 否则,它必须是可声明返回类型的类型。 如果此方法返回的值是null和接口方法的返回类型是基本类型,那么NullPointerException将由代理实例的方法调用抛出。 如上所述,如果此方法返回的值,否则不会与接口方法的声明的返回类型兼容,一个ClassCastException将代理实例的方法调用将抛出。
异常
Throwable - 从代理实例上的方法调用抛出的异常。 异常类型必须可以分配给接口方法的throws子句中声明的任何异常类型java.lang.RuntimeException检查的异常类型java.lang.RuntimeException或java.lang.Error 。 如果检查的异常是由这种方法是不分配给任何的中声明的异常类型throws接口方法的子句,则一个UndeclaredThrowableException包含有由该方法抛出的异常将通过在方法调用抛出代理实例。
Proxy类
public class Proxy
extends Object
implements SerializableProxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
Modifier and Type Method and Description
static InvocationHandler getInvocationHandler(Object proxy)
返回指定代理实例的调用处理程序。
static 类<?> getProxyClass(ClassLoader loader, 类<?>... interfaces)
给出类加载器和接口数组的代理类的 java.lang.Class对象。
static boolean isProxyClass(类<?> cl)
如果且仅当使用 getProxyClass方法或 newProxyInstance方法将指定的类动态生成为代理类时,则返回true。
static Object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。