设计模式: 代理模式 - 概念、实现及spring AOP代理模式源码解析

学习设计模式不光要学习设计模式的思想,还要去深入理解,为什么要用这个设计模式。
如何深入理解?读优秀的框架代码,看别人代码,了解它们的使用场景。 - - - 博主老师(感谢他)

本文先介绍了代理模式的概念及简单实现。简单聊了下为什么要使用代理模式,并介绍了代理模式在spring中的使用

1、概念

定义:为其他对象提供一种代理以控制对这个对象的访问。

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

2、实现

考虑这么个例子:
只有管理员才能看销售数据

2.1 静态代理

静态代理在代理类中持有关键业务实例的引用

用户接口

public interface IUser {

    void visitData();

}

用户类

public class User implements IUser {
    private String type;

    public User(String type) {
        this.type = type;
    }

    @Override
    public void visitData() {
        System.out.println("看到了数据...");
    }

    public String getType() {
        return type;
    }
}

用户代理

public class UserProxy implements IUser {

    private User user;

    public UserProxy(User user) {
        this.user = user;
    }

    @Override
    public void visitData() {
        if (user.getType().equals("admin")) {
            user.visitData();
        } else {
            System.out.println("没有权限");
        }
    }
}

测试

public class Test {

    public static void main(String[] args) {
        User admin = new User("admin");
        User normal = new User("normal");

        UserProxy adminProxy = new UserProxy(admin);
        adminProxy.visitData();

        UserProxy normalProxy = new UserProxy(normal);
        normalProxy.visitData();
    }
}

输出

看到了数据...
没有权限

2.2 动态代理

代理目标可以在运行时动态注入,代理的关键业务随着代理目标实例的不同而不同。常见的技术实现手段有:JDK提供的Proxy类+InvocationHandler接口,CGLIB,Javassist库等。我们使用jdk的InvocationHandler实现。

动态代理可以基于jdk的InvocationHandler实现

public class DynamicProxy implements InvocationHandler {

    private Object obj;

    public DynamicProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(obj, args);
        return result;
    }
}

测试类

public class Test2 {

    public static void main(String[] args) {
        IUser user = new User("admin");

        DynamicProxy proxy = new DynamicProxy(user);

        ClassLoader loader = proxy.getClass().getClassLoader();
        // 1、ClassLoader 方法需要动态生成一个类 需要一个ClassLoader来加载该类  
        // 2、Class[] interfaces 需要代理的对象的Class数组 
        // 3、InvocationHandler 调用处理器
        IUser userProxy = (IUser) Proxy.newProxyInstance(loader, new Class[] {IUser.class}, proxy);

        userProxy.visitData();
    }
}

输出

看到了数据...

3、为什么使用代理类模式

在上面的例子中,权限过滤为什么要让代理类去做?而不是直接在user的visitData方法里做判断呢?
因为面向对象的六大原则中有个叫:单一职责原则

  1. 代理模式可以在不破坏原有业务处理边界的前提下,增加额外的定制需求。
  2. 假如要调用的对象是一个远程的对象,需要跨网络访问。如果让你直接coding去调用,你需要处理网络连接、处理打包、解包等等非常复杂的步骤,所以为了简化客户端的处理,我们使用代理模式,在客户端建立一个远程对象的代理,客户端就象调用本地对象一样调用该代理,再由代理去跟实际对象联系。

4、Spring中的代理模式

我们都知道spring aop是基于动态代理实现的。

通过@EnableAspectJAutoProxy可以开启aop

package org.springframework.context.annotation;


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
    // aop的实现方式,ture:CGLIB, false:jdk的InvocationHandler, 默认是false
    boolean proxyTargetClass() default false;

    // 暴露方式,默认为false, 为true的时候,可以解决内部调用不能使用的场景
    // 假设类有方法doSomething1()和doSomething2(),doSomething1()方法内部调用doSomething2()。在调用doSomething1()的时候,默认情况下aop只会对doSomething1()做增强,不会对doSomething2()做增强(尽管doSomething1内部调用了doSomething2(),且单独调用doSomething2()的是,2也能被增强)。  
    // 为true的时候,可以实现内部调用,不过在doSomething1()中调用doSomething2()的代码也要改下:XX proxy=(xx) AopContext.currentProxy();proxy.doSomething2();
    boolean exposeProxy() default false;
}

@Import:将bean加载到spring容器
源码里:@Import({AspectJAutoProxyRegistrar.class}) 就是将AspectJAutoProxyRegistrar这个bean加载到容器中。

// ImportBeanDefinitionRegistrar的类只能通过@import加载
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        // 将AspectJAnnotationAutoProxyCreator装载到spring容器。  
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
		...
	}

}

将AspectJAnnotationAutoProxyCreator装载到spring容器。
AnnotationAwareAspectJAutoProxyCreator实现了(父类实现)BeanPostProcessor接口,接口的postProcessAfterInitialization方法会在bean初始化完成后被调用。

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) {
			    // 对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;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}
}

上面的代码可以看到,如果有切面,bean会在初始化完只后,被包装成代理对象。

createProxy方法

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
		@Nullable Object[] specificInterceptors, TargetSource targetSource) {

	if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
		AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
	}
    // 1.创建proxyFactory, 代理的工厂类
	ProxyFactory proxyFactory = new ProxyFactory();
	proxyFactory.copyFrom(this);

	if (!proxyFactory.isProxyTargetClass()) {
		if (shouldProxyTargetClass(beanClass, beanName)) {
			proxyFactory.setProxyTargetClass(true);
		}
		else {
			evaluateProxyInterfaces(beanClass, proxyFactory);
		}
	}
    // 2.将当前bean适合的advice,重新封装下,封装为Advisor类,然后添加到ProxyFactory中
	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	proxyFactory.addAdvisors(advisors);
	proxyFactory.setTargetSource(targetSource);
	customizeProxyFactory(proxyFactory);

	proxyFactory.setFrozen(this.freezeProxy);
	if (advisorsPreFiltered()) {
		proxyFactory.setPreFiltered(true);
	}

	return proxyFactory.getProxy(getProxyClassLoader());
}

getProxy根据不同的情况,生产不同的代理对象。我们看使用JDK自带的代理方式的实现

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
}

JDK动态代理模式,真正的方法执行在invoke()方法里,继续看这个类的invoke方法

	/**
	 * Implementation of {@code InvocationHandler.invoke}.
	 * <p>Callers will see exactly the exception thrown by the target,
	 * unless a hook method throws an exception.
	 */
	@Override
	@Nullable
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		...

		try {
			// 获取当前bean被拦截方法链表
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// 如果为空,则直接调用target的method
			if (chain.isEmpty()) {
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			// 不为空,则逐一调用chain中的每一个拦截方法的proceed
			else {
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				retVal = invocation.proceed();
			}
            ...
			return retVal;
		}
		...
	}

参考:
https://www.cnblogs.com/silverLee/archive/2010/02/05/1664577.html

https://blog.csdn.net/JinXYan/article/details/89302126

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值