代理模式:代理模式是一种结构型设计模式,它通过引入一个代理对象来控制对实际对象的访问。代理对象充当了客户端与实际对象之间的中介,客户端通过代理对象间接地访问实际对象,而无需直接与实际对象进行交互。

代理模式的主要目的是在不改变实际对象的情况下,提供一种额外的访问控制和功能扩展的方式。它可以用于实现许多不同的功能,例如远程代理(将对远程对象的访问封装在代理对象中)、虚拟代理(延迟加载实际对象的创建)、保护代理(控制对实际对象的访问权限)等。

用图表示如下:

代理模式的优点:

  • 代理模式可以实现对实际对象的访问控制。代理对象可以根据需要限制对实际对象的访问权限,从而提供更加安全和可靠的访问方式。

  • 代理模式可以实现对实际对象的功能扩展。代理对象可以在调用实际对象的方法前后进行一些额外的操作,从而为客户端提供更多的功能,例如缓存、日志记录等。

  • 代理模式可以实现对实际对象的远程访问。通过使用远程代理,客户端可以通过网络访问位于不同地址空间的实际对象,从而实现分布式系统的构建。

代理模式的缺点:

  • 引入代理对象会增加系统的复杂性。代理模式需要引入额外的类和对象,增加了系统的结构复杂性,可能会导致理解和维护的困难。

  • 代理模式可能会导致性能损失。由于代理对象需要额外的处理逻辑,可能会导致访问实际对象时的性能下降,特别是在远程代理的情况下,网络延迟会进一步增加性能开销。

代理模式的主要角色如下。

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。

  2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。

  3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

根据代理的创建时期,代理模式分为静态代理和动态代理。

  • 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类就已经存在了。

  • 动态:在程序运行时,运用反射等机制动态创建而成

代理模式的实现

1.静态代理

//抽象主题
interface Subject {
    void request();
}

//真实主题
class RealSubject implements Subject {
    
     @Override
    public void request() {
        System.out.println("访问真实主题方法...");
    }
}

//代理
class Proxy implements Subject {
    
    private Subject subject;
   
     public Proxy() {
        this.subject = new RealSubject();
    }
    
    @Override
    public void request() {
        preRequest();
        subject.request();
        postRequest();
    }
    
    private void preRequest() {
        System.out.println("访问真实主题之前的预处理。");
    }
    
    private void postRequest() {
        System.out.println("访问真实主题之后的后续处理。");
    }
}

// 测试类
public class ProxyTest {
    public static void main(String[] args) {
        Subject proxy = new Proxy();
        proxy.request();
    }
}

程序运行的结果如下:

访问真实主题之前的预处理。
访问真实主题方法...
访问真实主题之后的后续处理。

静态代理总结:

优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。

缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。   

2.JDK动态代理

示例代码如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicProxyHandler implements InvocationHandler {
     
     private Object object;
 
     public DynamicProxyHandler(final Object object) {
         this.object = object;
     }
 
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) 
             throws Throwable {
         preRequest();
         Object result = method.invoke(object, args);
         postRequest();
         return result;
     }
     
     private void preRequest() {
        System.out.println("访问真实主题之前的预处理。");
    }
    
    private void postRequest() {
        System.out.println("访问真实主题之后的后续处理。");
    }
 }
 
 public class TestDynamicProxy {
     public static void main(String[] args) {
         Subject subject = new RealSubject();
         Subject proxySubject = (Subject) java.lang.reflect.Proxy
             .newProxyInstance(
             Subject.class.getClassLoader(), 
             new Class[]{Subject.class}, 
             new DynamicProxyHandler(subject));
         proxySubject.request();
     }
 }

 注:Proxy.newProxyInstance() 方法接受三个参数:

  • ClassLoader loader: 指定当前目标对象使用的类加载器,获取加载器的方法是固定的

  • Class[] interfaces: 指定目标对象实现的接口的类型,使用泛型方式确认类型

  • InvocationHandler: 指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

程序运行的结果与静态代理一致:

访问真实主题之前的预处理。
访问真实主题方法...
访问真实主题之后的后续处理。

JDK动态代理总结:

虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是JDK动态代理仅支持接口代理,只能代理实现了接口的类,无法代理没有实现接口的类。使用JDK动态代理,首先需要定义一个接口,然后创建一个实现了该接口的实际对象。接下来,通过使用java.lang.reflect.Proxy类的静态方法newProxyInstance()来创建代理对象。该方法需要传入一个ClassLoader对象、一组接口和一个InvocationHandler对象。代理对象将会实现指定的接口,并将方法调用委托给InvocationHandler对象来处理。

JDK动态代理在Mybatis框架中的应用

Mybatis框架中,就是使用到了JKD动态代理技术。我们从数据库查询数据,并封装成一个对象进行返回,只需要使用下面这一段代码就可以实现:

package org.mybatis.example;

public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);
}

public static TestMapper {
    
    private static SqlSessionFactory sqlSessionFactory;
    
    static {
        ……
    }
    
    public static void main(String[] args) {
      // 使用和指定语句的参数和返回值相匹配的接口    
      try (SqlSession session = sqlSessionFactory.openSession()) {
         BlogMapper mapper = session.getMapper(BlogMapper.class);
         Blog blog = mapper.selectBlog(101);
      }
   }
}

可以看到,我们只是定义了Mapper接口,并没有写对应的实现类,但是我们却可以直接执行接口方法。 session.getMapper这个方法,其实就是Mybatis框架中使用JDK动态代理返回了一个代理对象。Mybatis框架的架构图如下:

Mybatis框架Mapper映射实现源代码

SqlSession#getMapper方法最终会调用MapperRegistry#getMapper方法。

package org.apache.ibatis.session

public class DefaultSqlSession implements SqlSession {
    
    private final Configuration configuration;
    
    @Override
    public <T> T getMapper(Class<T> type) {
      return configuration.getMapper(type, this);
    }
}

public class Configuration {   

    protected final MapperRegistry mapperRegistry = 
        new MapperRegistry(this);

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      return mapperRegistry.getMapper(type, sqlSession);
    }
}   

MapperRegistry#getMapper方法中,就会通过使用JDK动态代理的方式返回一个代理对象。

package org.apache.ibatis.binding

public class MapperRegistry {
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      final MapperProxyFactory<T> mapperProxyFactory = 
          (MapperProxyFactory<T>) knownMappers.get(type);
      if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type 
            + " is not known to the MapperRegistry.");
      }
      try {
        return mapperProxyFactory.newInstance(sqlSession);
      } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause:" 
            + e, e);
      }
    }
}

public class MapperProxyFactory<T> {    
    protected T newInstance(MapperProxy<T> mapperProxy) {
      return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), 
          new Class[] { mapperInterface }, mapperProxy);
    }
}

3.CGLIB动态代理

CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。

示例代码如下:

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 CglibProxy implements MethodInterceptor {

    public static Object getInstance(Class<?> targetInstanceClazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetInstanceClazz);
        enhancer.setCallback(new CglibProxy());
        return enhancer.create();
    }

    @Override
    public Object intercept(Object object, Method method, Object[] args, 
        MethodProxy methodProxy) throws Throwable {
        preRequest();
        Object result = methodProxy.invokeSuper(object, args);
        postRequest();
        return result;
    }

    private void preRequest() {
        System.out.println("访问真实主题之前的预处理。");
    }

    private void postRequest() {
        System.out.println("访问真实主题之后的后续处理。");
    }

}

public class TestCglibProxy {

    public static void main(String[] args) {
        RealSubject subjectCglibProxy = (RealSubject) CglibProxy
            .getInstance(RealSubject.class);
        subjectCglibProxy.request();
    }

}

程序运行的结果与之前的也是一致的。

CGLIB动态代理总结: 

CGLIB动态代理是一种基于继承的代理模式实现方式,它通过创建目标类的子类来实现代理。使用CGLIB动态代理,无需目标类实现接口,可以直接代理目标类。在运行时,CGLIB会生成一个目标类的子类,并在子类中重写目标类的方法,将方法调用委托给一个MethodInterceptor对象来处理。由于采用创建子类的方法,对于final修饰的方法无法进行代理。

CGLIB动态代理在Spring框架中的应用

Spring框架中AOP就是使用CGLIB动态代理来实现的。在开发过程中,我们想监控方法的执行时长,只需要使用下述代码:

@Aspect
public class ProfilingAspect {
    
    @Pointcut("execution(public * com.example.service..*(..))")
    private void methodsToBeProfiled() {} 
    
    // 给所有的public方法添加一个基于时间的剖析器(打印方法执行用时)
    @Around("methodsToBeProfiled()")
    public Object profile(ProceedingJoinPoint pjp) throws Throwable {
        StopWatch sw = new StopWatch(getClass().getSimpleName());
        try {
            sw.start(pjp.getSignature().getName());
            return pjp.proceed();
        } finally {
            sw.stop();
            System.out.println(sw.prettyPrint());
        }
    }
}

Spring框架中集成了AspectJ,以便利用AspectJ的高级功能。我们可以使用AspectJ注解来定义切面,并使用其代理机制来将切面织入到目标对象中,这样在指定包下的Bean方法执行时,就会执行这里定义的切面方法。Spring框架AOP的架构图如下:

Spring框架AOP实现源代码

Spring框架为Bean创建代理对象,使用过BeanPostProcessor#postProcessAfterInitialization方法,在Bean初始化完成后,通过JDK动态代理(代理目标是接口)或CGLIB动态代理(代理目标是一个类)来创建代理对象(存在循环依赖的代理对象创建稍有不同,这里不进行讨论)。

package org.springframework.aop.framework.autoproxy;

public abstract class AbstractAutoProxyCreator 
    extends ProxyProcessorSupport
    implements SmartInstantiationAwareBeanPostProcessor,BeanFactoryAware {
          
    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, 
            String beanName) {
       if (bean != null) {
          Object cacheKey = getCacheKey(bean.getClass(), beanName);
          if (this.earlyProxyReferences.remove(cacheKey) != bean) {
             return wrapIfNecessary(bean, beanName, cacheKey);
          }
       }
       return bean;
    }      
          
    protected Object wrapIfNecessary(Object bean, String beanName, 
            Object cacheKey) {
                
       ……
    
       // Create proxy if we have advice.
       Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(
           bean.getClass(), beanName, null);
       if (specificInterceptors != DO_NOT_PROXY) {
          this.advisedBeans.put(cacheKey, Boolean.TRUE);
          Object proxy = createProxy(bean.getClass(), beanName, 
              specificInterceptors, new SingletonTargetSource(bean));
          this.proxyTypes.put(cacheKey, proxy.getClass());
          return proxy;
       }
    
       ……
    }
    
    protected Object createProxy(Class<?> beanClass, 
            @Nullable String beanName,
            @Nullable Object[] specificInterceptors, 
            TargetSource targetSource) {
       ……
    
       ProxyFactory proxyFactory = new ProxyFactory();
    
       ……
    
       Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
       proxyFactory.addAdvisors(advisors);
       proxyFactory.setTargetSource(targetSource);
       
       ……
    
       return proxyFactory.getProxy(getProxyClassLoader());
    }      
          
} 

ProxyFactory中会调用AopProxyFactory#createAopProxy方法来创建代理对象,在创建代理对象前,会先判断代理目标是一个接口还是一个类,如果是接口的话直接使用JDK动态代理,是一个具体的类的话就使用CGLILB动态代理。

package org.springframework.aop.framework;

public class ProxyFactory extends ProxyCreatorSupport {
    
    public Object getProxy(@Nullable ClassLoader classLoader) {
       return createAopProxy().getProxy(classLoader);
    }
    
    protected final synchronized AopProxy createAopProxy() {
       if (!this.active) {
          activate();
       }
       return getAopProxyFactory().createAopProxy(this);
    }
}

public class DefaultAopProxyFactory implements AopProxyFactory {

   @Override
   public AopProxy createAopProxy(AdvisedSupport config) 
           throws AopConfigException {
      if (config.isOptimize() || config.isProxyTargetClass() 
              || hasNoUserSuppliedProxyInterfaces(config)) {
         Class<?> targetClass = config.getTargetClass();
         if (targetClass == null) {
            throw new AopConfigException("...");
         }
         if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)){
            return new JdkDynamicAopProxy(config);
         }
         return new ObjenesisCglibAopProxy(config);
      }
      else {
         return new JdkDynamicAopProxy(config);
      }
   }
   
}   

ObjenesisCglibAopProxy#getProxy中就会CGLIB动态代理来生成代理对象。

package org.springframework.aop.framework;

class ObjenesisCglibAopProxy extends CglibAopProxy { …… }

class CglibAopProxy implements AopProxy {
    
    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
       
       ……
    
      // Configure CGLIB Enhancer...
      Enhancer enhancer = createEnhancer();
      if (classLoader != null) {
         enhancer.setClassLoader(classLoader);
         if (classLoader instanceof SmartClassLoader &&
               ((SmartClassLoader) classLoader).isClassReloadable(
                   proxySuperClass)) {
            enhancer.setUseCache(false);
         }
      }
      enhancer.setSuperclass(proxySuperClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(
          this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(
          classLoader));

      Callback[] callbacks = getCallbacks(rootClass);
      Class<?>[] types = new Class<?>[callbacks.length];
      for (int x = 0; x < types.length; x++) {
         types[x] = callbacks[x].getClass();
      }
      // fixedInterceptorMap only populated at this point, 
      // after getCallbacks call above
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
            this.advised.getConfigurationOnlyCopy(), 
            this.fixedInterceptorMap, this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);

      // Generate the proxy class and create a proxy instance.
      return createProxyClassAndInstance(enhancer, callbacks);
       ……
    }
    
    protected Object createProxyClassAndInstance(Enhancer enhancer, 
            Callback[] callbacks) {
       enhancer.setInterceptDuringConstruction(false);
       enhancer.setCallbacks(callbacks);
       return (this.constructorArgs != null 
               && this.constructorArgTypes != null ? enhancer.create(
               this.constructorArgTypes, this.constructorArgs) : 
               enhancer.create());
    }
    
} 

文章作者: Hakurei
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Zero
设计模式 设计模式 Java
喜欢就支持一下吧