静态代理和动态代理
根据加载被代理类的时机不同,将代理分为静态代理和动态代理。
- 编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;
- 运行时才确定被代理的类是哪个,那么可以使用类动态代理。
1、静态代理
public interface Subject {
public void sayGoodBye();
public void sayHello(String str);
}
2、 定义被代理类(原来功能类)并实现被代理类的功能逻辑:
public class RealSubject implements Subject {
@Override
public void sayGoodBye() {
System.out.println("RealSubject sayGoodBye");
}
@Override
public void sayHello(String str) {
System.out.println("RealSubject sayHello " + str);
}
}
3、 定义静态代理类(功能增加类),这个代理类也必须要实现和被代理类相同的Subject接口,便于对原有功能的增强:
public class ProxySubject implements Subject {
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public void sayGoodBye() {
//代理类,功能的增强
System.out.println("ProxySubject sayGoodBye begin");
//在代理类的方法中 间接访问被代理对象的方法
subject.sayGoodBye();
System.out.println("ProxySubject sayGoodBye end");
}
@Override
public void sayHello(String str) {
//代理类,功能的增强
System.out.println("ProxySubject sayHello begin");
//在代理类的方法中 间接访问被代理对象的方法
subject.sayHello(str);
System.out.println("ProxySubject sayHello end");
}
}
4、使用:
public static void main(String[] args) {
//被代理的对象,某些情况下 我们不希望修改已有的代码,我们采用代理来间接访问
RealSubject realSubject = new RealSubject();
//代理类对象
ProxySubject proxySubject = new ProxySubject(realSubject);
//调用代理类对象的方法
proxySubject.sayGoodBye();
System.out.println("******");
proxySubject.sayHello("Test");
}
总结: 静态代理(传统代理模)的实现方式比较暴力直接,需要将所有被代理类的所有方法都写一遍,并且一个个的手动转发过去。有点累
二、动态代理
1、在java的动态代理机制中,有两个重要的类或接口
- 一个是 InvocationHandler(Interface)
- 另一个则是Proxy(Class)
- 这一个类和接口是实现我们动态代理所必须用到的
2、 InvocationHandler
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
- proxy:指代生成的代理对象;
- method:指代的是我们所要调用真实对象的某个方法的Method对象;
- args:指代的是调用真实对象某个方法时接受的参数;
- 每一个代理实类例的invocation handler都要实现InvocationHandler这个接口。并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke 方法来进行调用
3、 Proxy这个类的 newProxyInstance 这个方法
JDK动态代理需要借助接口来实现,如果我们要代理的对象功能没有抽成任何接口,那么我们就无法通过JDK动态代理的方式来实现。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
- loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
- interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
- 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。
第一步:定义一个接口
public interface Subject {
public void sayGoodBye();
public void sayHello(String str);
}
第二步:定义真是对象(被代理类):
public class RealSubject implements Subject {
@Override
public void sayGoodBye() {
System.out.println("RealSubject sayGoodBye");
}
@Override
public void sayHello(String str) {
System.out.println("RealSubject sayHello " + str);
}
}
第三步: 定义一个InvocationHandler, 相当于一个代理处理器
SubjectInvocationHandler并不是真正的代理类,而是用于定义代理类需要扩展、增强那些方法功能的类。在invoke函数中,对代理对象的所有方法的调用都被转发至该函数处理。在这里可以灵活的自定义各种你能想到的逻辑。
public class SubjectInvocationHandler implements InvocationHandler {
//这个就是我们要代理的真实对象
private Object subject;
//构造方法,给我们要代理的真实对象赋初值
public SubjectInvocationHandler(Object subject) {
this.subject = subject;
}
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
//在代理真实对象前我们可以添加一些自己的操作
System.out.println("before Method invoke");
System.out.println("Method:" + method);
//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
method.invoke(subject, args);
//在代理真实对象后我们也可以添加一些自己的操作
System.out.println("after Method invoke");
return null;
}
}
第四步:调用
public static void main(String[] args) {
//被代理类
Subject realSubject = new RealSubject();
//我们要代理哪个类,就将该对象传进去,最后是通过该被代理对象来调用其方法的
SubjectInvocationHandler handler = new SubjectInvocationHandler(realSubject);
//生成代理类
Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(), handler);
//输出代理类对象
System.out.println("Proxy : "+ subject.getClass().getName());
System.out.println("Proxy super : "+ subject.getClass().getSuperclass().getName());
System.out.println("Proxy interfaces : "+ subject.getClass().getInterfaces()[0].getName());
//调用代理类sayGoodBye方法
subject.sayGoodBye();
System.out.println("--------");
//调用代理类sayHello方法
subject.sayHello("Test");
}
Proxy : com.sun.proxy.$Proxy0
Proxy super : java.lang.reflect.Proxy
Proxy interfaces : com.company.ha.Subject
before Method invoke
Method:public abstract void com.company.ha.Subject.sayGoodBye()
RealSubject sayGoodBye
after Method invoke
--------
before Method invoke
Method:public abstract void com.company.ha.Subject.sayHello(java.lang.String)
RealSubject sayHello Test
after Method invoke
总结:
与静态代理相比,动态代理具有如下的优点:
- 代理转发的过程自动化了,实现自动化搬砖;
- 代理类的代码逻辑和具体业务逻辑解耦,与业务无关;
首先来看看 $Proxy0 这东西,这个东西就是真正的代理类对象,我们定义SubjectInvocationHandler类则是用于添加对代理类的功能扩展!而 $Proxy0类继承java.lang.reflect.Proxy类 并实现Subject接口 ,因此它的类声明方式如下:
同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
如果想添加功能可以:
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
if (method.getName().equals("sayGoodBye")) {//在调用sayGoodBye方法的时候 对返回值进行处理
int result = (int) method.invoke(subject, args);
return result + 10;
} else {//其他方法一律不处理
return method.invoke(subject, args);
}
}