前言
spring是如何实现AOP切面的,从原理上来说是动态代理,那么怎样去实现这个动态代理呢就是本篇的内容。
动态代理
spring中用到的动态代理有两种:JDK的动态代理和CGLIB的动态代理。
jdk的动态代理
要代理的对象,需要实现一个接口。
代码语言:javascript复制public interface IPlay {
void play();
}
代码语言:javascript复制public class PlayGame implements IPlay {
@Override
public void play() {
System.out.println("play 。。。");
}
}
代码语言:javascript复制public class PlayProxy implements InvocationHandler {
private IPlay play;
public PlayProxy(IPlay play) {
this.play = play;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理。。。");
method.invoke(play, args);
return null;
}
}
代码语言:javascript复制// 要代理的对象
IPlay play = new PlayGame();
// 代理处理
PlayProxy playProxy = new PlayProxy(play);
// 生成代理的对象
IPlay instance =(IPlay) Proxy.newProxyInstance(play.getClass().getClassLoader(), new Class[]{IPlay.class}, playProxy);
instance.play();
CGLIB
代码语言:javascript复制public class PlayCglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib proxy。。。");
methodProxy.invokeSuper(o, objects);
return null;
}
}
代码语言:javascript复制Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PlayGame.class);
enhancer.setCallback(new PlayCglibProxy());
PlayGame o = (PlayGame) enhancer.create();
o.play();
区别
jdk的动态代理只能为实现了接口的类做代理,面向接口;
而CGLIB动态代理是对字节码进行修改和动态生成的,面向方法。
AOP的动态代理
spring对jdk的动态代理和cglib代理,抽象出一个代理工程ProxyFactory
,而其中有几个概念:
advice:代理的逻辑
pointcut:切点
advisor:advice pointcut;表示advice可以用在什么地方
使用spring提供的ProxyFactory类创建一个aop代理:
代码语言:javascript复制IPlay play = new PlayGame();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(play);
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("method before。。。");
}
});
IPlay proxy = (IPlay) proxyFactory.getProxy();
proxy.play();
第二种写法:
代码语言:javascript复制@Component
public class CustomAdvisor implements PointcutAdvisor {
@Override
public Pointcut getPointcut() {
NameMatchMethodPointcut methodPointcut = new NameMatchMethodPointcut();
methodPointcut.addMethodName("play");
return methodPointcut;
}
@Override
public Advice getAdvice() {
MethodBeforeAdvice methodBeforeAdvice = new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行方法前" method.getName());
}
};
return methodBeforeAdvice;
}
@Override
public boolean isPerInstance() {
return false;
}
}
代码语言:javascript复制 IPlay play = new PlayGame();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(play);
// proxyFactory.addAdvice(new MethodBeforeAdvice() {
// @Override
// public void before(Method method, Object[] args, Object target) throws Throwable {
// System.out.println("method before。。。");
// }
// });
proxyFactory.addAdvisor(new CustomAdvisor());
IPlay proxy = (IPlay) proxyFactory.getProxy();
proxy.play();
那我们跟进源码看看:
代码语言:javascript复制public Object getProxy() {
return createAopProxy().getProxy();
}
代码语言:javascript复制public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 如果设置的targetClass是一个接口,会使用jdk动态代理
// 默认情况下(optimize为false, isProxyTargetClass为false), ProxyFactory添加了接口时,也会使用jdk动态代理
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
// 抛异常 省略。。。
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
从上面代码看,我们可以得出结论,当我们设置了接口类时,它就会使用jdk动态代理,没有设置接口,使用cglib。
JdkDynamicAopProxy
代码语言:javascript复制IPlay play = new PlayGame();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(play);
// proxyFactory.setTargetClass(PlayGame.class);
// 这里设置接口,不设置class
proxyFactory.setInterfaces(IPlay.class);
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("method before。。。");
}
});
IPlay proxy = (IPlay) proxyFactory.getProxy();
proxy.play();
代码语言:javascript复制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);
// 判断这些接口中有没有定义equals方法,hashcode方法
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 针对所指定的接口生成代理对象,包括用户所添加的接口以及SpringProxy、Advised、DecoratingProxy
// 所以生成的代理对象可以强制转换成任意一个接口类型
// 因为JdkDynamicAopProxy实现了InvocationHandler接口,所以这里是this)
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
jdk动态代理需要接口,所以这里是获取接口的
代码语言:javascript复制 Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
这个方法,我们分两个部分来看
代码语言:javascript复制 // 我们添加了接口,那么这里会获取到接口
// proxyFactory.setInterfaces(IPlay.class) 设置是添加到了属性 interfaces
// 这里getProxiedInterfaces是从属性:interfaces 拿的
Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
// 如果ProxyFactory中没有指定接口,就查看targetClass是不是接口,如果是则添加这个接口
if (specifiedInterfaces.length == 0) {
// 这里是,在发现没有设置接口时,做的一个后手准备
// proxyFactory.setTarget(play) -》是设置targetSource
// getTargetClass -》targetSource.getTargetClass(),
Class<?> targetClass = advised.getTargetClass();
if (targetClass != null) {
if (targetClass.isInterface()) {
advised.setInterfaces(targetClass);
}
else if (Proxy.isProxyClass(targetClass)) {
advised.setInterfaces(targetClass.getInterfaces());
}
specifiedInterfaces = advised.getProxiedInterfaces();
}
}
所以上面这段基本上就是对必要属性的处理,因为我们要用jdk的动态代理,原始对象就必须实现一个接口,那么这里首先是判断我们有设置接口没,如果没有,它也不能出一个弹窗告诉你设置,所以它自己去找,那么它找的话,只能从原始对象去找,因为它猜测,接口都会忘记设置的人,其他属性也不能保证设置了,所以从最原始的对象去找才是最有效的,当然也只有它自己在用。
代码语言:javascript复制 // 除开用户添加的接口之后,Spring还要添加几个默认的接口SpringProxy、Advised、DecoratingProxy
boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
int nonUserIfcCount = 0;
if (addSpringProxy) {
nonUserIfcCount ;
}
if (addAdvised) {
nonUserIfcCount ;
}
if (addDecoratingProxy) {
nonUserIfcCount ;
}
Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length nonUserIfcCount];
System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
int index = specifiedInterfaces.length;
if (addSpringProxy) {
proxiedInterfaces[index] = SpringProxy.class;
index ;
}
if (addAdvised) {
proxiedInterfaces[index] = Advised.class;
index ;
}
if (addDecoratingProxy) {
proxiedInterfaces[index] = DecoratingProxy.class;
}
return proxiedInterfaces;
上面这段就是添加3个默认的接口类型:SpringProxy、Advised、DecoratingProxy,这也意味着我们得到的代理对象,也可以强转为这3个接口中的一个,对我们也没多大用处。
在看invoke方法:
代码语言:javascript复制 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
// 获取原始对象
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
// 省了。。。
Object retVal;
// advised就是ProxyFactory
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 原始对象
// targetSource分为两种:SingletonTargetSource、EmptyTargetSource
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 根据当前方法获取对应的拦截器链
// 就是一开始我们添加的advice
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 这里的参数对应jdk动态代理里invoke方法里的参数,然后chain是拦截器链,因为它有多个拦截操作。
// proxy ->代理对象
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 执行操作
retVal = invocation.proceed();
}
// 省略。。。
return retVal;
}
finally {
// 省略。。。
}
}
代码语言:javascript复制public Object proceed() throws Throwable {
// 判断当前的拦截方法执行完没
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get( this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
return proceed();
}
}
else {
// 执行对应的invoke方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
MethodBeforeAdviceInterceptor#invoke
代码语言:javascript复制public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
// 这个方法又到上面的 proceed
// 但是proceed方法,一开始有一个判断,判断拦截方法执行完没
return mi.proceed();
}
这里有一个递归的思想在里面,每当进入这个proceed方法,就会去执行拦截链里的方法,比如拦截链里有3个:before、after、return,那么第一次它只取一个,第一次取before,然后执行invoke后,又回到了proceed,这时还有2个方法没执行,然后它判断拦截链里的方法执行完没,它一看,还有,然后再取一个,在执行invoke方法,再执行proceed方法,直到拦截链里是空的。
到这里我想起之前在《mybatis解读篇》中的插件原理有介绍过,插件的原理,mybatis里的是直接invoke,返回proxy(代理对象),然后再代理,和这里的完全不一样,这其中的优劣在哪?
然后在执行原始对象方法的是在这里:
代码语言:javascript复制protected Object invokeJoinpoint() throws Throwable {
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
代码语言:javascript复制public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
throws Throwable {
// 省略。。。
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
// 省略。。。
}
ObjenesisCglibAopProxy
和jdk动态代理一样,这个方法依旧是先生成一个CGLIB的代理对象
代码语言:javascript复制public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating CGLIB proxy: " this.advised.getTargetSource());
}
try {
Class<?> rootClass = this.advised.getTargetClass(); //拿到被代理的类
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader);
// CGLIB的配置
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);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " this.advised.getTargetClass()
": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
调用业务方法时,触发CGLIB的代理方法
代码语言:javascript复制public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 如果某个Advisor中的Advice没有实现MethodInterceptor接口,那么则会把这个Advice适配成MethodInterceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 直接调用目标方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
// proxy: 代理类产生的代理对象
// target: 被代理的目标对象
// method: 当前正在执行的目标类中的方法对象
// args: 当前正在执行的方法参数
// targetClass: 被代理的类
// chain: 和当前被代理的类和正在执行的方法匹配的所有的advisor
// methodProxy:当前正在执行的代理类中的方法对象
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
然后这里的proceed方法,就和jdk里执行的是一样的。
自动代理
spring提供了自动代理功能,即只要生成一个bean注册到容器就行了。
有两种方式配置。
BeanNameAutoProxyCreator
例子
第一种,BeanNameAutoProxyCreator。
代码语言:javascript复制@Component
public class PlayGame implements IPlay {
@Override
public void play() {
System.out.println("play 。。。");
}
}
代码语言:javascript复制@Component
public class PlayGame2{
public void play() {
System.out.println("play 2...");
}
}
代码语言:javascript复制 @Bean
public BeanNameAutoProxyCreator creator(){
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
beanNameAutoProxyCreator.setBeanNames("playGame", "playGame2");
beanNameAutoProxyCreator.setInterceptorNames("customAdvisor");
return beanNameAutoProxyCreator;
}
代码语言:javascript复制AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
IPlay playGame = applicationContext.getBean("playGame", IPlay.class);
playGame.play();
PlayGame2 playGame2 = (PlayGame2)applicationContext.getBean("playGame2");
playGame2.play();
**注意:**这里我配置了两个需要代理的类PlayGame、PlayGame2
,PlayGame是实现了接口IPlay的,那么在getBean的时候不一样,我强转是用的接口, 因为我PlayGame实现了接口,那么就像上面源码那样,会去使用JDk的动态代理,而代理出来的代理对象,并非是之前的PlayGame,所以在创建bean时,返回的是代理对象,并不是PlayGame,所以,强制为PlayGame的话是会报下面的异常。
使用接口的类,被自动代理后,使用子类强制报错:
代码语言:javascript复制Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'playGame' is expected to be of type 'com.lry.aop.service.PlayGame' but was actually of type 'com.sun.proxy.$Proxy10'
原理
BeanNameAutoProxyCreator
其实是一个后置处理器,我们看他的类图就知道了。
所以在创建bean的时候,就会经过后置处理器的初始化后方法:postProcessAfterInitialization;
调用点有两个地方:
第一个地方:实例化前
AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
这个地方需要实现了InstantiationAwareBeanPostProcessor
的后置处理器再postProcessBeforeInstantiation
返回一个bean,这个地方是在实例化前,调用我们的后置处理器生成的bean(我们自己生成返回的bean),那么这意味着,bean的生命周期已经结束,想要在做干涉只能在postProcessBeforeInstantiation
方法后面,所以才会有下面这一段:
if (targetType != null) {
// 实例化前
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
// 在bean返回之前,做一些后置的初始化操作进行干涉
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
第二个地方:初始化完
AbstractAutowireCapableBeanFactory#initializeBean
看一下这个代码,它是返回null是相当于不做任何操作的,那么在看处理器中的代码是怎么回事。
代码语言:javascript复制Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
// 这里返回null就直接返回了
if (current == null) {
return result;
}
result = current;
}
return result;
在处理器中是这样的,这段紧接着上面这段;AbstractAutoProxyCreator#postProcessAfterInitialization
代码语言:javascript复制public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// earlyProxyReferences中存的是哪些提前进行了AOP的bean,beanName:AOP之前的对象
// 注意earlyProxyReferences中并没有存AOP之后的代理对象 BeanPostProcessor
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 没有提前进行过AOP,则进行AOP
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
那么,再接着看wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 在当前targetSourcedBeans中存在的bean,表示在实例化之前就产生了代理对象
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 当前这个bean不用被代理
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 先判断当前bean是不是要进行AOP,比如当前bean的类型是Pointcut、Advice、Advisor等那就不需要进行AOP
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
// 这个由上面的那个判断就知道这个map的作用只是用了判断bean是否进行了aop
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 获取当前beanClass所匹配的advisors
// 这里的实现和DefaultAdvisorAutoProxyCreator的不一样
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 如果匹配的advisors不等于null,那么则进行代理,并返回代理对象
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 基于bean对象和Advisor创建代理对象
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;
}
这个createProxy方法,就和上面ProxyFactory的使用是一样的。
代码语言:javascript复制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);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this); // 复制配置参数
// 是否指定了必须用cglib进行代理
if (!proxyFactory.isProxyTargetClass()) {
// 如果没有指定,那么则判断是不是应该进行cglib代理(判断BeanDefinition中是否指定了要用cglib)
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// 是否进行jdk动态代理,如果当前beanClass实现了某个接口,那么则会使用JDK动态代理
evaluateProxyInterfaces(beanClass, proxyFactory); // 判断beanClass有没有实现接口
}
}
// 将commonInterceptors和specificInterceptors整合再一起
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors); // 向ProxyFactory中添加advisor
proxyFactory.setTargetSource(targetSource); // 被代理的对象
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy); //
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 生成代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}
怎么添加advisor的,其实到这里我也猜到它会怎么做了,我们在@Bean那里是这样定义的:
代码语言:javascript复制beanNameAutoProxyCreator.setInterceptorNames("customAdvisor");
再看上面代码
代码语言:javascript复制Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 上面buildAdvisors方法进去的,开头第一句,这个resolveInterceptorNames方法就是从bean工厂中获取的bean
Advisor[] commonInterceptors = resolveInterceptorNames();
代码语言:javascript复制private Advisor[] resolveInterceptorNames() {
BeanFactory bf = this.beanFactory;
ConfigurableBeanFactory cbf = (bf instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) bf : null);
List<Advisor> advisors = new ArrayList<>();
for (String beanName : this.interceptorNames) {
if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) {
Assert.state(bf != null, "BeanFactory required for resolving interceptor names");
Object next = bf.getBean(beanName);
// 这里就是它为我们注册advisor的地方
advisors.add(this.advisorAdapterRegistry.wrap(next));
}
}
return advisors.toArray(new Advisor[0]);
}
**还有一点:**从wrap这个方法来看,自定义拦截的类支持下面三种类型:Advisor、MethodInterceptor、AdvisorAdapter
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) {
// So well-known it doesn't even need an adapter.
return new DefaultPointcutAdvisor(advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// Check that it is supported.
if (adapter.supportsAdvice(advice)) {
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}
这里MethodInterceptor
其实是实现了advice接口的,advice没有一个抽象接口,所以这里需要强转为MethodInterceptor
。
而后,在进行封装为DefaultPointcutAdvisor
,如果都不属于advisor,和MethodInterceptor,那么有必要再检查看看是否是AdvisorAdapter
类型的,AdvisorAdapter
接口可以实现获取MethodInterceptor
,也算是一种类型吧。
这个方法就是进行了转化,说成适配还更合适一点。
简单做一个验证,只验证一种:
代码语言:javascript复制@Component
public class CustomMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("customMethodIntercepter...");
return invocation.proceed();
}
}
代码语言:javascript复制@Bean
public BeanNameAutoProxyCreator creator(){
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
beanNameAutoProxyCreator.setBeanNames("playGame", "playGame2");
beanNameAutoProxyCreator.setInterceptorNames("customMethodInterceptor");
return beanNameAutoProxyCreator;
}
流程梳理
- bean初始化完(两个地方),进入后置处理器
AbstractAutoProxyCreator#postProcessAfterInitialization
的方法 - 判断当前bean是否需要代理
- 判断当前bean是否需要aop
- 获取advisor
- 返回
DO_NOT_PROXY
或者不是DO_NOT_PROXY
- 返回
- 判断是否用cglib
- beanFactory获取advisor bean
- ProxyFactory生成代理对象
DefaultAdvisorAutoProxyCreator
看这个的UML图,发现它也是个后置处理器,那么它和上面BeanNameAutoProxyCreator的逻辑基本差不多,不同的是怎么注册advisor。
代码语言:javascript复制@Bean
public DefaultAdvisorAutoProxyCreator creator() {
return new DefaultAdvisorAutoProxyCreator();
}
这个类,它的自动性比BeanNameAutoProxyCreator
强,不需要做其他操作,直接注入后,它自己会去查找代理,那为什么spring会弄出这两种?
我的理解,两种方式,一种全自动注入,可以少敲几行代码,可以不用去为了配置,将beanName设置进去,也不用设置advisor名称,另一种,是需要手动设置要代理的beanName,和设置advisor名称,显然第二种的方式灵活性更高,可以依据自己的需要进行配置。
原理
前3步:
- bean初始化完,进入后置处理器
AbstractAutoProxyCreator#postProcessAfterInitialization
的方法 - 判断当前bean是否需要代理
- 判断当前bean是否需要aop
如果不理解,跟不上,重新再看一遍BeanNameAutoProxyCreator
的原理部分。
他们实现了一样的postProcessor,所以从:AbstractAutoProxyCreator#wrapIfNecessary 下的接口实现开始看,这个方法里面获取advisor的实现是:AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
代码语言:javascript复制protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
// 针对当前bean查找合格的Advisor
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
下面这段是获取advisor的方法,看看它是否与BeanNameAutoProxyCreator
里的一样。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 得到所有的Advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 进行筛选
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 提供给子类去额外的添加advisor
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 按order进行排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
// 返回匹配的Advisor
return eligibleAdvisors;
}
方法findCandidateAdvisors
所找的类型是advisor
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
然后,在找到所有的advisor后,需要进一步的过滤
代码语言:javascript复制public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
// IntroductionAdvisor和@DeclareParents注解配合使用
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
这一段是筛选的逻辑,它过滤出IntroductionAdvisor、PointcutAdvisor这两种类型
代码语言:javascript复制 public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
其他代码都是AbstractAutoProxyCreator
里的,所以省略。。。
流程梳理
- bean初始化完(两个地方),进入后置处理器
AbstractAutoProxyCreator#postProcessAfterInitialization
的方法 - 判断当前bean是否需要代理
- 判断当前bean是否需要aop
- 获取advisor
- 获取所有的advisor
- 筛选出IntroductionAdvisor、PointcutAdvisor两种类型
- 判断是否用cglib
- beanFactory获取advisor bean
- ProxyFactory生成代理对象
@EnableAspectJAutoProxy
我们使用aop一般是注解@Aspect、@Pointcut、@Before
等等这些,然后使用这些注解还需要@EnableAspectJAutoProxy
开启功能。
那么它是怎么实现的?
这个注解上导入了一个配置类:@Import(AspectJAutoProxyRegistrar.class)
,
进入AspectJAutoProxyRegistrar
的,回调方法有下面这一句:
// 根据方法名分析:注册Aspect的自动代理注册器
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
跟踪到底层:
代码语言:javascript复制registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
找到,最后是AnnotationAwareAspectJAutoProxyCreator
注册到了bean工厂,那么从AnnotationAwareAspectJAutoProxyCreator
看:
它也实现了后置处理器接口,也是只有:findCandidateAdvisors 不同
其中有一个熟悉的方法findCandidateAdvisors
,它重写了:AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
// 它重写了AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
// Advisor=Pointcut Advice (切点加建议) ,获得到所有Advisor类型的bean
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
// 找的是通过AspectJ的方式定义的Advisor
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
出现了super,没错,它的这个方法和DefaultAdvisorAutoProxyCreator
那边是一样的,因为他们都继承了了同一个AbstractAdvisorAutoProxyCreator
那么,不同点就是在下面的buildAspectJAdvisors
方法。
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
// 拿到beanFactory中所有的bean
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
// 是不是一个合格的Aspect的beanName
if (!isEligibleBean(beanName)) {
continue;
}
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
// beanType是不是一个切面,判断beanType上是否存在@Aspect注解
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
//
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 解析Advisor (解析@before,@after这些注解)
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
if (this.beanFactory.isSingleton(beanName)) {
// 省略。。。。
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
看一下List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
这个方法是解析注解的。
代码语言:javascript复制 public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); validate(aspectClass); MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); List<Advisor> advisors = new ArrayList<>(); // 遍历所有非@PointCut的方法,获取@Before、@After等注解中的表达式切点,加上当前方法(也就是代理逻辑)一起封装为一个Advisor for (Method method : getAdvisorMethods(aspectClass)) { Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { advisors.add(advisor); } } // If it's a per target aspect, emit the dummy instantiating aspect. if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // Find introduction fields. // 处理DeclareParents注解(这个没用过,就不看了) for (Field field : aspectClass.getDeclaredFields()) { Advisor advisor = getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; }
上面getAdvisorMethods
是获取到所有的非@Pointcut
的方法,然后在循环再判断是否是配标注了@Before、@After
等这些注解的。
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); // 得到当前candidateAdviceMethod方法上的所定义的expression表达式,也就是切点 AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut == null) { return null; } // 构造一个Advisor,封装了切点表达式和当前方法 return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
上面代码,是在获取到被注解标注的切点信息,如:@Before(value = “execution(* com.lry.aop.*.play(…))”)
而下面这个代码是获取到方法上的注解信息,并解析成切入点对象。
代码语言:javascript复制private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
// 获取当前方法上是否存在Pointcut、Around, Before, After, AfterReturning, AfterThrowing注解
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// 注解中所配置的表达式
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (this.beanFactory != null) {
ajexp.setBeanFactory(this.beanFactory);
}
return ajexp;
}
上面步骤最终是将解析的切入点信息保存为InstantiationModelAwarePointcutAdvisorImpl
对象,添加到advisor列表里。
那么它是怎么匹配到我们的pointCut的,上面的匹配被我忽略了,是因为,他们可以根据方法名称去匹配到,但是这里用了注解,使用的是表达式,具体看:AopUtils#canApply
这个方法是找出所有advisor后,进行筛选动作的一个判断,这个里面就是进行了一些匹配操作
代码语言:javascript复制public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
// 这里好像PointcutAdvisor的类都是true
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
// 这里是我们使用@EnableAspectJAutoProxy后,通过注解扫描生成的对象
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
// 遍历targetClass以及targetClass的父类和接口中的所有方法
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
// 表达式的匹配,具体的就是在这个方法里进行匹配的。
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
流程梳理
- 配置注解:@EnableAspectJAutoProxy
- 配置了Aspect类,即标注了@Aspect注解的aop类,有@Before这些注解的类
- 启动spring,
- 扫描到@EnableAspectJAutoProxy注解,执行
AnnotationAwareAspectJAutoProxyCreator
注册 - 两个地方调用后置处理器方法,
- 判断当前bean是否需要代理
- 判断当前bean是否需要aop
- 获取advisor
- 获取原始对象的beanType
- 通过beanType判断是否有@Aspect注解
- 查找被@Before、@After这些注解,进行解析,然后生成InstantiationModelAwarePointcutAdvisorImpl(PointCutAdvisor实现类)添加advisor
- 处理
@DeclareParents
注解,添加advisor
- 判断是用cglib
- beanFactory.getBean拿到对应的advisor
- 生成代理对象
- 返回并添加单例池
它和BeanNameAutoProxyCreator不同的是获取advisor的方式不同个,它自己实现了自己的获取方法。
总结
- 我们所用的注解方式(@Before)怎么实现代理的?
- aop 用的什么动态代理?
- aop 是否可以不用注解?
我们所用的注解方式(@Before)怎么实现代理的?
- 使用@EnableAspectJAutoProxy,该注解上标注了@Import,所以在spring启动扫描时,会将@Import导入的类注册,这个类属于后置处理器,在bean初始化完后会调用这个处理器,
- 处理器方法中:查找到标注了@Aspect注解的类,然后解析方法上的注解,生成一个pointCutAdvisor的子类,
- 判断是否用cglib
- beanFactory.getBean
- 生成代理对象
aop 用的什么动态代理?
aop中用到了两种代理:jdk的动态代理,cglib代理;
使用jdk的动态代理,需要有接口,要注意的是,有实现的接口的原始对象生成代理对象后,不能强转为子类,因为是同一个接口,但不是同一个类。
aop 是否可以不用注解?
aop不用注解实现自动代理,有两种方式,一是注册BeanNameAutoProxyCreator,二是注册DefaultAdvisorAutoProxyCreator。
第一种需要手动设置要代理的beanName,和设置代理的advisor的名称,而第二种是自动的查找代理,那么就会将所有匹配的方法代理。
还有就是非自动代理的方式,使用spring提供的ProxyFactory,这个比起上面两种不是很有应用场景,毕竟这种方式比较底层,而且代码量多,甚至可能出现错误。