spring理解与springMVC的使用

本文主要内容:

一:spring的配置准备和注入使用

二:spring的IOC/DI常用注入

三:spring中的AOP

四:springMVC的基本配置与使用

五:springMVC的视图定位、使用注解、客户端跳转、接收表单数据、以及session的获取。

六:springMVC的拦截器的使用、拦截器与控制器的比较

七:springMVC的一些配置问题深入解析

spring框架是一个很NB的框架,它的设计是基于IOC和AOP结构的J2EE系统框架,所谓IOC/DI就是也就是反转控制,什么意思呢,从功能上理解,原本在程序中需要某个类中的一些属性和方法时,我们会去通过调用构造器new一个新对象出来,然后进行使用。现在这件事情交给spring去做了,我们通过spring对某个bean引入类或者对该类中的某些属性注入值的方式,将该类对象new出来,在后台程序中直接调用该bean即可直接使用已经注入值的对象。

那么会有人有疑问,原本一个对象我在后台直接new一下,就完事了,何必这么麻烦spring来做呢?我原本也有类似的疑问,但是当项目的类和接口和实现类足够多时,就会发现spring注入的方便和好处了,我们可以通过对action类中需要用的接口层层对它注入对象,在action中直接使用的方式,省去了很多繁琐的步骤,而且对于自己的项目使用了哪些接口哪些实现类一目了然,很方便追踪和管理。

一:spring的配置准备和注入使用

1. 要使用spring,则必然需要导入该框架所需的jar包使用相关类,相关jar可以在官网下载获取,将jar包导入项目中

src下新建 spring的核心控制配置文件:applicationContext.xml文件,接下来我们通过spring给实体类属性注入值,然后在action中直接获取该值。

2. 在pojo包下新建category实体类,并设置name和id属性,设置get/set方法。

3. applicationContext.xml文件给name注入值type1

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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.0.xsd
   http://www.springframework.org/schema/aop
   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
   http://www.springframework.org/schema/tx
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context     
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">
  
    <bean name="c" class="pojo.Category">
        <property name="name" value="type1" />
    </bean>
  
</beans>

4. 准备test类,获取该bean并输入name值:

public class Test { 
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                new String[] { "applicationContext.xml" }); 
        Category c = (Category) context.getBean("c");         
        System.out.println(c.getName());
    }
}

此时输出的值为type1。到这里,获取就已经大概明白了spring是用来做什么的了,但是spring能做的还远远不止这些。

二:spring的常用注入

设想一个场景,我们在action中需要调用一些操作数据库的方法,根据设计结构,我们分为了service层、service实现层和dao层、dao实现层。现在通过spring注入,在action中直接调用service接口,访问dao的实现方法。

配置文件层层注入:

  <bean id="testIn" class="action.TestIn">
    	<property name="productService" ref="productService" />
    </bean>
   
   <bean id="productService" class="serviceImpl.ProductService">
   		<property name="productDao" ref="productDao" />
   </bean>
   
    <bean id="productDao" class="DaoImpl.ProductDao">
   </bean>

action中:可直接调用dao实现层的add方法。

public class TestIn {
	private ProductService productService;
	public static void show() {
		ApplicationContext context = new ClassPathXmlApplicationContext(
				new String[] {"applicationContext.xml"});
		TestIn ti = (TestIn) context.getBean("testIn");
		ti.getProductService().show();
	}
    public static void main(String[] args) {
		show();
	}
	public ProductService getProductService() {
		return productService;
	}
	public void setProductService(ProductService productService) {
		this.productService= productService;
	}
}

打印输出了ProductDaoImpl最终的show方法。

spring的IOC反转控制(也叫DI)注入时,ref可以引用其他xml文件中的bean,但是最好做一下区分,例如,如果引入本xml中的bean,则可以<ref local="">
如果引用其他xml中的bean,可以:<ref bean="">当然,直接<ref="">也是可以的,但是为了项目的可读性,万一存在相同名字的bean,岂不是悲剧了。

三:spring中的AOP

AOP即为Aspect Oriented Program意思为面向切面编程。何为面向切面编程,假如我们把处理主业务逻辑的后台类作为一个中心,那AOP就是针对这个中心作为切面来搞一些事情。也就是说针对主线任务,做一些副本任务和辅助业务。可以在action执行前,执行后做一些处理,可以是打印日志、性能统计、异常收集等操作。把主线任务和副本任务分开单独开发,最后通过spring组织在一起。这就是AOP。

1. 编写主线任务:main方法中执行s的方法。

public class TestSpring {
 
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
        ProductService s = (ProductService) context.getBean("s");
        s.doSomeService();
    }
}

 
public class ProductService {
     
    public void doSomeService(){
        System.out.println("main"); 
    } 
}

<bean name="s" class="com.how2java.service.ProductService">
</bean>  

2. 现在编写切面类副本任务,设置在该方法执行前和执行后分别做一些其他操作。

import org.aspectj.lang.ProceedingJoinPoint;
 
public class LoggerAspect {
 
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("执行前 log:" + joinPoint.getSignature().getName());
        Object object = joinPoint.proceed(); //执行主线任务
        System.out.println("执行后 log:" + joinPoint.getSignature().getName());
        return object;
    }
}

3. 配置文件声明切面,将切面和主线缠绵在一起

<bean name="s" class="com.how2java.service.ProductService">
    </bean>   
     
    <bean id="loggerAspect" class="com.how2java.aspect.LoggerAspect"/>
     
    <aop:config>
        <aop:pointcut id="loggerCutpoint"
            expression=
            "execution(* com.how2java.service.ProductService.*(..)) "/>
             
        <aop:aspect id="logAspect" ref="loggerAspect">
            <aop:around pointcut-ref="loggerCutpoint" method="log"/>
        </aop:aspect>
    </aop:config>    

声明主线任务bean:s,声明副本任务bean:loggerAspect,通过aop:config标签把他们编制在一起。

<aop:pointcut id="loggerCutpoint"
            expression=
            "execution(* com.how2java.service.ProductService.*(..)) "/>
 此处声明了在aop中执行那个核心业务,即执行哪个是主线任务,这里的意思有三个任意:第一个*指任意返回类型,第二个*指任意方法,(..)指任意类型的参数。满足该类下这些条件都将是主线任务。都会被执行切面操作。

 <aop:aspect id="logAspect" ref="loggerAspect">
            <aop:around pointcut-ref="loggerCutpoint" method="log"/>
        </aop:aspect>

指定执行什么切面,这里指向了bean:loggerAspect。则将会把这个作为副本任务。

然后通过aop:config包起来。配置完成。

spring可以通过一些注解完成aop的配置和spring注入的配置


springMVC

springMVC简单来讲就是前台页面发起请求,交由springMVC拦截请求跳转到指定的控制器中,在控制器中处理完业务逻辑之后,返回指定的页面。

四:springMVC的基本配置与使用

1. 在web项目中导入所需要的jar包,然后在WEB-INF下的web.xml文件中配置一个dispatcherServlet类的servlet,并给它命名,导入的该类是springMVC所用的核心类,是一个前置控制器,他负责拦截交给springMVC处理的所有请求。交给对应的配置文件的bean处理。配置文件如下:

  <servlet>
  	<servlet-name>springmvc</servlet-name>
  	<servlet-class>
  		org.springframework.web.servlet.DispatcherServlet
  	</servlet-class>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>springmvc</servlet-name>
  	<url-pattern>/</url-pattern>
  </servlet-mapping>

设置servlet-name,该请求走到这一步时,服务器会自动寻找WEB-INF下的springmvc-servlet.xml也就是{servlet-name}-servlet.xml文件。所以此时就需要创建springMVC的配置文件了。

注:该配置文件可以系统自动去找,也可以自行配置,其实该文件的寻找是根据DispatcherServlet这个servlet的初始化方法来决定的。默认会去WEBI-INF下找servlet-name}-servlet.xml文件。如果需要自行设置,则只需将该servlet设置为默认自启动(随着服务器的启动而启动),并设置初始化方法中的参数,告诉服务器去哪里寻找这个配置文件。如下:

<servlet>  
    <servlet-name>springmcv</servlet-name>  
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
    <init-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>classpath*:/springmvc-servlet.xml</param-value>  
    </init-param>  
    <load-on-startup>1</load-on-startup>  
</servlet>  
<servlet-mapping>  
    <servlet-name>springmvc</servlet-name>  
    <url-pattern>/</url-pattern>  
</servlet-mapping>  

2. springMVC的配置文件:WEB-INF下创建springmvc-servlet.xml

该文件主要就是配置springMVC的bean,通过注册获取这些bean来完成对项目的控制,首先创建一个simpleUrlHandlerMapping的bean,用它来完成拦截特定的请求,并将该请求交给那个控制器来完成后台处理。如下:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="simpleUrlHandlerMapping"             
          class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
		<property name="mappings">
			<props>
				<prop key="/addProduct">proController</prop>
			</props>
		</property>
	</bean>
	<bean id="proController" class="controller.ProController"/>
</beans>

该配置文件的意思为:请求客户端发起,dispatcherServlet拦截该请求,交给springmvc-servlet.xml,该配置文件注册了该bean,该bean中配置了将会拦截/addProduct这个请求后缀,并对应了proController这个bean,

注册了一个proController的bean,指向我们的控制器类中。则该请求就会跳转至该控制器类中去了。

3. 控制器类:

springMVC能作为控制器的类的方式有两种,一种是实现controller接口,一种是通过注解@controller的方式。

先介绍通过实现接口的方式

public class IndexController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mav = new ModelAndView("index.jsp");//跳转至index.jsp
        mav.addObject("message", "Hello Spring MVC");//放入message参数
        return mav;返回该对象
    }
}

该请求会访问handleRequest这个方法,在该方法中,有一个modelAndView类,该类是springMVC中比较重要的类,(具体怎么重要需要看框架的源码,我还没看完-.-),通过该类完成跳转,也可以在该类中添加类似map一样的key,value对象。在前台页面可以直接EL表达式获取并显示。然后返回该对象。此时页面会跳转至index.jsp(项目的根目录)中。在index.jsp中通过${message}可以显示该参数中的值。至此,完成了springMVC跳转并传值的过程。

五:springMVC的视图定位、使用注解、客户端跳转、接收表单数据、以及session的获取。

1. springMVC的试图定位

所谓视图定位,也就是给前台页面制定跳转路径,上面的例子中,ModelAndView指定跳转了默认根目录下的index.jsp,那如果我们需要跳转至WEB-INF/page/index.jsp怎么设置呢?此时需要我们在springMVC的配置文件中声明跳转时定位在哪个路径下,则跳转时控制器会自动加上该路径,完成跳转。配置文件如下:此时就会把ModelAndView中设置的跳转路径加上该前缀和后缀名。

    <bean id="viewResolver"     
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/page/"/>
		<property name="suffix" value=".jsp"/>
	</bean>

控制器中:修改为:ModelAndView mv = new ModelAndView("index");即可完成跳转。此例知识介绍了如何将进行视图定位,具体的项目使用中,当然不会如此单调的进行配置。

2. 使用注解

springMVC提供了强大的注解功能,也正是因为这些注解使用很方便和简介,使springMVC很受欢迎,使用注解之后,我们就不需要每拦截一个请求都去设置一个bean了,直接在配置文件中声明,让springMVC去扫描制定路径下的包,扫描那些类被@controller注解过则被认为这是一个控制器,在指定方法前加@requestMapping("/index")注解,则index请求会执行该方法。配置文件如下:

<!-- 告诉springMVC,扫描注解,扫描包controller下的所有注解,去完成请求。 -->
	<context:component-scan base-package="controller" />

类文件:指定的方法名和参数不是固定的,根据需求来确定参数,方法名任意。

@Controller
public class IndexController1 {
	@RequestMapping("/index")
	public ModelAndView  handleRequest(HttpServletRequest req, HttpServletResponse res) 
			throws Exception {
		ModelAndView mv = new ModelAndView("index");
		mv.addObject("msg","Hello Spring MVC");
		return mv;
	}
}

3. 客户端跳转以及接收表单数据

springMVC的ModelAndView类跳转是进行的服务器端跳转,那如何进行客户端跳转呢?很简单,只需要在该类的构造器中声明new ModelAndView("redirect:/index");即可。如下:

    @RequestMapping("/jump")
	public ModelAndView jump() {
		ModelAndView mv = new ModelAndView("redirect:/index");
		mv.addObject("msgg","hello jump");
		return mv;
	}

接收表单数据:jsp页面中提交表单请求,在控制器中是如何接收数据的呢?创建add.jsp页面,提交form表单:

<body>
	<form action="addProduct" method="post">
		商品名称:<input name="name"/><br>
		商品价格:<input name="price"/><br>
		<input type="submit" value="提交">
	</form>
</body>

在控制器中:

@Controller
public class ProductController {
	private ModelAndView mv;
	@RequestMapping("/addProduct")
	public ModelAndView add(Product product) {
		mv = new ModelAndView("showPro");
		return mv;
	}
}

注:jsp中提交数据时,name属性中不需要写product.name,直接提交name属性名(需要和product类中的属性名一模一样),在进入到控制器时,springMVC会自动根据参数的类型,将提交的name和price属性注入到给对象中,此时该方法中的product形参对象已经有了值,并且在该方法中,ModelAndView会自动添加product这个形参到该对象中,即自动完成了:mv.addObject("product",product);然后在showPro.jsp中可以使用${product.name}直接显示该值了。

拓展:springMVC是如何进行参数绑定的呢?首先form表单提交时,在该方法中的形参,springMVC会调用框架提供的转换器(converter)来将页面中的参数绑定到方法的形参中,springMVC提供了很多转换器,一般都能够自动转换完成,特殊的情况下需要自定义转换器满足业务需求。转换器的参数绑定大致可分为:普通类型转换(java Bean)、简单类型转换(前台在参数中传入id,方法中直接定义integer id形参的方式)、默认支持的类型(request、session等)、集合类型的绑定。具体绑定以及如何使用,还有如何自动以转换器。请大家参考这位博主的博客。讲的很详细:https://blog.csdn.net/qq_33530388/article/details/72784199

或者参考这位博主的文章,也很简洁的介绍了参数绑定的使用:http://www.cnblogs.com/HD/p/4107674.html

4. session的获取

对于session的作用,可以说它很强大,我们在项目开发的过程中,遇见难以解决的问题一般都会想到session。那在springMVC中如何获取session呢?这里设置一个例子,访问一个地址,每访问一次,就在该方法的session中+1,然后在jsp页面中显示session的值,

方法如下:访问check,返回check.jsp,在jsp中使用${count}显示session中的值。

   @RequestMapping("/check")
    public ModelAndView check(HttpSession session) {
        Integer i = (Integer) session.getAttribute("count");
        if (i == null)
            i = 0;
        i++;
        session.setAttribute("count", i);
        ModelAndView mav = new ModelAndView("check");
        return mav;
    }

上面介绍controller类中方法的参数绑定时,讲到过默认支持的类型转换,其中就包括了HttpSession类型,HttpSession对象在web程序中是一直存在的,springMVC不过是将该类型获取,通过形参的方式给出到方法中进行使用。

六:springMVC的拦截器的使用、拦截器和控制器的比较

springMVC提供了非常强大的拦截器,基于AOP切面编程的设计思想,拦截器可以渗入到执行控制器的方法执行前后。协助项目完成一些辅助操作。springMVC的拦截器如何使用呢?它主要通过继承handlerInterceptorAdaper父类完成拦截器的创建。

1. 新建一个拦截器类,拦截某个请求方法,并在该方法执行前、执行后返回view前、view后打印输出一些记录。注释中详细介绍了三个方法的执行功能和顺序。仔细查阅。

public class IndexInterceptor extends HandlerInterceptorAdapter {
	
	/**
	 * 可以进行安全设置,权限设置,编码设置等
	 * 
	 * 
	 * 此方法是在controller被访问之前调用,web.xml将所有的请求交给springmvc-servlet.xml进行处理,该文件会去寻找url访问的请求所匹配的业务控制器
	 * 则此方法就是在控制器被访问之前进行的拦截操作,例如可以用作验证用户是否登录操作。
	 * 如果返回true,则会按照顺序执行下面的拦截器的preHandle方法,拦截器执行完之后,访问controller业务控制器,然后再跳转到视图层也就是前台页面之前
	 * 逆序访问拦截器的postHandle方法,然后再进入到视图层,
	 * 在访问完视图层之后,最后逆序执行拦截器的afterCompletion方法。
	 * 结束该流程。
	 * 
	 * 如果返回false,则代表次请求拦截器没有通过,相当于过滤器的不放行,则不会访问controller层,
	 * 则此时拦截器会直接逆序调用当前拦截器之前的所有拦截器的afterCompletion方法,拦截器相当于假装已经请求响应完成了,工作完成了,执行afterCompletion完事之后回家吃饭了。
	 */
	public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler)
		throws Exception{
		System.out.println("preHandle()方法被执行,在访问controller之前被调用。");
		return true;
	}
	
	/**
	 * 此方法在controller之后,jsp之前,可以在modelAndView中add参数,带到JSP中进行使用或者验证。
	 * 可以有机会修改modelAndView对象。
	 */
	public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView mv)
		throws Exception{
		System.out.println("执行了postHandle(),此时位置是:访问完成了controller,在跳转到jsp之前执行的方法.");
		mv.addObject("nowDate",new Date());
	}
	
	/**
	 * 最后执行,可用于资源的释放、日志监控等功能
	 */
	public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex)
		throws Exception{
		System.out.println("执行了afterCompletion(),在视图层jsp之后调用。");
	}

}

2. 创建好了拦截器,需要告诉springMVC拦截哪个请求,以及该请求调用哪个拦截器,则需要在springmvc-servlet.xml配置文件中:

    <mvc:interceptors>   
        <mvc:interceptor>   
            <mvc:mapping path="/index"/> 
            <!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 --> 
            <bean class="interceptor.IndexInterceptor"/> <!--拦截器类的路径--!>
        </mvc:interceptor> 
    </mvc:interceptors>

则当我们访问/index时,拦截器是什么时候执行的呢?此时这个请求经过控制器适配器,交给handleMapping,handleMapping去寻找有没有执行该请求的控制器,如果有则会执行配置好的拦截器进行拦截,进行拦截器的工作。如果没有找到对应的控制器,则拦截器不会执行。

springMVC并没有一个总的拦截器,不可以对所有请求进行拦截,它是属于handleMapping级别的,可以设置多个intercept分别对不同的handleMapping进行拦截。可以通过实现handleInterceptor接口或者继承handleInterceptorAdapter类来完成拦截器的创建。

合理合适的使用拦截器,能够让项目更好管理更加完善。

3. 拦截器、过滤器的比较:

首先,过滤器是什么:过滤器本质上讲他是基于java的反射机制。运行在servlet容器中的,只被初始化一次,只能用在servlet前后也就是只能用在请求前后。

拦截器是什么:拦截器是一种AOP的运行,运行在插件上的,比如hibernate、struts、spring等,本质上讲它是基于方法的回调函数完成工作的,它不仅能拦截web的请求,servlet的请求,还可以拦截action请求,可以在某个方法执行前后进行拦截,使用更灵活。

两者均可以对请求进行拦截,只不过他们的作用不同,拦截器更细化,过滤器更专注于统一原则。

七:springMVC的一些配置问题

1. 中文配置问题:

在springMVC架构下,经常会出现表单提交导致中文乱码问题,解决这个问题很简单,有一个一劳永逸的方法,就是在web.xml中配置一个过滤器,将所有的请求统一使用中文编码即可:配置如下:

    <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>    

2. 对于web.xml中dispatcherServlet拦截哪些请求的问题,

     <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

此时是将所有的不带后缀名的请求进行过滤,提交给springMVC处理,

 <url-pattern>*.do</url-pattern>这种方式是过滤所有以.do为后缀的请求。

如果直接设置为/的话,此时在项目中如果需要访问.js/.css/.jpg等静态资源时也会被过滤,交给springMVC处理该请求,可是springMVC中如果没有访问该静态资源的设置,就会导致404,找不到资源。如果解决这种问题呢?

①将过滤请求修改为.do之类的具体后缀名,此时.js/.css/.jpg等请求不符合以.do结尾,dispatcherServlet不会进行过滤,则可以正常访问静态资源

②如果非要用/的方式,可以启用servlet的默认servlet:defaultServlet,并设置在dispatcherServlet这个servlet之前,也就是说在.js/.css/.jpg这些请求进入到web.xml时,会先被默认servlet拦截,然后成功去访问静态资源,就不回进入到springMVC的工作中去了。

配置如下:需要访问什么静态资源就设置什么,本例需要访问.jpg/.js这些后缀。

<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>

    <servlet>
  	<servlet-name>springmvc</servlet-name>
  	<servlet-class>
  		org.springframework.web.servlet.DispatcherServlet
  	</servlet-class>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>springmvc</servlet-name>
  	<url-pattern>/</url-pattern>
  </servlet-mapping>

③如果以上方法都不想用,还有一种最繁琐的方法,就是允许.jpg这些请求被dispatcherServlet拦截,进入到springMVC的工作中,我们在springMVC的配置中声明对这些访问的设置。也可以访问到静态资源。在springmvc-servlet.xml配置文件中如下:

<mvc:resources mapping="/images/**" location="/images/" />
使用该配置需要声明<mvc:annotation-driven />自动注册两个bean

指定静态资源的位置,则请求会自动去寻找,具体使用方法不是很熟悉,这里只介绍有这样的方法行得通。

猜你喜欢

转载自blog.csdn.net/qq_41908550/article/details/84072603