springmvc中的几个问题

时间:2023-03-09 20:42:03
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层:

springmvc中的几个问题
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;
springmvc中的几个问题

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

六 @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 
springmvc中的几个问题
 added on 2017/08/02 经本地调试, BeanPostProcessor前置处理和afterPropertiesSet的顺序应该调换
 MyBeanPostProcessor.java
springmvc中的几个问题
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;
  }
}
springmvc中的几个问题

<!--配置自己的后置处理器,类似过滤器-->
    <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属性,所以就不能使用上面的那种方法去配置,可以改如下的格式:

springmvc中的几个问题
  <bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/jdbc.properties</value>
</list>
</property>
</bean>
springmvc中的几个问题

九 springmvc工作原理

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);
}

观察下接口的层次图

springmvc中的几个问题

可以看到经常使用的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"/>