什么是代理模式?
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
为什么要用代理模式?
解偶作用:有时可以利用代理模式,将客户端同委托类进行解偶,这样当委托类进行替换或是升级时,客户端(调用端)无需修改代码。
功能增强:比如添加日志,记录方法执行时间等机械重复的操作可以利用代理模式,将功能增强的代码在代理类中实现,这样就可以大大节省代码。 注意:代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是通过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
案例
我们以吃面包为例。委托类 Bread 和代理类 BreadProxy 都实现了 IFood 接口,在代理类中实例化委托类并实现真正的业务。代理类中还可以添加其他功能。做功能增强。
静态代理
public class Test {
public static void main(String[] args) {
IFood food = new BreadProxy();
food.eat();
}
}
interface IFood {
void eat();
}
class Bread implements IFood {
@Override
public void eat() {
System.out.println("吃面包");
}
}
class BreadProxy implements IFood {
Bread bread = new Bread();
@Override
public void eat() {
System.out.println("揉面。。。");
System.out.println("发酵。。。");
System.out.println("烘烤。。。");
bread.eat();
System.out.println("清扫。。。");
}
}
JDK 动态代理
试想,如果我们委托类过多,我们岂不是要为每一个委托类都写一个代理类,代码成本太高,这种情况我们可以使用动态代理器来动态创建代理对象。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
// 动态代理测试
IFood food2 = (IFood) Proxy.newProxyInstance(IFood.class.getClassLoader(), new Class[] {IFood.class}, new DynamicProxyHandler(new Bread()));
food2.eat();
}
}
interface IFood {
void eat();
}
class Bread implements IFood {
@Override
public void eat() {
System.out.println("吃面包");
}
}
/**
* 动态代理器可以很好的屏蔽掉委托类,
*/
class DynamicProxyHandler implements InvocationHandler {
// 委托对象
private Object object;
public DynamicProxyHandler(Object object) {
this.object = object;
}
// 在此方法内完成委托方法的增强
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("委托类方法调用前");
Object result = method.invoke(object, args);
System.out.println("委托类方法调用后");
return result;
}
}
CGLIB 动态代理
JDK 动态代理的模式已经可以帮忙解决大部分情况,但是委托类和代理类有一个限制,必须实现同一个接口。如果没有预先定义接口,则无法实现代理模式。 CGLIB 则可以帮助解决这个问题,CGLIB 是采用了非常底层的字节码技术,其原理是通过字节码为委托类创建一个子类,子类中采用拦截技术拦截所有父类方法的调用,顺势植入切面逻辑。 但因为是通过继承实现,委托类不能有 final 修饰,JDK 动态代理与CGLIB 动态代理均是 Spring AOP 的基础。
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 Test {
public static void main(String[] args) {
Bread foodProxy = (Bread) new CglibProxy().getInstance(new Bread());
foodProxy.eat();
}
}
/**
* 我们为了区别于上面 JDK 的方式,故意不去构建接口
*/
class Bread {
public void eat() {
System.out.println("吃面包");
}
}
/**
* 动态代理器可以很好的屏蔽掉委托类
*/
class CglibProxy implements MethodInterceptor {
// 委托对象
private Object object;
// 完成代理类的初始化
public Object getInstance(Object object) {
this.object = object;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.object.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
// 在拦截器中完成AOP
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("委托方法调用前。。。");
Object result = methodProxy.invokeSuper(object, args);
System.out.println("委托方法调用后。。。");
return result;
}
}
输出结果:
委托方法调用前。。。
吃面包
委托方法调用后。。。
如果我们把 Bread 这个类加上 final 修饰词,我们会得到如下错误:
Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class com.example.demo1.Bread
at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:565)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:332)
at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)