• springmvc问题整理


    一 url-pattern的问题

      <!-- No mapping found for HTTP request with URI [/WEB-INF/jsp/homePage.jsp] in DispatcherServlet with name 'dispatcher'
        其中/和/*的区别:
        <url-pattern> / </url-pattern>   不会匹配到*.jsp,即:*.jsp不会进入spring的 DispatcherServlet类.
        <url-pattern> /* </url-pattern>  会匹配*.jsp,会出现返回jsp视图时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错
    
        -->
        <servlet-mapping>
            <servlet-name>dispatcher</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>

    二 访问静态资源的三种方式

    (1)最后匹配,此处理器允许当dispatcherServlet的mapping为"/"时利用容器加载静态资源,会处理所有其它请求没有匹配后指向的缺省 servlet.比如的webapp下有一个1.jpg,不写这句无法加载.因为DispatcherServlet拦截“/”,拦截了所有的请求,同时 对*.js,*.jpg的访问也就被拦截了

      <mvc:default-servlet-handler/>

    (2)

      <mvc:resources location="/img/" mapping="/img/**"/> 
      <mvc:resources location="/js/" mapping="/js/**"/>  
      <mvc:resources location="/css/" mapping="/css/**"/

    (3)web.xml里添加如下的配置

    <servlet-mapping>
         <servlet-name>default</servlet-name>
         <url-pattern>*.css</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.gif</url-pattern>
    
    </servlet-mapping>
       
    <servlet-mapping>
         <servlet-name>default</servlet-name>
         <url-pattern>*.jpg</url-pattern>
    </servlet-mapping>
       
    <servlet-mapping>
         <servlet-name>default</servlet-name>
         <url-pattern>*.js</url-pattern>
    </servlet-mapping>

    三 重复打印各种无用日志

    tomcat部署springmvc(什么也没配置的..)显示启动成功,但一直在重复打印下面几句话.浏览器访问可以:

    19:49:07.507 [http-bio-8080-exec-10] DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request
    19:49:07.510 [http-bio-8080-exec-10] DEBUG o.s.web.servlet.DispatcherServlet - DispatcherServlet with name 'dispatcher' processing HEAD request for [/]
    19:49:07.510 [http-bio-8080-exec-10] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /
    19:49:07.511 [http-bio-8080-exec-10] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Did not find handler method for [/]
    19:49:07.511 [http-bio-8080-exec-10] WARN  o.s.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/] in DispatcherServlet with name 'dispatcher'


    网上查了不少原因,什么jar包冲突,配置错误,dispatcher路径等,均不管用. 最后多次尝试得到这个结论:
    如果配置了tomcat after launch且并没有index.jsp首页就会发生这个情况.若是没有勾选after launch,则有无index.jsp都无所谓,都能正常启动不刷info 和 debug

    四 读取配置文件

    <util:properties id="properties" location="classpath:properties/authority.properties"/>

    五 spring自带的@Validated校验

    controller层:

       @RequestMapping(value = "saleInfoList.json", method = RequestMethod.GET)
        @ResponseBody
        public ApiResult saleInfoList(@Validated CustomerDataParam param, BindingResult bindingResult) {
            if (bindingResult.hasErrors()) {
                return ApiResult.fail(bindingResult.getFieldError().getDefaultMessage());
            }

    bean层:

    复制代码
    public class CustomerDataParam extends PrintableBean implements Serializable {
    
        private static final long serialVersionUID = 8765907263903227625L;
    
        /**
         * 门店num
         */
        @NotNull(message = "门店num不能为空")
        private String num;
    
        /**
         * 查询开始日期
         */
        @NotNull(message = "开始日期不能为空")
        @DateTimeFormat(pattern = "yyyy-MM-dd")
        private Date beginDate;
    
        /**
         * 查询结束日期
         */
        @NotNull(message = "结束日期不能为空")
        @DateTimeFormat(pattern = "yyyy-MM-dd")
        private Date endDate;
    
        /**
         * 指标id
         *
         * @see com.xx.xx.enums.xx
         */
        @Min(value = 1, message = "指标id不合法")
        @Max(value = 8, message = "指标id不合法")
        private Integer metricId;
    复制代码

    那么在对应的字段出现指定的出错时会有相应的提示

    六 @Autowired和@Resource区别

    1、@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。 
    2、@Autowired默认按类型装配(这个注解是属于spring的),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下: 
    @Autowired() @Qualifier("baseDao")   
    private BaseDao baseDao; 
     
     3、@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定, 
    如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
     
    @Resource(name="baseDao")   
    private BaseDao baseDao;  
     
    推荐用 @Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。最重要的这样代码看起就比较优雅

    七 spring bean的作用域和生命周期

    bean的作用域:

    Spring Framework支持五种作用域(其中有三种只能用在基于web的)。

    singleton

    在每个Spring IoC容器中一个bean定义对应一个对象实例。

    prototype

    一个bean定义对应多个对象实例。

    request

    在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。

    session

    在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

    global session

    在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。

     
      
    (2) 获取bean的方法:
    1     ClassPathXmlApplicationContext:从类路径中加载
    2     FileSystemXmlApplicationContext: 从文件系统加载
    3     XmlWebApplicationContext:从web系统中加载
     
     
    (3) bean的生命周期(scope=singleton为例说明)
    (a)实例化(当程序加载beans.xml文件时),把我们的bean实例化到内存中,也可用factory-method 来调用有参的构造器
    (b)设置属性,前提是有setter才能成功
    (c)bean实现BeanNameAware接口,则可以通过setBeanName获取id号
    (d)bean实现BeanFactoryAware接口,则可以获取beanFactory
    (e)bean实现ApplicationContextAware接口,则调用setApplicationContext
    (f)bean如果和一个后置处理器关联,则会调用两个方法,见下面的程序示例,执行”before“
    (g)实现InitializingBean接口,则会调用afterPropertiesSet()方法
    (h)调用定制(只有所配置的bean有,非aop)的初始化方法,xml bean里面写init-method
    (i)后置处理器的”after“
    (j)使用bean
    (k)容器关闭
    (l)bean实现DisposableBean的destroy()方法关闭数据连接,socket,文件流等
    (m)调用定制的销毁方法  xml bean里面写 destroy-method 
     
     added on 2017/08/02 经本地调试, BeanPostProcessor前置处理和afterPropertiesSet的顺序应该调换
     
     MyBeanPostProcessor.java
    复制代码
    package com.myBeanPostProcessor;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor; /** * Created by balfish on 15-3-29. */

    class MyBeanPostProcessor implements BeanPostProcessor { @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException { System.out.println("postProcessBeforeInitialization 函数被调用");
       return o;
    }
    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException { System.out.println("postProcessAfterInitialization 函数被调用");
       return o;
      } }

     <!--配置自己的后置处理器,类似过滤器-->
        <bean id="myBeanPostProcessor" class="com.myBeanPostProcessor.MyBeanPostProcessor" />

    八 分散配置

    Spring简化了加载资源文件的配置,可以通过<context:property-placeholder去加载,这个元素的写法如下:

    <context:property-placeholder location="classpath:jdbc.properties"/>

    如果想要配置多个properties文件

    <context:property-placeholder location="classpath:jdbc1.properties"/>
    <context:property-placeholder location="classpath:jdbc2.properties"/>

    这种方式是不被允许的,一定会出"Could not resolve placeholder"

    解决方案:

    (1) 在Spring 3.0中,可以写:

    <context:property-placeholder location="xxx.properties" ignore-unresolvable="true"/>
    <context:property-placeholder location="xxx.properties" ignore-unresolvable="true"/>

    (2) 但是在Spring 2.5中,<context:property-placeholder>没有ignore-unresolvable属性,所以就不能使用上面的那种方法去配置,可以改如下的格式:

    复制代码
      <bean id="propertyConfigurer"
              class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:/jdbc.properties</value>
                </list>
            </property>
        </bean>
    复制代码

    九 springmvc工作原理

    上面的是springMVC的工作原理图:

    1、客户端发出一个http请求给web服务器,web服务器对http请求进行解析,如果匹配DispatcherServlet的请求映射路径(在web.xml中指定),web容器将请求转交给DispatcherServlet.

    2、DipatcherServlet接收到这个请求之后将根据请求的信息(包括URL、Http方法、请求报文头和请求参数Cookie等)以及HandlerMapping的配置找到处理请求的处理器(Handler)。

    3-4、DispatcherServlet根据HandlerMapping找到对应的Handler,将处理权交给Handler(Handler将具体的处理进行封装),再由具体的HandlerAdapter对Handler进行具体的调用。

    5、Handler对数据处理完成以后将返回一个ModelAndView()对象给DispatcherServlet。

    6、Handler返回的ModelAndView()只是一个逻辑视图并不是一个正式的视图,DispatcherSevlet通过ViewResolver将逻辑视图转化为真正的视图View。

    7、Dispatcher通过model解析出ModelAndView()中的参数进行解析最终展现出完整的view并返回给客户端。

    十 spring IOC

    inversion of control,控制反转,这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。传统的程序开发,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个或是JNDI查询一个),使用完之后还要将对象销毁(比如Connection),对象始终会和其他的接口或类耦合起来

    那么IoC是如何做的呢。在spring中,所有的类都会在spring容器中登记,告诉spring它是个什么样的东西,需要什么样的东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所以类的创建,销毁都是由spring控制,也就是说控制对象生命周期的不再是引用它的对象,而是spring。控制反转

    IoC的一个重点是在系统运行中,动态地向某个对象提供它所需要的其他对象,这一点是通过DI(Dependency Injection, 依赖注入)来实现的。比如对象A需要操作数据库,以前我们是通过在A中自己编写代码来获得一个Connection对象,有了spring后我们只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。

    那么DI是如何实现的呢,java1.3之后一个重要特征是反射(reflection), 它允许程序在运行的时候动态地生成对象,执行对象的方法,改变对象的属性。spring就是通过反射来实现注入的。

    十一 spring IOC容器的初始化过程

    IOC容器初始化过程分为三个步骤:  (1) Resource定位       (2) 载入BeanDefinition     (3) 注册这些bean 

    简单来说,IOC容器的初始化是由refresh方法(FileSystemXmlApplicationContext中的一个构造方法中调用这个refresh方法)来启动的,这个方法标志着IOC容器的正式启动。在我们使用DefaultListableBeanFactory的时候,我们可以清楚地看到step1(Resource资源定位)和step2(载入过程)的的接口调用

    ClassPathResource res =new ClassPathResource("applications.xml"); 
    DefaultListableBeanFactory factory =new DefaultListableBeanFactory();
    XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);
    reader.loadBeanDefinitions(res);

    Spring把这三个过程分开,并且用不同的模块来完成,比如用相应的ResourceLoader,BeanDefinitionReader等模块,通过这样的设计方式,可以让用户灵活地根据需要来对这些模块进行裁剪和添加

    1 Resource定位

    这个Resource定位指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口来完成,并且这个Resource对各种形式的BeanDefinition都提供了统一的接口。比如在文件系统中的BeanDefinition可以使用FileSystemResource来进行抽象,在类路径中的BeanDefinition可以使用前面提到的ClassPathResource来使用。其实就是让程序定位到定制的Resource资源,比如"application.xml",里面定义了许多bean的信息,所以这个过程就是怎样让程序找到这个文件,然后还要将它封装成resource的类型(Resource是spring用来封装I/O的类)

    2 BeanDefinition的载入

    这个载入过程就是把我们定义好的Bean表示成Ioc内部的数据结构 - 其实就是BeanDefination。具体来说,这个BeanDefinition就是POJO对象在IOC容器中的抽象,通过这个BeanDefinition定义的数据结构,使得IOC容器能够方便地对POJO对象,也就是Bean,进行管理

    3 向IOC容器注册这些Bean

    接下来就是通过调用BeanDefinitionRegistry接口来把这些BeanDefinition向Ioc容器中注册,下面是BeanDefinitionRegistry接口的源码

    public interface BeanDefinitionRegistry extends AliasRegistry {
      void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
                throws BeanDefinitionStoreException;
        //移除
      void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
        //得到
      BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
        //判断是否包含
      boolean containsBeanDefinition(String beanName);
    //返回所有在这个registry中定义的bean的名字,如果不存在就返回一个空数组
      String[] getBeanDefinitionNames();
      int getBeanDefinitionCount();
    //判断指定的name是否已经在这个registry中使用,或者判断是否有一个bean的name或者是别名使用的是这个name
      boolean isBeanNameInUse(String beanName);
    }

    观察下接口的层次图

     可以看到经常使用的DefaultListableBeanFactory正是实现了该接口,可以看下DefaultListableBeanFactory的代码(摘取一部分)

    public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
            implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    
    private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
                new ConcurrentHashMap<String, Reference<DefaultListableBeanFactory>>(8);
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);
    
    private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64);
    
    private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64);
    
    private volatile List<String> beanDefinitionNames = new ArrayList<String>(256);
    
    private volatile Set<String> manualSingletonNames = new LinkedHashSet<String>(16);
    .....
    ........
    }

    可以看到,在DefaultListableBeanFactory内部是通过将这些BeanDefination放入HashMap中管理的

    4 注意⚠️:  在这个IoC容器初始化的过程中,一般不包含Bean依赖注入的实现。在Spring IoC的设计中,Bean定义的载入和依赖注入是两个独立的过程。依赖注入一般发生在应用第一次通过getBean向IoC容器索取Bean的时候,但是也有例外,那就是设置了lazy-init=false(延迟加载)的时候

    lazy-init = "true"表示我们需要这个Bean延迟加载,那么在Ioc初始化的时候就不会实例化这个bean,直到你索要的时候才会实例化(getBean(String name)), 如果没设置,默认是false(不延迟加载),所以默认会初始化这些Bean,可能导致Spring启动速度很慢

    如果有两个Bean: bean1, bean2. bean1是延迟加载,bean2没有,bean2里面依赖bean1,然后bean2在容器初始化的时候就实例化了,那么这会导致bean1也会实例化而不是延迟加载。因为容器在实例化bean2的时候,如果bean1没有实例化,那bean2怎么依赖它,所以必须使得bean2也实例化。 因此如果IoC容器在启动的时候创建了那些设置为延迟实例化的bean的实例,也不要奇怪,可能是这些bean在配置的某个地方被注入到了一个非延迟处理化的bean里面

    十二 spring AOP

    AOP(Aspect - Oriented Programming, 面向切面编程),可以说是OOP(Object - Oriented Programing,面向对象编程)的补充和完善。OOP引入封装,继承和多态性等概念来建立一种对象层次结构,用来模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。这种散步在各处的无关的代码被称为横切(cross - cutting)代码,在OOp设计中,它导致了大量代码的重复,而不利于各个模块的重用。

    而AOP技术是利用一种称为"横切"的技术,刨解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为"aspect", 即切面。所谓"切面", 简单滴说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

    使用"横切"技术,AOP把软件系统分为两个部分: 核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似,比如权限,日志,事务处理。实现AOP的技术,主要分为两大类: 一是采用动态代理技术,利用拦截方法的方式,对该方法进行装饰,以取代原有对象行为的执行; 二是采用静态织入的方式,引入特定的语法创建"切面",从而使得编译器可以在编译期间织入有关"切面"的代码。

    十三 spring AOP之jdk动态代理和cglib代理的区别

    1 JDK动态代理

        1. 通过实现 InvocationHandler 接口创建自己的调用处理器;

        2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

        3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

        4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

    2 GCLIB代理

     1. cglib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。

     2. cglib封装了asm,可以在运行期动态生成新的class。

    3 两者之间的区别

        1. JDK的动态代理必须基于接口,CGLIB没有这个要求

        2. java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

      

    4 JDK动态代理和CGLIB字节码生成的区别? 

        1. JDK动态代理只能对实现了接口的类生成代理,而不能针对类 

        2. CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法因为是继承,所以该类或方法最好不要声明成final

    5 什么情况下会用哪种方式实现动态代理

        1. 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 

        2. 如果目标对象实现了接口,可以强制使用CGLIB实现AOP 

        3. 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

    6 如何强制使用CGLIB实现AOP? 

        1. 添加CGLIB库,SPRING_HOME/cglib/*.jar 

        2. 在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

  • 相关阅读:
    vue简单总结
    浅拷贝 与递归实现深拷贝封装
    利用mock生成随机的东西
    你不知道的JavaScript--面向对象高级程序设计
    超实用的JavaScript代码段 --倒计时效果
    超实用的JavaScript代码段 Item4 --发送短信验证码
    WEB前端性能优化:HTML,CSS,JS和服务器端优化
    超实用的JavaScript代码段 Item8 -- js对象的(深)拷贝
    web开发必须知道的javascripat工具
    加快页面的运行速度
  • 原文地址:https://www.cnblogs.com/balfish/p/4731170.html
Copyright © 2020-2023  润新知