Spring的AOP思想 & SpringMVC基本操作

Spring的AOP:   ( Aspect Oriented  Programming ) 面向切面编程

         就是将一些重复的逻辑抽取为一个通知方法,然后通过切点来匹配哪些目标方法需要应用这个通知方法,其中利用了代理技术,在代理中检查切点是否匹配,是否要调用哪些通知方法,然后再进行调用目标方法

        代理技术:Spring中的代理技术:JDK动态代理 和 CGlib 代理

 

静态代理(Proxy):代理类由程序员自己编写的,不是由程序自动生成的

       ****注意:代理类 必须与 目标类 实现 同一接口 ,                                                                                                                                              静态代理实现中,一个委托类对应一个代理类,代理类在编译期间就已经确定

         静态代理实现的例子: 用 **伪代码** 实现对 事务管理的重复逻辑 的重用

               1:提供一个代理类(UserServiceProxy)

                           1) 获取目标方法的对象和实际参数

                           2)调用通知类的 invoke() 方法 

import java.lang.reflect.Method;

/**
 *  UserService 的静态代理类,必须与目标类实现同一 UserService 接口,(目的是让使用者察觉不出是代理替换了原来的目标)
 *   1) 调用通知类的invoke方法
 * 	 2) 利用反射获取方法对象和方法实际参数
 */
public class UserServiceProxy implements UserService {

    private TranscationAdvice advice = new TranscationAdvice();

    static Method business1;
    static Method business2;

    static {
        // 通过反射获得 UserService 类对象
        Class<UserService> c = UserService.class;
        try {
            // 通过反射调用获得方法对象,
            business1 = c.getMethod("business1");
            business2 = c.getMethod("business2");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void business1() {
        //调用通知类的 invoke() 方法
        advice.invoke(this,business1,new Object[]{});
    }

    @Override
    public void business2() {
        //调用通知类的 invoke() 方法
        advice.invoke(this,business2,new Object[]{});
    }
}

              2:提供一个通知类(TranscationAdvice):将 目标方法通知方法 结合在一起

                           1)   实现了事务管理的重复代码

                           2)利用反射调用目标对象的方法 

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 *  通知类: 放置重复的逻辑代码,例如 事务管理,   实现 InvocationHandler 通知接口(包含invoke()方法),使得代码更加通用
 *
 *      1) 反射调用了目标对象的方法
 * 		2) 把重复代码和目标方法联系在了一起
 */
public class TranscationAdvice implements InvocationHandler {

    // 目标类
    private UserServiceTarget target = new UserServiceTarget();

    /**
     *  通知类中重复代码的方法,通过方法的反射调用
     * @param proxy
     * @param method
     * @param args
     * @return
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args){
        System.out.println("开启事务");
        Object obj = null;
        try {
            // 调用目标方法
            obj = method.invoke(target,args);

            System.out.println("提交事务");
        }catch (Exception e) {
            System.out.println("回滚事务");
            e.printStackTrace();
        }

        return obj;
    }
}

测试代码:

import com.chen.service.UserService;
import com.chen.service.UserServiceProxy;
import com.chen.service.UserServiceTarget;

/**
 *  静态代理: 自己实现代理类  *.java -> javac -> *.class -> java -> 加载该class到虚拟机
 */
public class AopTest {

    public static void main(String[] args) {
        /**
         * 获得的是代理类对象:class com.chen.service.UserServiceProxy
         **/
        // 通过 代理类 将 通知类 和 目标类 结合在一起调用
        UserService service = new UserServiceProxy();
        service.business1();

        System.out.println("================================");

        // 直接调用目标类,获得的是 目标类对象:class com.chen.service.UserServiceTarget
        UserService service1 = new UserServiceTarget();
        service1.business1();

    }
}

 

动态代理:

             与静态代理不同,动态代理不需要我们手动编写代理类,在程序运行期间,                             jdk会根据反射机制自动生成一个实现代理接口的匿名类(代理类),                                       在调用具体方法前调用InvokeHandler(通知接口)来处理

动态代理的代码实现:  主要实现  Proxy.newProxyInstance(类加载器, 要实现的接口数组, InvocationHandler);

    测试代码: 

import java.lang.reflect.Proxy;

/**
 *  JDK 的动态代理:自动生成代理类,不用程序员自己实现代理类,直接生成了*.class字节码, 加载该class到虚拟机
 */
public class JdkDynamicProxy {

    public static void main(String[] args) {
        /**
         * 创建一个新的代理类对象
         *         参数1, 类加载器
         *         参数2, 代理类, 要实现哪些接口 Class[]{}
         *         参数3, InvocationHandler (通知对象), 其中包含了代理要执行的代码
         */
        // 得到类加载器
        ClassLoader classLoader = JdkDynamicProxy.class.getClassLoader();

        // 返回的是一个 代理类对象:JDK自动生成的代理类
        UserService service = (UserService) Proxy.newProxyInstance(classLoader,new Class[]{UserService.class},new TranscationAdvice());

        service.business1();
    }
}

 

动态代理的优缺点

         优点: 

                   动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方                            便的对委托类的方法进行统一处理,如添加方法调用次数、添加日志功能等等

         缺点:

                  通过反射类Proxy和InvocationHandler回调接口实现的jdk动态代理,要求委托类必须实现一个接口,                                    但事实上并不是所有类都有接口,对于没有实现接口的类,便无法使用该方式实现动态代理

 

CGLib动态代理:CGLib采用底层的字节码技术,原理是通过字节码技术为一个类创建子类,                     并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑

                           ***cglib生成的代理类,继承了代理对象,代理与被代理对象是继承关系***

 

Spring两种代理方式: 默认使用 JDK动态代理,若 没实现接口,则使用CGLib动态代理

JDK动态代理
      若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。 
                  优点:因为有接口,所以使系统更加松耦合 
                  缺点:为每一个目标类创建接口

CGLib动态代理
     若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。 
                 优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。 
                 缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好
 

 

Spring的AOP实现:

      1)在 pom.xml 文件中添加  AOP 相关依赖   

     <!-- 新加的依赖 -->
        <!-- spring aop -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>4.3.17.RELEASE</version>
        </dependency>

        <!-- 第三方 aop依赖 aspectJ -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.9</version>
        </dependency>

   2)在 spring.xml的配置文件中引入 aop 的命名空间(xmlns:aop

<!-- 引入 aop 的命名空间 -->
<beans xmlns="http://www.springframework.org/schema/beans"
       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:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd
                           
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd">

     <!-- 启动 aop相关的注解  能够帮我们生成底层的代理对象 Proxy -->
    <aop:aspectj-autoproxy/>

    <!-- 目标类 -->
    <bean id="orderServiceTarget" class="com.chen.spring.service.OrderServiceTarget"></bean>

    <!-- 通知类 -->
    <bean id="transcationAdvice" class="com.chen.spring.advice.TranscationAdvice"></bean>
    <bean id="beforeAdvice" class="com.chen.spring.advice.BeforeAdvice"></bean>
    <bean id="afterAdvice" class="com.chen.spring.advice.AfterAdvice"></bean>
    <bean id="afterReturningAdvice" class="com.chen.spring.advice.AfterReturningAdvice"></bean>
    <bean id="afterThrowingAdvice" class="com.chen.spring.advice.AfterThrowingAdvice"></bean>

    <bean id="countTimeAdvice" class="com.chen.spring.advice.CountTimeAdvice"></bean>


</bean>

3) 编写相应的 通知类(Advice

package com.chen.spring.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

/**
 * 通知类: 事务管理的方法
 *
 */

//切面= 通知+切点
@Aspect
public class TranscationAdvice {
    /**
     *  @Around:环绕通知:  决定了哪些目标方法会与 该通知方法一起使用, 通过切点表达式 within(包名.类名) 匹配**哪些目标方法**要和下面的**通知方法**结合到一起使用
     *
     *         作用: 当 OrderServiceTarget中的任意一个方法调用时,都会和该 transcation() 方法结合在一起
     */
   @Around("within(com.chen.spring.service.OrderServiceTarget)")

    /**
     * execution(访问修饰符 返回值类型 包名.类名.方法名( 参数类型... )): 指定目标方法配合该公职方法一起使用
     * 注意 ' * ' :可以匹配任意类型, 可以出现在方法返回值,方法名,类名当中
     * 注意' .. ' :可以匹配方法参数,表示参数的个数和类型都是任意的
     */
   /* @Around("execution(public * com.chen.spring.service.OrderServiceTarget.service3(..))")*/

    /**
     * @annotation(包名.注解名)
     * 它是根据方法上的注解进行匹配, 如果有注解则匹配, 只匹配使用了 @Transcational 注解的目标方法
     *
     * */
    //@Around("@annotation(org.springframework.transaction.annotation.Transactional)")

    // 这个方法统称为 通知方法,负责处理重复的逻辑代码,并在内部调用相对应的目标方法
    public Object transcation(ProceedingJoinPoint point){
        System.out.println("开启事务");
        Object obj = null;
        try{
            // 调用目标方法
            obj = point.proceed();

            System.out.println("提交事务");
        } catch (Throwable throwable) {
            System.out.println("回滚事务");
            throwable.printStackTrace();
        }

        return  obj;
    }
}

测试代码:

package test;

import com.chen.spring.service.OrderService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Spring 的AOP 动态代理调用流程:使用上,尽量实现了无侵入的效果,原来的代码不受影响
 *    1) 获得 Spring 容器,检查 **是否** 要为哪些 <bean> 创建代理类
 *    2) 检查所有的切点表达式( @Around("within()") ),看哪些目标**匹配到了表达式**,为匹配到的目标动态**创建代理类**
 *    3) 获得对象时( getBean() ),检查该对象是否有已经创建好的代理类,如果已经有代理类,则返回该对象的代理类对象
 *    4) 代理类调用目标方法时,先经过 (多个)通知类,继续匹配切点表达式,如果匹配,则进行下面的通知调用(根据不同类型的通知进行先后调用)
 *    5) 匹配的通知调用后,再在通知内调用目标方法
 */
public class SpringAopTest {
    @Test
    public void test1(){
        // 获得 spring 容器,
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        // 返回的是 Spring 自动生成的动态代理类
        OrderService service = context.getBean(OrderService.class);

        service.service5();
    }
}

 

SpingAOP动态代理的调用流程


    1) 获得 Spring 容器,检查 **是否** 要为哪些 <bean> 创建代理类
    2) 检查所有的切点表达式( @Around("within()") ),看哪些目标**匹配到了表达式**,为匹配到的目标动态**创建代理类**
    3) 获得对象时( getBean() ),检查该对象是否有已经创建好的代理类,如果已经有代理类,则返回该对象的代理类对象
    4) 代理类调用目标方法时,先经过 (多个)通知类,继续匹配切点表达式,如果匹配,则进行下面的通知调用(根据不同类型的通知进行先后调用)
    5) 匹配的通知调用后,再在通知内调用目标方法
 

 

AOP: 

       代理 Proxy      代理类

       目标 Target     被代理的对象

       通知 Advice    包含重复逻辑的代码

       切点 Pointcut  把匹配到的目标方法和通知方法结合在一起,定义的是一种匹配规则

       切面 Aspect  = 切点 + 通知

             Spring的AOP思想 & SpringMVC基本操作

aspectj中定义的一些切点表达式   


       1) within(包名.类名) 这个类中所有方法执行时,都会应用通知方法
                    eg:      @Around("within(com.chen.spring.service.OrderServiceTarget)")

       2)execution(访问修饰符 返回值类型 包名.类名.方法名( 参数类型... ))
                注意  *  : 可以匹配任意类型, 可以出现在方法返回值,方法名,类名当中
                注意  .. :可以匹配方法参数,表示参数的个数和类型都是任意的
                    eg:     @Around("execution(public * com.chen.spring.service.OrderServiceTarget.service3(..))")

       3)@annotation(包名.注解名)
                 它是根据方法上的注解进行匹配, 如果有注解则匹配

                   eg:      @Around("@annotation(org.springframework.transaction.annotation.Transactional)")

通知类型:

环绕通知:         @Around(切点表达式) 加在通知方法上(功能最全的), 通知方法环绕着目标方法
前置通知:         @Before  通知代码只会出现在目标方法之前
正常结束通知:  @AfterReturning 在目标方法正常结束后,调用通知方法
异常通知:         @AfterThrowing 在目标方法出现异常后,调用通知方法
结束通知:         @After 在目标方法结束后(正常或异常),总会调用的通知

 

Spring MVC:是一个基于 spring构建 web应用程序的全功能MVC模块,它把程序分为                                    ModelViewController三个部分,将每个部分赋予不同的职能,                                         减少了错误的出现,增加了组内成员的配合,提高了程序的可维护性

    Model 模型 - 数据和操作数据的逻辑(狭义的就是数据)   包括了实体类和业务类(例如 User,UserService)
    View   视图 - 数据展现, 包括(jsp, jstl, el)
    Controller 控制器 把模型和视图关联在一起, 包括servlet  

   

Spring-MVC的重要特点:

  1.   Spring-MVC 拥有强大的灵活性,非入侵性和可配置性
  2.   Spring-MVC 提供了一个前控制器(DispatcherServlet),开发者无需额外开发控制器对象
  3.   Spring-MVC 分工明确,包括控制器,验证器,命令对象,模型对象,处理程序映射。视图解析器.. 每个功能实现都    由一个专门的对象负责完成
  4.   Spring-MVC 可以自动绑定用户输入,并正确地转换数据类型,能自动将字符串解析为模型数据相对应的数据类型
  5.   Spring-MVC 使用一个 名称=值 的Map对象实现更加灵活的模型数据传输
  6.   Spring-MVC 内置了常见的校验器,可以校验用户输入,如果校验不通过,则重定向回输入表单
  7.   Spring-MVC 支持国际化,根据用户的区域显示多国语言,并且国际化的配置非常简单
  8.   Spring-MVC 支持多种视图技术,常见的有 jsp,FreeMarker,Velocity
  9.   Spring-MVC 提供了一个简单而强大的JSP标签库,支持数据绑定功能,使得编写 JSP 页面更加容易

     

     Spring-MVC的工作原理:

   

Spring的AOP思想 & SpringMVC基本操作

 

Spring-MVC的基本使用:

            1)在 pom.xml 的文件中添加进 mvc 相关的依赖

                   ***注意:pom.xml文件中需要添加`<packaging>war</packaging>` (决定了是maven的web项目)
                                  然后手动补充一个src/main/webapp目录, src/main/webapp/WEB-INF/web.xml
                                  在配置tomcat时选择 `项目名:war exploded`

   <dependencies>
        <!-- spring 容器 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.17.RELEASE</version>
        </dependency>

        <!-- spring 对mvc的支持 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.17.RELEASE</version>
        </dependency>

        <!-- 添加servlet相关依赖 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope> <!-- tomcat已经提供了该jar包,此jar包只工作在编译环境 -->
        </dependency>

        <!-- jsp 的依赖-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.2.1</version>
            <scope>provided</scope> <!-- tomcat已经提供了该jar包,此jar包只工作在编译环境 -->
        </dependency>

        <!-- jstl 的依赖-->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!--
           如果转换json时出现了错误: No converter found for return value of type:
           是因为json转换的jar包没有加, 可以在pom文件中加入相应的依赖:
         -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.1</version>
        </dependency>

    </dependencies>

    2) 在 resources 目录下 配置 spring-mvc.xml 文件

            引入 mvc 的命名空间(xmlns:mvc

<?xml version="1.0" encoding="UTF-8" ?>

<!-- 引入 mvc 命名空间(xmlns:mvc)-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd
 http://www.springframework.org/schema/mvc
 http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 解决无法直接访问静态资源(图片,html,css,js等)
            原因: DispatcherServlet的路径是/ 跟tomcat中处理静态资源(图片)的Servlet路径冲突了
		          所以所有静态资源(图片)的请求被DispatcherServlet所处理,把图片的路径当做了控制器路径
	        解决办法:
		          在spring配置文件中加入:`<mvc:default-servlet-handler/>`
     -->
    <mvc:default-servlet-handler/>

    <!-- 启动 mvc 相关注解-->
    <mvc:annotation-driven/>

    <!-- 将 控制类 交给 spring容器 管理-->
    <bean id="controllerDemo" class="com.chen.controller.ControllerDemo"></bean>

    <!-- 视图解析器:处理控制类返回的视图名,判断该视图是哪种视图
         解析为 jsp视图技术:
            根据 控制中的方法返回的 视图名 进行 前缀(/:指的是从当前目录(webapp)下访问) 和 后缀(后缀名".jsp")的拼接
            "/" + "视图名" + ".jsp" ==>  /视图名.jsp :相当于生成了访问jsp页面的路径,再进行请求转发 request.getRequestDispatcher("/hello.jsp").forward(request,response);
                 例:"/" + "hello" + ".jsp" ==> /hello.jsp
    -->
    <mvc:view-resolvers>
        <mvc:jsp prefix="/" suffix=".jsp"/>
    </mvc:view-resolvers>

</beans>

    3)配置 web.xml 文件 ,配置 前控制器(DispatcherServlet)

 

  <!-- 前控制器的职责:
             1.作为统一入口
             2.创建spring容器
             3.在tomcat启动时,就把spring容器创建好
    -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 告诉 DispatcherServlet springmvc.xml 配置文件在哪里,并将spring容器的创建好-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- 在tomcat启动时,就创建这个 DispatcherServlet,并调用初始化方法-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- 给 DispatcherServlet 指定路径
        假设浏览器  /hello  没有,会找 / 这个路径
                   /s1     没有,会找 / 这个路径

                 只要没有其他servlet能够精确匹配这个请求路径,这个请求都会被 / 的这个servlet来处理
     -->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    4) 编写控制器,并将该控制器类要交给spring容器管理
        在 spring-mvc.xml 中加入:`<mvc:annotation-driven/>` 来启用mvc的相关功能

package com.chen.controller;

import com.chen.entity.User;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Date;

/**
 *  请求被处理的调用流程:
 *       1) 请求从浏览器发送到tomcat: http://localhost:8080/test1
 *       2) tomcat先去精确匹配该访问路径的servlet,如果匹配到了,则进入该servlet进行业务处理
 *       3) 若没有匹配到,则匹配到了**前控制器**(DispatcherServlet)的路径 “ / ”,由它来处理请求
 *       4) DispatcherServlet 则去spring容器中查找控制器中每个带有@RequestMapper("/路径")的方法
 *       5) 如果匹配成功,则执行该方法,并返回一个结果(视图名)到 视图解析器中,解析器解析出该请求应该跳转到哪个jsp页面,并返回响应到浏览器上显示
 *       6) 否则报 404 错误!!!
 */

//指示该类是个 Controller 控制器类
@Controller
public class ControllerDemo {

    @RequestMapping("/test1")
    public String test1(){
        System.out.println("进入了ControllerDemo控制器");
        //返回的是 视图名(jsp的名字)
        return "hello";
    }

    /**
     * 获取请求参数的三种方法: 相当于servlet中的 request.getParameter("参数名")
     *     1) 通过 方法参数 与 请求参数 一一对应 (表单get方式的提交),spring容器会自动将数据类型转化
     *           注意: 日期格式 @DateTimeFormat(pattern="yyyy-MM-dd")将日期按规定格式输出,否则报 400 bad request
     *           eg: http://localhost:8080/test2?name=chen&age=18&birthday=1997-11-24
     *
     *     2) 将 请求参数封装到一个**实体类**中,方法参数就是这个实体类
     *           eg: http://localhost:8080/test3?name=chen&age=18&birthday=1997-11-24
     *
     *     3) 路径参数: 将请求参数写到访问路径中,通过 **RequestMapper(" /路径/{参数名} ")** 获取到该请求参数的值
     *                   再在方法参数中利用注解**@PathVariable("参数名")**将获取到的**请求参数值**赋值给方法中的**参数**
     *           eg: @RequestMapping("/test4/{id}/{name}")
     *               @PathVariable("id")int x,@PathVariable("name")String n
     *               http://localhost:8080/test4/1/chen
     *
     */
    @RequestMapping("/test2")
    public String test2(String name, int age, @DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday){
        System.out.println("name: "+name+" age: "+age+" birthday:"+birthday);
        // 返回视图名,进入视图解析器
        return "hello";
    }

    @RequestMapping("/test3")
    public String test3(User user){
        System.out.println("name: "+user.getName()+" age: "+user.getAge()+" birthday:"+user.getBirthday());
        return "hello";
    }

    @RequestMapping("/test4/{id}/{name}")
    public String test4(@PathVariable("id")int x,@PathVariable("name")String n){
        System.out.println("name: "+n+" id: "+x);
        return "hello";
    }

    /**
     * Model: 代表了模型数据, 它也是加在控制器方法之上 可以直接使用它的实现类ModelMap或Map
     *         向模型添加数据: model.addAttribute("变量名", 值);
     *         model中的数据在转发之前,都会被存入request作用域
     *         相当于 request.setAttribute("username","zhangsan");,添加进 request 的作用域,在 jsp 页面上用 EL(${}) 表达式取出来
     */

    @RequestMapping("/test5")
    public String test5(User user, Model model){
        model.addAttribute("name",user.getName());
        model.addAttribute("age",user.getAge());
        model.addAttribute("birthday",user.getBirthday());

        return "hello";
    }

    @RequestMapping("/test6")
    @ResponseBody//把方法的返回值转换成json字符串
    public User test6(){
        User user = new User("chen",18,new Date());
        return user;
    }

}

 5) 处理视图名: 控制器方法返回的就是视图名,通过视图解析器将视图名解析出来相应的 jsp 路径                                                                           根据该路径请求转发到浏览器上显示 ( request.getRequestDispatcher("jsp路径").forward(request,response))

    在 spring-mvc.xml  文件中配置视图解析器

 <!-- 视图解析器:处理控制类返回的视图名,判断该视图是哪种视图
         解析为 jsp视图技术:
            根据 控制中的方法返回的 视图名 进行 前缀(/:指的是从当前目录(webapp)下访问) 和 后缀(后缀名".jsp")的拼接
            "/" + "视图名" + ".jsp" ==>  /视图名.jsp :相当于生成了访问jsp页面的路径,再进行请求转发 request.getRequestDispatcher("/hello.jsp").forward(request,response);
                 例:"/" + "hello" + ".jsp" ==> /hello.jsp
    -->

    <mvc:view-resolvers>
        <mvc:jsp prefix="/" suffix=".jsp"/>
    </mvc:view-resolvers>

6) 启动 tomcat 服务器,进行访问

           Spring的AOP思想 & SpringMVC基本操作

Spring-MVC请求被处理流程:

  1. 请求从浏览器发送到tomcat, 地址为:http://localhost:8080/htest1
  2. tomcat先去精确匹配该访问路径的servlet,如果匹配到了,则进入该servlet进行业务处理
  3. 若没有匹配到,则匹配到了**前控制器**(DispatcherServlet)的路径 “ / ”,由它来处理请求
  4. DispatcherServlet 则去spring容器中查找控制器中每个带有@RequestMapper("/路径")的方法
  5. 如果匹配成功,则执行该方法,并返回一个结果(视图名)到 视图解析器中,解析器解析出该请求应该跳转到哪个jsp页面,并返回响应到浏览器上显示
  6. 否则报 404 错误!!!

 

Spring-MVC获取请求参数的三种方式:

  1) 通过 方法参数请求参数 一一对应 (表单get方式的提交),spring容器会自动将数据类型转化
             注意: 日期格式 @DateTimeFormat(pattern="yyyy-MM-dd")将日期按规定格式输出,否则报 400 bad request
               eg: http://localhost:8080/test2?name=chen&age=18&birthday=1997-11-24
    
  2) 将 请求参数封装到一个**实体类**中,方法参数就是这个实体类
               eg: http://localhost:8080/test3?name=chen&age=18&birthday=1997-11-24
    
  3) 路径参数: 将请求参数写到访问路径中,通过 **RequestMapper(" /路径/{参数名} ")** 获取到该请求参数的值
                         再在方法参数中利用注解**@PathVariable("参数名")**将获取到的**请求参数值**赋值给方法中的**参数**
               eg: @RequestMapping("/test4/{id}/{name}")
                     @PathVariable("id")int x,@PathVariable("name")String n
                      http://localhost:8080/test4/1/chen
  

 

Spring-MVC使用模型接口(Model)进行数据处理


    /**
     * Model: 代表了模型数据, 它也是加在控制器方法之上 可以直接使用它的实现类ModelMap或Map
     *         向模型添加数据: model.addAttribute("变量名", 值);
     *         model中的数据在转发之前,都会被存入request作用域
     *         相当于 request.setAttribute("username","zhangsan");,添加进 request 的作用域,在 jsp 页面上用 EL(${}) 表达式取出来
     */

    @RequestMapping("/test5")
    public String test5(User user, Model model){
        model.addAttribute("name",user.getName());
        model.addAttribute("age",user.getAge());
        model.addAttribute("birthday",user.getBirthday());

        return "hello";
    }

JSP代码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
hello,world!!!!
${name}
${age}
${birthday}
</body>
</html>

 

页面显示:

Spring的AOP思想 & SpringMVC基本操作

 

 

Spring-MVC的常见错误:

        1)中文乱码问题:表单提交数据时,中文显示乱码

              解决办法: 配置一个编码过滤器(CharacterEncodingFilter)进行解码,防止出现乱码问题

<!-- 防止中文乱码: spring提供了一个已经实现的编码过滤器(CharacterEncodingFilter),只需配置就行 -->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <!-- 配置编码 utf-8 -->
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    2)静态资源(图片,html,css,js等)无法访问问题

                原因:  DispatcherServlet的路径是/ 跟tomcat中处理静态资源(图片)的Servlet路径冲突
                              所以所有静态资源(图片)的请求被DispatcherServlet所处理,把图片的路径当做了控制器路径
            解决办法:
                  在 spring-mvc.xml 配置文件中加入:`<mvc:default-servlet-handler/>`

                  配置之后,springMVC容器会定义一个 DefaultServletHttpRequestHandler,它就像是个检查员,对进入

                  DispatcherServlet 的 URL 进行筛选,如果发现它是静态资源的请求,则将该请求转由给 web 服务器默认的

                  Servlet进行处理,如果不是静态资源的请求,才由 DispatcherServlet 继续进行处理

<!-- 解决无法直接访问静态资源(图片,html,css,js等)
            原因: DispatcherServlet的路径是/ 跟tomcat中处理静态资源(图片)的Servlet路径冲突了
		          所以所有静态资源(图片)的请求被DispatcherServlet所处理,把图片的路径当做了控制器路径
	        解决办法:
		          在spring配置文件中加入:`<mvc:default-servlet-handler/>`
     -->
    <mvc:default-servlet-handler/>