设计模式之 【代理模式】

227 阅读3分钟

代理模式 Proxy

当目标对象的功能不足以满足客户端的要求,系统为该对象创建一个代理对象,而代理对象可以增强原目标对象的功能。代理模式应用非常广泛,常见的有spring事务管理 为方法创建异步线程执行

代理模式分为静态代理和动态代理

静态代理


// 代理类与被代理类实现同一个接口,代理类持有被代理类引用

public interface Light {
    // 照明
    public void lighting();
}

// 吊灯
public class LampProxy implements Light{

private int power;
private Color color;

 // 代理类持有被代理类的引用
 private Lamp lamp;
 
 public  class LampProxy(Lamp lamp){
     this.lamp = lamp
 }

 @Override
 public void lighting(){
    // 加大瓦数
    incrPower();
    // 变换色彩
    changeColor();
    // 发亮
    lamp.lighting();
   }
 }
 
 Lamp lamp = new LampProxy(lamp)
 lamp.lighting();

动态代理 (JDK) 接口代理

JDK 代理通过生成代理类并持有被代理对象。代理对象需要实现被代理对象的接口,在实际调用被代理对象方法前后进行方法加强,然后通过反射进行方法调用所以实际执行者还是被代理类。

  1. 代理对象需要实现 InvocationHandler接口
publi class LampHandler implements InvocationHandler {
    
    // 被代理对象
    private Object target;
    
    public void setTarget(Object target){
        this.target = target;
    }
    
    // 执行动态代理对象的所有方法,都会被替换成执行如下方法
    public Object invoke(Object proxy, Method method, Object[] args){
      // do something before
      Object result method.invoke(target, args)
      // do something after 
      return result;
    }
    
}

  1. 使用代理对象替换被代理对象
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException



public class MyProxyFactory{

    public static Object getProxy(Object target){
    
        LampHandler lampHandler = new LampHandler();
        
        // 为LampHandler设置Target值
        handler.setTarget(target);
        
        // 创建并返回一个动态代理
        return Proxy.newProxyInstance(target.getclass.getClassLoader(), target.getClass.getInterfaces(), handeler)
    }
}



动态代理 (CGLIB) 类代理

cglib动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法(cglib无法对final方法进行代理)。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。


public class Lawyer {

    /**
     * 辩护
     */
    void plead() {
        System.out.println("为代理人辩护!");
    }

}


public class LawyerInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        methodProxy.invokeSuper(o, objects);
        return o;
    }
}


public class LawyerTest {

    public static void main(String[] args) {

        // 有兴趣的同学可以看下CGLIB 动态生成的class文件源码
         System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/penglin/Desktop/");");

        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(Lawyer.class);
        //设置回调函数
        enhancer.setCallback(new LawyerInterceptor());
        Lawyer lawyer = (Lawyer) enhancer.create();
        //调用代理类的eat方法
        lawyer.plead();
    }
}

使用aop动态代理模式的注解什么时候会失效

  1. JDK代理模式中 被代理类中的方法在接口中未定义

  2. CGLIB被代理类中被final/private关键字修饰的方法

  3. 相同实例方法内部调用

springboot 默认动态代理模式为 CGLIB


@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
		AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true",
		matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	// 获取properties 如果 spring.aop.proxy-target-class = false 则启用JDK 动态代理 
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class",
			havingValue = "false", matchIfMissing = false)
	public static class JdkDynamicAutoProxyConfiguration {

	}

    
	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	// 获取properties 如果 spring.aop.proxy-target-class = true 则启用Cglib 动态代理 
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class",
			havingValue = "true", matchIfMissing = true)
	public static class CglibAutoProxyConfiguration {

	}

}