概念:
代理模式是给一个对象提供一种代理对象以控制该对象的访问,通过创建的代理对象作为"替身"代替原来的对象,从而达到控制对象访问的目的(代替处理)
作用: 在不修改原来对象代码的基础上,对原来对象的功能进行修改或者增强
创建
相关概念:
目标类:原对象,通过代理对象控制他的访问扩展其功能
代理类:代理模式生成的对象,是原对象的替身,在原对象的基础上修改逻辑,增强功能
静态代理
概念:静态代理就是指我们在给一个类扩展功能的时候,我们需要去书写一个静态的类,相当于在之前的类上套了一层,这样我们就可以在不改变之前的类的前提下去对原有功能进行扩展。
实现方式:静态代理通过一个代理类与被代理对象使用同一个接口(保证方法名相同),然后在代理类里通过组合的方式把需要被代理的对象传入重写的对象中调用被代理对象的方法即可实现。
缺点:若有大量的类需要被代理,则会增大代码量。
接口:
public interface PublicInterface {
void publicMethod();
}
目标类
public class Target implements PublicInterface{
public String name;
public Target(String name) {
this.name = name;
}
@Override
public void publicMethod() {
System.out.println("我是:"+name);
}
}
代理类
public class ProxyClass implements PublicInterface{
public Target target;
public ProxyClass(Target target) {
this.target = target;
}
@Override
public void publicMethod() {
System.out.println("方法增强前");
target.publicMethod();
System.out.println("方法增强后");
}
}
jdk动态代理
JDK动态代理主要由JDK提供的Proxy实现。动态代理类是在运行时生成指定接口的代理类,每个代理实例都有一个关联的调用处理程序对象,此对象实现InvocationHandler。最终的业务逻辑是在InvocationHandler的invoke方法中实现。
JDK动态代理时基于接口的方式,换句话说就是代理类和目标类都实现同一接口,这样方法名就相同了。
public class RpcClientProxy implements InvocationHandler {
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<T> clazz) {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
log.info("invoked method: [{}]", method.getName());
}
public static void main(String[] args) {
HelloService helloService = rpcClientProxy.getProxy(HelloService.class);
}
}
invoke在何处被调用的?
代理对象在继承了 InvocationHandler接口后,必须重写实现invoke方法,
//将动态代理对象反编译生成到工程目录下
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true")
执行proxy类的newProxyInstance方法将生成目标对象的代理实例
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h){
/*根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0.$Proxy0类 实现了interfaces的接口,并继承了Proxy类.*/
Class cl = getProxyClass(loader, interfaces);
}
/*实例化$Proxy0并在构造方法中把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下: */
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
在动态生成的源码中有如下代码(该代码是字节码文件,需使用反编译方法才能生成)
public final class $Proxy0 extends Proxy implements Retention {
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final RetentionPolicy value() throws {
try {
return (RetentionPolicy)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
该类为jdk自动生成的动态代理类,其内部的 value()方法调用了父类接口的invoke方法,其实质是调用了自己编写的实现了InvocationHandler接口的invoke()方法,这样就解释类,编写的invoke方法在何处被调用执行的问题
总结:
基于接口的动态代理,实际上是在内存中生成了一个对象,该对象实现了指定的目标类对象拥有的接口,所以代理类和目标类对象是兄弟关系
cglib动态代理
CGLIB基于目标类生成该类的一个子类作为代理类,目标类必须可继承
实现步骤:
- 创建Enhancer实例
- 通过setSuperclass方法来设置目标类
- 通过setCallback方法来设置拦截对象
- create方法生成Target的代理类,并返回代理类的实例
CGLIB代理时代理类去继承目标类,然后重写目标类的方法,这样以保证代理类拥有目标类的同名方法。
public class SelfInterceptor implements MethodInterceptor {
/**
Enhancer可能是CGLIB中最常用的一个类,和Java1.3动态代理中引入的Proxy类差不多。
和Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口。
Enhancer创建一个被代理对象的子类并且拦截所有的方法调用
(包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法
*/
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("动态代理处理前");
System.out.println("被拦截的方法"+method.getName());
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("结果:"+result);
System.out.println("动态代理处理后");
return result;
}
public static void main(String[] args) {
TargetClass target = (TargetClass)(new SelfInterceptor().getProxy(TargetClass.class));
target.target("我是目标");
}
}