代理模式
代理模式:代理模式是一种结构型设计模式,它通过引入一个代理对象来控制对实际对象的访问。代理对象充当了客户端与实际对象之间的中介,客户端通过代理对象间接地访问实际对象,而无需直接与实际对象进行交互。
代理模式的主要目的是在不改变实际对象的情况下,提供一种额外的访问控制和功能扩展的方式。它可以用于实现许多不同的功能,例如远程代理(将对远程对象的访问封装在代理对象中)、虚拟代理(延迟加载实际对象的创建)、保护代理(控制对实际对象的访问权限)等。
用图表示如下:
代理模式的优点:
代理模式可以实现对实际对象的访问控制。代理对象可以根据需要限制对实际对象的访问权限,从而提供更加安全和可靠的访问方式。
代理模式可以实现对实际对象的功能扩展。代理对象可以在调用实际对象的方法前后进行一些额外的操作,从而为客户端提供更多的功能,例如缓存、日志记录等。
代理模式可以实现对实际对象的远程访问。通过使用远程代理,客户端可以通过网络访问位于不同地址空间的实际对象,从而实现分布式系统的构建。
代理模式的缺点:
引入代理对象会增加系统的复杂性。代理模式需要引入额外的类和对象,增加了系统的结构复杂性,可能会导致理解和维护的困难。
代理模式可能会导致性能损失。由于代理对象需要额外的处理逻辑,可能会导致访问实际对象时的性能下降,特别是在远程代理的情况下,网络延迟会进一步增加性能开销。
代理模式的主要角色如下。
抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
代理(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());
}
}