Spring源码分析:AOP代理

Aop使用

AOP与OOP有什么区别

AOP(Aspect Oriented Programming,面向切面编程),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

区别:
OOP(面向对象编程),针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

简单来说:AOP是运用动态代理技术对某部分业务逻辑做增强,可以统一处理日志,异常,权限等需求;而OOP的特性是封装、继承和多态。

Aop切面代码实现

定义一个环绕通知

@Configuration
@Aspect
public class AopTime {

    @Around("execution(* gdut.ff.aop.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) {
        long start = System.currentTimeMillis();
        Object obj = null;
        try {
            obj = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("消耗时间:" + (end - start) + " interface name is : " + joinPoint.getTarget());
        return obj;
    }

}

接口 && 实现

public interface ITestService {

    public void test();

}

@Service
public class TestServiceImpl implements ITestService{
    @Override
    public void test() {
        System.out.println("Test");
    }
}

运行

@EnableAspectJAutoProxy
@ComponentScan("gdut.ff.aop")
public class TestAop {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestAop.class);
        TestServiceImpl  testServiceImpl = context.getBean("testServiceImpl",TestServiceImpl.class);
        testServiceImpl.test();
    }
}

AOP的通知类型有哪些

前置通知(Before Advice)

在连接点之前执行的Advice,但是它不能阻止执行流程继续进行到连接点(除非它引发异常)。

public interface MethodBeforeAdvice extends BeforeAdvice {
    
    
	void before(Method method, Object[] args, Object target) throws Throwable;

}
public interface BeforeAdvice extends Advice {
    
    

}
public interface Advice {
    
    

}

后置通知 (After Retuning Advice)

在连接点正常结束之后执行的Advice。例如,如果一个方法没有抛出异常正常返回。

public interface AfterReturningAdvice extends AfterAdvice {
    
    
	void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;

}
public interface AfterAdvice extends Advice {
    
    

}

异常通知(After Throwing Advice)

如果一个方法通过抛出异常来退出的话,这个Advice就会被执行。

public interface ThrowsAdvice extends AfterAdvice {
    
    

}

最终通知(After/finally Advice)

无论连接点是通过什么方式退出的(正常返回或者抛出异常)都会执行。在finally里调用这个通知。

环绕通知(Around Advice)

围绕连接点执行的Advice,在方法前后执行这个通知。

Pointcut切点

Pintcut(切点)决定Advice通知应该作用哪个连接点,也就是说通过Pointcut来定义需要增强的方法的集合,这些集合的选取可以按照一定的规则来完成。Pointcut意味着标识方法,例如,这些需要增强的方法可以由某个正则表达式进行标识,或者根据某个方法名进行匹配等。

JdkRegexpMethodPointcut

正则表达式匹配切点

protected boolean matches(String pattern, int patternIndex) {
    
    
	Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
	return matcher.matches();
}

NameMatchMethodPointcut

根据方法名匹配切点

public boolean matches(Method method, Class<?> targetClass) {
    
    
	for (String mappedName : this.mappedNames) {
    
    
		if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
    
    
			return true;
		}
	}
	return false;
}

Advisor通知器

定义应该使用哪个通知并在哪个关注点使用它。

DefaultPointcutAdvisor

DefaultPointcutAdvisor定义了一个pointcut变量:
private Pointcut pointcut = Pointcut.TRUE;

Pointcut接口中对Pointcut.TRUE的定义是:
Pointcut TRUE = TruePointcut.INSTANCE;
TruePointcut实现了Pointcut,通过私有构造函数创建了常量INSTANCE,确保不会被创建第二次。

class TruePointcut implements Pointcut, Serializable {

	public static final TruePointcut INSTANCE = new TruePointcut();

	//私有构造函数
	private TruePointcut() {
	}
}

在TruePoint的getMethodMatcher实现中,使用TrueMethodMatcher作为方法匹配器。这个方法匹配器对任何方法名的匹配要求,都会返回匹配成功的结果。TrueMethodMatcher也是一个单例实现。

public MethodMatcher getMethodMatcher() {
	return MethodMatcher.TRUE;
}

MethodMatcher对MethodMatcher.TRUE的定义:
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

TrueMethodMatcher实现了MethodMatcher接口,通过私有构造函数创建了常量INSTANCE,确保不会被创建第二次。matches方法默认返回true。

class TrueMethodMatcher implements MethodMatcher, Serializable {

	public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher();

	private TrueMethodMatcher() {
	}

	public boolean matches(Method method, Class<?> targetClass) {
		return true;
	}
}

JDK代理和Cglib代理

JDK代理与Cglib代理区别

JDK代理只能对实现了接口的类生成代理。
CGLIB代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类和方法最好不要声明为final。底层通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成类。

JDK代理

JDK代理需要实现InvocationHandler接口:

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

这个接口方法只声明了一个invoke方法。三个参数分别是:代理对象实例、Method方法对象(当前Proxy被调用的方法),被调用的方法中的参数。

为什么JDK动态代理是实现接口,而不是继承类

byte[] $Proxies = ProxyGenerator.generateProxyClass("$Proxy", new Class[]{ITestService.class});
FileOutputStream fileOutputStream = new FileOutputStream("D:\\Test.class");
fileOutputStream.write($Proxies);
fileOutputStream.flush();
fileOutputStream.close();

输出Test.class,复制到IDEA查看。
Java的特性是单继承多实现,生成的代理类已经继承了Proxy类,所以只能实现接口了。在这里插入图片描述

源码分析

接下来,我们一步步解析Spring是如何代理对象的。

获取代理对象

先来看看context.getBean()方法是如何获取到代理对象的。

AbstractBeanFactory的getBean方法会调用AbstractBeanFactory的doGetBean方法
在这里插入图片描述

从单例缓存池获取对象

doGetBean方法调用DefaultSingletonBeanRegistry的getSingleton方法,singletonObjects是一个ConcurrentHashMap对象,用来缓存单例对象,key是Bean的名称,value是Bean实例。从缓存对象中根据名称获取到的是一个JDK动态代理对象。

/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

在这里插入图片描述
那这个代理对象是什么时候放入缓存中的呢?

将对象添加到单例缓存池

我们在DefaultSingletonBeanRegistry类中找找this.singletonObjects.put关键字,看看这个对象是什么时候放入缓存的。由下图可以看到,是通过AnnotationConfigApplicationContext的构造方法将代理对象加载到缓存中的。
在这里插入图片描述那这个代理对象是怎么生成的呢?Spring是如何判断应该使用JDK动态代理还是Cglib代理呢?

生成代理对象

获取Bean

调用AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsBeforeInstantiation方法
获取到的其中一个processor是AnnotationAwareAspectJAutoProxyCreator,属性
proxyTargetClass=false; optimize=false; opaque=false; exposeProxy=false; frozen=false

将InstantiationAwareBeanPostProcessors应用于指定的bean定义(按类和名称),调用其postProcessBeforeInstantiation方法。任何返回的对象都将用作bean,而不是实际实例化目标bean。 来自后处理器的null返回值将导致目标Bean被实例化。
在这里插入图片描述
AbstractAutoProxyCreator的postProcessBeforeInstantiation方法,
如果beanName为空,并且targetSourcedBeans集合不包含这个beanName,判断获取的缓存key是否存在于advisedBeans集合中,如果存在,直接返null;如果不存在,判断是否是Advice,Pointcut,Advisor和AopInfrastructureBean对象或者子类,如果是,直接返回null;
在这里插入图片描述

private final Set<String> targetSourcedBeans =
			Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<Object, Boolean>(256);					

获取缓存的key值,
(1)如果给定的beanName为空,直接返回beanClass;
(2)如果给定的beanName不为空,判断给定的beanClass是否是FactoryBean对象,或者是否是FactoryBean的子类,如果是,返回的beanName加上前缀&;如果不是,直接返回beanName;
在这里插入图片描述
返回给定的bean类是否表示基础结构类。默认实现将Advices,Advisor和AopInfrastructureBeans视为基础结构类。
在这里插入图片描述
class1.isAssignableFrom(class2) 判定此 class1 对象所表示的类或接口与指定的 class2 参数所表示的类或接口是否相同,或是否是其超类或超接口。如果是则返回 true;否则返回 false。如果该 class1 表示一个基本类型,且指定的 class2 参数正是该 Class 对象,则该方法返回 true;否则返回 false。

public native boolean isAssignableFrom(Class<?> cls);

创建Bean实例

通过DefaultListableBeanFactory的preInstantiateSingletons方法,调用AbstractBeanFactory的getBean方法,再调用doGetBean方法,从缓存中获取不到对应的实例,调用AbstractAutowireCapableBeanFactory的createBean方法,再调用initializeBean方法。

AbstractAutowireCapableBeanFactory的createBeanInstance方法使用适当的实例化策略为指定的bean创建一个新实例:工厂方法,构造函数自动装配或简单实例化。
(1)获取beanClass;
为指定的bean定义解析bean类,将bean类名解析为Class引用(如有必要),并将解析后的Class存储在bean定义中以备将来使用。
在这里插入图片描述
(2) 判断beanClass是否是public方法,如果不是public方法,且不允许访问非公共的构造函数和方法,会抛出BeanCreationException异常;
在这里插入图片描述
instantiateBean方法使用其默认构造函数实例化给定的bean。getInstantiationStrategy()返回一个初始化策略对象CglibSubclassingInstantiationStrategy。
在这里插入图片描述

/** Strategy for creating bean instances */
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

BeanUtils的instantiateClass方法,使用给定构造函数实例化类的便捷方法。
在这里插入图片描述
initializeBean方法调用AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsAfterInitialization方法,
在这里插入图片描述
再调用AbstractAutoProxyCreator的postProcessAfterInitialization方法。
如果Bean被子类标识为要代理的bean,则使用配置的拦截器创建代理。
在这里插入图片描述
再调用wrapIfNecessary方法,getAdvicesAndAdvisorsForBean方法获取增强切面。如果获取的增强切面不为空,就创建代理对象。
在这里插入图片描述
AbstractAdvisorAutoProxyCreator的getAdvicesAndAdvisorsForBean方法。如果找不到切面,返回null,DO_NOT_PROXY的值是null。
在这里插入图片描述
查找所有匹配的通知以自动代理该Class。
在这里插入图片描述
BeanFactoryAspectJAdvisorsBuilder类的buildAspectJAdvisors方法,在当前的bean工厂中查找带有AspectJ注释的Aspect bean,并返回代表它们的Spring AOP Advisor列表。
在这里插入图片描述
创建代理对象时,如果当前beanFactory是ConfigurableListableBeanFactory,公开指定bean的给定目标类。如果beanName不为空,且bean定义中有这个beanName对应的值,设置该bean定义的ORIGINAL_TARGET_CLASS_ATTRIBUTE属性为对应的targetClass。定义一个ProxyFactory,
在这里插入图片描述
ProxyFactory的getProxy方法。
在这里插入图片描述
ProxyCreatorSupport的createAopProxy方法,子类应该调用这个方法获得新的AOP代理。
在这里插入图片描述
返回ProxyConfig使用的AopProxyFactory,是DefaultAopProxyFactory。
在这里插入图片描述
对于是选择使用JDK代理 还是CGLIB代理,Spring有以下三个判断条件:
(1)ProxyConfig类的optimize值是否是true,默认值是false;optimize用来控制通过CGLIB创建的代理是否使用激进的优化策略。
(2)ProxyConfig类的proxyTargetClass参数的值是否是true,默认值是false;为true时表示目标类本身被代理而不是目标类的接口。指定使用CGLIB代理:
基于注解:@EnableAspectJAutoProxy(proxyTargetClass=true)
基于XML:<aop:aspectj-autoproxy proxy-target-class="true"/>
(3)hasNoUserSuppliedProxyInterfaces是否实现了接口,没有实现接口就用CGLIB代理。
在这里插入图片描述
JdkDynamicAopProxy的getProxy方法生成代理对象,从AdvisedSupport对象获取需要代理的接口。

public Object getProxy(ClassLoader classLoader) {
	if (logger.isDebugEnabled()) {
		logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
	}
	Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
	findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

CglibAopProxy的getProxy方法创建代理对象,创建并配置CGLIB的Enhancer,包括设置代理接口,回调方法。在Enhancer的callback回调设置中,是通过设置DynamicAdvisedInterceptor拦截器来完成AOP功能的。

通过AopProxy对象封装target目标对象后,ProxyFactoryBean的getObject方法得到的是一个AopProxy代理对象。对于不同的Aop代理对象,使用不同的拦截回调入口:
(1) 对于Cglib生成的代理对象,使用设置好的callback回调。对于AOP实现,是通过DynamicAdvisedInterceptor的intercept方法。
(2) 对于jdk生成的代理对象,使用InvocationHandler的invoke方法。

JdkDynamicAopProxy的inovke方法:
(1) 如果目标对象没有实现Object类的基本方法equals,当前调用的目标方法名是equals,会另外处理;
(2) 如果目标对象没有实现Object类的基本方法hashCode,当前调用的目标方法名是hashCode,会另外处理;
(3) 如果目标类是Advised接口的父类,也进行另外处理;
(4) 如果没有定义好的拦截器链,直接调用目标方法;
(5) 如果有定义好的拦截器链,创建ReflectiveMethodInvocation对象,先调用拦截器,再调用目标方法;

DynamicAdvisedInterceptor的intercept方法:
与JdkDynamicAopProxy的inovke方法调用类似,不同的是创建的是CglibMethodInvocation对象执行拦截器方法。CglibMethodInvocation继承了ReflectiveMethodInvocation。

目标方法的调用:
(1) Jdk的代理对象是通过AopUtils的invokeJoinpointUsingReflection方法实现的。
method.invoke(target, args);

(2) Cglib的代理对象是直接在DynamicAdvisedInterceptor的intercept方法实现的。
methodProxy.invoke(target, argsToUse);

AOP拦截器链的调用通过ReflectiveMethodInvocation的proceed方法实现:
在运行拦截器的拦截方法之前,需要对代理方法完成一个matches判断,通过这个匹配判断决定拦截器是否满足切面增强的要求。如果是,从拦截器中得到通知器,并启动通知器的invoke方法进行切面增强。这个方法逻辑如下:
(1) 如果拦截器已经执行完毕,调用目标方法;
(2) 获取当前位移的通知拦截器,如果是InterceptorAndDynamicMethodMatcher类型,对拦截器进行动态匹配判断,如果匹配,这个advice将得到执行;如果不匹配,proceed方法被递归调用,知道所有的拦截器被执行完;如果拦截器不是InterceptorAndDynamicMethodMatcher类型,直接调用这个拦截器的方法。

List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)

配置通知器:
AdvisedSupport的getInterceptorsAndDynamicInterceptionAdvice方法:

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
	MethodCacheKey cacheKey = new MethodCacheKey(method);
	List<Object> cached = this.methodCache.get(cacheKey);
	if (cached == null) {
		cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
				this, method, targetClass);
		this.methodCache.put(cacheKey, cached);
	}
	return cached;
}

advisorChainFactory这里是DefaultAdvisorChainFactory,生成通知器链工厂。DefaultAdvisorChainFactory会通过一个AdvisorAdapterRegistry实现拦截器的注册。

DefaultListableBeanFactory的基类AbstractAutowireCapableBeanFactory,在基类中可以看到一个初始化方法initializeBean。这个初始化方法会判断这个Bean的类型是不是实现了BeanFactoryAware接口,如果是,它一定实现了BeanFactoryAware定义的接口方法,通过这个接口方法,可以把IOC容器设置到Bean自身定义的一个属性中去。

if (bean instanceof BeanFactoryAware) {
	((BeanFactoryAware)bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}

执行过程如下:
在这里插入图片描述

设计模式

代理模式

也就是,代理是一个看起来像另一个对象的对象,但是在幕后增加了特殊功能。

单例模式

GlobalAdvisorAdapterRegistry是一个标准的单例模式的实现,配置了一个静态的final变量instance,这个对象在加载类的时候就生成了,而且GlobalAdvisorAdapterRegistry是一个抽象类,不能被实例化,保证instance对象的唯一性,只能通过getInstance方法获取instance。

public abstract class GlobalAdvisorAdapterRegistry {

	private static AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry();

	public static AdvisorAdapterRegistry getInstance() {
		return instance;
	}

	static void reset() {
		instance = new DefaultAdvisorAdapterRegistry();
	}
}

适配器模式

AdvisorAdapter接口的supportsAdvice方法判断取得的advice属于什么类型的advice通知,从而根据不同的advice类型来注册不同的AdviceInterceptor。
在这里插入图片描述
MethodBeforeAdvice是前置通知方法,MethodBeforeAdviceInterceptor是用来执行前置通知的拦截器,实现了MethodInteceptor接口,调用invoke方法先执行前置通知,再执行目标方法。AdvisorAdapter是通知器适配器,有两个方法,一个是判断是否支持指定的通知;另一个是创建一个方法拦截器实例。适配器类MethodBeforeAdviceAdapter使用MethodBeforeAdviceInterceptor来处理MethodBeforeAdvice。类似的还有AfterReturningAdviceAdapter、ThrowsAdviceAdapter

//前置通知
public interface MethodBeforeAdvice extends BeforeAdvice {
	void before(Method method, Object[] args, Object target) throws Throwable;
}

//前置通知拦截器
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {

	private MethodBeforeAdvice advice;

	public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}

    //触发advice的before回调,然后是MethodInvocation的proceed方法。
	public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
		return mi.proceed();
	}
}

public interface AdvisorAdapter {
	boolean supportsAdvice(Advice advice);
    MethodInterceptor getInterceptor(Advisor advisor);
}	

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

	@Override
	//如果advice是MethodBeforeAdvice的实例,返回true。
   public boolean supportsAdvice(Advice advice) {	
		return (advice instanceof MethodBeforeAdvice);
	}

	//创建一个MethodBeforeAdviceInterceptor对象,通过这个对象把advice通知包装起来
	public MethodInterceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		return new MethodBeforeAdviceInterceptor(advice);
	}
}

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。比如,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

猜你喜欢

转载自blog.csdn.net/u012734723/article/details/107547366