簡述
AOP是面向切面編程(Aspect-Oriented Programming)的簡稱。它不是一項技術,和平常說的OOP(Object-Oriented Programming)一樣,它是一種編程思想。這里不再做更多的名詞解釋。上圖:
從這個丑陋的圖中可以看出,利用AOP后,OOP的Objects 都可以只專注於自己的事情,而不需要去管用戶是否登錄以及記錄本次操作日志的事情了。 而且關於用戶的判斷以及日志記錄的代碼也只需要一份,不再關心需要將這些額外的操作加在哪里。
實現
aop的實現主要有兩種方式,一種是通過回調函數,另一種是代理。
1、回調
web中常見的通過回調的方式實現的aop有Filter(過濾器)和Interceptor(攔截器)。首先附上一張圖,看一下在運用springMVC時,一個請求的部分生命周期。
(1)、客戶端發起請求到服務器,服務器(這里以tomcat為例)會接受到請求,經過內部一系列包裝以后,會生成編程時候需要用到的HttpServletRequest和HttpServletResponse。這其中的包裝細節,這里不多說,貼個學習地址:https://blog.csdn.net/sunyunjie361/article/details/60126398。
(2)(11)(3)(10)、tomcat在StandardWrapperValve.invoke()方法中並不是直接調用dispatherServlet(分發器),而是將項目中注冊的FilterList和dispatherServlet一起構造一個ApplicationFilterChain對象,再調用filterChain.doFilter(request,response)。在FilterChain中,會依次回調所有的Filter的doFilter方法,每個方法又通過調用FilterChain的doFilter來繼續往下走,直到調用DispathServlet后,再逆向執行每個fitler中在調用FilterChain的doFilter后的代碼。邏輯大致分這么幾部,但具體的實現這里不做具體研究。有機會再寫一篇關於tomcat的文章再具體說這個。
這個FilterChain是個挺有意思的算法,簡單版本的:
public class myChain implements FilterChain { int pos = 0; List<Filter> list = new ArrayList<Filter>(); @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { Filter filter = list.get(pos++); filter.doFilter(request,response,this); } } class MyFitler implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request,response); } @Override public void destroy() { } }
Filter(SpringBoot版本)示例:
@WebFilter @Order(Ordered.HIGHEST_PRECEDENCE) public class CrosFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse httpServletResponse = (HttpServletResponse) response; //這里填寫你允許進行跨域的主機ip httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); //允許的訪問方法 httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH"); //Access-Control-Max-Age 用於 CORS 相關配置的緩存 httpServletResponse.setHeader("Access-Control-Max-Age", "3600"); httpServletResponse.setHeader("Access-Control-Allow-Headers", "content-type,sessionid,x-requested-with"); if(!HttpUtils.isOptionsRequest(request)){ filterChain.doFilter(request, response); } } @Override public void destroy() { } }
(4)(5)、在DispatherServlet第一次被調用的時候,它會先執行initStrategies(context) 方法,改方法會初始化分發器所需要的一些參數。包括了HandleMappings。獲取需要初始化的handleMapping很簡單,直接調用BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);方法,這個方法返回注冊在SPringIOC中且實現了HandlerMapping接口的對象。當然handleAdapter也是這樣做的。
當DispatherServlet執行doService時,它會遍歷的HandleMapping,依次調用每個handlerMapping的getHandler()。每個handleMapping根據自己的需求重寫getHandle或者getHandlerInternal()方法。在該方法中,根據reqeust判斷是否是該自己處理的請求,如果不是,返回null,遍歷繼續,如果是自己該處理的請求,則返回this,同時遍歷也就結束了。當然這里有個重要的事情就是:如果對於同一個Request有多個HandleMapping符合時,只會執行第一個。而第一個也不是根據注冊時的順序來的,是根據AnnotationAwareOrderComparator.sort(this.handlerMappings)方法來排序。這個方法其實就是@Order注解了。
HandlerExecutionChain在返回之前,會進行一些配置,其中就包括了適配Interceptor。這里以RequestMappingHandlermapping(springMVC中用於處理Controller的處理器)為例,它會根據url匹配規則,HandlerExecutionChain中會保存所有matches方法返回true的MappedInterceptor和直接實現HandleInterceptor的Interceptors。實現的具體請看代碼:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
下面是adaptedInterceptors列表的初始化
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( getApplicationContext(), MappedInterceptor.class, true, false).values()); }
它會加載所有的MappedInterceptor以及其子類的SpringBean。然而,我們並不能簡單的通過編寫一個繼承自MappedInterceptor的實體類來實現一個攔截器,因為MappedInterceptor.class是用final修飾的。這是一個裝飾者模式的設計。所以,我們需要兩部來編寫一個Interceptor。首先編寫一個實現了HandlerInterceptor接口的類,然后將其一個實例加上urlPattern來裝飾為一個mappedInterceptor,並交由springIOC管理即可。
當然也可以用xml配置的方式來實現,讓容器在啟動時就加載Interceptor,這樣就省去配置mappedInterceptor了:
<mvc:interceptors> <!--定義一個handleInterceptor , 攔截所有的請求 --> <bean class="com.host.app.web.interceptor.AllInterceptor"/> <!--定義一個mappedInterceptor 裝飾了path屬性-->
<mvc:interceptor> <mvc:mapping path="/test/number.do"/> <bean class="com.host.app.web.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>
當然,最終SpringMVC還是會把handleInterceptor變成mappedInterceptor。
protected void initInterceptors() { if (!this.interceptors.isEmpty()) {
//遍歷handleInterceptor for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); }
//通過adaptInterceptor方法將handlerInterceptor轉化為MappedInterceptor,並加入到adaptedInterceptors中 this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } }
在SpringBoot中,我們也有兩種方式配置Interceptor,第一種還是配置mappedInterceptor,第二種是自定義WebMvcConfigurerAdapter適配器,在適配器中添加Interceptor。
@SpringBootApplication @EnableTransactionManagement @ServletComponentScan public class DemoApplication extends WebMvcConfigurerAdapter { protected static final Logger logger = LoggerFactory.getLogger(DemoApplication.class);
//通過重寫這個方法來添加攔截器 @Override public void addInterceptors(InterceptorRegistry registry) {
//添加一個攔截器,並為它配置urlPath。 這是java中不常用的火車頭式寫法,不要被迷惑,注意addInterceptor方法的返回值 registry.addInterceptor(new ValidatorInterception()).addPathPatterns("/**"); registry.addWebRequestInterceptor(new TestWebRequestInterceptor()).addPathPatterns("/**"); super.addInterceptors(registry); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); logger.error("CONGRATULATIONS!! demo effective!"); } }
(6)(7)(8)(9) 在DispatherServlet中,獲得了HandlerExecutionChain以后,通過回調來實現AOP,代碼如下:(去掉了其他與攔截器無關的代碼,源碼在DispatherServlet.doDispatch()方法中。)
try { ModelAndView mv = null; Exception dispatchException = null; try {//獲取handler mappedHandler = getHandler(processedRequest);//獲取adapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 執行所有Interceptor的preHandle()方法 對應圖中第6步
if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 通過適配器執行contoller 對於圖中第 7 、8在這方法中執行,這里不具體研究 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //通過視圖解析器解析modleAndView 對應途中第9步
applyDefaultViewName(processedRequest, mv); //執行所有Interceptor的PostHandle()方法 也在第9步
mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { ... } }catch (Throwable err) { ... } finally { //執行Interceptors的afterCompletion()方法
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); }
通過代碼發現,handleInterceptor接口中的三個方法都有被回調一次;其中:afterCompletion方法是一定會執行的,一般情況下preHandle方法也會執行,但PostHandle方法則不一定執行。
示例:
public class ValidatorInterception implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { if(HttpUtils.isOptionsRequest(httpServletRequest)){ return true; } HandlerMethod method = (HandlerMethod)o; Method method1 = method.getMethod(); List<Object> objs = new ArrayList<Object>(); MethodParameter[] methodParameters = method.getMethodParameters(); for(int i = 0 ; i < methodParameters.length; i++){ MyValidator parameterAnnotation = methodParameters[i].getParameterAnnotation(MyValidator.class); String parameter = httpServletRequest.getParameter(methodParameters[i].getParameterName()); if(!Objects.isNull(parameterAnnotation)){ } } Parameter[] parameters = method1.getParameters(); for(int i = 0 ; i < parameters.length; i++){ MyValidator annotation = parameters[i].getAnnotation(MyValidator.class); if(!Objects.isNull(annotation)){ /* objs.add()*/ } } return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
通過回調的方式,這里主要講的就是Filter和Interceptor,從以上可以發現一個問題,那就是通過回調的方式實現的AOP需要一個回調的步驟,耦合度較高,如果框架沒有提供這個回調的過程,那我們編寫的時候是很難做到的。如果覺得這種方式有點low,那接下來我們就討論一下另一種實現方式:
2、代理
首先說一下代理模式:
定義:為另一個對象提供一個替身或者占位符以控制對這個對象的訪問。
名詞解釋啥的,本人不擅長。上面這句話引用自《Head First 設計模式 》。多說無益,上代碼:
//代理對象和實際對象一樣都繼承共同接口
public class PersonBeanProxy implements Person{ //代理對象
PersonBean personBean; public SimpleProxy(PersonBean personBean) { this.personBean = personBean; } //控制對象的訪問 public void say() { personBean.say(); } } interface Person{ void say(); } //被代理對象的類 class PersonBean implements Person{ String name; public PersonBean(String name) { this.name = name; } @Override public void say() { System.out.println("my name is "+name); } }
這是靜態代理的一個簡單例子,這個模式很容易和裝飾者模式以及適配器模式混淆,其實細細品味,概念上還是能分出個所以然來。筆者覺得這需要個人理解,很難解釋。
嗯哼~ 大好時光,何必糾結這些名詞解釋,說點實在的。在java中,動態代理模式已經有兩種現成的實現,一種是jdk自帶的(是不是很激動,jdk自帶了,不用糾結怎么去實現了),而另一種就是CGlib了。至於動態代理的具體實現,這里就不說了。后面會出SpringIOC相關知識,再細說代理。接下來,我們直接說spring如何運用代理來實現AOP的。
SpringAop
這里簡單花了一下springAop實現的流程圖:
默認情況下springIOC再啟動后會利用DefaultListableBeanFactory來實例化所有的bean。在實例化bean的時候,springIOC中會有多個beanPostProcessors(實例化bean后的處理器),也就是說大部分的bean實際上都是經過代理后的代理對象,而不是實際的對象。所以在debug調試中經常會看到一些bean的名字結尾是$proxy或者$cglibProxy等等,說明它們都是經過包裝后的代理類對象。源碼再DefaultListableBeanFactory.class的父類AbstractAutowireCapableBeanFactory.class中:
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
在眾多的處理器中,包括了一個AnnotationAwareAspectJAutoProxyCreator,它其實也只是眾多代理bean類處理器中的一個,它主要用來助理@AspectJ自動代理的處理器。在它的內部存儲了用@AspectJ注解標注的advisor(通知器),這里有個有趣的事情,其實這些處理器和通知器本身也是Bean,后面學習SpringIOC的時候,再細細研究這個。
這里給出部分類圖:
在AnnotationAwareAspectJAutoProxyCreator內,會先構建一個ProxyFactory對象。源碼:
ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); for (Advisor advisor : advisors) { proxyFactory.addAdvisor(advisor); } proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); }
ProxyFactory內部持有一個DefaultAopProxyFactory對象,利用DefaultAopProxyFactory.createAopProxy(AdvisedSupport config)來創建代理對象。源碼:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { 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."); } if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
這里第一個判斷:代理服務器設置是否應該執行積極的優化,第二個判斷:是否啟動cglib,第三個為判斷:bean是不是繼承了SpringProxy。其中前兩個判斷可以利用參數來配置:
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean" p:interceptorNames="before" p:target-ref="userServiceImpl" p:optimize="false" p:proxyTargetClass="false" > </bean>
當然還有其他的一些判斷,主要是因為兩種aop的實現是不同的,jdk代理是基於接口,也就是說生成的代理類是實現了傳入的接口,而cglib是基於類的,它生成代理類是繼承了傳入的類。所以兩種實現所需的參數不同,也就適用於不同的情況。
先了解一下jdk版本的。Proxy類提供了newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) 方法來獲取代理對象。 它返回的對象實現了接口的所有方法,這樣對這個代理對象的訪問就像訪問一個實例一樣簡單,而每個方法的具體實現,實際上jdk的Proxy是沒有的。它並不會去管代理對象的方法該實現怎樣的邏輯,而是通過調用傳入的InvocationHandler 的的invoke方法來執行具體的細節。而invoke方法是需要用戶自己實現的。當然在spring框架中,spring是已經實現了的。源碼:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Class<?> targetClass = null; Object target = null; try { Object retVal;// Get the interception chain for this method. List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // We need to create a method invocation... invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); }return retVal; } finally { ... }
這里就會執行我們aop中的代碼了。由於spring的aop是可以多層的嵌套的,這里也是通過鏈式調用來完成逐層調用。
然后來看下spring自己的cglilb實現:個人感覺cglib的實現就是比較暴力的了,它先創建Enhancer 對象,源碼:
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 ClassLoaderAwareUndeclaredThrowableStrategy(classLoader)); Callback[] callbacks = getCallbacks(rootClass);
然后直接生產字節碼的類文件,再調用classCLoad加載該類,然后生產一個代理對象。生產的類文件我還沒辦法找到,不過收藏了一篇 https://www.jianshu.com/p/9a61af393e41?from=timeline&isappinstalled=0
@Aspect @Configuration public class ValidatorAop { @Autowired Validator validator; @Pointcut("execution(public * com.example.controller..*.*(..))") public void validate(){} @Before("validate()") @After("") @Around("") public void validating(JoinPoint point)throws Throwable{ Object[] args = point.getArgs(); Method method = ((MethodSignature) point.getSignature()).getMethod(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); Before annotation1 = method.getAnnotation(Before.class); annotation1.value(); Parameter[] parameters = method.getParameters(); for(int i = 0 ; i < parameters.length; i++){ Parameter parameter = parameters[i]; Object value = args[i]; MyValidator annotation = parameter.getAnnotation(MyValidator.class); if(!Objects.isNull(annotation)){ ValidatorByType(args, i, value, annotation); } } } private void ValidatorByType(Object[] args, int i, Object value, MyValidator annotation) { if(Map.class.isAssignableFrom(value.getClass())){ Map<Object,Object> newValue = (Map<Object,Object>) value; for(Map.Entry<Object,Object> entry : newValue.entrySet()){ Object value1 = entry.getValue(); if(!value1.getClass().isPrimitive() && !(value1 instanceof String)){ validatedUseHibernateValidator(value1, annotation); } } }else if(Collection.class.isAssignableFrom(value.getClass())){ Collection newValue = (Collection) value; Iterator iterator = newValue.iterator(); while (iterator.hasNext()){ validatedUseHibernateValidator(iterator.next(), annotation); } }else{ validatedUseHibernateValidator(args[i], annotation); } } private void validatedUseHibernateValidator(Object value, MyValidator annotation) { Set<ConstraintViolation<Object>> validate = validator.validate(value, annotation.value()); if(!CollectionUtils.isEmpty(validate)){ throw new ConstraintViolationException(validate); } } }
兩種實現的總結:
不管是通過方法回調還是代理的方式,再使用的時候都需要遵守一些規范,Fitler和Interceptor都需要實現指定的接口,而代理的方式是通過注解來注冊通知器,但是代理的方式就顯得特別靈活,可以隨意控制攔截的目標,而回調的方式攔截的只能是再回調方法調用的地方了。但對於新手來說,攔截器和過濾器如果用代理的方式來實現的話,壓根就不知道該攔截誰,這就很尷尬。即使初級開發,也很多不知道該如何去攔截。所以Filter和Interceptor是框架為我們提供的一個便利,不讓我們迷失在框架的源碼中。