spring的AOP

 

目录

一、动态代理模式

2、spring实现AOP的流程


一、动态代理模式

        首先我们先聊一下动态代理,先从一个案例开始讲起:小明是一个程序员,每天都要996,没时间找女朋友,然后他的父母帮他找对象,然后找到之后再结婚生子。        

        这个场景中:

  •     小明是被代理对象
  •     小明的父母:小明的代理,帮助小明找对象
  •     是对小明的一个功能增强:小明没时间找对象,找到之后帮他结婚生子

        动态代理中有一下几个内容:

  •         被代理对象
  •         代理对象
  •         增强、通知

使用jdk动态代理来实现:

      1、先定义一个接口

public interface People{
   void findMM();
}

      2、小明

public class Xiaoming implements People{

   @Override
   public void findMM() {
      System.out.println("小明经常加班到很晚,没时间找女朋友,需要找个mm来释放他的双手");
   }
}

      3、小明的父母

package com.chen.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 增强
 */
public class Parent implements InvocationHandler {

   private People people;

   public Parent(People people) {
      this.people = people;
   }

   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      beforeMethod();
      method.invoke(people);
      afterMethod();
      return null;
   }

   private void afterMethod() {
      System.out.println("父母帮小明带小孩");
   }

   private void beforeMethod() {
      System.out.println("父母帮小明找对象");
   }
}

      4、编写测试类

package com.chen.proxy;

import java.lang.reflect.Proxy;
public class Test {
   public static void main(String[] args) {
      Xiaoming xiaoming = new Xiaoming();
      //使用Appclassloader去加载class文件
      People o = (People) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class<?>[]{People.class}, new Parent(xiaoming));
      o.findMM();
   }
}

那现在有个问题:Parent implements InvocationHandler,invoke方法是谁调用的呢?

首先Proxy.newProxyInstance用来创建代理对象,其中有三个参数:

  •         第一个参数是类加载器
  •         第二个参数是代理类要实现的接口列表
  •         第三个参数是代理类(实现InvocationHandler)

我们使用一副图来整理下调用的过程

然后看$Proxy0.class文件的代码如下:

public final class $Proxy0 extends Proxy implements People {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void findMM() throws  {
        try {
            //h其实就是Parent对象,这里会调Parent的invoke方法
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.chen.myspring.proxy.People").getMethod("findMM");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

newProxyInstance这个方法会生成一个代理实例,那生成的步骤是什么?

  • 1、生成一个java类,通过字符串拼凑出来$Proxy0这个类,以.java结尾
  • 2、把字符串用流的方式写到$Proxy0.java文件
  • 3、把java文件编译成class文件
  • 4、把.class文件加载到jvm内存
  • 5、在内存里面执行,然后返回代理实例

2、spring实现AOP的流程

1)  首先要加上@EnableAspectJAutoProxy注解,@EnableAspectJAutoProxy Import了一个AspectJAutoProxyRegistar,然后将AnnotationAwareAspectJAutoProxyCreator注册到IOC容器中,beanDefinition的名称是org.springframework.aop.config.internalAutoProxyCreator,其中AnnotationAwareAspectJAutoProxyCreator继承了AbstractAutoProxyCreator。

源码如下:

	RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
	beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
	beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
	return beanDefinition;

2)   AnnotationAwareAspectJAutoProxyCreator实现了BeanProcessor,然后会在执行registerBeanPostProcessors的过程中实例化这个类

3) 在执行initializeBean方法的时候会调用到AnnotationAwareAspectJAutoProxyCreator,然后执行它的postProcessAfterInitialization,执行wrapIfNecessary,然后执行findEligibleAdvisors,首先会容器中查找Advisor的实现类,这是spring的一个扩展点

 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Advisor.class, true, false);

4) 然后从容器中获取所有的bean定义的名称,判断是否有@Aspect注解,如果有@Aspect注解的类,则获取并且遍历这个类中没有@Pointcut注解的方法 , 从注解中获取切点表达式,包装成Pointcut对象,创建一个Advisor切面类,Advisor=pointcut+advice,然后根据注解生成不同的对象:AspectJAroundAdvice , AspectJAfterAdvice , AspectJAfterThrowingAdvice , AspectJMethodBeforeAdvice,AspectJAfterReturningAdvice,即一个pointcut对应多个advice,然后将得到的advice和pointcut包装成Advisor之后,添加到Advisor的容器中。判断当前的bean是否可以匹配候选的Advisors,即根据ClassFilter(判断当前bean是否在表达式中)和MethodMatcher(获取bean的所有方法,看是否和Advisor中的MethodMathcer匹配)

5) 对Advisor进行执行顺序排序:

  1.   @Around前置
  2.   @Before
  3.   执行joinPoint.proceed();
  4.   @Around后置
  5.   @After

6) 创建代理

      1. 将被代理对象封装成TargetSource

      2. 创建代理工厂ProxyFactory对象,ProxyFactory包含目标对象,以及advisors,如果目标对象是接口走jdk动态代理,否则走cglib动态代理

      3. 比如我们是jdk动态代理,JdkDynamicAopProxy实现了InvocationHandler接口

      4. 然后调用JdkDynamicAopProxy类的getProxy方法,这里面会生成动态代理:

return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

 7) 在调用目标类的方法的时候,实际上会调用JdkDynamicAopProxy的invoke方法

MethodInvocation invocation =
	new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();


public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		//如果索引的值等于拦截器集合的长度-1的值就终止调用
		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 {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			//调用MethodInterceptor的invoke方法
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值