想了解java中的动态代理,我们首先需要了解的是一种常用的设计模式,代理模式。
代理模式
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
简单来说,为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责为委托了(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不实现具体服务,而是利用委托类来完成服务,并将执行结果封装处理。
其实就是代理类为被代理类预处理消息、过滤消息并在此之后将消息转发给被代理类,之后还能进行消息的后置处理。代理类和被代理类通常会存在关联关系(即上面提到的持有的被代理对象的引用),代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。
针对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理。
静态代理
简单来说,静态代理需要我们先创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。
动态代理
目前动态代理的实现分为两种
- 基于JDK的动态代理
- 基于CGILB的动态代理
在业务中使用动态代理,一般是为了给需要实现的方法添加预处理或者添加后续操作,但是不干预实现类的正常业务,把一些基本业务和主要的业务逻辑分离。我们一般所熟知的Spring的AOP原理就是基于动态代理实现的。
基于jdk的动态代理
基于jdk的动态代理就需要知道两个类:1.InvocationHandler(接口)、2.Proxy(类)
然后还需要知道JDK是基于接口的动态代理。
代码演示
创建接口
package proxy;
public interface Subject {
void hello(String param);
}
实现接口
package proxy;
public class SubjectImpl implements Subject{
@Override
public void hello(String param){
System.out.println("hello"+param);
}
}
创建接口实现类的代理类
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class SubjectProxy implements InvocationHandler {
private Subject subject;
public SubjectProxy(Subject subject){
this.subject=subject;
}
@Override
public Object invoke(Object proxy, Method method,Object[] args) throws InvocationTargetException, IllegalAccessException {
System.out.println("begin");
Object invoke = method.invoke(subject,args);
System.out.println("end");
return invoke;
}
}
使用动态代理,代理对象调用任意方法,代理的invoke都会执行
invoke参数:
- proxy:代理对象
- method:代理对象调用的方法,被封装成的对象
- args:代理对象调用方法适合传递的实际参数
代理类的实际调用
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
Subject subject = new SubjectImpl();
InvocationHandler subjectProxy = new SubjectProxy(subject);
Subject proxyInstance = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), //代理的类加载器
subject.getClass().getInterfaces(), //被代理的接口,如果有多个 就是数组形式传入
subjectProxy); //代理类实例
proxyInstance.hello("world");
}
}
输出的结果是:
begin
helloworld
end
以这个结果来说,实际上在Subject类中只会输出一条hello world,但是在被代理之后,实际调用的方法是SubjectProxy的invoke方法,这样可以在不修改业务类的情况下对业务类增加一些日志等其他操作。