代理模式 Proxy Pattern
代理模式是指为目标对象提供一种代理,以控制这个对象的访问,是一种结构型模式。
代理对象在目标对象与调用者之前起到中介的作用。
代理模式用于目标对象进行增强,在调用前后增加一些代码逻辑。
代理模式用于保护目标对象
代理模式可以理解为中介者模式。代理对象了解目标对象,负责与目标对象交互,并附加一些逻辑。
代理分为静态代理和动态代理。
静态代理模式的三种角色
- ISubject : 接口层,主题
- SubjectImpl: 实现层,目标对象
- SubjectProxy: 代理层,持有目标对象,实现 ISubject 接口,并附加增强的逻辑。
静态代理实例
// 主题,接口抽象层
public interface ISubject {
void doSomething();
}
// 实现,目标对象
public class SubjectImpl implements ISubject {
@Override
public void doSomething() {
System.out.println("SubjectImpl do something");
}
}
// 代理对象
public class SubjectProxy implements ISubject {
// 持有目标对象
private ISubject target;
public SubjectProxy(ISubject target) {
this.target = target;
}
@Override
public void doSomething() {
// 在调用目标对象前
System.out.println("SubjectProxy . before do something ");
// 调用目标对象
target.doSomething();
// 在调用目标对象后
System.out.println("SubjectProxy . after do something ");
}
}
动态代理
为什么要使用动态代理? 静态代理需要开发者静态编写代码,动态代理更灵活,开发者仅需关注需要增强、需要改变的逻辑。
动态代理分为jdk动态代理,和cglib动态代理。
jdk动态代理
先准备一个接口和实现类
// 接口
public interface ISubject {
void doSomething();
void eat();
}
// 实现类
public class SubjectImpl implements ISubject {
@Override
public void doSomething() {
System.out.println("SubjectImpl do something");
}
@Override
public void eat() {
System.out.println("SubjectImpl eat..");
}
}
创建 InvocationHandler
public class ProxyInvocationHandler implements InvocationHandler {
// 目标实现类,这不是必须的
private final ISubject target;
public ProxyInvocationHandler(ISubject target) {
this.target = target;
}
/**
* @param proxy 代理对象
* @param method 调用的方法
* @param args 调用的方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodInfo = method.toString();
// 处理前
System.out.println("--before method [" + methodInfo + "] call");
// 执行目标方法
// 这里可以由开发者决定执行、或者不执行
Object rt = method.invoke(target, args);
// 调用后执行
System.out.println("--after method [" + methodInfo + "] call");
return rt;
}
}
创建jdk动态代理对象
public static void main(String[] args) {
final SubjectImpl target = new SubjectImpl();
Class<?>[] interfaces = {ISubject.class};
// 创建一个代理对象
ISubject proxyObj = (ISubject) Proxy.newProxyInstance(
// 类加载器
ISubject.class.getClassLoader(),
// 代理对象将实现的接口
interfaces,
// 调用处理器,对目标方法的调用,将转到这个处理器里面
new ProxyInvocationHandler(target)
);
proxyObj.doSomething();
proxyObj.eat();
proxyObj.toString();
}
执行结果:
--before method [public abstract void org.smileface.designpattern.proxy.jdkProxy.ISubject.doSomething()] call
SubjectImpl do something
--after method [public abstract void org.smileface.designpattern.proxy.jdkProxy.ISubject.doSomething()] call
--before method [public abstract void org.smileface.designpattern.proxy.jdkProxy.ISubject.eat()] call
SubjectImpl eat..
--after method [public abstract void org.smileface.designpattern.proxy.jdkProxy.ISubject.eat()] call
--before method [public java.lang.String java.lang.Object.toString()] call
--after method [public java.lang.String java.lang.Object.toString()] call
可以看到,每次执行代理的对象的方法,都会触发 InvocationHandler.invoke
方法的调用,并执行了我们附加的执行前、执行后逻辑。
Proxy.newProxyInstance参数说明
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
第一个参数(ClassLoader loader):类加载器,使用那个类加载器加载代理对象
第二个参数(Class<?>[] interfaces):接口集合,新创建的代理对象,将实现这些给定的接口
第三个参数(InvocationHandler h):调用处理器,方法拦截器。代理对象调用的所有方法都将转到这个处理器中,此处理器可在方法调用前增加逻辑、可决定是否执行目标方法、可在调用后增加逻辑等等。
cglib动态代理
pom
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.7</version>
</dependency>
先准备一个接口和实现类
public interface ISubject {
void doSomething();
}
public class SubjectImpl implements ISubject {
@Override
public void doSomething() {
System.out.println("SubjectImpl do something");
}
}
创建cglib动态代理对象
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibMain {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SubjectImpl.class);
enhancer.setCallback(new MethodInterceptor() {
/**
*
* @param o 代理对象(EnhancerByCGLIB )
* @param method 原方法 SubjectImpl类的方法
* @param args 方法参数
* @param methodProxy 代理对象的方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 方法执行前,执行的逻辑
System.out.println("before ... " + method);
// 执行目标方法(由开发者决定是否要调用目标方法)
Object result = methodProxy.invokeSuper(o, args);
// 方法执行后执行的逻辑
System.out.println("after ..." + method);
return result;
}
});
// 创建代理对象
ISubject subject = (ISubject) enhancer.create();
subject.doSomething();
}
}
执行结果如下:
before ... public void org.smileface.designpattern.proxy.cglib.SubjectImpl.doSomething()
SubjectImpl do something
after ...public void org.smileface.designpattern.proxy.cglib.SubjectImpl.doSomething()
Enhancer
是cglib 最核心的类,相当于jdk动态代理的proxy类。
jdk动态代理和cglib动态代理的区别
- JDK动态代理实现了被代理的接口,CGLIB动态代理继承了被代理对象
- 两者都在运行期生成字节码文件,JDK动态代理直接写字节码文件,CGLIB使用ASM框架写字节码文件,CGLIB实现更复杂,所以生成代理类这个过程CGLIB效率更低。
- JDK动态代理调用代理方法时通过反射机制调用的,CGLIB是通过FastClass机制调用的,所以CGLIB的执行效率更高。
FastClass机制
简单理解:为代理类和被代理类各生成一个类,这个类会为代理类和被代理类的方法分配一个index,通过这个index当做入参,FastClass直接定为到要调用的方法并直接调用,省去了反射的时间。