框架部分知识梳理

框架

一套规范。

实际是他人实现的一系列接口和类的集合。通入导入对应框架的jar文件(maven项目导入对应的依赖),进行适当的配置,就能使用其中的所有内容。

开发者可以省去很多模板代码,如dao中的CRUD,MVC模式下层与层之间的关联。只需要集中精力实现项目中的业务逻辑部分。

Java主流框架

Spring、SpringMVC、MyBatis、MyBatisPlus、Hibernate、JPA等。

SSH:最初是Spring+Stucts2+Hibernate组成,之后Stucts2被SpringMVC取代。

SSM:Spring+SpringMVC+MyBatis

新项目使用SpringBoot,早起的SSH项目由于维护成本高,基本不会推翻重做,但会维护一些SSM项目。

无论是SSH还是SSM,Spring、SpringMVC必不可少。从2004年推出至今,依旧是主流框架中不可获取的一部分。

Spring

概念

一个轻量级开源的Java框架。是一个管理项目中对象的容器,同时也是其他框架的粘合器,目的就是对项目进行解耦。

轻量级:对原有代码的侵入很小。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ptN1p5iS-1676948376472)(https://bkimg.cdn.bcebos.com/pic/6a63f6246b600c337b73ab94174c510fd9f9a160?x-bce-process=image/watermark,image_d2F0ZXIvYmFpa2UxMTY=,g_7,xp_5,yp_5)]

Spring的核心是IOC控制反转和AOP面向切面编程

组成

img

名词解释

IOC

Inversion Of Control 控制反转

DI

Dependency Injection 依赖注入

举例说明

用代码描述场景:员工食堂每天提供事物

  • 食物:米饭类

    public class Rice{
        public Rice(){
            sout("今天吃米饭");
        }
    }
    
  • 食物:面条类

    public class Noodles{
        public Noodles(){
            sout("今天吃面条");
        }
    }
    
  • 厨师类

    public class Cook{
        //定义一个做饭的方法,做什么饭new什么对象,创建食物对象的权限,是由当前厨师类决定。
        public void cooking(){
            //new Rice();
            new Noodles();
        }
    }
    
  • 员工:main方法

    psvm(){
        Cook cook = new Cook();
        cook.cooking();
    }
    

这种方式,Cook类中的cooking()方法创建什么对象,就输出什么内容。(厨师做什么,员工就吃什么)。

如果有人想要吃指定食物,就要修改源代码cooking()方法中创建的对象。

解决方案:定义一个接口:Food

public interface Food{
    void info();
}

让原本的所有食物Rice类和Noodles类实现该接口

public class Rice implements Food{
    @Override
    public void info(){
        sout("今天吃米饭");
    }
}
public class Noodles implements Food{
    @Override
    public void info(){
        sout("今天吃面条");
    }
}

给厨师Cook类中cooking()方法定义一个参数:Food接口

public class Cook{
    public void cooking(Food food){
        food.info();
    }
}

这时Cook对象调用cooking()方法时,需要提供一个Food接口类型的实现类。

psvm(){
    Cook cook = new Cook();
    //传递什么参数,旧调用该参数重写后的方法
    cook.cooking(new Rice());
    cook.cooking(new Noodles());
}

整个过程中,将创建什么食物对象的控制权,由厨师交给用户,这就是控制反转(IOC)。

厨师对象调用cooking()方法的参数,就是对于食物Food对象的依赖,是由用户注入进来的,这就是依赖注入(DI)。

这样一来,各个对象之间互相独立(没有在某个类中new另一个类的对象),降低了代码之间的耦合度。

总结:控制反转(IOC)是一种思想,就是让创建对象的控制权由自身交给第三方,控制反转这种思想,通过依赖注入(DI)的方式实现。

IOC和DI其实都是在描述控制反转,IOC是思想,DI是具体实现方式。

这里的第三方,就是Spring。Spring是一个容器,可以管理所有对象的创建和他们之间的依赖关系。

可以理解为:“Spring就是用来管理对象的,在需要用到某个对象的时候,帮我们自动创建”。

如Servlet+JSP模式写Web项目时,会在控制层Servlet中创建业务逻辑层Service对象,在Service层中创建数据访问层Dao对象。

有了Spring后,就不会出现new这些对象的代码了。

Spring需要导入对应的jar文件后,定义一个配置文件,在该配置文件中配置程序运行过程中所需的对象。

AOP

Aspect Orintend Programming 面向切面编程

Spring控制台应用

1.创建一个普通的Maven项目,不选择模板

2.添加Spring核心依赖

jdk8用5.x版本

<!-- spring-context表示spring核心容器 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.23</version>
</dependency>

3.创建一个Java类

package com.hqyj.spring01;
/*
* 定义一个普通的Java类
* PlainOrdinaryJavaObject pojo  相当于简化的javabean
* entity  vo  dto  pojo  都是在描述实体类
* */
public class PlainOrdinaryJavaObject {
    public void fun(){
        System.out.println("一个PlainOrdinaryJavaObject(普通的Java类)对象");
    }
}

4.创建Spring配置文件

创建一个Spring的配置文件,在其中注入上一步创建的类的对象

在resources目录下,创建一个xml文件,选择spring config,通常命名为application

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">


    <!--bean标签表示,在Spring容器中,注入某个类的对象-->
    <!--class属性表示要注入哪个类,写类的全限定名(包名+类名)-->
    <!--id表示给类的对象的名称-->
    <bean class="com.hqyj.spring01.PlainOrdinaryJavaObject" id="pojo"></bean>

</beans>

5.创建main方法所在类

解析Spring配置文件,获取注入的对象。

package com.hqyj.spring01;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        //创建一个自定义类的对象
        //传统方式创建对象
        //PlainOrdinaryJavaObject pojo = new PlainOrdinaryJavaObject();

        //使用spring容器获取对象

        //创建一个用于解析Spring配置文件的对象,参数为配置文件的名称。实际就是获取Spring容器
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");

        //获取容器中的对象
        /*
        只通过名称获取,返回值为Object类型,需要转换
        Object obj = context.getBean("pojo");
        PlainOrdinaryJavaObject pojo=(PlainOrdinaryJavaObject) obj;
         */
        //使用这种方式,直接用指定类型接收
        PlainOrdinaryJavaObject pojo = context.getBean("pojo", PlainOrdinaryJavaObject.class);
        //能成功调用方法,说明Spring管理了该类的对象
        pojo.fun();
        //关闭Spring容器
        context.close();
    }
}

思考

  • 1.对象在什么时候创建?

    默认情况下,在解析Spring配置文件的时候,自动创建一个对象。

  • 2.如果不想在解析时自动创建怎么办?

    在配置文件的某个<bean>标签中,添加一个"lazy-init=true"属性,表示该对象设置为懒加载,

    在初始化Spring容器时不会创建对象,只有在调用getBean()时才会创建对象

  • 3.如果设置为懒加载,是不是每次调用getBean(),都会创建一个对象呢?

    默认情况下,Spring容器只会创建一个对象。

    在配置文件的某个<bean>标签中,添加一个"scope=‘prototype’"属性,表示每次调用getBean()方法,就会创建一个对象。

    该属性的值默认为"singleton",表示单例模式,只会创建一个对象。

总结:默认情况下,通过<bean>标签定义的类,在Spring容器初始化时,创建一个对象。

bean标签常用属性

属性作用
class定义类的全限定名
id定义对象的名称
lazy-init是否为懒加载。默认值为false,在解析配置文件时就会创建对象。设置为true表示懒加载,只有在getBean()时才会创建对象。
scope单例/原型模式。默认值为singleton,表示单例模式,只会创建一个对象。设置为prototype,表示原型模式,每调getBean()就创建一个对象。
init-method初始化时触发的方法。在创建完该对象时自动调用的方法。该方法只能是无参方法,该属性的值只需要写方法名即可
destory-method销毁时触发的方法。Spring容器关闭时自动调用的方法,该方法只能是无参方法。只有在单例模式下有效。

属性注入

给某个bean添加属性的方式有两种:构造器注入和setter注入

setter注入

这种方式注入属性时,类中必须要有set方法

在bean标签中,加入<property></property>标签,

该标签的name属性通常表示该对象的某个属性名,但实际是setXXX()方法中的XXX单词。

如有age属性,但get方法为getNianLing(),name属性就需要写成nianLing。

该标签的value属性表示给该类中的某个属性赋值,该属性的类型为原始类型或String

该标签的ref属性表示给该类中除String以外的引用类型属性赋值,值为Spring容器中另一个bean的id。

<!--注入Car类对象并用set方式注入其属性-->
<bean class="com.hqyj.spring01.Car" id="c">
    <!--该属性是字符串或原始类型,使用value赋值-->
    <property name="brand" value="宝马"></property>
    <!--name并不是类中是属性名,而是该属性对应的getXXX()方法中XXX的名称-->
    <!--如Car类中有color属性,但get方法名为getColo(),这里就要写为colo-->
    <property name="colo" value="白色"></property>
</bean>

<!--注入Person类对象并用set方式注入其属性-->
<bean class="com.hqyj.spring01.Person" id="p1">
    <property name="name" value="王海"></property>
    <property name="age" value="22"></property>
    <!--属性是引用类型,需要通过ref赋值,值为另外的bean的id ref即references-->
    <property name="car" ref="c"></property>
</bean>

构造方法注入

这种方式注入属性时,类中必须要有相应的构造方法

在bean标签中,加入<constructor-arg></constructor-arg>标签,

该标签的name属性表示构造方法的参数名,index属性表示构造方法的参数索引。

赋值时,原始类型和字符串用value,引用类型用ref。

<!--注入Person类对象并用构造方法注入其属性-->
<bean class="com.hqyj.spring01.Person" id="p2">
    <!--constructor-arg表示构造方法参数  name是参数名 index是参数索引-->
    <constructor-arg name="name" value="张明"></constructor-arg>
    <constructor-arg index="1" value="20"></constructor-arg>
    <constructor-arg name="car" ref="c"></constructor-arg>
</bean>

复杂属性注入

/*
* 定义电影类
* */
public class Movie {
    //电影名
    private String movieName;
    //导演
    private String director;
    //时长
    private int duration;
    //主演
    private List<String> playerList;
    //类型
    private String movieType;
    //放映时间,最终格式为yyyy/MM/dd HH:mm:ss
    private String showTime;
}

List类型的属性

<!--注入Movie对象-->
<bean class="com.hqyj.spring01.Movie" id="movie1">
    <property name="movieName" value="夏洛特烦恼"></property>
    <property name="director" value="闫非、彭大魔"></property>
    <property name="duration" value="87"></property>
    <!--List类型属性赋值-->
    <property name="playerList" >
        <!--使用list标签-->
        <list>
            <!--如果集合中保存的是引用类型,使用ref标签-->
            <!--如果集合中保存的是原始类型或字符串,使用value标签-->
            <value>沈腾</value>
            <value>艾伦</value>
            <value>马丽</value>
        </list>
    </property>
    <property name="movieType" value="喜剧"></property>
    <property name="showTime" value="2010/06/01 0:0:0"></property>
</bean>

Set类型的属性

<!--注入PetShop对象-->
<bean class="com.hqyj.spring01.test3.PetShop" id="petShop">
    <property name="shopName" value="xxx宠物店"></property>

    <property name="petSet">
        <!--Set类型的属性-->
        <set>
            <!--如果中保存的是原始类型或字符串,使用value子标签-->
            <!--如果保存的是引用类型,使用ref子标签加bean属性设置对应的id-->
            <ref bean="p1"></ref>
            <ref bean="p2"></ref>
            <ref bean="p3"></ref>
        </set>
    </property>
</bean>

Map类型的属性

<!--注入Cinema对象-->
<bean class="com.hqyj.spring01.Cinema" id="cinema">
    <property name="name" value="万达影城"></property>
    <property name="timeTable">
        <!--Map类型属性赋值,标明键和值的类型-->
        <map key-type="java.lang.Integer" value-type="com.hqyj.spring01.Movie">
            <!--entry标签表示键值对 如果键值对都是原始类型或字符串,使用key和value-->
            <!--如果键值对中有引用类型,使用key-ref或value-ref-->
            <entry key="1" value-ref="movie1"></entry>
            <entry key="2" value-ref="movie2"></entry>
        </map>
    </property>
</bean>

属性值如果通过某个方法调用而来

如使用String保存yyyy/MM/dd格式的日期,需要通过SimpleDateFormat对象调用parse()方法而来

<!--
       SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
   -->
<!--注入SimpleDateFormat对象,设置日期格式-->
<bean class="java.text.SimpleDateFormat" id="sdf">
    <!--如果构造方法注入时,该构造方法只有一个参数,可以不用写name或index-->
    <constructor-arg value="yyyy/MM/dd"></constructor-arg>
</bean>
<!--
        Date now = new Date();
    -->
<!--注入Date对象-->
<bean class="java.util.Date" id="now"></bean>

<!--注入Pet对象-->
<bean class="com.hqyj.spring01.test3.Pet" id="p1">
    <property name="petType" value="哈士奇"></property>
    <property name="petNickName" value="小哈"></property>
    <!--使用当前时间作为该属性的值,以yyyy/MM/dd-->
    <!--
            sdf.format(now);
        -->
    <property name="petBirthday">
        <!--如果某个值是通过某个bean调用了某个方法而来-->
        <bean factory-bean="sdf" factory-method="format">
            <!--方法的实际参数-->
            <constructor-arg ref="now"></constructor-arg>
        </bean>
    </property>
</bean>

属性自动注入autowire

以上所有案例中,如果要在A对象中注入一个引用类型的对象B,都是手动将对象B注入到对象A中。

如在Person中注入Car对象,在Cinema中注入Movie等。

这种情况下,如果当某个被注入的bean的id更改后,所有引用了该bean的地方都要进行修改。

所以将手动注入更改为自动注入(自动装配),就无需添加相应的<property>标签,甚至可以无需定义bean的id。

实现自动注入

在某个bean标签中,加入autowire属性,设置值为"byName"或"byType"。通常设置为"byType"。

Car类

package com.hqyj.spring01.test1;

import java.util.HashMap;

public class Car {
    private String brand;
    private String color;

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", color='" + color + '\'' +
                '}';
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

Person类

package com.hqyj.spring01.test1;

public class Person {
    private String name;
    private int age;
    private Car car;

    @Override
    public String toString() {
        return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            ", car=" + car +
            '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Car getCar() {
        return car;
    }
    //自动注入时,必须要有该方法
    //byType方式自动注入,会自动在容器中寻找该方法参数类型
    //byName方式自动注入,会自动在容器中寻找该方法setCar中的car这个id
    public void setCar(Car car) {
        this.car = car;
    }
}

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">

    <!--在容器中注入Car类型的bean-->
    <bean class="com.hqyj.spring01.test1.Car" id="car"> 
        <property name="brand" value="宝马"></property>
        <property name="colo" value="白色"></property>
    </bean>



    <!--注入Person类的bean-->
    <!--autowire="byType"表示自动检测该类中是否需要使用引用类型参数,如果容器中正好有唯一的一个对应类型的bean,就会自动赋值给对应的属性-->
    <bean class="com.hqyj.spring01.test1.Person" id="person" autowire="byType">
        <property name="name" value="赵明"></property>
        <property name="age" value="26"></property>
    </bean>

     <!--autowire="byName"表示自动检测该类中的setXXX方法,如果某个setXXX方法的XXX和容器某个对象的id对应,就会自动赋值给对应的属性-->
    <bean class="com.hqyj.spring01.test1.Person" id="person2" autowire="byName">
        <property name="name" value="王海"></property>
        <property name="age" value="26"></property>
    </bean>
</beans>

Main

package com.hqyj.spring01.test1;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        Person person = context.getBean("person", Person.class);
        //此时打印时,会输出姓名、年龄和Car对象
        System.out.println(person);
        context.close();
    }
}

autowire属性的值

  • byType

    • 类中要有被注入的属性的setter()方法
    • 被自动注入的对象可以没有id
    • Spring容器中,某个对象的类型要与该setter()方法的参数类型一致,且容器中只有一个该类型的对象。
      • 如setCar(Car c),Spring就会自动在容器中寻找类型为Car的对象自动装配
  • byName

    • 类中要有被注入的属性的setter()方法
    • 被自动注入的对象必须要有id
    • 实际是根据setXXX()方法set后的单词XXX关联
      • 如setCar(Car c),Spring就会自动在容器中寻找id为car的对象自动装配

在Web项目中,可以利用自动装配,在控制层中自动装配业务逻辑层的对象,在业务逻辑层中自动装配数据访问层的对象。

属性自动注入练习

在电影院案例的基础上

1.创建一个dao层CinemaDao类,包含Cinema对象,定义一个fun()方法,输出Cinema对象

package com.hqyj.spring01.test2.dao;

import com.hqyj.spring01.test2.Cinema;

/*
* 定义数据访问层
* 模拟查询数据库得到结果
* */
public class CinemaDao {

    private Cinema cinema;

    public void setCinema(Cinema cinema) {
        this.cinema = cinema;
    }

    public void fun(){
        System.out.println(cinema);
    }
}

2.创建一个controller层CinemaController类,包含CinemaDao对象,定义方法fun()调用CinemaDao中的fun()方法

package com.hqyj.spring01.test2.controller;

import com.hqyj.spring01.test2.dao.CinemaDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/*
* 定义控制层类,该层需要使用下一层dao中的方法
* */
public class CinemaController {
    //这里无需创建对象,让Spring自动注入CinemaDao对象
    private CinemaDao cinemaDao;

    public void setCinemaDao(CinemaDao dao) {
        this.cinemaDao = dao;
    }

    public void fun(){
        cinemaDao.fun();
    }
}

最终在Main方法中获取Controller对象后,调用fun()

public static void main(String[] args) {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("movie.xml");
    CinemaController controller = context.getBean("controller", CinemaController.class);
    controller.fun();
}

配置文件

<!--省略movie对象-->

<!--注入Cinema对象-->
<bean class="com.hqyj.spring01.test2.Cinema" id="cinema">
    <property name="name" value="万达影城"></property>
    <property name="timeTable">
        <!--Map类型属性赋值,标明键和值的类型-->
        <map key-type="java.lang.Integer" value-type="com.hqyj.spring01.test2.Movie">
            <!--entry标签表示键值对 如果键值对都是原始类型或字符串,使用key和value-->
            <!--如果键值对中有引用类型,使用key-ref或value-ref-->
            <entry key="1" value-ref="movie1"></entry>
            <entry key="2" value-ref="movie2"></entry>
        </map>
    </property>
</bean>

<!--该类中有一个Cinema类型的属性,这里使用了autowire=byType自动装配,会在容器中寻找Cinema类型的对象自动赋值给属性-->
<bean class="com.hqyj.spring01.test2.dao.CinemaDao" autowire="byType"></bean>

<!--该类中有一个CinemaDao类型的属性,这里使用了autowire=byType自动装配,会在容器中寻找CinemaDao类型的对象自动赋值给属性-->
<bean class="com.hqyj.spring01.test2.controller.CinemaController" id="controller" autowire="byType"></bean>

Spring核心注解

在Spring配置文件中加入

<!--设置要扫描的包,扫描这个包下所有使用了注解的类-->
<context:component-scan base-package="com.hqyj.spring02.bookSystem"></context:component-scan>

类上加的注解

  • @Component
    • 当一个类不好归纳时,定义为普通组件
  • @Controller
    • 定义一个类为控制层组件
  • @Service
    • 定义一个类为业务层组件
  • @Repository
    • 定义一个类为持久层(数组访问层)组件
  • @Lazy/@Lazy(value=true)
    • 设置该类为懒加载。
  • @Scope(value=“singleton/prototype”)
    • 设置为单例/原型模式。

说明

以上注解公共特点

  • 都是将对应类的对象注入到Spring容器中,用于替换配置文件中的bean标签
  • 都默认是单例模式非懒加载
  • 默认注入的对象id为当前类的类名首字母小写形式
    • 如在BookDao类上添加,id默认为bookDao
  • 可以通过注解的value属性自定义注入的对象的id名,如@Component(value=“key”)表示注入的对象id为key

属性上加的注解

  • @Autowired

    • 优先使用byType方式从Spring容器中获取对应类型的对象自动装配。先检索Spring容器中对应类型对象的数量,如果数量为0直接报错;数量为1直接装配

      数量大于1,会再尝试使用byName方式获取对应id的对象,但要配合@Qualifier(value=“某个对象的id”)一起使用,指定id进行装配

  • @Qualifier(value=“某个对象的id”)

    • 配合@Autowired注解,使用byName方式获取某个对象id的bean进行装配
  • @Resource(name=“某个对象的id”)

    • 该注解相当于@Autowired+@Qualifier(value=“某个对象的id”)

    • 优先使用byName方式,从Spring容器中检索name为指定名的对象进行装配,如果没有则尝试使用byType方式,要求对象有且只有一个,否则也会报错。

说明

  • 如果要在某个类中使用Spring容器中的某个对象时,只需定义成员变量,无需创建对象,通过@Autowired或@Resource注解进行自动装配

  • 实际开发中,绝大部分情况下,需要自动装配对象有且只有一个,并且命名规范,所以@Autowired或@Resource区别不是很大。@Autowired优先使用byType方式,@Resource优先使用byName方式

  • 如果@Resource不能使用,是因为缺少javax.annotation包,需要引入对应依赖

    <dependency>
        <groupId>javax.annotation</groupId>
        <artifactId>javax.annotation-api</artifactId>
        <version>1.3.2</version>
    </dependency>
    

在Web项目中使用Spring

1.创建基于Maven的web-app项目

2.添加依赖

<!--servlet-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>
<!--spring容器-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.23</version>
</dependency>
<!--web集成spring-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.23</version>
</dependency>

3.在main目录下创建java和resources目录,修改web.xml版本为4.0

4.在resources目录下创建Spring配置文件application.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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--扫描使用了Spring注解的根包-->
    <context:component-scan base-package="com.hqyj.springweb"></context:component-scan>
</beans>

5.创建一个类,使用@Componet注解将其注入到Spring容器中

package com.hqyj.springweb.entity;

import org.springframework.stereotype.Component;

@Component
public class Pojo {
    public void fun(){
        System.out.println("hello springweb!");
    }
}

如何初始化Spring容器(解析Spring配置文件)

在控制台应用程序中,可以在main方法中通过ClassPathXmlApplicationContext来解析Spring配置文件,初始化Spring容器。

在web项目中没有main方法,只有servlet中的service方法,如果在service方法中创建ClassPathXmlApplicationContext对象,会每次访问都执行。

而Spring容器只需初始化一次,在项目启动时就解析Spring配置文件,全局监听器就是一个很好的选择。

spring-web包中提供了一个ContextLoaderListener类,它实现了ServletContextListener,属于项目级别的全局监听器。

这个类需要一个contextConfigLocation参数,表示要解析的Spring配置文件的路径。

这个监听器会在项目启动时,读取指定的Spring配置文件路径,并且创建WebApplicationContext对象,即Spring容器。

6.在web.xml中配置监听器用于初始化Spring容器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置监听器ContextLoaderListener-->
    <listener>
        <!--监听器全限定名-->
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!--定义全局参数contextConfigLocation用于读取Spring配置文件-->
    <context-param>
        <!--参数名固定contextConfigLocation-->
        <param-name>contextConfigLocation</param-name>
        <!--只是Spring配置文件的路径 classpath:表示从根目录出发-->
        <param-value>classpath:application.xml</param-value>
    </context-param>
</web-app>

7.创建一个Servlet,访问该Servlet,获取Spring容器,从容器中获取注入的对象

package com.hqyj.springweb.controller;

import com.hqyj.springweb.entity.Pojo;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/hello")
public class MyServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取Spring容器
        WebApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        //从容器中获取某个bean
        Pojo pojo = app.getBean("pojo", Pojo.class);
        pojo.fun();
    }
}

在Spring-web项目中使用Spring-jdbc

Spring提供了一套自己的JDBC解决方案,相较于自己写的JDBC,省去了繁琐的过程(获取连接、关闭),简化了对数据库的操作。

Spring-jdbc后续会用其他持久层框架代替,如MyBatis、JPA。

1.引入相关依赖

当前案例中核心的依赖是 spring-jdbc

<!--servlet-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>
<!--mysql-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.31</version>
</dependency>
<!--jstl-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<!--spring容器-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.23</version>
</dependency>
<!--web集成spring-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.23</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.23</version>
</dependency>

2.创建包结构(controller、service、dao、entity),创建实体类

public class Hero {
    private int id;
    private String name;
    private String position;
    private String sex;
    private int price;
    private String shelfDate;
    //省略get/set、构造方法、toString等
}

3.在Spring配置文件中配置数据源和JDBC模板

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--扫描使用了Spring注解的根包-->
    <context:component-scan base-package="com.hqyj.springweb"></context:component-scan>

    <!--配置数据源-->
    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
        <!--配置MySQL驱动-->
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <!--配置MySQL地址-->
        <property name="url" value="jdbc:mysql://localhost:3306/gamedb?serverTimezone=Asia/Shanghai"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!--配置JDBC模板-->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <!--设置要操作的数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

4.数据访问层(dao)的写法

package com.hqyj.springweb.dao;

import com.hqyj.springweb.entity.Hero;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public class HeroDao {

    @Autowired
    //通过jdbc模板对象实现CRUD
    private JdbcTemplate jdbcTemplate;

    /*
     * 查询所有
     * */
    public List<Hero> queryAll() {
        //定义sql语句
        String sql = "select * from hero";
        //BeanPropertyRowMapper对象表示将查询到的字段与指定的类中的属性进行映射
        BeanPropertyRowMapper<Hero> mapper = new BeanPropertyRowMapper<>(Hero.class);
        //query()方法执行查询
        List<Hero> list = jdbcTemplate.query(sql, mapper);
        return list;
    }

    /*
     * 删除
     * */
    public boolean delete(int id) {
        String sql = "delete from hero where id=?";
        int i = jdbcTemplate.update(sql, id);
        return i > 0;
    }

    /*
     * 修改
     * */
    public boolean update(Hero hero) {
        String sql = "update hero set name=?,sex=?,position=?,price=?,shelf_date=? where id=?";
        int i = jdbcTemplate.update(sql, hero.getName(), hero.getSex(), hero.getPosition(), hero.getPrice(), hero.getShelfDate(), hero.getId());
        return i > 0;
    }

    /*
     * 添加
     * */
    public boolean insert(Hero hero) {
        String sql = "insert  into hero values(null,?,?,?,?,?)";
        int i = jdbcTemplate.update(sql, hero.getName(), hero.getSex(), hero.getPosition(), hero.getPrice(), hero.getShelfDate());
        return i > 0;
    }

    /*
     * 根据id查询
     * */
    public Hero findById(int id) {
        String sql = "select * from hero where id=?";
        BeanPropertyRowMapper<Hero> mapper = new BeanPropertyRowMapper<>(Hero.class);
        Hero hero = jdbcTemplate.queryForObject(sql, mapper, id);
        return hero;
    }
}

忽略后续service、servlet、jsp等;

JDBCTemplate常用方法

方法作用说明
query(String sql,RowMapper mapper)无条件查询返回值为List集合
update(String sql)无条件更新(删除、修改)返回值为受影响的行数
query(String sql,RowMapper mapper,Object… objs)条件查询可变参数为?的值
update(String sql,Object… objs)条件更新(增加、删除、修改)可变参数为?的值
queryForObject(String sql,RowMapper mapper)无条件查询单个对象返回值为指定对象
queryForObject(String sql,RowMapper mapper,Object… objs)条件查询单个对象返回值为指定对象
execute(String sql)执行指定的sql无返回值

AOP

概念

Process Oriented Programming 面向过程编程POP

Object Oriented Programming 面向对象编程OOP

Aspect Oriented Programming 面向切面编程AOP

以上都是编程思想,但AOP不是OOP和POP的替代,而是增强、拓展和延伸。主流编程思想依然是OOP。

作用

在传统的OOP思想中,我们将程序分解为不同层次的对象,通过封装、继承、多态等特性,

将对象组织成一个整体来完成功能。但在某些场景下,OOP会暴露出一些问题。

如在处理业务中,除了核心的业务代码外,通常还会添加一些如果参数验证、异常处理、事务、记录日志等操作。

这些内容会分散在各个业务逻辑中,依旧会出现大量重复操作。如果将这些重复的代码提取出来,在程序编译运行时,

再将提出来的内容应用到需要执行的地方,就可以减少很多代码量。方便统一管理,更专注于核心业务。

简单来说,就是将不同位置中重复出现的一些事情拦截到一处进行统一处理。

如图,这是用户模块的不同操作,其中除了核心业务之外,其余都是重复代码

将相同的位置进行切分

切分后

使用

一个普通业务层service类

package com.hqyj.springweb.service;

import com.hqyj.springweb.dao.HeroDao;
import com.hqyj.springweb.entity.Hero;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/*
 * 一个普通业务层service类
 * */
@Service
public class HeroService {
    @Autowired
    private HeroDao dao;


    public List<Hero> queryAll() {
        //在没有使用AOP时,比如要在执行前记录日志
        //System.out.println("============");
        //System.out.println("操作开始执行"+new Date());

        System.out.println("xxx进行了查询操作");
        //核心业务代码
        List<Hero> list=dao.queryAll();

        //在没有使用AOP时,比如要在执行后记录日志
        //System.out.println("操作执行结束"+new Date());
        //System.out.println("============");
        return list;
    }

    public void delete(int id) {

        //在没有使用AOP时,比如要在执行前记录日志
        //System.out.println("============");
        //System.out.println("操作开始执行"+new Date());

        System.out.println("xxx删除了"+id);
        //核心业务代码
        dao.delete(id);

        //在没有使用AOP时,比如要在执行后记录日志
        //System.out.println("操作执行结束"+new Date());
        //System.out.println("============");

    }

}

提取在执行某个业务方法前后要执行的公共代码

package com.hqyj.springweb;

import org.springframework.stereotype.Component;

import java.util.Date;

/*
* 模拟各个业务中都要执行的记录日志的操作,单独定义在一个类中
* 将其注入到Spring容器中
* */
@Component
public class LogOperate {

    public void start(){
        System.out.println("============");
        System.out.println("操作开始执行"+new Date());
    }


    public void end(){
        System.out.println("操作执行结束"+new Date());
        System.out.println("============");
    }
}

在项目中添加AOP所需依赖

<!-- 切面织入包 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.19</version>
    <scope>runtime</scope>
</dependency>

在Spring配置文件中配置AOP

<!--配置AOP-->
<aop:config>
    <!--配置切面(配置拦截后要做的事情)-->
    <!--ref是提取出的公共代码所在类LogOperate在Spring容器中的id。由于使用了@Componet注解,默认id名为类名首字母小写-->
    <aop:aspect ref="logOperate">
        <!--配置切点(对哪个包中的哪个类、哪个方法执行的时候进行拦截)-->
        <!--id是切点名,自定义-->
        <aop:pointcut id="logPointcut" expression="execution(* com.hqyj.springweb.service..*.*(..))"/>
        <!--配置前置增强(切点之前执行的方法)-->
        <aop:before method="start" pointcut-ref="logPointcut"></aop:before>
        <!--配置后置增强(切点之后执行的方法)-->
        <aop:after method="end" pointcut-ref="logPointcut"></aop:after>
    </aop:aspect>
</aop:config>

上面的配置代码中execution是切点指示符,小括号中是一个切点表达式,用于配置需要切入后增强处理的方法的特征

execution(* com.hqyj.springweb.service..*.*(..))
第一个*表示匹配所有方法的返回值
com.hqyj.springweb.service  表示匹配指定包
..* 表示匹配该包下的所有子包及所有类
.* 表示匹配该类下的所有方法
(..) 表示匹配所有参数类型和个数

整体表示在com.hqyj.springweb.service包中的一切类中的一切方法在执行前后,都会进入切入增强处理。

MVC

MVC设计思想并不是某个语言特有的设计思想,而是一种通用的模式。

是将一个应用分为三个组成部分:Model模型,View视图,Controller控制器

这样会降低系统的耦合度,提高它的可扩展性和维护性。

SpringMVC

在Web阶段中,控制层是由Servlet实现,传统的Servlet,需要创建、重写方法、配置映射。使用时极不方便,SpringMVC可以替换Servlet

SpringMVC是Spring框架中位于Web开发中的一个模块,是Spring基于MVC设计模式设计的轻量级Web框架。

SpringMVC提供了一个DispatcherServlet的类,是一个Servlet。它在指定映射(通常设置为/或*.do)接收某个请求后,调用相应的模型处理得到结果,再通过视图解析器,跳转到指定页面,将结果进行渲染。

原理大致为:配置SpringMVC中的DispatcherServlet将其映射设置为/或.do。*

如果是/表示一切请求先经过它,如果是*.do表示以.do结尾的请求先经过它,

它对该请求进行解析,指定某个Controller中的某个方法,这些方法通常返回一个字符串,

这个字符串是一个页面的名称,再通过视图解析器,将该字符串解析为某个视图的名称,跳转到该视图页面。

详细流程

使用SpringMVC

1.创建webapp项目

  • 修改web.xml版本为4.0
  • 创建java和resources目录
  • 创建包结构

2.添加依赖

<!-- spring-webmvc -->
<!-- 这个依赖会包含spring-web和spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.23</version>
</dependency>

3.配置初始化Spring

  • 在resources目录下创建Spring配置文件application.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:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--设置扫描使用了注解的根包-->
        <context:component-scan base-package="com.hqyj.springmvc"></context:component-scan>
    </beans>
    
  • 在web.xml中使用监听器ContextLoaderListener初始化Spring

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!--配置全局监听器初始化Spring-->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        <!--定义全局参数读取Spring配置文件-->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application.xml</param-value>
        </context-param>
    </web-app>
    

4.配置SpringMVC

  • 在resources目录下创建配置SpringMVC的配置文件springmvc.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:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
  <!--扫描控制层所在的包-->
  <context:component-scan base-package="com.hqyj.springmvc.controller"></context:component-scan>

  <!--配置内部资源视图解析器-->
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <!--最终控制层跳转的页面所在的路径及页面自身后缀名-->
      <!--jsp页面不建议直接通过浏览器访问。在WEB-INF目录下在资源,无法通过浏览器直接方法,所以将jsp保存在WEB-INF目录下,最好创建一个pages-->
      <property name="prefix" value="/WEB-INF/pages/"></property>
      <!--现阶段使用jsp输出数据,所以后缀为.jsp-->
      <property name="suffix" value=".jsp"></property>
  </bean>
- 在web.xml中配置DispatcherServlet

  - 将该Servlet的请求映射设置为/,表示所有请求都会访问该Servlet,由该Servlet再进行分发

  ```xml
  <!--配置DispatcherServlet-->
  <servlet>
      <servlet-name>dispatcherServlet</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <!--设置该Servlet的初始化参数,用于读取SpringMVC配置文件-->
      <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:springmvc.xml</param-value>
      </init-param>
  </servlet>
  <servlet-mapping>
      <servlet-name>dispatcherServlet</servlet-name>
      <!--设置该servlet的映射为/或*.do-->
      <url-pattern>/</url-pattern>
  </servlet-mapping>

5.在WEB-INF目录下创建一个pages目录,在其中创建一个welcome.jsp页面

  • 通常jsp页面不允许被浏览器直接访问,需要保存在WEB-INF目录下

6.编写控制层代码

  • 在controller包下创建一个类,加上@Controller注解

  • 该类中定义的方法方法加入@RequestMapping()注解表示访问该方法的映射

  • 该类中定义的方法返回值通常为字符串,表示某个页面的名称,也可以是另一个controller中的方法映射名

    package com.hqyj.springmvc.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    //加入@Controller注解表示该类是一个控制层类,替换之前的servlet
    @Controller
    //该注解如果只设置请求映射,直接填字符串
    @RequestMapping("/first")
    public class HelloController {
    
        //该注解如果还有其他参数要设置,路径用path赋值
        @RequestMapping(path="/hello")
        public String hello(){
            //返回的字符串是某个页面的名称或另一个控制层中方法的请求映射
            return "welcome";
        }
    }
    

将项目部署到tomcat后,访问http://localhost:8080/SpringMVC_war_exploded/first/hello,即可跳转到指定页面

SpringMVC相关注解

  • @Controller

    • 只能写在类上,表示该类属于一个控制器
  • @RequestMapping(“/请求映射名”)/@RequestMapping(value=“/请求映射名”)/@RequestMapping(path=“/请求映射名”)

    • 该注解可以写在类或方法上。写在类上用于区分功能模块,写在类上用于区分具体功能
    • 默认写一个属性或value或path后的值,都表示访问该类或该方法时的请求映射
  • @RequestMapping(value=“/请求映射名”,method=RequestMethod.GET/POST/PUT/DELETE)

    • method属性表示使用哪种请求方式访问该类或该方法
    • 如果注解中不止一个属性,每个属性都需要指定属性名
  • **@GetMapping(“/请求映射名”)**相当于@RequestMapping(value=“/请求映射名”,method=RequestMethod.GET)

    • post、put、delete同理
    • @GetMapping只能写在方法上
  • @PathVariable

    • 该注解写在某个方法的某个形参上

    • 通常配合@RequestMapping(“/{path}”)获取请求时传递的参数

      @RequestMapping("/{path}")
      public String fun(@PathVariable("path") String pageName){
          return pageName;
      }
      //当前方法如果通过"localhost:8080/项目名/hello"访问,就会跳转到hello.jsp
      //当前方法如果通过"localhost:8080/项目名/error"访问,就会跳转到error.jsp
      //映射中的/{path}就是获取路径中的hello或error,将其赋值给形参
      //通常用于跳转指定页面
      
  • @RequestParam(value=“传递的参数名”,defaultValue =“没有传递参数时的默认值”)

    • 该注解写在某个方法的某个参数上
    • 用于获取提交的数据,可以设置默认值在没有提交数据时使用

控制层中获取请求时传递的参数

  • controller中方法的形参名和表单的name或?后的参数名一致

    表单或a标签

    <form action="${pageContext.request.contextPath}/first/sub">
        <input type="text" name="username">
        <input type="text" name="password">
        <input type="submit" >
    </form>
    
    <a href="${pageContext.request.contextPath}/first/sub?username=admin">xxx</a>
    

controller

public String login(String username,int password){
    //此时可以正常获取
    //无关数据类型,但是提交数据时必须是对应的类型,否则会有400错误
}
  • controller中方法的形参名和表单的name或?后的参数名不一致

    controller

    public String login(@RequestParam(value="username",defaultValue="admin")String name,@RequestParam("username")int pwd){
        //如果没有提交数据时,会使用设置的默认值
    }
    
  • 如果传递的参数都是某个实体类中的属性时

    User类

    package com.hqyj.springmvc.entity;
    
    public class User {
        private String username;
        private String password;
    
        @Override
        public String toString() {
            return "User{" +
                    "username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    '}';
        }
    
        public User() {
        }
    
        public User(String username, String password) {
            this.username = username;
            this.password = password;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }
    

    controller

    @RequestMapping("/login")
    public String login(User user){
        //某个实体类的属性和提交的参数名一致时,可以直接将对象作为形参,会自动将对应参数赋值给对应属性
        System.out.println(user);
        return "";
    }
    

解决提交数据时的中文乱码

使用过滤器解决中文乱码。

在web.xml中配置spring-web提供的CharaterEncodingFilter过滤器

<!--定义解决中文乱码的过滤器CharacterEncodingFilter-->
<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--设置编码格式-->
    <init-param>
        <!--该过滤器需要设置一个encoding属性,用于设置编码格式-->
        <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>

将数据保存到作用域中

  • 在controller中的某个方法的参数列表里添加Model参数

    • 调用Model对象的addAttribute(String str,Object obj)方法,将某个obj对象保存在request作用域中,命名为str
    @RequestMapping("/queryAll")
    public String queryAll(Model model){
        ArrayList list = new ArrayList<>();
        list.add("qwe");
        list.add(123);
        list.add(true);
        list.add("哈哈");
        //将list保存到request作用域中
        model.addAttribute("list",list);
        //这种跳转属于请求转发
        return "welcome";
    }
    
  • 在controller中的某个方法的参数列表里添加ModelAndView参数,同时将该方法的返回值设置为ModelAndView类型

    • 使用ModelAndView对象调用addObject(String str,Object obj),将某个obj对象保存在request作用域中,命名为str
    • 使用ModelAndView对象调用setViewName(String viewName),跳转到viewName页面
    package com.hqyj.springmvc.controller;
    
    import com.hqyj.springmvc.service.HeroService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.SessionAttributes;
    import org.springframework.web.servlet.ModelAndView;
    

@Controller
@RequestMapping(“/hero”)
//如果要将数据保存到session中,使用该注解定义session中对象的名称
@SessionAttributes({“str1”,“str2”})
public class HeroController {
@Autowired
private HeroService heroService;

  @RequestMapping("/queryAll")
  public String queryAll(Model model){
      model.addAttribute("list", heroService.queryAll());
      return "heroList";
  }

  @RequestMapping("/showAll")
  public ModelAndView queryAll(ModelAndView mav){
      //默认保存查询出的对象到request中,命名为list
      mav.addObject("list",heroService.queryAll());
      //也可以保存到session
      //mav.addObject("str1",heroService.queryAll());
      //设置要跳转的页面
      mav.setViewName("heroList");
      return mav;
  }

}

**数据都是保存在request作用域中**,在页面中使用jsp内置对象或EL获取。

如果要保存到session中,需要在类上加入@SessionAttributes({"str1","str2"})注解,str1,str2表示保存到session中的对象的名称,再在方法中,使用

Model对象的addAttribute(String str,Object obj)将其保存在session中。



# SpringMVC中的跳转

- 控制层跳转到某个jsp页面

- 在控制层中定义方法,这种方式跳转,属于请求转发

- 如果要使用重定向跳转,在页面名之前添加"redirect:"

  ```java
  @RequestMapping("/hello")
  public String hello(){
      //返回页面名称
      return "hello";//请求转发
  }

  @RequestMapping("/hello")
  public ModelAndView hello(ModelAndView mav){
      //设置页面名称
      mav.setViewName("hello");
      return mav;
  }
  • 在springmvc配置文件中

    <mvc:view-controller path="请求映射" view-name="页面名称"></mvc:view-controller>
    <!-- 访问项目根目录,跳转到welcome.jsp页面 -->
    <mvc:view-controller path="/" view-name="welcome"></mvc:view-controller>
    <!-- 这个标签使用时,会让@RequesMapping失效,如果要共存,添加以下标签 -->
    <!--来自于xmlns:mvc="http://www.springframework.org/schema/mvc" -->
    <mvc:annotation-driven></mvc:annotation-driven>
    
  • 控制层跳转到另一个控制层中的方法

  • 方法的返回值为"redirect/forward:另一个控制层中方法的映射名"

    @RequestMapping("/hello")
    public String hello(){
        return "redirect:hero";//使用重定向的方式,跳转到映射名为hero的控制层
        return "forward:hero"//使用请求转发的方式,跳转到映射名为hero的控制层
    }
    
  • jsp页面跳转另一个jsp页面

  • 当前项目中jsp页面都在WEB-INF下,无法直接访问,a标签同样如此,只能通过控制层跳转页面

  • 定义用于跳转页面控制层类

    package com.hqyj.springmvc.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /*
     * 定义一个用于跳转指定页面或controller的控制层
     * */
    @Controller
    public class ToPageController {
    
        /*
            项目启动时或直接访问根目录,跳转到指定的controller
        */
        @RequestMapping("/")
        public String toIndex() {
            return "redirect:/hero/queryAll";
        }
    
        /*
        * 这个方法的作用:会将请求中第一个/后的单词截取出来命名为path赋值给参数page
        * 如 localhost:8080/web/hero,就会识别出hero,return "hero";
        * 就会跳转到 /WEB-INF/pages/hero.jsp页面
        * */
        @RequestMapping("/{path}")
        public String toPage(@PathVariable("path") String page) {
            return page;
        }
    }
    
  • 这时在页面中这样跳转

    <%--这个路径实际是/项目名/addHero,会截取addHero,跳转到/项目名/WEB-INF/pages/addHero.jsp--%>
    <a href="${pageContext.request.contextPath}/addHero">添加</a>
    

文件上传

使用apche提供的通用文件上传组件实现。

1.导入所需依赖

<!--文件上传-->
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.4</version>
</dependency>

2.向Spring容器中注入上传文件的核心类

在application.xml中注入通用多部件解析器CommonsMultipartResolver

<!--注入上传文件的核心类:通用多部件解析器CommonsMultipartResolver-->
    <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
        <!--设置上传的单个文件最大字节 10M:1024*1024*10-->
        <property name="maxUploadSizePerFile" value="10485760"></property>
    </bean>

3.上传页面的表单

<!--上传的表单控件使用file-->
<!--提交方式为post-->
<!--添加enctype="multipart/form-data"属性-->
<form action="/upload" method="post" enctype="multipart/form-data">
    请选择图片<input type="file" name="uploadFile">
    <input type="submit" value="上传">
</form>

4.控制层获取上传的文件

package com.hqyj.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

@Controller
@RequestMapping("/admin")
public class UploadController {

    @RequestMapping("/upload")
    public String upload(@RequestParam("uploadFile") MultipartFile uploadFile) throws IOException {//uploadFile就是上传的文件对象
        //获取上传的文件名
        String oldName = uploadFile.getOriginalFilename();
        //得到源文件的后缀名
        String prefix = oldName.substring(oldName.lastIndexOf("."));
        //有可能不同的人上传的文件名相同,所以获取源文件的后缀后,生成一个随机文件名,拼接新文件名。
        String newName = UUID.randomUUID()+prefix;
        //创建一个文件File对象,
        File file = new File("d:\\上传文件夹", newName);
        //将文件写入硬盘中
        uploadFile.transferTo(file);
        return "welcome";
    }
}

如果要在某个页面中显示上传的文件,要配置一个虚拟目录

配置Spring+SpringMVC时用到的关键类

  • 在web.xml中配置Spring全局监听器

    • ContextLoaderListener

      <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      
      <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:application.xml</param-value>
      </context-param>
      
  • 在Springmvc配置文件中配置SpringMVC内部资源视图解析器

    • InternalResourceViewResolver

      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
          <property name="prefix" value="/WEB-INF/pages/"></property>
          <property name="suffix" value=".jsp"></property>
      </bean>
      
  • 在web.xml中配置SpringMVC请求分发Servlet

    • DispatcherServlet

      <servlet>
          <servlet-name>dispatcherServlet</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath:springmvc.xml</param-value>
          </init-param>
      </servlet>
      
      <servlet-mapping>
          <servlet-name>dispatcherServlet</servlet-name>
          <url-pattern>/</url-pattern>
      </servlet-mapping>
      
  • 在web.xml中配置字符编码过滤器

    • CharacterEncodingFilter

      <filter>
          <filter-name>encodingFilter</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>encodingFilter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>
      

传的文件,要配置一个虚拟目录

配置Spring+SpringMVC时用到的关键类

  • 在web.xml中配置Spring全局监听器

    • ContextLoaderListener

      <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      
      <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:application.xml</param-value>
      </context-param>
      
  • 在Springmvc配置文件中配置SpringMVC内部资源视图解析器

    • InternalResourceViewResolver

      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
          <property name="prefix" value="/WEB-INF/pages/"></property>
          <property name="suffix" value=".jsp"></property>
      </bean>
      
  • 在web.xml中配置SpringMVC请求分发Servlet

    • DispatcherServlet

      <servlet>
          <servlet-name>dispatcherServlet</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath:springmvc.xml</param-value>
          </init-param>
      </servlet>
      
      <servlet-mapping>
          <servlet-name>dispatcherServlet</servlet-name>
          <url-pattern>/</url-pattern>
      </servlet-mapping>
      
  • 在web.xml中配置字符编码过滤器

    • CharacterEncodingFilter

      <filter>
          <filter-name>encodingFilter</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>encodingFilter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习一个框架知识框架可以按照以下步骤构建: 首先,你可以通过阅读官方文档来了解框架的架构和设计思想。这有助于你对框架的整体认知和理解。你可以尝试结合框架的不同部分来加深理解,并通过自己动手实践一些轮子来验证自己的理解是否正确。这种方式可以帮助你更好地理解框架的工作原理和使用方式。 其次,阅读框架的源代码是学习一个开源框架最强大的一步。通过仔细阅读源代码,你可以深入了解框架的实现细节和内部机制。这有助于你对框架的工作原理有更深入的理解。 最后,将你学到的知识整理成思维导图,并进行总结。你可以将零散的知识点串联起来,形成一个完整的网络。这有助于你更好地理解和记忆所学的知识。你还可以将你的学习成果写成文章并发布出去,以便与他人分享和交流。 通过以上步骤,你可以逐步构建起一个完整的知识框架,更好地理解和应用所学的框架。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [如何学习一个框架](https://blog.csdn.net/mucaoyx/article/details/119123681)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值