代理模式
为其他对象提供一种代理,以控制对这个对象的访问,代理对象在客户端和目标对象之间起到了中介的作用。
适用于:
保护目标对象;
增强目标对象。
优点:
将代理对象和真实被调用的目标对象分离;
降低耦合,拓展性好;
保护目标对象,增强目标对象。
缺点:
造成类的数目增加,增加复杂度;
客户端和目标对象增加代理对象,会造成处理速度变慢。
代理模式的主要角色如下。
抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问,控制或扩展真实主题的功能。
1.静态代理
通过在代码中显式地定义了一个代理类,在代理类中通过同名的方法对目标对象的方法进行包装,客户端通过调用代理类的方法来调用目标对象的方法。
1.创建一个蛋糕接口
public interface CakeService {
void makeCake();
}
2.创建一个蛋糕的实现类
public class CakeServiceImpl implements CakeService {
@Override
public void makeCake() {
System.out.println("制作蛋糕");
}
}
3.要对CakeServiceImpl 的makeCake方法增强,我们需要创建一个代理对象CakeServiceProxy ,
并在方法调用前调用代理类的beforeMethod方法,方法调用后调用代理类的afterMethod方法。
public class CakeServiceProxy implements CakeService {
private CakeService cakeService;
@Override
public void makeCake() {
beforeMethod();
cakeService=new CakeServiceImpl();//调用实现类
cakeService.makeCake();
afterMethod();
}
private void beforeMethod() {
System.out.println("准备制作材料");
}
private void afterMethod() {
System.out.println("注意保鲜");
}
}
4.测试输出
public class Test {
public static void main(String[]args){
CakeServiceProxy cakeServiceProxy=new CakeServiceProxy();
cakeServiceProxy.makeCake();
}
}
//准备制作材料
//制作蛋糕
//注意保鲜
2.动态代理
JDK的动态代理只能代理接口,通过接口的方法名在动态生成的代理类中调用业务实现类的同名方法。
静态代理的缺点就是每需要代理一个类,就需要手写对应的代理类。这个问题可以用动态代理来解决。举个动态代理的例子:
1.创建一个蛋糕接口
public interface CakeService {
public void makeCake(String fruitName);
}
2.创建一个蛋糕的实现类
public class CakeServiceImpl implements CakeService {
@Override
public void makeCake(String fruitName) {
System.out.println("制作"+fruitName+"口味的蛋糕");
}
}
3.动态代理类通过实现InvocationHandler的invoke方法实现,proxy用于生成代理对象。剩下的步骤和静态代理类似,完善CakeServiceProxy
public class CakeServiceProxy implements InvocationHandler {
private Object object;//代理对象
public CakeServiceProxy(Object object) {
this.object = object;
}
public Object proxy(){
Class<?>c=object.getClass();
// 生成代理对象
return Proxy.newProxyInstance(c.getClassLoader(),c.getInterfaces(), this);
}
/**
* @param proxy 动态生成的代理对象
* @param method 代理方法
* @param args 代理方法的方法参数
* @return 结果
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeMethod(object);
// 反射执行代理对象的目标方法
Object result = method.invoke(object, args);
afterMethod(object);
return result;
}
private void beforeMethod(Object object) {
if (object instanceof CakeService) {
System.out.println("准备蛋糕的材料");
}else {
throw new RuntimeException("暂不支持代理" + object.getClass() + "类型");
}
}
private void afterMethod(Object object) {
if (object instanceof CakeService) {
System.out.println("注意保鲜");
}else {
throw new RuntimeException("暂不支持代理" + object.getClass() + "类型");
}
}
}
4.测试输出
public class Test {
public static void main(String[]args){
CakeService cakeService=(CakeService)new CakeServiceProxy(new CakeServiceImpl()).proxy();
cakeService.makeCake("苹果");
}
}
//准备蛋糕的材料
//制作苹果口味的蛋糕
//注意保鲜
从上面输出可以看到,通过动态代理我们实现了目标方法增强,并且不需要手写目标类的代理对象。
3.CGLib代理
使用JDK创建动态代理有一个限制, 即它只能为接口创建代理实例. 对于没有定义接口的业务方法的类, 使用CDGlib 进行动态代理.
CGLib是一个强大的, 高性能的代码生成库. 被广泛应用于 AOP 框架. 用以提供方法拦截操作.
CGLib采用底层的字节码技术, 可以为一个类创建子类, 在子类中采用方法拦截的技术拦截所有父类方法的调用, 并织入横切逻辑.
通过实现MethodInterceptor接口生成方法拦截器,CGLib 定义的 intercept() 接口方法, 拦截所有目标类方法的调用. 其中 o 代表目标类的实例, method 为目标类方法的反射; args为方法的动态入参, proxy为代理类实例.
代理对象的生成由 Enhancer 类实现. Enhancer是CGLib的字节码增强器. 可以很方便的对类进行扩展.
Enhancer 创建代理对象的大概步骤如下:
1.生成代理类 Class 二进制字节码.
2.通过 Class.forname() 加载字节码文件, 生成 Class 对象.
3.通过反射机制获得实例构造, 并创建代理类对象.
//实现MethodInterceptor接口,来获取intercept拦截方法
public class CakeServiceProxy2 implements MethodInterceptor {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
beforeMethod();
// 反射执行代理对象的目标方法
Object result = methodProxy.invokeSuper(proxy, args);
afterMethod();
return result;
}
private void beforeMethod() {
System.out.println("准备蛋糕的材料");
}
private void afterMethod() {
System.out.println("注意保鲜");
}
}
public class Test {
public static void main(String[]args){
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(CakeServiceImpl.class);
// 设置enhancer的回调对象
enhancer.setCallback(new CakeServiceProxy2());
// 创建代理对象
CakeServiceImpl proxy= (CakeServiceImpl)enhancer.create();
// 通过代理对象调用目标方法
proxy.makeCake("苹果");
}
}
//准备蛋糕的材料
//制作苹果口味的蛋糕
//注意保鲜
4.Spring对代理模式的拓展
当Bean有实现接口时,使用JDK动态代理;
当Bean没有实现接口时,使用CGLib代理。