代理(Proxy)是一种设计模式,定义:为其他对象提供一个代理以控制对某个对象的访问,即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法
示例代码: https://github.com/steveNash12/microservice/tree/master/microservice/proxy
静态代理
在编译期确定代理对象。静态代理在使用是时,需要定义接口类,并且被代理的对象和代理类需要一起实现接口类。
例子:
接口类
public interface Subject {
public void play();
public void sayHi();
}
被代理的类(真实业务类)
public class RealSubject implements Subject {
@Override
public void play() {
System.out.println("this is RealSubject");
}
@Override
public void sayHi() {
System.out.println("hello, everyone this is RealSubject");
}
}
代理类
package xyz.songxl.staticp;
import xyz.songxl.pattern.RealSubject;
import xyz.songxl.pattern.Subject;
/**
* @author songxl
* @create 2019-07-10 15:17
* @desc 静态代理 代理类和正式的业务类需要实现同一个接口
**/
public class StaticProxy implements Subject {
private RealSubject realSubject;
public StaticProxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void play() {
System.out.println("this is proxy");
try {
realSubject.play();
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Override
public void sayHi() {
System.out.println("this is proxy");
try {
realSubject.sayHi();
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
测试类:
public static void main(String[] args) {
Subject staticProxy = new StaticProxy(new RealSubject());
// 执行代理方法
staticProxy.play();
staticProxy.sayHi();
}
静态代理总结:
1. 优点 : 可以做到在不修改目标对象的情况下,对目标对象进行功能扩展
2. 缺点:每个需要代理的方法都需要手动去写代理方法 对于方法比较多的情况下,会有很多的重复工作
动态代理(jdk 动态代理)
由于上面静态代理工作量重复的问题,动态代理应运而生。
动态代理通过反射机制实现
例子:
业务类继续使用上面的 RealSubject.java
代理类
package xyz.songxl.dynamic;
import xyz.songxl.pattern.RealSubject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author songxl
* @create 2019-07-10 15:34
* @desc jdk 动态代理
**/
public class JdkProxySubject implements InvocationHandler {
private RealSubject realSubject;
public JdkProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("dynamic JdkProxySubject");
Object result = null;
try {
// 通过反射 找到调用的方法
result = method.invoke(realSubject,args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} finally {
System.out.println("after ");
}
return result;
}
}
测试类
Subject jdkProxy = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Subject.class},new JdkProxySubject(new RealSubject()));
jdkProxy.play();
jdkProxy.sayHi();
jdk 动态代理总结:
代理类不需要实现接口,但是目标类一定要实现接口,否则不能使用动态代理
CGLIB 动态代理
例子:
package xyz.songxl.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author songxl
* @create 2019-07-10 17:13
* @desc cglib 动态代理模式
**/
public class CglibProxySubject implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CglibProxySubject");
Object result = null;
try {
result = methodProxy.invokeSuper(o,objects);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return result;
}
}
测试类
// cglib 动态代理
Enhancer enhancer = new Enhancer();
// 设置真实的业务类
enhancer.setSuperclass(RealSubject.class);
// 需要置入的代码类(代理类)
enhancer.setCallback(new CglibProxySubject());
// 创建代理实例
Subject cglibProxy = (Subject) enhancer.create();
cglibProxy.sayHi();
cglib代理总结:
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
代理的类不能为final,否则报错;目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
总结
代理方式 | 实现 | 优点 | 缺点 | 特点 |
---|---|---|---|---|
静态代理 | 需要定义接口类,并且被代理的对象和代理类需要一起实现接口类 | 实现简单 | 每个需要代理的方法都需要手动去写代理方法 对于方法比较多的情况下,会有很多的重复工作 | 可扩展目标对象功能 |
jdk动态代理 | 代理类需要实现InvocationHandler 接口并重写invoke方法 | 不需要为每一个需要代理的方法实现代理方法,代码复用率高 | 只能代理有实现了接口的代理 | 反射 |
cglib 动态代理 | 代理类实现MethodInterceptor接口 并重写intercept方法 | 不需要为每一个需要代理的方法实现代理方法,代码复用率高 。也可以对代理对象进行扩展,代理对象不用实现接口 | 不能对static final 类、private static 方法进行代理 | 通过 节码处理框架ASM来转换字节码并生成新的子类来实现代理 |