搜索词>>aop 耗时0.0040
  • Spring AOP 实现原理

    Spring AOP 实现原理基础讲解<h2>什么是AOP</h2> <p>AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。</p> <p> </p> <p>而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。</p> <p> </p> <p>使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”</p> <p> </p> <p>实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。</p> <p> </p> <h2>AOP使用场景</h2> <p>AOP用来封装横切关注点,具体可以在下面的场景中使用:</p> <p> </p> <p>Authentication 权限</p> <p>Caching 缓存</p> <p>Context passing 内容传递</p> <p>Error handling 错误处理</p> <p>Lazy loading 懒加载</p> <p>Debugging  调试</p> <p>logging, tracing, profiling and monitoring 记录跟踪 优化 校准</p> <p>Performance optimization 性能优化</p> <p>Persistence  持久化</p> <p>Resource pooling 资源池</p> <p>Synchronization 同步</p> <p>Transactions 事务</p> <p> </p> <h2>AOP相关概念</h2> <p>方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用spring的 Advisor或拦截器实现。</p> <p> </p> <p>连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。</p> <p> </p> <p>通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice</p> <p> </p> <p>切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上</p> <p> </p> <p>引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口</p> <p> </p> <p>目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO</p> <p> </p> <p>AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。</p> <p> </p> <p>织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯JavaAOP框架一样,在运行时完成织入。</p> <p> </p> <h2>Spring AOP组件</h2> <p>下面这种类图列出了Spring中主要的AOP组件<br /> <img alt="Spring AOP结构图" class="img-thumbnail" src="/assist/images/blog/1e67c550-0908-411b-a4e1-77f8eed8fddd.png" style="height:1770px; width:2206px" /></p> <h2>如何使用Spring AOP</h2>   <p>可以通过配置文件或者编程的方式来使用Spring AOP。</p> <p> </p> <p>配置可以通过xml文件来进行,大概有四种方式:</p> <p>1.        配置ProxyFactoryBean,显式地设置advisors, advice, target等</p> <p>2.        配置AutoProxyCreator,这种方式下,还是如以前一样使用定义的bean,但是从容器中获得的其实已经是代理对象</p> <p>3.        通过<aop:config>来配置</p> <p>4.        通过<aop: aspectj-autoproxy>来配置,使用AspectJ的注解来标识通知及切入点</p> <p> </p> <p>也可以直接使用ProxyFactory来以编程的方式使用Spring AOP,通过ProxyFactory提供的方法可以设置target对象, advisor等相关配置,最终通过 getProxy()方法来获取代理对象</p> <p> </p> <p>具体使用的示例可以google. 这里略去</p> <p> </p> <h2>Spring AOP代理对象的生成</h2>   <p>Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。下面我们来研究一下Spring如何使用JDK来生成代理对象,具体的生成代码放在JdkDynamicAopProxy这个类中,直接上相关代码:<br />  </p> <pre> <code class="language-java">/** * <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); } </code></pre> <p>那这个其实很明了,注释上我也已经写清楚了,不再赘述。</p> <p> </p> <p>下面的问题是,代理对象生成了,那切面是如何织入的?</p> <p>我们知道InvocationHandler是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。而通过JdkDynamicAopProxy的签名我们可以看到这个类其实也实现了InvocationHandler,下面我们就通过分析这个类中实现的invoke()方法来具体看下Spring AOP是如何织入切面的。<br />  </p> <pre> <code class="language-java">publicObject 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); } } } </code></pre> <p>主流程可以简述为:获取可以应用到此方法上的通知链(Interceptor Chain),如果有,则应用通知,并执行joinpoint; 如果没有,则直接反射执行joinpoint。而这里的关键是通知链是如何获取的以及它又是如何执行的,下面逐一分析下。</p> <p> </p> <p>首先,从上面的代码可以看到,通知链是通过Advised.getInterceptorsAndDynamicInterceptionAdvice()这个方法来获取的,我们来看下这个方法的实现:<br />  </p> <pre> <code class="language-java">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; } </code></pre> <p>可以看到实际的获取工作其实是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()这个方法来完成的,获取到的结果会被缓存。</p> <p>下面来分析下这个方法的实现:<br />  </p> <pre> <code class="language-java">/** * 从提供的配置实例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; } </code></pre> <p>这个方法执行完成后,Advised中配置能够应用到连接点或者目标类的Advisor全部被转化成了MethodInterceptor.</p> <p> </p> <p>接下来我们再看下得到的拦截器链是怎么起作用的。</p> <pre> <code class="language-java">if (chain.isEmpty()) { retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args); } else { //创建MethodInvocation invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed(); } </code></pre>    从这段代码可以看出,如果得到的拦截器链为空,则直接反射调用目标方法,否则创建MethodInvocation,调用其proceed方法,触发拦截器链的执行,来看下具体代码 <pre> <code class="language-java">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); } } </code></pre> <p>代码也比较简单,这里不再赘述。</p>
  • Spring AOP为何诞生

    Spring AOP来由,为何会出现Spring AOP这样的框架? 上一篇从Web开发演进过程的一个侧面简述了一下为什么会有Spring框架?事实上只介绍了为什么会有Spring IOC(控制反转/依赖注入)以及Spring IOC的雏形。我们知道Spring的两个核心知识点是:IOC和AOP。因此,这一篇还是以Web开发演进过程为线索继续探讨一下为什么会有Spring AOP?等介绍完这两个核心的知识点之后,才会进一步展开对Spring核心原理的探讨!<h2>引言</h2>     上一篇从Web开发演进过程的一个侧面简述了一下为<a rel="" target="_blank"href="http://www.leftso.com/blog/335.html" rel="" target="_blank">什么会有Spring框架?</a>事实上只介绍了为什么会有Spring IOC(控制反转/依赖注入)以及Spring IOC的雏形。我们知道Spring的两个核心知识点是:IOC和AOP。因此,这一篇还是以Web开发演进过程为线索继续探讨一下为什么会有Spring AOP?等介绍完这两个核心的知识点之后,才会进一步展开对Spring核心原理的探讨!<br />   <h2><strong>一、Web开发演进到一定阶段的痛点</strong></h2> <p>我们在初学习Java Web的时候,应该都经历了以下的阶段:</p> <p>(1)一个主函数main中包含了所有的方法; <br /> (2)将主函数中的方法进行拆分封装,抽取为一个个的方法; <br /> (3)按照每一个方法不同的功能分为一个个的类; <br /> (4)有了MVC模型之后,我们按照MVC的思想将我们的代码拆分为三层,每层负责不同的功能,进行分门别类的管理;</p> <p>很多程序的功能还可以通过继承关系而得到重用,进一步提高了开发效率。再后来,又出现了各种各样的设计模式,使设计程序功能变得得心应手。</p> <p>在面向对象的大环境下,我们可以很好地组织代码,通过继承、封装和多态的思想去设计一个个比较让人满意的类,但是我们慢慢的发现,我们的代码中逐渐多了很多重复性的代码,有人可能会想到,把这些重复性的代码抽取出来不就好了吗?是这样的,我们看一下这种思路的一个实例:<br /> <img alt="操作实例" class="img-thumbnail" src="/assist/images/blog/77597820f9204bc8892a8c54a39aa026.png" /></p> <p>    可以看到,上述代码功能上确实可以实现,但是我们的业务代码已经被这些非核心的代码所混淆,并且占据了大量的空间!显然这种显示的调用过程成为了我们开发过程中的一个痛点,如何将类似这种的非核心的代码剥离出去成为一个迫切需要解决的问题!</p> <p>    不仅如此,假设我们要控制每一个方法的访问权限,只允许一部分用户进行访问,在不考虑过滤器的情况下,我们是不是需要在每一个方法开始的时候判断用户是否具有该权限,如果有的话就可以进行访问,如果没有的话,就不允许进行访问!</p> <p>    诸如此类,还有数据库事务的控制,数据库连接的创建和关闭等等,这些都充斥这大量重复性的模板代码!一个很现实的问题,假如有一天,业务需求不需要进行日志记录了,那岂不是我们需要把以前写的代码,全部删掉!想想都是一件很可怕的事情!<br /> <br />  </p> <h2><strong>二、使用设计模式进行一次改进</strong></h2> <p>    如果你对设计模式玩的比较熟的话,这个时候你可能会想到使用<strong>JDK动态代理设计模式</strong>(动态代理设计模式可以在原有的方法前后添加判断、选择或其他逻辑)对上述代码进行改进,(关于什么是JDK动态代理,这里不再详细赘述,有不懂的的可以查阅相关资料具体了解一下!)修改后的代码如下:<br /> <img alt="Spring Aop实例代码片段1" class="img-thumbnail" src="/assist/images/blog/6345b45100d740f5bff96e5bb827c1a7.png" /><br /> 上述为代理类,红色框中圈出的表示以前业务中的模板代码,这里直接输出表示方法执行的过程,以前的UserServiceImpl修改为如下(直接用输出的方式表示方法执行了):<br /> <img alt="Spring Aop实例代码片段2" class="img-thumbnail" src="/assist/images/blog/4992beb9fe9749bcabc1877a68217a37.png" /><br /> 测试代码如下:<br /> <img alt="Spring Aop实例代码片段3" class="img-thumbnail" src="/assist/images/blog/c243721d002947199bae526adb3d78d5.png" /><br />  </p> <p>    上述的执行结果可以看出,每次调用一个方法的时候前后都会调用我们期望的代码,实现了我们期望的标准!</p> <p>    通过JDK动态代理的方式,让我们彻底的解放出来了!</p> <h2><strong>三、撕开披在AOP身上的一层薄纱</strong></h2> <p>    上述过程中,我们看到在<strong>动态代理的<code>invoke</code></strong>方法里边,我们相当于在原有方法的调用前后“<strong>植入</strong>”了我们的通用日志记录代码,如果你看到这一层的话,那么恭喜你!你已经领悟到了AOP思想最核心的东西了!</p> <p>    上述抽取公共代码其实就是AOP中<strong>横切</strong>的过程,代理对象中在方法调用前后“<strong>植入</strong>”自己写的通用日志记录代码其实就是AOP中<strong>织入</strong>的过程!这个织入的代码也就是<strong>横切逻辑</strong>,织入代码的过程其实就是在原有的方法前后<strong>增强</strong> 原方法的过程!总的来说,我们想解决我们开发中的痛点,然后就出现了一种技术,这种技术手段就是AOP。</p> <p>AOP书面表述如下:<br /> <br />     AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容(Spring核心之一),是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。<br />  </p> <h2><strong>四、AOP与Spring AOP的关系</strong></h2> <p>    AOP是一种思想,不同的厂商或企业可能有不同的实现方式,为了更好的应用AOP技术,技术专家们成立了AOP联盟来探讨AOP的标准化,AOP联盟定义的AOP体系结构把与AOP相关的概念大致分为由高到低、从使用到实现的三层关系,AOP联盟定义的AOP体系结构如下图:<br /> <img alt="AOP联盟定义的AOP结构图" class="img-thumbnail" src="/assist/images/blog/0411549085a543a3b156bfff159b1a73.png" /><br />  </p> <p>    在AOP联盟定义的AOP体系结构下有很多的实现者,例如:AspectJ、AspectWerkz、JBoss AOP、Spring AOP等。Spring AOP就是在此标准下产生的,这里不再深入Spring AOP的其他概念,这些概念会在后期探讨。</p> <h2><strong>五、其他问题</strong></h2> <p>    上述通过动态代理的方式实现了简单的AOP,但是值得注意的是,我们的代理目标对象必须实现一个接口,要是一个接口的实现类,这是因为再生成Proxy对象的时候这个方法需要一个目标对象的接口:<br />  </p> <p><img alt="Spring Aop实例代码片段4" class="img-thumbnail" src="/assist/images/blog/08942f7d864949a996120c9e0cde7037.png" /><br />  </p> <p>    显然,有些特殊的场景使用JDK动态代理技术的话,已经不能够满足我们的场景了,又遇到痛点了!凡事不劳我们操心的Spring框架已经替我们想到了,既然你有这种需求,我就使用一种技术帮你实现就行了,Spring在这里使用CGLib动态代理的方式实现了我们的这种诉求。</p> <p>CGLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势的织入横切逻辑。</p> <p>    看到这里,我们会想以后会不会还有CGLib解决不了得问题啊?我们已经很清楚的知道了对于Spring AOP来说,使用到了JDK动态代理技术和CGLib动态代理技术,这两种方式已经实现了我们绝大多数的场景,如果还有不能满足的需求,迫切需要解决的痛点,我相信Spring还会采用相应的技术来满足这些场景。</p> <h2><strong>六、总结</strong></h2> <p>    上述的过程,大致从一个侧面探讨了一下我们为什么需要AOP,AOP与Spring AOP的关系以及Spring AOP两种实现的方式(JDK动态代理和CGLib动态代理)。</p> <p>    Spring不尝试提供最为完善的AOP实现,它更侧重于提供一种和Spring IOC容器整个的AOP实现,用于解决实际的问题,在Spring中无缝的整合了Spring AOP、Spring IOC和AspectJ。</p> <p>    当然,Spring AOP的内容不仅仅有这些!例如:我们在使用Spring AOP的时候只是简单的配置了一下(通过XML或注解进行配置),没有像<code>ProxyDemo</code>测试类中的那样,还需要我们手动的调用<code>ProxyFactory</code> 来创建代理对象,然后调用我们的目标方法,其实Spring AOP在内部已经帮我们把这些事情做好了,具体的原理后期会继续探讨。另外,Spring如何整合Spring IOC和AOP的,这一点也会在后期探讨。</p> <p>    最后补充一下!动态代理或者设计模式重要吗?很重要!Spring AOP用到了动态代理,Spring事务管理用到了动态代理,MyBatis数据库连接池用到了动态代理,MyBatis创建Mapper用到了动态代理等等,你说重要不!要想踏进这些高层框架原理的大门,设计模式首先是我们的第一段台阶!</p> <p> </p>
  • spring mvc aop对controller切面编程无效解决

    spring mvc aop对controller切面编程无效解决,在初步使用Spring MVC框架中使用切面的时候可能会遇到AOP对controller层切面无效。这其实是spring容器的原因。下面将详细讲解。<h2>引言</h2>     在初步使用Spring MVC框架中使用切面的时候可能会遇到AOP对controller层切面无效。这其实是spring容器的原因。下面将详细讲解。切面配置没有问题的情况下,junit单元测试调用controller里面的方法,可以触发切点,实现切面编程。但是web部署到tomcat后,直接url访问触发切点失败! <h2><br /> 问题起因</h2> 经查看,项目中有两个配置文件。 <ul> <li>spring-mvc.xml</li> <li>applicationContext.xml </li> </ul> <h5>applicationContext.xml 部分代码:</h5> <pre> <code class="language-xml"><!-- 自动扫描项目下面的包 ,将带有注解的类 纳入spring容器管理 扫描service、dao --> <context:component-scan base-package="com.test"></context:component-scan> <!-- 配置使Spring采用CGLIB代理 --> <aop:aspectj-autoproxy proxy-target-class="true" /></code></pre> <h5>spring-mvc.xml配置代码:</h5> <pre> <code class="language-xml"><!-- 默认的注解映射的支持 --> <mvc:annotation-driven /> <!-- 自动扫描该包,使SpringMVC认为包下用了@controller注解的类是控制器 --> <context:component-scan base-package="com.test.controller" /></code></pre> <br /> 其实通过上一篇转载的<a rel="" target="_blank"href="http://www.leftso.com/blog/307.html" rel="" target="_blank" title="Spring Context 与Spring MVC Context">Spring Context 与Spring MVC Context</a> 博客中可以知道,spring 和spring mvc是两个容器,且spring mvc是spring的子容器。上面的启用代理的配置在父级spring容器中,由于spring 容器中,子容器可以访问父级容器资源但是父级容器无法访问子容器。所以导致了controller的AOP无效. <h3>解决办法:</h3> <br /> 将以下配置AOP注解启用代理的片段代码移入spring-mvc.xml配置文件中,这样AOP就可以代理两个容器所有资源。 <pre> <code class="language-xml"><!-- 配置使Spring采用CGLIB代理 --> <aop:aspectj-autoproxy proxy-target-class="true" /></code></pre>
  • Spring Boot 入门 AOP 日志处理

    Spring Boot 入门 AOP 日志处理,这里主要讲解spring boot 中采用AOP方式进行日志的统一处理。spring 框架的处理也是一样。综合来讲,本文主要讲解了spring boot aop 拦截 AOP预处理日志/AOP后处理日志/AOP环绕日志和异常日志的处理。<h2>引言</h2>     在Java编程开发中,经常会遇到用户业务日志的记录处理,如果每一个地方都去写大量的日志信息,那么工作量也是惊人的。这里主要讲解spring boot 中采用AOP方式进行日志的统一处理。spring mvc框架也可以参考。 <h2>一.spring boot 项目结构图</h2> <br /> <br /> <img alt="spring boot 入门项目结构图" class="img-thumbnail" src="/assist/images/blog/793811a6732a41078b2a90b3fee8b7c4.png" /> <h2>二.代码讲解</h2> <h2>2.1 spring boot mavn pom.xml依赖</h2> pom.xml: <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.xqlee.project</groupId> <artifactId>demo-springboot-aop-log</artifactId> <version>2.0</version> <packaging>jar</packaging> <name>demo-springboot-aop-log</name> <description>demo project</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.8.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> </code></pre> 这里只是简单的实现AOP日志功能所以只引入了AOP和web包,实际业务情况可能会有数据库操作。 <h3>2.2 spring boot 配置启用AOP</h3> AspectConfig.java: <pre> <code class="language-java">package net.xqlee.project.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; /** * 启用切面 * * @author xqlee * */ @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) public class AspectConfig { } </code></pre> <h3>2.3 spring boot中自定义一个注解,用于处理日志</h3> <pre> <code class="language-java">package net.xqlee.project.aop.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 系统日志记录 * * @author xqlee * */ @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ServiceLog { /** * 操作类型,新增用户?删除用户 ?调用xx服务?使用接口?... * * @return */ public String operation(); /** * 日志级别 * * @return */ public LogType level() default LogType.INFO; } </code></pre> <pre> <code class="language-java">package net.xqlee.project.aop.annotation; public enum LogType { INFO, WARN, ERROR } </code></pre> <h3>2.4 spring boot 入门之编写处理AOP日志</h3> <pre> <code class="language-java">package net.xqlee.project.aop.aspect; import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import net.xqlee.project.aop.annotation.ServiceLog; @Component @Aspect public class LogAspect { private static final Logger log = LoggerFactory.getLogger(LogAspect.class); /** * 切入点 */ @Pointcut("@annotation(net.xqlee.project.aop.annotation.ServiceLog) ") public void entryPoint() { // 无需内容 } @Before("entryPoint()") public void before(JoinPoint joinPoint) { log.info("=====================开始执行前置通知=================="); try { String targetName = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class<?> targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); String operation = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class<?>[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { operation = method.getAnnotation(ServiceLog.class).operation();// 操作人 break; } } } StringBuilder paramsBuf = new StringBuilder(); for (Object arg : arguments) { paramsBuf.append(arg); paramsBuf.append("&"); } // *========控制台输出=========*// log.info("[X用户]执行了[" + operation + "],类:" + targetName + ",方法名:" + methodName + ",参数:" + paramsBuf.toString()); log.info("=====================执行前置通知结束=================="); } catch (Throwable e) { log.info("around " + joinPoint + " with exception : " + e.getMessage()); } } /** * 环绕通知处理处理 * * @param joinPoint * @throws Throwable */ @Around("entryPoint()") public Object around(ProceedingJoinPoint point) throws Throwable { // 先执行业务,注意:业务这样写业务发生异常不会拦截日志。 Object result = point.proceed(); try { handleAround(point);// 处理日志 } catch (Exception e) { log.error("日志记录异常", e); } return result; } /** * around日志记录 * * @param point * @throws SecurityException * @throws NoSuchMethodException */ public void handleAround(ProceedingJoinPoint point) throws Exception { Signature sig = point.getSignature(); MethodSignature msig = null; if (!(sig instanceof MethodSignature)) { throw new IllegalArgumentException("该注解只能用于方法"); } msig = (MethodSignature) sig; Object target = point.getTarget(); Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes()); // 方法名称 String methodName = currentMethod.getName(); // 获取注解对象 ServiceLog aLog = currentMethod.getAnnotation(ServiceLog.class); // 类名 String className = point.getTarget().getClass().getName(); // 方法的参数 Object[] params = point.getArgs(); StringBuilder paramsBuf = new StringBuilder(); for (Object arg : params) { paramsBuf.append(arg); paramsBuf.append("&"); } // 处理log。。。。 log.info("[X用户]执行了[" + aLog.operation() + "],类:" + className + ",方法名:" + methodName + ",参数:" + paramsBuf.toString()); } @AfterThrowing(pointcut = "entryPoint()", throwing = "e") public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { // 通过request获取登陆用户信息 // HttpServletRequest request = ((ServletRequestAttributes) // RequestContextHolder.getRequestAttributes()).getRequest(); try { String targetName = joinPoint.getTarget().getClass().getName(); String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class<?> targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); String operation = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class<?>[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { operation = method.getAnnotation(ServiceLog.class).operation(); break; } } } StringBuilder paramsBuf = new StringBuilder(); for (Object arg : arguments) { paramsBuf.append(arg); paramsBuf.append("&"); } log.info("异常方法:" + className + "." + methodName + "();参数:" + paramsBuf.toString() + ",处理了:" + operation); log.info("异常信息:" + e.getMessage()); } catch (Exception ex) { log.error("异常信息:{}", ex.getMessage()); } } } </code></pre> <h3>三.测试AOP日志</h3> <h3>3.1 编写一个service来进行日志测试</h3> 接口: <pre> <code class="language-java">package net.xqlee.project.service; import java.util.Map; public interface TestService { public boolean insert(Map<String, Object> params, String id); public boolean update(String name, String id); public boolean delete(String id); public boolean doError(String id); } </code></pre> 实现类: <pre> <code class="language-java">package net.xqlee.project.service; import java.util.Map; import org.springframework.stereotype.Service; import net.xqlee.project.aop.annotation.ServiceLog; import net.xqlee.project.aop.annotation.LogType; @Service public class TestServiceImp implements TestService { @ServiceLog(operation = "新增用户信息测试操作。。。。。") @Override public boolean insert(Map<String, Object> params, String id) { return false; } @ServiceLog(operation = "更新用户信息操作....") @Override public boolean update(String name, String id) { return false; } @ServiceLog(operation = "删除操作。。。。") @Override public boolean delete(String id) { return false; } @ServiceLog(operation = "异常操作测试", level = LogType.ERROR) @Override public boolean doError(String id) { try { @SuppressWarnings("unused") int i = 1 / 0; } catch (Exception e) { throw new RuntimeException(e.getMessage()); } return false; } } </code></pre> <h3>3.2 编写并执行spring boot的测试类</h3> <pre> <code class="language-java">package net.xqlee.project; import java.util.HashMap; import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import net.xqlee.project.service.TestService; @RunWith(SpringRunner.class) @SpringBootTest public class DemoSpringbootAopLogApplicationTests { @Autowired TestService testService; @Test public void contextLoads() { Map<String, Object> params = new HashMap<>(); params.put("key1", "v1"); params.put("key2", "v2"); testService.insert(params, "000"); testService.update("name", "id"); testService.delete("leftso"); testService.doError("leftso.com"); } } </code></pre> <br /> 执行核心控制台结果:<br /> <img alt="spring boot aop日志执行结果" class="img-thumbnail" src="/assist/images/blog/688a7889d8d24b3f9e60be0d0c4b11ec.png" />从上图中可以看见,spring boot aop方式的日志已经拦截成功。并且注意观察在异常的时候并没有进行around日志拦截,也证实了代码中的备注。<br /> 综合来讲,本文主要讲解了spring boot aop 拦截 AOP预处理日志/AOP后处理日志/AOP环绕日志和异常日志的处理。
  • Spring AOP 切面表达式语法详解

    简述由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而executi简述由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。表达式组成基础语法结构如下是execution表达式的语法:execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)这里问号表示当前项可以有也可以没有,其中各项的语义如下:modifiers-pattern:方法的可见性,如public,protected;ret-type-pattern:方法的返回值类型,如int,void等;declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;name-pattern:方法名类型,如buisinessService();param-pattern:方法的参数类型,如java.lang.String;throws-pattern:方法抛出的异常类型,如java.lang.Exception;如下是一个使用execution表达式的例子:execution(public * com.spring.service.BusinessObject.businessService(java.lang.String,..))上述切点表达式将会匹配使用public修饰,返回值为任意类型,并且是com.spring.BusinessObject类中名称为businessService的方法,方法可以有多个参数,但是第一个参数必须是java.lang.String类型的方法。上述示例中我们使用了..通配符。关于通配符的类型主要有两种:* 通配符,该通配符主要用于匹配单个单词,或者是以某个词为前缀或后缀的单词。.. 通配符,该通配符表示0个或多个项,主要用于declaring-type-pattern和param-pattern中,如果用于declaring-type-pattern中,则表示匹配当前包及其子包,如果用于param-pattern中,则表示匹配0个或多个参数* 通配符案例如下示例表示返回值为任意类型,在com.spring.service.BusinessObject类中,并且参数个数为零的方法:execution(* com.spring.service.BusinessObject.*())下述示例表示返回值为任意类型,在com.spring.service包中,以Business为前缀的类,并且是类中参数个数为零方法:execution(* com.spring.service.Business*.*()).. 通配符案例如下示例表示匹配返回值为任意类型,并且是com.spring.service包及其子包下的任意类的名称为businessService的方法,而且该方法不能有任何参数:execution(* com.spring.service..*.businessService())这里需要说明的是,包路径service...businessService()中的..应该理解为延续前面的service路径,表示到service路径为止,或者继续延续service路径,从而包括其子包路径;后面的.businessService(),这里的*表示匹配一个单词,因为是在方法名前,因而表示匹配任意的类。如下示例是使用..表示任意个数的参数的示例,需要注意,表示参数的时候可以在括号中事先指定某些类型的参数,而其余的参数则由..进行匹配:execution(* com.spring.service.BusinessObject.businessService(java.lang.String,..))
  • Java连接redis启动报错Error redis clients jedis HostAndPort cant resolve localhost address

    Java连接redis启动报错Error redis clients jedis HostAndPort cant resolve localhost address<h2>问题描述</h2> 错误环境:<br /> 本地window开发环境没有问题。上到Linux环境,启动出现问题。<br /> 错误信息:<br /> Error redis clients jedis HostAndPort cant resolve localhost address <pre> <code>15:44:52.039 [localhost-startStop-1] DEBUG o.m.spring.SqlSessionFactoryBean - Parsed mapper file: 'file [/data/website/test/filetest/WEB-INF/classes/com/cqax/business/dao/WebConfigMapper.xml]' 07-Aug-2017 15:44:52.529 SEVERE [localhost-startStop-1] redis.clients.jedis.HostAndPort.getLocalHostQuietly cant resolve localhost address java.net.UnknownHostException: template: template: Name or service not known at java.net.InetAddress.getLocalHost(InetAddress.java:1473) at redis.clients.jedis.HostAndPort.getLocalHostQuietly(HostAndPort.java:105) at redis.clients.jedis.HostAndPort.<clinit>(HostAndPort.java:12) at redis.clients.jedis.JedisFactory.<init>(JedisFactory.java:37) at redis.clients.jedis.JedisPool.<init>(JedisPool.java:185) at redis.clients.jedis.JedisPool.<init>(JedisPool.java:162) at redis.clients.jedis.JedisPool.<init>(JedisPool.java:92) at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.createRedisPool(JedisConnectionFactory.java:227) at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.createPool(JedisConnectionFactory.java:204) at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.afterPropertiesSet(JedisConnectionFactory.java:195) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1531) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1276) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:522) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:496) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:627) at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:318) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:372) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1173) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1067) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108) at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:634) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:145) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1193) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1095) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) at org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans(BeanFactoryAdvisorRetrievalHelper.java:92) at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findCandidateAdvisors(AbstractAdvisorAutoProxyCreator.java:102) at org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors(AnnotationAwareAspectJAutoProxyCreator.java:88) at org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.shouldSkip(AspectJAwareAdvisorAutoProxyCreator.java:103) at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessBeforeInstantiation(AbstractAutoProxyCreator.java:248) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:1037) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:1011) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:473) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1531) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1276) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) at org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans(BeanFactoryAdvisorRetrievalHelper.java:92) at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findCandidateAdvisors(AbstractAdvisorAutoProxyCreator.java:102) at org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors(AnnotationAwareAspectJAutoProxyCreator.java:88) at org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.shouldSkip(AspectJAwareAdvisorAutoProxyCreator.java:103) at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessBeforeInstantiation(AbstractAutoProxyCreator.java:248) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:1037) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation(AbstractAutowireCapableBeanFactory.java:1011) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:473) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:443) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:325) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4745) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5207) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1419) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:744) Caused by: java.net.UnknownHostException: template: Name or service not known at java.net.Inet6AddressImpl.lookupAllHostAddr(Native Method) at java.net.InetAddress$1.lookupAllHostAddr(InetAddress.java:901) at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1293) at java.net.InetAddress.getLocalHost(InetAddress.java:1469) ... 149 more </code></pre> <h2>问题解决</h2> 解决办法:<br /> 1.查看Linux系统的主机名 <pre> <code>[root@template conf]# hostname template [root@template conf]# </code></pre> 2.查看/etc/hosts文件中是否有127.0.0.1对应主机名,如果没有则添加<br /> <br /> <img alt="查看hosts" class="img-thumbnail" src="/assist/images/blog/c635cbc26ec7400faf5c052c25a159c2.png" />
  • Spring框架Spring IoC容器的核心原理

    Spring框架Spring IoC容器的核心原理,前三篇已经从历史的角度和大家一起探讨了为什么会有Spring,Spring的两个核心概念:IoC和AOP的雏形,Spring的历史变迁和如今的生态帝国。本节的主要目的就是通过一个切入点带大家一起学习一下Spring IoC的核心原理,正如从历史的角度出发讲述为什么会有Spring一样,希望通过这个切入点能让你轻松的掌握住Spring IoC的核心原理。<h2><br /> 引言</h2> <blockquote> <p><a rel="" target="_blank"href="http://www.leftso.com/blog/335.html" rel="" target="_blank" title="java编程为啥会出现spring框架">java编程为啥会出现spring框架</a><br /> <a rel="" target="_blank"href="http://www.leftso.com/blog/336.html" rel="" target="_blank" title="Spring AOP为何诞生">Spring AOP为何诞生</a><br /> <a rel="" target="_blank"href="http://www.leftso.com/blog/338.html" rel="" target="_blank" title="Spring框架每个版本的特性及历史介绍">Spring框架每个版本的特性及历史介绍</a> </p> </blockquote> <p>前三篇已经从历史的角度和大家一起探讨了为什么会有Spring,Spring的两个核心概念:IoC和AOP的雏形,Spring的历史变迁和如今的生态帝国。本节的主要目的就是通过一个切入点带大家一起学习一下Spring IoC的核心原理,正如从历史的角度出发讲述为什么会有Spring一样,希望通过这个切入点能让你轻松的掌握住Spring IoC的核心原理。</p> <blockquote> <div> </div> </blockquote> <p>本篇文章假设你已经可以熟练的使用Spring了,因此对于某一个细节如何实现的不会在进行详细的阐述!</p> <h2>二、IoC和DI的基本概念</h2> <p>IoC(控制反转,英文含义:Inverse of Control)是Spring容器的内核,AOP、事务等功能都是建立在此基础上的。从字面意思上可以把IoC拆分为两层含义:<strong>控制和反转</strong>。控制可以理解为是接口实现类的选择权,反转可以理解为这个选择权交给第三方进行管理;总的来说就是某一接口具体实现类的选择控制权从调用类中移除,转交给第三方进行决定,即由Spring容器通过Bean配置来进行控制,这样的话应用程序本身就不用负责依赖对象的创建和维护,而由Spring容器进行管理。</p> <p>尽管我们现在对IoC的基本概念都已经熟读与心了,但是在老一辈的时候,IoC的概念还不是很容易被人理解。在那个年代,业界的一位大佬,软件界泰斗级的人物Martin Fowler提出了DI(Dependency Injection,依赖注入)的概念,来代替IoC。</p> <p>依赖注入的概念和控制反转的概念从本质上是一样的,只是从不同的侧面描述了这个功能。依赖注入的概念描述的是让调用类对某一接口实现类的依赖关系有第三方容器或其他东西注入,以此来移除对某一接口实现类的依赖。</p> <p><img alt="这里写图片描述1" class="img-thumbnail" src="/assist/images/blog/98835d648f434ef9b78c9061d9d5fa09.png" title="" /></p> <p>时至今日,我们常说的IoC/DI的时候也是把依赖注入和控制反转作为同一个概念来进行阐述的!</p> <h2><a rel="external nofollow" target="_blank" name="t2"></a>三、从哪里入手IoC容器?</h2> <p>我曾尝试过很多次,想踏进Spring原理的大门,但是一次次都被毫无头绪的开端而打退!后来逐渐翻阅一些书籍逐渐形成了那么一点点思路,接下来主要按着我的思路探讨一下Ioc容器的基本原理。</p> <p>正如我们学习骑自行车一样,开始的时候都是先看别人如何骑的,然后自己才能慢慢的学会(当然发明自行车的人是天才)。学习Spring原理也是一样,只有掌握了基本的Spring的使用,才有可能踏进Spring原理的大门。因此,这里我们从如何使用开始哪?</p> <p>1、首先看一下项目结构</p> <p><img alt="这里写图片描述2" class="img-thumbnail" src="/assist/images/blog/67afa4c676734762bcdf1ec05068a98c.png" title="" /></p> <p>继承关系:</p> <p><img alt="这里写图片描述" class="img-thumbnail" src="/assist/images/blog/5a0999708616490b9cf94f43f29dcbb5.png" title="" /></p> <p>bean的配置:</p> <p><img alt="这里写图片描述" class="img-thumbnail" src="/assist/images/blog/75876548846045d1b49683e15fac4fba.png" title="" /></p> <p>Main代码如下:</p> <p><img alt="这里写图片描述" class="img-thumbnail" src="/assist/images/blog/eadd5ccf203944409f0b99d2d456d54e.png" title="" /></p> <p>2、相信每一个学习Spring的小伙伴都是从上述的方式学起的,上图中最显眼的两个类就是红色圈圈出的,也设置我们在最开始使用到的,使用UML工具显示最基本的类图关系:</p> <p><img alt="这里写图片描述6" class="img-thumbnail" src="/assist/images/blog/66b540ffabc7457ebd8f4a69d796a85f.png" title="" /></p> <p>庞大的一个继承和实现体系!看到这里大致上也就是我要说的<strong>第二条路线</strong>了(下文会详细介绍)!这条路线向我们展示了从Spring最接近用开发人员使用的<code>ClassPathXmlApplicationContext</code>、<code>FileSystemXmlApplicationContext</code>类到Spring的顶层接口之间层层的继承和实现关系。</p> <p>看到这里我们似乎还是毫无头绪,这就需要我们借鉴前人的经验了,这个经验就是如何正确的理解<code>BeanFactory</code>和<code>ApplicationContext</code>之间的关系。</p> <h2><a rel="external nofollow" target="_blank" name="t3"></a>四、BeanFactory和ApplicationContext之间的关系</h2> <p>我们都知道Spring是通过配置文件、注解、Java类等方式描述Bean与Bean之间的依赖关系,利用Java的反射功能实例化Bean并建立Bean与Bean之间的依赖关系;</p> <p>这些底层的工作正是由Spring IoC容器完成的,除此之外Spring IoC容器还提供了Bean实例缓存、生命周期管理、时间发布等高级服务。</p> <p>而这里要说的<code>BeanFactory</code>和<code>ApplicationContext</code>都作为Spring IoC容器的形态存在,只不过有些许区别而已,简单的来说:(1)BeanFactory接口的实现类是一个<strong>简单容器</strong>系列,该系列的容器只实现了容器最基本的功能;(2)ApplicationContext接口的实现类是一个<strong>高级容器</strong>系列,该系列的容器在简单容器的基础上增加了很多面向框架的特性,对应用环境做了很多适配,同时添加了很多面向应用的功能,例如:国际化支持和框架事件体系结构等。</p> <blockquote> <p>通常情况下,我们习惯称BeanFactory为Ioc容器,而称ApplicationContext为应用上下文,有时候我们还直接称ApplicationContext为Spring容器。</p> </blockquote> <p>至此,我应该可以引出我要说的前两条路线:第一条路线是基于BeanFactory的简单容器系列;第二天路线是基于ApplicationContext的高级容器系列;</p> <h2><a rel="external nofollow" target="_blank" name="t4"></a>五、第一条路线:基于BeanFactory的简单容器系列</h2> <p>既然BeanFactory的实现类也是一个容器,那么我们就应该可以使用它来注入我们的Bean和获取我们的Bean,如何使用哪?请看代码:</p> <p><img alt="这里写图片描述" class="img-thumbnail" src="/assist/images/blog/1cbdd66390524ab68f115dacf5c6238d.png" title="" /></p> <blockquote> <p>(1)创建IoC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息(也就是我们在bean.xml文件中配置的一个bean的数据结构); <br /> (2)创建一个BeanFactory,这里使用的是DefaultListableBeanFactory; <br /> (3)创建一个载入BeanDefinition的读取器,这里使用的是XmlBeanDefinitionReader来载入XML形式的BeanDefinition,通过一个回调配置给BeanFactory; <br /> (4)从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。</p> </blockquote> <p>上述的过程,完成了整个载入和注册Bean的定义之后,我们所需要的IoC容器就建立起来了,这个时候我们就可以直接使用IoC容器了。</p> <p>上述代码中使用了<code>DefaultListableBeanFactory</code> 这个BeanFactory默认实现的容器完成了Bean的注入和获取操作,查看其继承和实现关系如下:</p> <p><img alt="这里写图片描述" class="img-thumbnail" src="/assist/images/blog/07193f8e6fc540a8925b3a37bd91b69a.png" title="" /></p> <p><code>BeanFactory</code>位于接口类结构的顶端,它主要定义了IoC容器中应该具有的基本特性,主要接口定义如下,根据名称就可以看出是什么作用,这里不再一一解释:</p> <p><img alt="这里写图片描述9" class="img-thumbnail" src="/assist/images/blog/682fe7d24d9540fbbeac7a4987dba7a3.png" title="" /></p> <p>面对如此多的接口或类,我们应该如何理解哪?举个栗子,就像一辆汽车一样,BeanFactory中定义了这辆汽车应该具有的基本功能,通过层层的接口继承和实现为这个基本的汽车架构定制了很多特性,比如:可以座几个人,是否可以倒车等,一直到最后才形成了一辆基本可以正常使用的汽车,但到这一步还是一个比较粗糙的产品或者半成品。(可以使用,但对于普通用户不会直接使用)</p> <p>而关于这些接口或类的介绍,由于篇幅有限,这里不再一一介绍,主要给大家提供一种思路,如何顺藤摸瓜,掌握第一条理解Spring IoC容器的路线。</p> <p>总的来说,BeanFactory是Spring框架的基础设置,面向的是Spring本身,下文中讲述的第二条路线其中也是使用到了上述代码中的过程,我们在实际的开发中很少会直接使用基于BeanFactory的简单容器系列。</p> <h2><a rel="external nofollow" target="_blank" name="t5"></a>六、第二条路线:基于ApplicationContext的高级容器系列</h2> <p>相对于第一条路线中的汽车半成品来说,第二个路线下的产品才真正算是一辆可以开的出去的汽车,在基于ApplicationContext的高级容器系列下为汽车新增了很多特性,比如:加了电子档位、加了倒车雷达、全景天窗、全液晶显示器什么的,一直到最后才形成了一辆可以使用的汽车(可以使用,普通用户也可以直接使用)。</p> <p><img alt="这里写图片描述10" class="img-thumbnail" src="/assist/images/blog/b084800b0a314fb3835b6ca0aef5678b.png" title="" /></p> <p>从上图中可以看出来,相对于BeanFactory来说ApplicationContext增加了很多新特性,例如MessageSource接口、ApplicationEventPublisher接口等,所以说ApplicationContext是一个高级形态意义上的IoC容器。</p> <p>ApplicationContext的主要实现类是<code>ClassPathXmlApplicationContext</code>、<code>FileSystemXmlApplicationContext</code>,前者是通过从类路径加载配置文件,后者模式从文件系统中装载配置。</p> <h2><a rel="external nofollow" target="_blank" name="t6"></a>七、第三条路线:基于WebApplicationContext的Web容器系列</h2> <p>从上边的介绍我们应该已经看出来了,不管是第一条路线还是第二条路线都是基于Java应用的,而我们使用最多的是JavaWeb应用,这也是接下来要说的第三条路线:基于WebApplicationContext的Web容器系列。</p> <p><img alt="这里写图片描述11" class="img-thumbnail" src="/assist/images/blog/1c05c71da2e947a19f2c2341488ae1de.png" title="" /></p> <p>WebApplicationContext是专门为Web应用准备的,由于Web应用比一般的Java应用拥有更多的特性,因此WebApplicationContext扩展了ApplicationContext。</p> <p><img alt="这里写图片描述12" class="img-thumbnail" src="/assist/images/blog/bf5cae9e908d49adac61478dd3aae3fc.png" title="" /></p> <p>我们在配置Spring集成Spring MVC的时候基本都会使用上述的方式配置Spring容器,ContextLoaderListener通过Web容器上下文参数<code>contextConfigLocation</code>获取Spring配置文件的位置。如果只是使用Xml配置的Bean的话,会使用WebApplicationContext的实现类XmlWebApplicationContext。</p> <h2><a rel="external nofollow" target="_blank" name="t7"></a>八、总结</h2> <p>本文的目的并不是详细的阐述Spring IoC容器的核心原理,这是因为市面上已经有很多书讲述Spring IoC容器的核心原理的,并且简单的一篇文章很难说清楚这么多的内容,这里主要是是希望通过将Spring IoC容器的核心原理内容进行划分,整理为3条基本路线,这样的话逐步击破,才能使自己不会被庞大的代码结构体系所吓到!</p> <blockquote> <p>纸上得来终觉浅,绝知此事要躬行!</p> </blockquote> <p>对于Spring IoC容器的核心原理远不止这些,但是基本都是在这三条主线上进行穿插,其他没有提到的如:容器初始化,配置文件解析过程、Bean的解析和注册等,希望大家在在进行学习的时候注意到!</p> <p>如果想进一步学习Spring原理的,这里推荐两本书籍《Spring技术内幕-深入解析Spring架构与设计原理》和《精通Spring 4.x 企业应用开发实战》,前者可能有点久,版本不是最新的,但是书中Spring IoC容器和AOP的讲解还是很有参考价值的,后者应该算是市面上一本讲解还算透彻的书籍,值得阅读!</p>
  • Spring框架每个版本的特性及历史介绍

    Spring框架每个版本的特性及历史介绍,从Spring框架开始1.0到最新的Spring 5.0进行讲解整个Spring生涯中的演变过程以及spring框架生态的扩展。<h2>引言</h2>     前面<a rel="" target="_blank"href="http://www.leftso.com/blog/335.html" rel="" target="_blank" title="java编程为啥会出现spring框架">java编程为啥会出现spring框架</a>/<a rel="" target="_blank"href="http://www.leftso.com/blog/336.html" rel="" target="_blank" title="Spring AOP为何诞生">Spring AOP为何诞生</a>分别讲解了Spring的来由以及面向对象编程的补充AOP面向切面编程。本文主要讲解Spring各个版本的特性。 <p>    前两篇从Web开发史的角度介绍了我们在开发的时候遇到的一个个坑,然后一步步衍生出Spring Ioc和Spring AOP的概念雏形。Spring从2004年第一个正式版1.0 Final Released发展至今,俨然已经成为了一个生态帝国(开局只有一把枪,装备全靠打!),目前也已经迭代到5.0,拥有诸多的子项目,基本可以解决绝大多数场景的应用!</p> <p>    而在进一步学习Spring的核心原理之前,有必要和大家一起梳理一下Spring历史版本的变迁,知晓一下每一个版本新增了哪些东西,解决了哪些我们开发中的问题,以便我们更清楚的理解这个生态帝国是如何一步一发展壮大的!<br /> <br />  </p> <h2><strong>一、Spring历史版本变迁</strong></h2> <p><strong>1、Spring 1.x</strong></p> <p>大概在2004年3月24日这一天,Spring Framework 1.0 final正式出现在我们的视野中,源码项目结构如下:<br /> <img alt="Spring框架1.0蓝图" class="img-thumbnail" src="/assist/images/blog/473ac88713754d34b9c77670807285f5.png" /><br /> 引用依赖如下:<br /> <img alt="Spring 1.0引用依赖" class="img-thumbnail" src="/assist/images/blog/b15a0087b6ac4b818fc78522acbd50c3.png" /><br />  </p> <p>    Spring 1.0当时只包含一个完整的项目,他把所有的功能都集中在一个项目中,其中包含了核心的Ioc、AOP,同时也包含了其他的诸多功能,例如:JDBC、Mail、ORM、事务、定时任务、Spring MVC等。</p> <p>    由于Spring超前的眼光和博大的精神,在第一个版本的时候已经支持了很多第三方的框架,例如:Hibernate、ibatis、模板引擎等。</p> <p>    尽管如此,此时的Spring除了最核心的Ioc和AOP之外,其他的模块犹如我们现在众多的开源项目一样,大多是对第三方框架的简单封装!我也相信很多个人或企业也基本都维护了一套类似这种的框架供项目开发使用。</p> <p>    此时的Spring还很懵懂,只支持基于XML的配置!关于更多关于Spring 1.0 的信息可以参考:</p> <p>    https://spring.io/blog/2004/03/24/spring-framework-1-0-final-released</p> <p><strong>2、Spring 2.x</strong></p> <p>Spring 2.x的源码项目结构如下:<br /> <img alt="Spring 2.x的源码项目结构如下" class="img-thumbnail" src="/assist/images/blog/70eb8fab1f2b45e0a8dc82311b9fda9b.png" /><br /> 引用依赖如下:<br /> <img alt="Spring框架2.0引用依赖如下" class="img-thumbnail" src="/assist/images/blog/2c93126477054d94b0862771511bd075.png" /><br />  </p> <p>通过上图中和1.0版本的对比,我们首先可以很直观的感受到Spring做了哪些改变。</p> <p>Spring 2.x增加对注解的支持,支持了基于注解的配置。</p> <p><strong>3、Spring 3.x</strong></p> <p>Spring在GitHub托管的代码,最早的版本只能看到Spring v3.1.0.M2(https://github.com/spring-projects/spring-framework),源码结构如下:<br /> <img alt="Spring 3.x" class="img-thumbnail" src="/assist/images/blog/00bebebab486427385be6b994d99313f.png" /><br /> Spring 3.x支持了基于Java类的配置。<br /> <strong>4、Spring 4.x<br /> <img alt="Spring 4.x" class="img-thumbnail" src="/assist/images/blog/e274499fdd2e4f4aa7f90f8eb7de474c.png" /></strong><br />  </p> <p><strong>(1)Spring 4.x新特性:</strong></p> <p>Spring 4.x全面支持Java 8.0,支持Lambda表达式的使用,提供了对@Scheduled和@PropertySource重复注解的支持,提供了空指针终结者Optional,对核心容器进行增加:支持泛型的依赖注入、Map的依赖注入、Lazy延迟依赖的注入、List注入、Condition条件注解注入、对CGLib动态代理类进行了增强。</p> <p>Spring 4.x还支持了基于Groovy DSL的配置,提高Bean配置的灵活性。</p> <p>Spring 4.x开始,Spring MVC基于Servlet 3.0 开发,并且为了方便Restful开发,引入了新的RestController注解器注解,同时还增加了一个AsyncRestTemplate支持Rest客户端的异步无阻塞请求。</p> <p><strong>(2)简单的思维导图如下:<br /> <img alt="Spring 4.x新特性" class="img-thumbnail" src="/assist/images/blog/fe32b90efec540538e398f793e29ab30.png" /><br /> 5、Spring 5.x<br /> <img alt="spring 5.0" class="img-thumbnail" src="/assist/images/blog/e923c8492a654469a6d09030f0491880.png" /></strong><br /> Spring 5.x主要新特性:<br /> <img alt="Spring 5.x主要新特性:" class="img-thumbnail" src="/assist/images/blog/8f7568ee710e407083566042e7856382.png" /><br />  </p> <p><strong>6、小结</strong></p> <p>    Spring 1.x、Spring 2.x、Spring 3.x由于版本比较久,而我从开始就是从Spring 4.0 开始用的,所以更多关于以前版本的信息这里解释的不是很全,上述的源码截图只是作为一种直观地感受,希望能感受到Spring版本的变迁过程。</p> <h2><strong>二、Spring如今的生态帝国</strong></h2> <p>      Spring从最初的一城一池,发展到如今已经发展为一个生态帝国,旗下拥有诸多的子项目,从最基本的Spring Ioc/AOP使用,到安全管理,再到大数据,Spring已经逐渐的渗入到各个领域。目前,几乎所有JavaWeb相关的开发都可以在Spring中找到合适的方案,为了在开发的时候,防止重造轮子,下边梳理一下Spring的各个子项目,做到心中有数:<br /> <img alt="所有框架" class="img-thumbnail" src="/assist/images/blog/322e83364b16404287a14fa897ffbf43.png" /></p>
  • java常见面试题

    java常见面试题之冒泡排序<h2>引言</h2> 记录常见的java面试题及解决思路方法。 <h2>java冒泡排序</h2> <pre> <code class="language-java">package net.xqlee.project; public class TestMaoPao { public static void main(String[] args) { int[] arr = new int[] { 0, 2, 3, 7, -1, 90, 290, -1, 100 }; int swap = 0; for (int i = 0; i < arr.length; i++) { for (int j = i; j < arr.length; j++) { if (arr[j] > arr[i]) { swap = arr[i]; arr[i] = arr[j]; arr[j] = swap; } } } System.out.println(arr[0]); } } </code></pre> <br /> 冒泡排序原理: 从第一个开始,与后面的每一个比较,找到比他大/小的就交换,一轮下来就可以将最大/小值放在索引的第一个位置,后面以此类推。 <h2><strong>servlet执行流程</strong></h2>   客户端发出http请求,web服务器将请求转发到servlet容器,servlet容器解析url并根据web.xml找到相对应的servlet,并将request、response对象传递给找到的servlet,servlet根据request就可以知道是谁发出的请求,请求信息及其他信息,当servlet处理完业务逻辑后会将信息放入到response并响应到客户端。 <h2><strong>springMVC的执行流程</strong></h2>   springMVC是由dispatchservlet为核心的分层控制框架。首先客户端发出一个请求web服务器解析请求url并去匹配dispatchservlet的映射url,如果匹配上就将这个请求放入到dispatchservlet,dispatchservlet根据mapping映射配置去寻找相对应的handel,然后把处理权交给找到的handel,handel封装了处理业务逻辑的代码,当handel处理完后会返回一个逻辑视图modelandview给dispatchservlet,此时的modelandview是一个逻辑视图不是一个正式视图,所以dispatchservlet会通过viewresource视图资源去解析modelandview,然后将解析后的参数放到view中返回到客户端并展现。 <h2><strong>Java设计模式思想(单列模式,工厂模式,策略模式,共23种设计模式)</strong></h2> <ul> <li>a) 单例模式:单例模式核心只需要new一个实例对象的模式,比如数据库连接,在线人数等,一些网站上看到的在线人数统计就是通过单例模式实现的,把一个计时器存放在数据库或者内存中,当有人登陆的时候取出来加一再放回去,有人退出登陆的时候取出来减一再放回去,但是当有两个人同时登陆的时候,会同时取出计数器,同时加一,同时放回去,这样的话数据就会错误,所以需要一个全局变量的对象给全部人使用,只需要new出一个实例对象,这就是单例模式的应用,并且单例模式节省资源,因为它控制了实例对象的个数,并有利于gc回收。</li> <li>b) 策略模式:就是将几个类中公共的方法提取到一个新的类中,从而使扩展更容易,保证代码的可移植性,可维护性强。比如有个需求是写鸭子对象,鸭子有叫,飞,外形这三种方法,如果每个鸭子类都写这三个方法会出现代码的冗余,这时候我们可以把鸭子中的叫,飞,外形这三个方法提取出来,放到鸭父类中,让每个鸭子都继承这个鸭父类,重写这三个方法,这样封装的代码可移植性强,当用户提出新的需求比如鸭子会游泳,那么对于我们oo程序员来讲就非常简单了我们只需要在鸭父类中加一个游泳的方法,让会游泳的鸭子重写游泳方法就可以了。</li> <li>c) 工厂模式:简单的工厂模式主要是统一提供实例对象的引用,通过工厂模式接口获取实例对象的引用。比如一个登陆功能,后端有三个类,controller类,interface类,实现接口的实现类。当客户端发出一个请求,当请求传到controller类中时,controller获取接口的引用对象,而实现接口的实现类中封装好了登陆的业务逻辑代码。当你需要加一个注册需求的时候只需要在接口类中加一个注册方法,实现类中实现方法,controller获取接口的引用对象即可,不需要改动原来的代码,这种做法是的可拓展性强。</li> </ul> <h2><strong>内部类与外部类的调用</strong></h2> <ul> <li>a) 内部类可以直接调用外部类包括private的成员变量,使用外部类引用的this.关键字调用即可</li> <li>b) 而外部类调用内部类需要建立内部类对象</li> </ul> <h2><strong>AOP与IOC的概念(即spring的核心)</strong></h2> <ul> <li>a) IOC:Spring是开源框架,使用框架可以使我们减少工作量,提高工作效率并且它是分层结构,即相对应的层处理对应的业务逻辑,减少代码的耦合度。而spring的核心是IOC控制反转和AOP面向切面编程。IOC控制反转主要强调的是程序之间的关系是由容器控制的,容器控制对象,控制了对外部资源的获取。而反转即为,在传统的编程中都是由我们创建对象获取依赖对象,而在IOC中是容器帮我们创建对象并注入依赖对象,正是容器帮我们查找和注入对象,对象是被获取,所以叫反转。</li> <li>b) AOP:面向切面编程,主要是管理系统层的业务,比如日志,权限,事物等。AOP是将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为切面(aspect),切面将那些与业务逻辑无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。</li> </ul> <h2><strong>Arraylist与linkedlist的区别</strong></h2> a) 都是实现list接口的列表,arraylist是基于数组的数据结构,linkedlist是基于链表的数据结构,当获取特定元素时,ArrayList效率比较快,它通过数组下标即可获取,而linkedlist则需要移动指针。当存储元素与删除元素时linkedlist效率较快,只需要将指针移动指定位置增加或者删除即可,而arraylist需要移动数据。 <h2><strong>数据库优化</strong></h2> <ul> <li>a) 选择合适的字段,比如邮箱字段可以设为char(6),尽量把字段设置为notnull,这样查询的时候数据库就不需要比较null值</li> <li>b) 使用关联查询( left join on)查询代替子查询</li> <li>c) 使用union联合查询手动创建临时表</li> <li>d) 开启事物,当数据库执行多条语句出现错误时,事物会回滚,可以维护数据库的完整性</li> <li>e) 使用外键,事物可以维护数据的完整性但是它却不能保证数据的关联性,使用外键可以保证数据的关联性</li> <li>f) 使用索引,索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快的多的速度检索特定的行,特别是对于max,min,order by查询时,效果更明显</li> <li>g) 优化的查询语句,绝大多数情况下,使用索引可以提高查询的速度,但如果sql语句使用不恰当的话,索引无法发挥它的特性。</li> </ul> <h2>Java集合类框架的基本接口有哪些</h2> Collection集合接口,<br /> List、set实现Collection接口,<br /> arraylist、linkedlist,vector实现list接口,<br /> stack继承vector,Map接口,<br /> hashtable、hashmap实现map接口 <h2><strong>事物的理解</strong></h2> <ul> <li>a) 事物具有原子性,一致性,持久性,隔离性</li> <li>b) 原子性:是指在一个事物中,要么全部执行成功,要么全部失败回滚。</li> <li>c) 一致性:事物执行之前和执行之后都处于一致性状态</li> <li>d) 持久性:事物多数据的操作是永久性</li> <li>e) 隔离性:当一个事物正在对数据进行操作时,另一个事物不可以对数据进行操作,也就是多个并发事物之间相互隔离。</li> </ul> <h2><strong>对象的创建</strong></h2> <ul> <li>a) 遇到一个新类时,会进行类的加载,定位到class文件</li> <li>b) 对所有静态成员变量初始化,静态代码块也会执行,而且只在类加载的时候执行一次</li> <li>c) New 对象时,jvm会在堆中分配一个足够大的存储空间</li> <li>d) 存储空间清空,为所有的变量赋默认值,所有的对象引用赋值为null</li> <li>e) 根据书写的位置给字段一些初始化操作</li> <li>f) 调用构造器方法(没有继承)</li> </ul> <h2>JAVA WEB 九大内置对象</h2>   九大内置对象分别是: request response session application out page config exception pageContent<br /> <br />   其中: <ul> <li>request  response  out page config exception pageContent对象的有效范围是当前页面的应用 </li> <li>session 有效范围是当前会话(当前客户端的所有页面)</li> <li>application 有效范围是整个应用程序,只要服务器不关闭对象就有效</li> </ul> ====================================================================<br /> request<br /> ====================================================================<br /> request.getParameter();获得用户提交的表单信息<br /> request.setCharacterEncoding("UTF-8");设置请求编码,防止乱码<br /> request.setAttribute("Unmae", new Object());将数据保存到request范围内的变量中<br /> request.forward(String Url);转发<br /> request.getRequestURL();获得当前页的IE地址<br /> request.getHeader("resref");获得请求也的IE地址<br /> request.getRemoteAddr();获得用户的IP地址<br /> ====================================================================<br /> cookie<br /> ====================================================================<br /> cookie.getCookies()获得所有cookie对象的集合<br /> cookie.getName()获得指定名称的cookie<br /> cookie.getValue()获得cookie对象的值<br /> URLEncoder.encode();将需要保存到cookie中的数据进行编码<br /> URLEncoder.decode();读取cookie信息时将信息解码<br /> ====================================================================<br /> response<br /> ====================================================================<br /> response.addCookie()将一个cookie对象发送到客户端<br /> response.sendRedirect(String path); 重定向<br /> ====================================================================<br /> application<br /> ====================================================================<br /> application.setAttribute(key,value);给application添加属性值<br /> application.getAttribute(key,value);获取指定的值<br /> ====================================================================<br /> session<br /> ====================================================================<br /> session.setMaxInactiveInterval(int num);设置session对象的有效活动时间<br /> session.isNew();判断是否为新用户  返回Boolean<br /> session.setAttribute();<br /> session.getAttribute();<br /> session.invalidate();销毁当前session<br /> ====================================================================
  • java常用框架SpringMVC3/4入门教程

    SpringMVC框架是一个java里面非常轻量级的mvc框架之一,与spring框架同源,整合方便快捷.java常用框架SpringMVC3/4入门教程 <p>1SpringMVC3</p> <p>1.1项目结构图</p> <img alt="java常用框架SpringMVC3/4入门教程" src="/assist/images/blog/50f775fc-8470-4e98-bab8-a9195d8d2671.png" style="height:527px; width:313px" /> <p>1.2Pom.xml</p> <pre> <code class="language-xml"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>seven.org</groupId>    <artifactId>springmvc3</artifactId>    <packaging>war</packaging>    <version>0.0.1-SNAPSHOT</version>    <name>springmvc3 Maven Webapp</name>    <url>http://maven.apache.org</url>    <properties>       <junit.version>4.12</junit.version>       <spring.version>3.2.2.RELEASE</spring.version>       <org.codehaus.jackson.version>1.9.13</org.codehaus.jackson.version>       <commons-fileupload.version>1.3.1</commons-fileupload.version>    </properties>    <dependencies>       <dependency>          <!-- test -->          <groupId>junit</groupId>          <artifactId>junit</artifactId>          <version>${junit.version}</version>          <scope>test</scope>       </dependency>         <!-- spring framework start -->       <dependency>          <groupId>org.springframework</groupId>          <artifactId>spring-core</artifactId>          <version>${spring.version}</version>       </dependency>       <dependency>          <groupId>org.springframework</groupId>          <artifactId>spring-context</artifactId>          <version>${spring.version}</version>       </dependency>       <dependency>          <groupId>org.springframework</groupId>          <artifactId>spring-web</artifactId>          <version>${spring.version}</version>       </dependency>       <dependency>          <groupId>org.springframework</groupId>          <artifactId>spring-webmvc</artifactId>          <version>${spring.version}</version>       </dependency>       <!-- spring framework end -->         <!-- 为SpringMVC提供json数据类型支持 -->         <!-- 注意,新版的 jackson-core-asl已经升级为Group:com.fasterxml.jackson.core|Artifact:jackson-core在SpringMVC4中需要使用新版 -->       <dependency>          <groupId>org.codehaus.jackson</groupId>          <artifactId>jackson-core-asl</artifactId>          <version>${org.codehaus.jackson.version}</version>       </dependency>       <dependency>          <groupId>org.codehaus.jackson</groupId>          <artifactId>jackson-mapper-asl</artifactId>          <version>${org.codehaus.jackson.version}</version>       </dependency>         <!-- el 标签库 -->       <!-- standard.jar -->       <dependency>          <groupId>taglibs</groupId>          <artifactId>standard</artifactId>          <version>1.1.2</version>       </dependency>       <!-- JSTL -->       <dependency>          <groupId>javax.servlet</groupId>          <artifactId>jstl</artifactId>          <version>1.1.2</version>       </dependency>       <!-- /el 标签库 -->         <!-- 文件上传下载 -->       <dependency>          <groupId>commons-fileupload</groupId>          <artifactId>commons-fileupload</artifactId>          <version>${commons-fileupload.version}</version>       </dependency>         <!-- slf4j 和 log4j合用的Maven配置 -->       <dependency>          <groupId>org.slf4j</groupId>          <artifactId>slf4j-log4j12</artifactId>          <version>1.7.2</version>       </dependency>      </dependencies>    <build>       <finalName>springmvc3</finalName>    </build> </project></code></pre>   <p>1.3web.xml</p> <pre> <code class="language-xml"><web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"    version="3.0">    <display-name>Archetype Created Web Application</display-name>      <!-- 字符集处理 -->    <filter>       <filter-name>CharacterEncodingFilter</filter-name>       <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>       <init-param>          <param-name>encoding</param-name>          <param-value>utf-8</param-value>       </init-param>    </filter>    <filter-mapping>       <filter-name>CharacterEncodingFilter</filter-name>       <url-pattern>/*</url-pattern>    </filter-mapping>        <!-- spring 配置 -->    <context-param>       <param-name>contextConfigLocation</param-name>       <param-value>classpath:/cfg/spring/spring-beans.xml</param-value>    </context-param>    <listener>       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    </listener>      <!-- 配置缓存清除监听器,负责处理由 JavaBean Introspector 功能而引起的缓存泄露 -->    <listener>       <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>    </listener>        <!-- springMVC配置 -->    <servlet>       <servlet-name>springMVCSerlet</servlet-name>       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>       <init-param>          <param-name>contextConfigLocation</param-name>          <param-value>classpath:/cfg/spring/spring-servlet.xml</param-value>       </init-param>       <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>       <servlet-name>springMVCSerlet</servlet-name>       <url-pattern>/</url-pattern>    </servlet-mapping>      <!-- 添加下面容器,防止同一个服务器部署多个项目web.root冲突 -->    <context-param>       <param-name>webAppRootKey</param-name>       <param-value>springMVC3</param-value>    </context-param>        <!-- log4j配置 -->    <context-param>       <param-name>log4jConfigLocation</param-name>       <param-value>classpath:/cfg/log/Log4j.properties</param-value>    </context-param>    <context-param>       <param-name>log4jRefreshInterval</param-name>       <param-value>60000</param-value>    </context-param>      <!-- session timeout -->    <session-config>       <!-- 分钟 -->       <session-timeout>60</session-timeout>    </session-config>      <!-- welcome page -->    <welcome-file-list>       <welcome-file>index.html</welcome-file>       <welcome-file>index.jsp</welcome-file>    </welcome-file-list>      <!-- session copy -->    <distributable /> </web-app></code></pre>   <p>1.4spring-servlet.xml</p> <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"    xmlns:context="http://www.springframework.org/schema/context"    xsi:schemaLocation="http://www.springframework.org/schema/beans             http://www.springframework.org/schema/beans/spring-beans-3.2.xsd             http://www.springframework.org/schema/mvc             http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd             http://www.springframework.org/schema/context             http://www.springframework.org/schema/context/spring-context-3.2.xsd ">      <!-- 自动扫描包 -->    <context:component-scan base-package="com" />      <!-- 注解驱动 -->    <mvc:annotation-driven />    <mvc:default-servlet-handler />    <!-- 资源管理不拦截 -->    <mvc:resources location="/resources/" mapping="/resources/**" />    <mvc:resources location="/upload/" mapping="/upload/**" />      <!-- 内部资源视图解析器 prefix + logicName + suffix /WEB-INF/jsps/ + index + .jsp -->    <bean id="internalResourceViewResolver"       class="org.springframework.web.servlet.view.InternalResourceViewResolver">       <!-- 前缀 -->       <property name="prefix" value="/WEB-INF/pages/" />       <!-- 后缀 -->       <property name="suffix" value="" />    </bean>        <!-- 上传图片/文件需要配置 -->    <bean id="multipartResolver"       class="org.springframework.web.multipart.commons.CommonsMultipartResolver">       <property name="maxUploadSize" value="104857600" />       <property name="maxInMemorySize" value="4096" />    </bean>        <!-- 配置JSON支持 start -->    <bean id="mappingJacksonHttpMessageConverter"    class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">       <property name="supportedMediaTypes">          <list>             <!-- 处理返回的json数据的编码,默认是ISO-88859-1 -->             <value>text/html;charset=UTF-8</value>          </list>       </property>    </bean>    <bean    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">       <!-- messageConverters属性中加入 mappingJacksonHttpMessageConverter用来处理json数据类行 -->       <property name="messageConverters">          <list>             <ref bean="mappingJacksonHttpMessageConverter" />          </list>       </property>    </bean>    <!-- 配置JSON支持 end -->     </beans></code></pre> <br /> 1.5spring-beans.xml <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jpa="http://www.springframework.org/schema/data/jpa"    xsi:schemaLocation="http://www.springframework.org/schema/beans                 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd             http://www.springframework.org/schema/mvc             http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd               http://www.springframework.org/schema/aop             http://www.springframework.org/schema/aop/spring-aop-3.2.xsd             http://www.springframework.org/schema/context             http://www.springframework.org/schema/context/spring-context-3.2.xsd             http://www.springframework.org/schema/tx             http://www.springframework.org/schema/tx/spring-tx-3.2.xsd             http://www.springframework.org/schema/data/jpa             http://www.springframework.org/schema/data/jpa/spring-jpa.xsd ">    <context:annotation-config />    <!-- 启动自动扫描该包下所有的注解(如@controller) -->    <context:component-scan base-package="com" />   </beans>  </code></pre>   <p>1.6Log4j配置文件</p> <pre> <code>#defind the rooeLogger ,the rootlogger min level is INFO,and two way to output log log4j.rootLogger = INFO,FILE,CONSOLE,DATABASE   #---------------------------------------------------------------------- #defind the FILE as file everyday                                     | #---------------------------------------------------------------------- log4j.appender.FILE = org.apache.log4j.DailyRollingFileAppender   #the log min level set as INFO log4j.appender.FILE.Threshold=INFO   #log file encoding set UTF-8 log4j.appender.FILE.encoding=UTF-8   #log file path log4j.appender.FILE.File=/home/tomcat/log/springmvc3/springmvc3   #log file name append log4j.appender.FILE.DatePattern='_'yyyy-MM-dd'.log'   #Immediate write the log is true log4j.appender.FILE.ImmediateFlush=true   #the layout of the log log4j.appender.FILE.layout=org.apache.log4j.PatternLayout   #the detail layout of log  in the log file log4j.appender.FILE.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} Springmvc3 %-5p [%c:%L] | %m%n   #------------------------------------------------------------------------- #this use the 'org.apache.log4j.ConsoleAppender' to output in the Console| #------------------------------------------------------------------------- log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender   #output min level is INFO log4j.appender.Threshold=INFO   #the output target is Console log4j.appender.CONSOLE.Target=System.out   #the layout of the log log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} springmvc3 %-5p | %m%n   #---------------------------------------------------------------------------- #insert the log into database                                               | #---------------------------------------------------------------------------- log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender #The jdbc url log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/springmvc3 #The jdbc driver log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver #DB user log4j.appender.DATABASE.user=root #DB password log4j.appender.DATABASE.password=root #sql log4j.appender.DATABASE.sql=INSERT INTO zhl_log (operationTime,logLevel,logClass,logDetail)  VALUES ("%d{yyyy-MM-dd HH:mm:ss}", "%-5p","%F:%L", "%m") #layout log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout #layout detail log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n</code></pre> <br /> 1.7测试vo <pre> <code class="language-java">package com.xqlee.springmvc.vo;   /**  *  *  * <pre>  * @功能说明 [测试bean]  * @其他说明 [暂无]  * @文件编码 [UTF-8]  * @所属包名 [com.xqlee.springmvc.vo]  * @项目名称 [cdws]  * @创建日期 []  * @创建人员 [lxq]  * @最后修改 []  * @修改说明 [暂无更新]  * @修改人员 [lxq]  * @公司组织 [org]  * @版本说明 [v 1.0]  *  * </pre>  */ public class Tobject {      private String id;    private String name;    private String num;      public Tobject(String id, String name, String num) {       this.id = id;       this.name = name;       this.num = num;    }      public String getId() {       return id;    }      public void setId(String id) {       this.id = id;    }      public String getName() {       return name;    }      public void setName(String name) {       this.name = name;    }      public String getNum() {       return num;    }      public void setNum(String num) {       this.num = num;    }   }</code></pre> <br /> 1.8测试controller <pre> <code class="language-java">package com.xqlee.springmvc.controller;   import java.util.Vector;   import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;   import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;   import com.xqlee.springmvc.vo.Tobject;   /**  *  *  * <pre>  * @功能说明 [测试类]  * @其他说明 [暂无]  * @文件编码 [UTF-8]  * @所属包名 [com.xqlee.springmvc.controller]  * @项目名称 [cdws]  * @创建日期 []  * @创建人员 [lxq]  * @最后修改 []  * @修改说明 [暂无更新]  * @修改人员 [lxq]  * @公司组织 [org]  * @版本说明 [v 1.0]  *  * </pre>  */ @Controller(value = "/beanTestController.do") public class BeanTestController {    @RequestMapping(params = "method=Vector")    // @ResponseBody    public Object testVector(HttpServletRequest request,          HttpServletResponse response) {       // 创建容器       Vector<Tobject> vtb = new Vector<Tobject>();       Tobject tb;       for (int i = 0; i <= 5; i++) {          tb = new Tobject("ID-" + i, "名称Name-" + i, "Num-" + i);          vtb.add(tb);       }       request.setAttribute("vtb", vtb);       return "../../vector.jsp";    }      @RequestMapping(params = "method=testJson")    @ResponseBody    public Object testJson(HttpServletRequest request,          HttpServletResponse response) {       // 创建容器       Vector<Tobject> vtb = new Vector<Tobject>();       Tobject tb;       for (int i = 0; i <= 5; i++) {          tb = new Tobject("ID-" + i, "名称Name-" + i, "Num-" + i);          vtb.add(tb);       }       return vtb;    }   }</code></pre>   <p>1.9index.jsp</p> <pre> <code class="language-html"><!DOCTYPE> <html> <body>    <h2>Hello World!</h2>    <a href="beanTestController.do?method=Vector">Vector Test</a> </body> </html></code></pre> <br /> 1.10vector.jsp <pre> <code class="language-html"><%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE> <html> <body>    <table border='1'>       <thead>          <tr>             <td>ID</td>             <td>Name</td>             <td>Num</td>          </tr>       </thead>       <tbody>          <c:forEach items="${vtb}" var="Object">             <tr>                 <td>${Object.id}</td>                 <td>${Object.name}</td>                 <td>${Object.num}</td>             </tr>          </c:forEach>       </tbody>    </table>     </body> </html></code></pre> <br /> 2 SpringMVC4 <p>2.1项目结构图</p> <img alt="2" src="/assist/images/blog/63555b40-5fa1-41e6-9d7c-3d9e088a6223.png" style="height:510px; width:387px" /> <p>2.2pom.xml</p> <pre> <code class="language-xml"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>seven.org</groupId>    <artifactId>springMVC</artifactId>    <packaging>war</packaging>    <version>0.0.1-SNAPSHOT</version>    <name>springMVC Maven Webapp</name>    <url>http://maven.apache.org</url>    <!-- 属性版本 -->    <properties>       <junit.version>4.12</junit.version>       <spring.version>4.1.7.RELEASE</spring.version>       <org.slf4j.version>1.7.2</org.slf4j.version>       <mysql.driver.version>5.1.32</mysql.driver.version>    <com.fasterxml.jackson.jaxrs.version>2.4.1</com.fasterxml.jackson.jaxrs.version>       <commons-fileupload.version>1.3.1</commons-fileupload.version>      </properties>    <dependencies>         <!-- 测试所需 -->       <dependency>          <groupId>junit</groupId>          <artifactId>junit</artifactId>          <version>${junit.version}</version>          <scope>test</scope>       </dependency>         <!-- spring core -->       <dependency>          <groupId>org.springframework</groupId>          <artifactId>spring-core</artifactId>          <version>${spring.version}</version>       </dependency>       <dependency>          <groupId>org.springframework</groupId>          <artifactId>spring-context</artifactId>          <version>${spring.version}</version>       </dependency>       <dependency>          <groupId>org.springframework</groupId>          <artifactId>spring-web</artifactId>          <version>${spring.version}</version>       </dependency>       <dependency>          <groupId>org.springframework</groupId>          <artifactId>spring-webmvc</artifactId>          <version>${spring.version}</version>       </dependency>         <!-- MySQL数据库驱动 导入 start -->       <dependency>          <groupId>mysql</groupId>          <artifactId>mysql-connector-java</artifactId>          <version>${mysql.driver.version}</version>       </dependency>       <!-- MySQL数据库驱动 导入 end -->           <!-- el 标签库 -->       <!-- standard.jar -->       <dependency>          <groupId>taglibs</groupId>          <artifactId>standard</artifactId>          <version>1.1.2</version>       </dependency>       <!-- JSTL -->       <dependency>          <groupId>javax.servlet</groupId>          <artifactId>jstl</artifactId>          <version>1.1.2</version>       </dependency>       <!-- /el 标签库 -->         <!-- 处理json -->       <dependency>          <groupId>com.fasterxml.jackson.jaxrs</groupId>          <artifactId>jackson-jaxrs-xml-provider</artifactId>          <version>${com.fasterxml.jackson.jaxrs.version}</version>       </dependency>         <!-- 文件上传下载 -->       <dependency>          <groupId>commons-fileupload</groupId>          <artifactId>commons-fileupload</artifactId>          <version>${commons-fileupload.version}</version>       </dependency>         <!-- slf4j 和 log4j合用的Maven配置 -->       <dependency>          <groupId>org.slf4j</groupId>          <artifactId>slf4j-log4j12</artifactId>          <version>${org.slf4j.version}</version>       </dependency>      </dependencies>    <build>       <finalName>springMVC</finalName>    </build> </project></code></pre> <br /> 2.3web.xml <pre> <code class="language-xml"><web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"    version="3.0">    <display-name>Archetype Created Web Application</display-name>      <!-- 字符集处理 -->    <filter>       <filter-name>CharacterEncodingFilter</filter-name>       <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>       <init-param>          <param-name>encoding</param-name>          <param-value>utf-8</param-value>       </init-param>    </filter>    <filter-mapping>       <filter-name>CharacterEncodingFilter</filter-name>       <url-pattern>/*</url-pattern>    </filter-mapping>        <!-- spring 核心配置 -->    <context-param>       <param-name>contextConfigLocation</param-name>       <param-value>classpath:/cfg/spring/spring-beans.xml</param-value>    </context-param>    <listener>       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    </listener>      <!-- 配置缓存清除监听器,负责处理由 JavaBean Introspector 功能而引起的缓存泄露 -->    <listener>       <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>    </listener>        <!-- springMVC配置 -->    <servlet>       <servlet-name>springMVCSerlet</servlet-name>       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>       <init-param>          <param-name>contextConfigLocation</param-name>          <param-value>classpath:/cfg/spring/spring-servlet.xml</param-value>       </init-param>       <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>       <servlet-name>springMVCSerlet</servlet-name>       <url-pattern>/</url-pattern>    </servlet-mapping>      <!-- 添加下面容器,防止同一个服务器部署多个项目web.root冲突 -->    <context-param>       <param-name>webAppRootKey</param-name>       <param-value>springMVC4</param-value>    </context-param>      <!-- session timeout -->    <session-config>       <!-- 分钟 -->       <session-timeout>60</session-timeout>    </session-config>      <!-- log4j配置 -->    <context-param>       <param-name>log4jConfigLocation</param-name>       <param-value>classpath:/cfg/log/Log4j.properties</param-value>    </context-param>    <context-param>       <param-name>log4jRefreshInterval</param-name>       <param-value>60000</param-value>    </context-param>    <!--添加监听 -->    <listener>       <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>    </listener>    <!-- /log4j配置 -->      <!-- welcome page -->    <welcome-file-list>       <welcome-file>index.html</welcome-file>       <welcome-file>index.jsp</welcome-file>    </welcome-file-list>      <!-- session copy -->    <distributable /> </web-app></code></pre>   <p>2.4 weblogic.xml</p> <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <weblogic-web-app xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee    http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd    http://xmlns.oracle.com/weblogic/weblogic-web-app    http://xmlns.oracle.com/weblogic/weblogic-web-app/1.2/weblogic-web-app.xsd">      <container-descriptor>       <!-- 针对weblogic10以下的版本使用该语句,不能与厦门的prefer-application-packages同时使用 -->       <!-- <prefer-web-inf-classes>true</prefer-web-inf-classes> -->       <!-- 针对weblogic10及以上的版本如果上面语句无法成功使用该语句,不能与厦门的prefer-web-inf-classes同时使用 -->       <prefer-application-packages>          <package-name>org.apache.commons.lang.*</package-name>          <package-name>antlr.*</package-name>          <package-name>org.hibernate.*</package-name>          <package-name>javax.persistence.*</package-name>       </prefer-application-packages>    </container-descriptor> </weblogic-web-app></code></pre>   <p>2.5spring-servlet.xml</p> <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"    xmlns:context="http://www.springframework.org/schema/context"    xsi:schemaLocation="http://www.springframework.org/schema/beans             http://www.springframework.org/schema/beans/spring-beans-4.1.xsd             http://www.springframework.org/schema/mvc             http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd             http://www.springframework.org/schema/context             http://www.springframework.org/schema/context/spring-context-4.1.xsd ">      <!-- 自动扫描controller包下所有 -->    <context:component-scan base-package="com.xqlee.springmvc.controller" />    <!-- 注解驱动 -->    <mvc:annotation-driven />    <mvc:default-servlet-handler />    <!-- 资源管理不拦截 -->    <mvc:resources location="/resources/" mapping="/resources/**" />    <mvc:resources location="/upload/" mapping="/upload/**" />      <!-- 内部资源视图解析器 prefix + logicName + suffix /WEB-INF/jsps/ + index + .jsp -->    <bean id="internalResourceViewResolver"       class="org.springframework.web.servlet.view.InternalResourceViewResolver">       <!-- 前缀 -->       <property name="prefix" value="/WEB-INF/pages/" />       <!-- 后缀 -->       <property name="suffix" value="" />    </bean>      <!-- 处理在controller中使用注解@ResponseBody标签返回JSON数据 Start -->    <!-- 注意[由于我在pom.xml引入的是2.4.1版本]:所以class=org.springframework.http.converter.json.MappingJackson2HttpMessageConverter -->    <!-- [如果pom.xml引入的是1.xx版本]class=org.springframework.http.converter.json.MappingJacksonHttpMessageConverter -->    <bean id="mappingJackson2HttpMessageConverter"    class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">       <property name="supportedMediaTypes">          <list>             <value>text/html;charset=UTF-8</value>             <value>text/json;charset=UTF-8</value>             <value>application/json;charset=UTF-8</value>          </list>       </property>    </bean>    <bean    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">       <property name="messageConverters">          <list>             <ref bean="mappingJackson2HttpMessageConverter" />          </list>       </property>    </bean>    <!-- JSON数据处理 End -->      <!-- 上传图片/文件需要配置 -->    <bean id="multipartResolver"       class="org.springframework.web.multipart.commons.CommonsMultipartResolver">       <property name="maxUploadSize" value="-1" /><!-- -1表示没有限制 -->       <property name="maxInMemorySize" value="4096" />    </bean>   </beans></code></pre>   <p>2.6spring-beans.xml</p> <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jpa="http://www.springframework.org/schema/data/jpa"    xsi:schemaLocation="http://www.springframework.org/schema/beans                 http://www.springframework.org/schema/beans/spring-beans-4.1.xsd             http://www.springframework.org/schema/mvc             http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd               http://www.springframework.org/schema/aop             http://www.springframework.org/schema/aop/spring-aop-4.1.xsd             http://www.springframework.org/schema/context             http://www.springframework.org/schema/context/spring-context-4.1.xsd             http://www.springframework.org/schema/tx             http://www.springframework.org/schema/tx/spring-tx-4.1.xsd             http://www.springframework.org/schema/data/jpa             http://www.springframework.org/schema/data/jpa/spring-jpa.xsd ">    <context:annotation-config />    <!-- 启动自动扫描该包下所有的注解(如@controller) 一般扫描service,entity,dao,controller由springMVC扫描 -->    <context:component-scan       base-package="com.xqlee.springmvc.service,com.xqlee.springmvc.entity,com.xqlee.springmvc.dao" /> </beans></code></pre>   <p>2.7 Log4j配置文件</p> <pre> <code>#defind the rooeLogger ,the rootlogger min level is INFO,and two way to output log log4j.rootLogger = INFO,FILE,CONSOLE,DATABASE   #---------------------------------------------------------------------- #defind the FILE as file everyday                                     | #---------------------------------------------------------------------- #log4j.appender.FILE = org.apache.log4j.DailyRollingFileAppender   #the log min level set as INFO #log4j.appender.FILE.Threshold=INFO   #log file encoding set UTF-8 #log4j.appender.FILE.encoding=UTF-8   #log file path #log4j.appender.FILE.File=/home/tomcat/log/springmvc4/springmvc4   #log file name append #log4j.appender.FILE.DatePattern='_'yyyy-MM-dd'.log'   #Immediate write the log is true #log4j.appender.FILE.ImmediateFlush=true   #the layout of the log #log4j.appender.FILE.layout=org.apache.log4j.PatternLayout   #the detail layout of log  in the log file #log4j.appender.FILE.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} springmvc4 %-5p [%c:%L] | %m%n   #------------------------------------------------------------------------- #this use the 'org.apache.log4j.ConsoleAppender' to output in the Console| #------------------------------------------------------------------------- log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender   #output min level is INFO log4j.appender.Threshold=INFO   #the output target is Console log4j.appender.CONSOLE.Target=System.out   #the layout of the log log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} springmvc4 %-5p | %m%n   #---------------------------------------------------------------------------- #insert the log into database                                               | #---------------------------------------------------------------------------- #log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender #The jdbc url #log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/springmvc4 #The jdbc driver #log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver #DB user #log4j.appender.DATABASE.user=root #DB password #log4j.appender.DATABASE.password=root #sql #log4j.appender.DATABASE.sql=INSERT INTO zhl_log (operationTime,logLevel,logClass,logDetail)  VALUES ("%d{yyyy-MM-dd HH:mm:ss}", "%-5p","%F:%L", "%m") #layout #log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout #layout detail #log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n</code></pre>   <p>2.8测试vo</p> <pre> <code class="language-java">package com.xqlee.springmvc.vo;   /**  *  *  * <pre>  * @功能说明 [测试bean]  * @其他说明 [暂无]  * @文件编码 [UTF-8]  * @所属包名 [com.xqlee.springmvc.vo]  * @项目名称 [cdws]  * @创建日期 []  * @创建人员 [lxq]  * @最后修改 []  * @修改说明 [暂无更新]  * @修改人员 [lxq]  * @公司组织 [org]  * @版本说明 [v 1.0]  *  * </pre>  */ public class User {      private String id;    private String name;    private String num;      public User(String id, String name, String num) {       this.id = id;       this.name = name;       this.num = num;    }      public String getId() {       return id;    }      public void setId(String id) {       this.id = id;    }      public String getName() {       return name;    }      public void setName(String name) {       this.name = name;    }      public String getNum() {       return num;    }      public void setNum(String num) {       this.num = num;    }   }</code></pre>   <p>2.9测试controller</p> <pre> <code class="language-java">package com.xqlee.springmvc.controller;   import java.util.Vector;   import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;   import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;   import com.xqlee.springmvc.vo.User;   /**  *  *  * <pre>  * &#64;功能说明 [测试类]  * &#64;其他说明 [暂无]  * &#64;文件编码 [UTF-8]  * &#64;所属包名 [com.xqlee.springmvc.controller]  * &#64;项目名称 [cdws]  * &#64;创建日期 []  * &#64;创建人员 [lxq]  * &#64;最后修改 []  * &#64;修改说明 [暂无更新]  * &#64;修改人员 [lxq]  * &#64;公司组织 [org]  * &#64;版本说明 [v 1.0]  *  * </pre>  */ @Controller(value = "/beanTestController.do") public class TestController {    /** 日志 **/    private static final Log log = LogFactory.getLog(TestController.class);      @RequestMapping(params = "method=Vector")    // @ResponseBody    public Object testVector(HttpServletRequest request, HttpServletResponse response) {       // 创建容器       Vector<User> vtb = new Vector<User>();       User tb;       for (int i = 0; i <= 5; i++) {          tb = new User("ID-" + i, "小Name-" + i, "李Num-" + i);          vtb.add(tb);       }       request.setAttribute("vtb", vtb);       log.info("即将跳转.....");       return "../../vector.jsp";    } }</code></pre>   <p>2.10 index.jsp</p> <pre> <code class="language-html"><!DOCTYPE> <html> <body>    <h2>Hello World!</h2>    <a href="beanTestController.do?method=Vector">Vector Test</a> </body> </html></code></pre>   <p>2.11 vector.jsp</p> <pre> <code class="language-html"><%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE> <html> <body>    <table border='1'>       <thead>          <tr>             <td>ID</td>             <td>Name</td>             <td>Num</td>          </tr>       </thead>       <tbody>          <c:forEach items="${vtb}" var="Object">             <tr>                 <td>${Object.id}</td>                 <td>${Object.name}</td>                 <td>${Object.num}</td>             </tr>          </c:forEach>       </tbody>    </table>     </body> </html></code></pre>   <p>2.12运行结果</p> <img alt="3" src="/assist/images/blog/d439127f-c160-4faa-a47b-1f3118f87ba5.png" style="height:213px; width:422px" /><br /> <img alt="4" src="/assist/images/blog/46005b58-d05f-4bab-b517-11ed209014c9.png" style="height:486px; width:611px" /><br />