当前位置: 首页 > news >正文

Java 随笔 代理模式 1-spring aop

0. 当一个闹钟不响的时候,太痛了。

真人改编的故事,给整破防了.


既然是随笔的话,那我要写的稍微奔放一点了…

1. Spring Aop底层的几个基类

Spring AOP 的演进过程

1. 1 借鉴上文中原始的xml配置结构:

<bean class="ProxyFactoryBean">
	<!-- 代理的接口类 -->
	<!-- 代理的具体实现类 -->
	
	<!-- 配置拦截器类(可以是advice advisor interceptor) -->
	<property name="interceptorNames">
		<list>
			<!-- 这里引用声明好的多个拦截器Bean -->
			<value>advice1</value>
			<value>advice2</value>
		</list>
	</property>
</bean>
  • ProxyFactoryBean:用于生成代理bean的工厂

  • Advisor:给代理bean绑定用于增强的advice
    NameMatchMethodPointcutAdvisor 根据aspect expression 进行绑定
    RegexpMethodPointcutAdvisor 根据正则

  • AutoProxyCreator(自动代理)
    spring提供了一个默认的、替代ProxyFactoryBean创建出代理bean的AutoProxyCreator:

BeanNameAutoProxyCreator:配置减少至 -> 增强类(前面那仨)+被代理的Bean
DefaultAdvisorAutoProxyCreator:仅仅只需指定被代理的bean(默认使用ioc中所有的advisor去拦截)

通过对比可知:“自动”即帮我们自动生成了相应的ProxyFactory

1.2 @AspectJ配置方式

  • 使用 @Aspect 注解的 bean 都会被 Spring 当做用来实现 AOP 的配置类(必须是一个spring bean)

  • @Pointcut 就是用来匹配 Spring 容器中的所有 bean 的方法的
    配置 pointcut 就是配置我们需要拦截哪些方法,方法保留签名即可,无需实现

  • 作用同Advisor的注解:
    @Before
    @AfterReturning
    @AfterThrowing
    @After
    会拦截正常返回和异常的情况
    @Around
    Spring 提供了非常简单的获取入参的方法,使用 org.aspectj.lang.JoinPoint 作为 Advice 的第一个参数即可

1.3 schema-based 配置

咋说呢,优点即可观性好

<!-- 设置全局的pointcut -->
	<aop:config>
		<aop:pointcut id="businessService" expression="execution(* com.javadoop.springaoplearning.service.*.*(..))"/>
		<!--也可以像下面这样-->
		<aop:pointcut id="businessService2" expression="com.javadoop.SystemArchitecture.businessService()"/>
	</aop:config>

<!-- 设置局部的pointcut -->
	<aop:config>
		<aop:aspect ref="logArgsAspect">
			<aop:pointcut id="internalPointcut"
					expression="com.javadoop.SystemArchitecture.businessService()" />
		</aop:aspect>
	</aop:config>

2. spring aop 源码

Spring AOP 源码解析


近来,有种钻源码的冲动;

考虑到spring的源码特点:支路旁系众多,这里就不搞那么多花里胡哨的了

通过IDE的debug以及UML功能我们可以发现DefaultAdvisorAutoProxyCreator 到头来是一个 BeanPostProcessor;
于是,我们从 getBean的过程 中着手分析创建代理的时机…这得从spring ioc说起了

只要跟随 step into 即可

2.1 从 ioc 到 aop

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
	if (System.getSecurityManager() != null) {
		AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
			invokeAwareMethods(beanName, bean);
			return null;
		}, getAccessControlContext());
	}
	else {
		// 如果实现了的话,回调 BeanNameAware、BeanClassLoaderAware 或 BeanFactoryAware 的实现,该Bean可以借此获取上下文的相关属性
		invokeAwareMethods(beanName, bean);
	}

	Object wrappedBean = bean;
	if (mbd == null || !mbd.isSynthetic()) {
		// step into...
		// 先看看是不是在这里???
		// 回调 beanPostProcessor 的before钩子
		wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	}

	try {
		// 执行 bean属性 init-method 方法
		invokeInitMethods(beanName, wrappedBean, mbd);
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				(mbd != null ? mbd.getResourceDescription() : null),
				beanName, "Invocation of init method failed", ex);
	}
	if (mbd == null || !mbd.isSynthetic()) {
		// 回调 beanPostProcessor 的after钩子
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	}

	return wrappedBean;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
	// step out ...
	// 有点尴尬,并不是那么回事
	return bean;
}

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
	if (System.getSecurityManager() != null) {
		AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
			invokeAwareMethods(beanName, bean);
			return null;
		}, getAccessControlContext());
	}
	else {
		// 如果实现了的话,回调 BeanNameAware、BeanClassLoaderAware 或 BeanFactoryAware 的实现,该Bean可以借此获取上下文的相关属性
		invokeAwareMethods(beanName, bean);
	}

	Object wrappedBean = bean;
	if (mbd == null || !mbd.isSynthetic()) {
		// 回调 beanPostProcessor 的before钩子
		wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	}

	try {
		// 执行 bean属性 init-method 方法
		invokeInitMethods(beanName, wrappedBean, mbd);
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				(mbd != null ? mbd.getResourceDescription() : null),
				beanName, "Invocation of init method failed", ex);
	}
	if (mbd == null || !mbd.isSynthetic()) {
		// step into ...
		// 那这下子估计没跑了
		// 回调 beanPostProcessor 的after钩子
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	}

	return wrappedBean;
}

// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		// 代理bean提前暴露其引用(remove返回的bean并不是当前bean)
		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
			// step into ...
			// 这个方法返回的bean大抵便是代理后的bean
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}


// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	// 我需要的方法是创建出代理bean的方法,这一坨判断与我无关
	if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
		return bean;
	}
	if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
		return bean;
	}
	if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}
	
	// 看着官方注释,估计没跑了
	// 返回了 advice advisor interceptor 的集合
	// Create proxy if we have advice.
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	
	if (specificInterceptors != DO_NOT_PROXY) {
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
		// step into ...
		Object proxy = createProxy(
				bean.getClass(), beanName, specificInterceptors, 
				// spring aop 框架中 对被代理的接口的默认实现类(被代理bean的一个包装类而已)
				// This is the default implementation of the TargetSource interface, as used by the Spring AOP framework
				new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}

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


// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#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);
	}

	// 回滚spring aop最远古的配置方式,就需要配置ProxyFactoryBean
	ProxyFactory proxyFactory = new ProxyFactory();
	proxyFactory.copyFrom(this);

	if (!proxyFactory.isProxyTargetClass()) {
		if (shouldProxyTargetClass(beanClass, beanName)) {
			proxyFactory.setProxyTargetClass(true);
		}
		else {
			// step into ...
			// 进入窥一窥,有点好奇
			evaluateProxyInterfaces(beanClass, proxyFactory);
		}
	}

	// 如果 specificInterceptors 中有 advice 和 interceptor,它们也会被包装成 advisor
	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());
}

// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
	// 获取这个类实现的所有接口
	Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
	boolean hasReasonableProxyInterface = false;
	for (Class<?> ifc : targetInterfaces) {
		if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
				ifc.getMethods().length > 0) {
			hasReasonableProxyInterface = true;
			break;
		}
	}
	// 将其实现的接口都列入到代理工厂bean中
	if (hasReasonableProxyInterface) {
		// Must allow for introductions; can't just set interfaces to the target's interfaces only.
		for (Class<?> ifc : targetInterfaces) {
			proxyFactory.addInterface(ifc);
		}
	}
	else {
		// 如果没有实现接口 -> 生成代理的bean就以这个类作为代理的目标类
		proxyFactory.setProxyTargetClass(true);
	}
}

// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#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);
	}

	// 回滚spring aop最远古的配置方式,就需要配置ProxyFactoryBean
	ProxyFactory proxyFactory = new ProxyFactory();
	proxyFactory.copyFrom(this);

	if (!proxyFactory.isProxyTargetClass()) {
		if (shouldProxyTargetClass(beanClass, beanName)) {
			proxyFactory.setProxyTargetClass(true);
		}
		else {
			// 找到代理bean需要涉及到的目标接口
			evaluateProxyInterfaces(beanClass, proxyFactory);
		}
	}

	// 如果 specificInterceptors 中有 advice 和 interceptor,它们也会被包装成 advisor
	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	proxyFactory.addAdvisors(advisors);
	proxyFactory.setTargetSource(targetSource);
	customizeProxyFactory(proxyFactory);

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

	// step into ...
	return proxyFactory.getProxy(getProxyClassLoader());
}

// org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)
public Object getProxy(@Nullable ClassLoader classLoader) {
	return
		// step into ...
		createAopProxy()
		.getProxy(classLoader);
}

// org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy
protected final synchronized AopProxy createAopProxy() {
	if (!this.active) {
		activate();
	}
	return
		// 这里没有啥逻辑,就直接返回 AopProxyFactory 的引用		
		getAopProxyFactory()
		// step into ...
		.createAopProxy(this);
}

// org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
// 先在这停留吧,接下来应该按两种代理模式兵分两路浏览源码了 ....
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	/* 翻译一下,如果需要代理的bean:
	 *  代理优化策略 (isOptimize默认false) ||
	 * 	强制使用类代理策略(proxy-target-class默认true) ||
	 *	被代理的类是否有实现的接口
	 */	
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
		Class<?> targetClass = config.getTargetClass();
		if (targetClass == null) {
			throw new AopConfigException("TargetSource cannot determine target class: " +
					"Either an interface or a target is required for proxy creation.");
		}
		// 被代理的bean是 接口实现类 || 代理类
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			// JDK 代理方式
			return new JdkDynamicAopProxy(config);
		}
		// ceglib 代理方式
		return new ObjenesisCglibAopProxy(config);
	}
	else {
		return new JdkDynamicAopProxy(config);
	}
}

2.2 【加餐】spring自带拦截器ExposeInvocationInterceptor

ExposeInvocationInterceptor


简单地说,就是为了演进拦截器链

2.2.1 ExposeInvocationInterceptor保存MethodInvocation到线程本地

// org.springframework.aop.interceptor.ExposeInvocationInterceptor#invoke
// 拦截器链index:0的ExposeInvocationInterceptor
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
	// step into ...
	// 先看一下这个spring.MethodInvocation(并不是java反射包sum.reflect下的)
	MethodInvocation oldInvocation = invocation.get();
	invocation.set(mi);
	try {
		return mi.proceed();
	}
	finally {
		invocation.set(oldInvocation);
	}
}

// MethodInvocation作用:给拦截器提供被调的方法
// MethodInvocation是一个连接点,可以被方法拦截器拦截。
/**
 * Description of an invocation to a method, given to an interceptor
 * upon method-call.
 *
 * <p>A method invocation is a joinpoint and can be intercepted by a
 * method interceptor.
 *
 * @author Rod Johnson
 * @see MethodInterceptor
 */
public interface MethodInvocation extends Invocation {

	// 获取被调用的方法。
	// 此方法是Joinpoint.getStaticPart()方法的友好实现(结果相同)
	/**
	 * Get the method being called.
	 * <p>This method is a friendly implementation of the
	 * {@link Joinpoint#getStaticPart()} method (same result).
	 * @return the method being called
	 */
	Method getMethod();
}

// org.springframework.aop.interceptor.ExposeInvocationInterceptor#invoke
private static final ThreadLocal<MethodInvocation> invocation = new NamedThreadLocal<>("Current AOP method invocation");
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
	MethodInvocation oldInvocation = invocation.get();
	invocation.set(mi);
	try {
		return mi.proceed();
	}
	finally {
		// 将 MethodInvocation 被调方法存入 threadlocal
		invocation.set(oldInvocation);
	}
}

ExposeInvocationInterceptor从threadLocal获取MethodInvocation

// org.springframework.aop.aspectj.AspectJAroundAdvice#invoke
// 例如:我们在拦截器链index:1的AroundAdvice
// public class AspectJAroundAdvice extends AbstractAspectJAdvice ...
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
	if (!(mi instanceof ProxyMethodInvocation)) {
		throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
	}
	ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
	ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
	// step into ...
	JoinPointMatch jpm = getJoinPointMatch(pmi);
	return invokeAdviceMethod(pjp, jpm, null, null);
}

// 该重载方式,需要methodInvocation被调方法作为入参
// Note: We can't use JoinPointMatch.getClass().getName() as the key, since
// Spring AOP does all the matching at a join point, and then all the invocations.
// Under this scenario, if we just use JoinPointMatch as the key, then
// 'last man wins' which is not what we want at all.
// Using the expression is guaranteed to be safe, since 2 identical expressions
// are guaranteed to bind in exactly the same way.
@Nullable
protected JoinPointMatch getJoinPointMatch(ProxyMethodInvocation pmi) {
	String expression = this.pointcut.getExpression();
	return (expression != null ? (JoinPointMatch) pmi.getUserAttribute(expression) : null);
}

// 如果没有MethodInvocation入参的情况下,ExposeInvocationInterceptor就发挥作用了
// org.springframework.aop.aspectj.AbstractAspectJAdvice#getJoinPointMatch()
/**
 * Get the current join point match at the join point we are being dispatched on.
 */
@Nullable
protected JoinPointMatch getJoinPointMatch() {
	// step into ...
	MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
	if (!(mi instanceof ProxyMethodInvocation)) {
		throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
	}
	return getJoinPointMatch((ProxyMethodInvocation) mi);
}

org.springframework.aop.aspectj.AbstractAspectJAdvice#getJoinPointMatch---------------------------
	
	// org.springframework.aop.interceptor.ExposeInvocationInterceptor#currentInvocation
	// 这里的this.invocation即前面提及的threadlocal
	/**
	 * Return the AOP Alliance MethodInvocation object associated with the current invocation.
	 * @return the invocation object associated with the current invocation
	 * @throws IllegalStateException if there is no AOP invocation in progress,
	 * or if the ExposeInvocationInterceptor was not added to this interceptor chain
	 */
	public static MethodInvocation currentInvocation() throws IllegalStateException {
		// 前面做了set,这里直接get
		MethodInvocation mi = invocation.get();
		if (mi == null) {
			throw new IllegalStateException(
					"No MethodInvocation found: Check that an AOP invocation is in progress and that the " +
					"ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that " +
					"advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor! " +
					"In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() " +
					"must be invoked from the same thread.");
		}
		return mi;
	}

3. spring aop 基类之间的关系

相关文章:

  • Kotlin(十一)Kotlin中的Object关键字
  • java服务器端开发-servlet:2_0、Servlet执行过程介绍:get请求与post请求、编码相关等
  • 自己整理的“无培训广告”的技术公众号!
  • springboot logback-spring.xml 整合apollo实现动态配置日志级别
  • 完全背包问题
  • 【python中级】func_timeout程序超时处理
  • JUC 并发编程_锁
  • java基于springboot+vue的高校大学生社团活动管理系统
  • 框架之SpringBoot基础(二)
  • 【小程序】中WXS的语法详解
  • Spring Cloud Gateway - GatewayFilter路由过滤器
  • 猿创征文|大数据之Kafka简介+基操
  • Shiro授权--注解式开发
  • CREO:CREO软件之零件【编辑】之修饰、用户定义特征的简介及其使用方法(图文教程)之详细攻略
  • Java并发 | 12.[方法] interrupt( )打断
  • 【个人向】《HTTP图解》阅后小结
  • 【前端学习】-粗谈选择器
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • docker容器内的网络抓包
  • HTTP 简介
  • js如何打印object对象
  • leetcode386. Lexicographical Numbers
  • React 快速上手 - 07 前端路由 react-router
  • Redux系列x:源码分析
  • vue总结
  • 当SetTimeout遇到了字符串
  • 猴子数据域名防封接口降低小说被封的风险
  • 后端_MYSQL
  • 理解 C# 泛型接口中的协变与逆变(抗变)
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 微信支付JSAPI,实测!终极方案
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • Oracle Portal 11g Diagnostics using Remote Diagnostic Agent (RDA) [ID 1059805.
  • 好程序员web前端教程分享CSS不同元素margin的计算 ...
  • 数据库巡检项
  • ​Python 3 新特性:类型注解
  • # 透过事物看本质的能力怎么培养?
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • (003)SlickEdit Unity的补全
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (分布式缓存)Redis分片集群
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (十二)python网络爬虫(理论+实战)——实战:使用BeautfulSoup解析baidu热搜新闻数据
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (原)Matlab的svmtrain和svmclassify
  • (转)平衡树
  • (转载)PyTorch代码规范最佳实践和样式指南
  • (自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载
  • .gitignore
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】