静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态:在程序运行时运用反射机制动态创建而成。
静态代理
准备接口
/**
* 角色接口
*/
interface Role {
void doSomething();
}
代理类
/**
* 代理类
*/
class ProxyRole implements Role{
//用被代理类对象进行实例化
private Role role;
public ProxyRole (Role role) {
this.role = role;
}
public void before () {
System.out.println("代理类做准备工作");
}
public void after () {
System.out.println("代理类做收尾工作");
}
@Override
public void doSomething() {
before();
role.doSomething();
after();
}
}
被代理类
/**
* 被代理类
* 具体的实现
*/
class Singer implements Role {
@Override
public void doSomething() {
System.out.println("歌手在唱歌");
}
}
客户端调用
public class StaticProxyTest {
public static void main(String[] args) {
//创建被代理对象
Singer singer = new Singer();
//创建代理类对象
Role proxyRole = new ProxyRole(singer);
proxyRole.doSomething();
}
}
输出结果
代理类做准备工作
歌手在唱歌
代理类做收尾工作
优点:
- 代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合)
缺点:
- 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
- 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。
example:
举例说明:代理可以对实现类进行统一的管理,如在调用具体实现类之前,需要打印日志等信息,这样我们只需要添加一个代理类,在代理类中添加打印日志的功能,然后调用实现类,这样就避免了修改具体实现类。满足我们所说的开闭原则。但是如果想让每个实现类都添加打印日志的功能的话,就需要添加多个代理类,以及代理类中各个方法都需要添加打印日志功能(如上的代理方法中删除,修改,以及查询都需要添加上打印日志的功能)
动态代理
要想实现动态代理,需要解决的问题
- 如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
- 当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。
动态代理类
/**
* 动态代理类
*/
class ProxyFactory implements InvocationHandler{
/**
* 调用方法返回对应的代理类对象
* @param obj 被代理类对象
* @return
*/
public static Object getProxyInstance(Object obj) {
MyInvocationHandler handler = new MyInvocationHandler(obj);
/**
* public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
*/
Object proxyInstance = Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
handler);
return proxyInstance;
}
//简化写法
// public static Object getProxyInstance(Object obj) {
// return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// System.out.println("准备工作");
// Object invoke = method.invoke(obj, args);
// System.out.println("收尾工作");
// return invoke;
// }
// });
// }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
Proxy 动态产生的代理会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。
class MyInvocationHandler implements InvocationHandler {
private Object obj;
public MyInvocationHandler(Object obj) {
this.obj = obj;
}
/**
* 期望通过代理类对象调用 xxx方法的时候进入此
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*原对象方法调用前处理日志信息*/
System.out.println("satrt-------");
//代理类调用的的方法,即为被代理类调用的方法
Object invokeMethod = method.invoke(obj, args);
/*原对象方法调用后处理日志信息*/
System.out.println("end-------");
return invokeMethod;
}
}
被代理对象obj通过参数传递进来,我们通过obj.getClass().getClassLoader()获取ClassLoader对象,然后通过obj.getClass().getInterfaces()获取它实现的所有接口,然后将obj包装到实现了InvocationHandler接口的MyInvocationHandler对象中。通过newProxyInstance函数我们就获得了一个动态代理对象。
客户端调用
public class DynamicProxyTest {
public static void main(String[] args) {
Singer singer = new Singer();
Role proxyInstance = (Role)ProxyFactory.getProxyInstance(singer);
proxyInstance.doSomething();
}
}
输出结果
satrt-------
歌手在唱歌
end-------
动态代理具体步骤:
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
动态代理优点:
- 动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强