1.Spring AOP是如何对目标对象的方法进行拦截的?
通过JDKProxy或CGLibProxy动态生成代理对象,当从外部调用目标对象的方法时,外部调用的对象为动态代理生成的Proxy对象。因此外部调用的方法实际为Proxy对象中的实现的方法,这个方法中包含了AOP中设置的Advice。然后通过反射机制调用目标对象的处理逻辑方法,从而达到对目标对象方法的增强。
如何判断是否执行增强的通知,在执行代理对象的方法时CglibAopProxy调用intercept(Object, Method, Object[], MethodProxy)方法,JDKProxy调用JdkDynamicAopProxy.invoke(Object, Method, Object[])方法,在这两个方法中我们可以看到,在调用目标对象的方法前会先去获取该目标对象方法的拦截器链:
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
获取拦截器链为空则直接调用目标对象的方法,拦截器链不为空则执行拦截器链中需要增强的通知方法,这样就达到了AOP动态增强的目标。
2.spring容器是如何初始化的?
首先,web容器在启动时会先初始化一个全局的ServletContext,为spring容器提供一个环境,web.xml中的配置的contextLoaderListener会监听web容器初始化事件,然后启动spring容器的初始化,它会读取web.xml中配置的context-param中的contextConfigLocation来初始化spring上下文默认为XmlWebApplicationContext。初始化根上下文后将其放入ServletContext中,这样在web容器中就可以拿到applicationContext。
IOC容器的初始化分为三个过程实现:
第一个过程是Resource资源定位。这个Resouce指的是BeanDefinition的资源定位。这个过程就是容器找数据的过程,就像水桶装水需要先找到水一样。
第二个过程是BeanDefinition的载入过程。这个载入过程是把用户定义好的Bean表示成Ioc容器内部的数据结构,而这个容器内部的数据结构就是BeanDefition。
第三个过程是向IOC容器注册这些BeanDefinition的过程,这个过程就是将前面的BeanDefition保存到HashMap中的过程。
3.内部方法调用AOP失效问题
内部方法调用时实际上是直接调用目标对象的方法,而不是调用代理对象的方法,因而就不会去执行代理对象中的拦截器链也就不会执行增强的通知方法。
4.拦截器拦截原理
Dispatchservlet在执行dodispatch方法时会根据request查询匹配的HandlerMapping,匹配请求URL的HandlerMapping会去查找所有拦截匹配该请求的拦截器,并将handler和所有的拦截器HandlerInterceptor组成处理执行器链HandlerExecutionChain返回给dispatchservlet。dispatchservlet在执行处理器的方法前先调用所有HandlerInterceptor的preHandle、postHandle、afterCompletion方法。