一、何为aop
AOP(Aspect-Oriented Programming)面向方面编程,可以说是OOP(Object-Oriented Programing)面向对象编程的补充和完善。AOP(面向切面编程)与OOP(面向对象编程)的区别是什么。其实AOP与OOP可以理解为不在同一层面上的两个独立的定义。也就是说其实这两个东西没法去做一个对等的比较,OOP专注于对象,我们利用对象的属性,行为来解决现实中的问题,而AOP则用来在使用OOP解决问题的过程中增强解决问题的能力,实现更好的模块化。
AOP编程思想,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
二、编程中的aop
aop并非在spring首先引入的,而是由来已久,他是前人智慧的结晶,仔细寻找就能发现其踪迹。
第一次接触servlet 过滤器 filter乱码的解决
默认的解决乱码的思路是在每个方法中写request.setCharacterEncoding("UTF-8");并且有其局限性,代码也不够简介明了,filter的出现打破了这个局限,在请求之前做响应的操作。
<filter> <filter-name>EncodingFilter</filter-name> <filter-class>com.mingxungu.filter.EncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>EncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>原理:通过过滤器拦截所有的请求,对中文乱码的解决通过一个拦截器类统一进行解决。原理图:
第二次接触Java的动态代理
代码:
//JDK动态代理 public class UserServiceProxyFactory implements InvocationHandler { public UserServiceProxyFactory(UserService us) { super(); this.us = us; } private UserService us; public UserService getUserServiceProxy(){ //生成动态代理 UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), this); //返回 return usProxy; } @Override public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable { //额外的责任 System.out.println("打开事务!"); //执行主体方法 Object invoke = method.invoke(us, arg2) //额外的责任 System.out.println("提交事务!");return invoke; } } 原理: 点击查看动态代理的原理invoke()主体方法,通过java的反射机制 Object invoke = method.invoke(us, arg2)获取执行主体方法,在前后加入与业务无关的额外的方法
原理图:
第三次接触struts拦截器Interceptor
我们发现在jsp中定义好name属性值,根据 对象名.属性名(user.name)的方式在Action中获取对象的时候其属性值已经被塞入其中。这不得不归功于Interceptor。
我们struts的一个重要功能之一就是参数赋值,我们就回顾一下其源码实现
@Override public String doIntercept(ActionInvocation invocation) throws Exception { Object action = invocation.getAction();//获取当前执行的Action对象 if (!(action instanceof NoParameters)) {//判断Action是否实现了NoParameters接口,实现该接口表示该Action没有任何请求参数 ActionContext ac = invocation.getInvocationContext();//获取ActionContext对象 final Map<String, Object> parameters = retrieveParameters(ac);//获取请求参数Map //省略... if (parameters != null) {//如果请求参数不为null Map<String, Object> contextMap = ac.getContextMap();//获取ActionContext内部的context Map,即OgnlContext对象 try { //省略... ValueStack stack = ac.getValueStack();//获取值栈 setParameters(action, stack, parameters);//为值栈设置参数 } finally { //省略... } } } return invocation.invoke();//调用下一个拦截器 } setParameters方法才是ParametersInterceptor拦截器的主要逻辑 protected void setParameters(Object action, ValueStack stack, final Map<String, Object> parameters) { ParameterNameAware parameterNameAware = (action instanceof ParameterNameAware) ? (ParameterNameAware) action : null;//判断Action有无实现ParameterNameAware接口 Map<String, Object> params; Map<String, Object> acceptableParameters;//合法参数集合 //判断参数设置是否有序,ordered默认为false,即无序 if (ordered) { params = new TreeMap<String, Object>(getOrderedComparator());//如果有序则要获取比较器 acceptableParameters = new TreeMap<String, Object>(getOrderedComparator()); params.putAll(parameters); } else { params = new TreeMap<String, Object>(parameters); acceptableParameters = new TreeMap<String, Object>(); } //迭代请求参数 for (Map.Entry<String, Object> entry : params.entrySet()) { String name = entry.getKey(); //判断参数是否合法,如果Action实现了ParameterNameAware则acceptableName(name)返回true且parameterNameAware.acceptableParameterName(name) //也返回true该参数才是合法的;如果Action没有实现ParameterNameAware则参数是否合法由acceptableName(name)方法决定 boolean acceptableName = acceptableName(name) && (parameterNameAware == null || parameterNameAware.acceptableParameterName(name)); //如果参数合法 if (acceptableName) { acceptableParameters.put(name, entry.getValue());//把合法参数添加到合法参数集合中 } } ValueStack newStack = valueStackFactory.createValueStack(stack); //省略... for (Map.Entry<String, Object> entry : acceptableParameters.entrySet()) {//迭代合法参数 String name = entry.getKey();//参数名 Object value = entry.getValue();//参数值 try { newStack.setValue(name, value);//将该参数设置到ValueStack中 } catch (RuntimeException e) { //省略... } } //省略... //看该方法的名称是将合法参数添加到ActionContext中,但在该拦截器中,该方法为空实现,无任何代码 //该方法被声明为protected,即子类可以覆盖该方法以改变行为 addParametersToContext(A原理:查看源码不难发现使用的是Interceptor拦截器获取request中的键值对和值栈中找有相应setter方法的对象进行赋值。原理图:
三、spring aop
首先了解其中几个名词
Pointcut (切入点):目标对象,已经增强的饭方法。
Joinpoint (连接点):目标对象中,所有可以增强的方法。
Advice(通知/增强):增强的代码。
Target (目标对象):被通知的对象。
Weaving(织入):将通知应用到切入点的过程。
Proxy (代理):将通知织入到目标对象之后,形成的代理对象。
Aspect(切面):切入点+通知
spring 的 aop的原理
spring使用动态代理来实现aop的。spring提供了两种方式来生成代理对象: JDK Proxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。下面我们来研究一下Spring如何使用JDK来生成代理对象,具体的生成代码放在JdkDynamicAopProxy这个类中,相关代码:
/** * <ol> * <li>获取代理类要实现的接口,除了Advised对象中配置的,还会加上SpringProxy, Advised(opaque=false) * <li>检查上面得到的接口中有没有定义 equals或者hashcode的接口 * <li>调用Proxy.newProxyInstance创建代理对象 * </ol> */ 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); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); } 我们知道InvocationHandler是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。而通过JdkDynamicAopProxy的签名我们可以看到这个类其实也实现了InvocationHandler,下面我们就通过分析这个类中实现的invoke()方法来具体看下Spring AOP是如何织入切面的。 public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable { MethodInvocation invocation = null; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Class targetClass = null; Object target = null; try { //eqauls()方法,具目标对象未实现此方法 if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){ return (equals(args[0])? Boolean.TRUE : Boolean.FALSE); } //hashCode()方法,具目标对象未实现此方法 if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){ return newInteger(hashCode()); } //Advised接口或者其父接口中定义的方法,直接反射调用,不应用通知 if (!this.advised.opaque &&method.getDeclaringClass().isInterface() &&method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations onProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args); } Object retVal = null; if (this.advised.exposeProxy) { // Make invocation available ifnecessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } //获得目标对象的类 target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } //获取可以应用到此方法上的Interceptor列表 List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass); //如果没有可以应用到此方法的通知(Interceptor),此直接反射调用 method.invoke(target, args) if (chain.isEmpty()) { retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args); } else { //创建MethodInvocation invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed(); } // Massage return value if necessary. if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy) &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned"this" and the return type of the method // is type-compatible. Notethat we can't help if the target sets // a reference to itself inanother returned object. retVal = proxy; } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come fromTargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } } 获取可以应用到此方法上的通知链(Interceptor Chain),如果有,则应用通知,并执行joinpoint; 如果没有,则直接反射执行joinpoint。而这里的关键是通知链是如何获取的以及它又是如何执行的,下面逐一分析下。 首先,从上面的代码可以看到,通知链是通过Advised.getInterceptorsAndDynamicInterceptionAdvice()这个方法来获取的,我们来看下这个方法的实现: public List<Object>getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) { MethodCacheKeycacheKey = 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); } returncached; }可以看到实际的获取工作其实是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()这个方法来完成的,获取到的结果会被缓存。
下面来分析下这个方法的实现: /** * 从提供的配置实例config中获取advisor列表,遍历处理这些advisor.如果是IntroductionAdvisor, * 则判断此Advisor能否应用到目标类targetClass上.如果是PointcutAdvisor,则判断 * 此Advisor能否应用到目标方法method上.将满足条件的Advisor通过AdvisorAdaptor转化成Interceptor列表返回. */ publicList getInterceptorsAndDynamicInterceptionAdvice(Advised config, Methodmethod, Class targetClass) { // This is somewhat tricky... we have to process introductions first, // but we need to preserve order in the ultimate list. List interceptorList = new ArrayList(config.getAdvisors().length); //查看是否包含IntroductionAdvisor boolean hasIntroductions = hasMatchingIntroductions(config,targetClass); //这里实际上注册一系列AdvisorAdapter,用于将Advisor转化成MethodInterceptor AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); Advisor[] advisors = config.getAdvisors(); for (int i = 0; i <advisors.length; i++) { Advisor advisor = advisors[i]; if (advisor instanceof PointcutAdvisor) { // Add it conditionally. PointcutAdvisor pointcutAdvisor= (PointcutAdvisor) advisor; if(config.isPreFiltered() ||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) { //TODO: 这个地方这两个方法的位置可以互换下 //将Advisor转化成Interceptor MethodInterceptor[]interceptors = registry.getInterceptors(advisor); //检查当前advisor的pointcut是否可以匹配当前方法 MethodMatcher mm =pointcutAdvisor.getPointcut().getMethodMatcher(); if (MethodMatchers.matches(mm,method, targetClass, hasIntroductions)) { if(mm.isRuntime()) { // Creating a newobject instance in the getInterceptors() method // isn't a problemas we normally cache created chains. for (intj = 0; j < interceptors.length; j++) { interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j],mm)); } } else { interceptorList.addAll(Arrays.asList(interceptors)); } } } } else if (advisor instanceof IntroductionAdvisor){ IntroductionAdvisor ia =(IntroductionAdvisor) advisor; if(config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) { Interceptor[] interceptors= registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } else { Interceptor[] interceptors =registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } return interceptorList; } 这个方法执行完成后,Advised中配置能够应用到连接点或者目标类的Advisor全部被转化成了MethodInterceptor.接下来我们再看下得到的拦截器链是怎么起作用的。 if (chain.isEmpty()) { retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args); } else { //创建MethodInvocation invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed(); } 从这段代码可以看出,如果得到的拦截器链为空,则直接反射调用目标方法,否则创建MethodInvocation,调用其proceed方法,触发拦截器链的执行,来看下具体代码 public Object proceed() throws Throwable { // We start with an index of -1and increment early. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()- 1) { //如果Interceptor执行完了,则执行joinPoint return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); //如果要动态匹配joinPoint if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher){ // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice; //动态匹配:运行时参数是否满足匹配条件 if (dm.methodMatcher.matches(this.method, this.targetClass,this.arguments)) { //执行当前Intercetpor returndm.interceptor.invoke(this); } else { //动态匹配失败时,略过当前Intercetpor,调用下一个Interceptor return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcutwill have // been evaluated statically before this object was constructed. //执行当前Intercetpor return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } } 部分代码参考:http://blog.csdn.net/moreevan/article/details/11977115/