Spring与Spring MVC

2020-02-18  本文已影响0人  AxisX

1.Spring简介

Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。 框架的主要优势之一就是其分层架构,允许用户选择使用哪一个组件,同时为J2EE应用程序开发提供集成的框架。Spring包含七个模块(Spring AOP、Spring ORM、Spring Web、Spring Context、Spring Web MVC、Spring Core),以核心容器为基础定义了创建、配置和管理 bean 的方式。

Spring架构

Spring框架所倡导的基于POJO(Plain Old Java Object,简单Java对象)的轻量级开发理念整个 Spring框架构建在Core核心模块之上,它是整个框架的基础。在该模块中,Spring为我们提供 了一个IoC容器(IoC Container)实现,用于帮助我们以依赖注入的方式管理对象之间的依赖关系。AOP模块以AOP的形式增强各POJO的能力,进而补足OOP/OOSD的不足。

2.Spring安装方法

2.1源码安装

本文以windows下的IDEA为例。
(1)首先安装Gradle, 它是一个构建工具,使用Groovy语言,不局限于某种开发平台,基于Gradle可以开发定制自己编译和打包规则。官方安装说明如下:
https://gradle.org/install/
按照说明配置相应的环境变量,在cmd终端输入Gradle测试是否安装成功。
(2)下载Spring源码。从github上下载
https://github.com/spring-projects/spring-framework
在cmd终端中输入gradlew :spring-oxm:compileTestJava进行编译.
(3)导入IDEA。File->New->Project From Existing Sources,选择spring-framework源码文件夹,点击OK,选择Import project from external model,选中Gradle。

2.2 工程安装

各个版本的下载地址如下:
https://maven.springframework.org/release/org/springframework/spring/
下载下来jar包很多,但开发spring至少需要的jar包是spring-aop.jar、spring-beans.jar、spring-context.jar、spring-core.jar、spring-expression.jar还有第三方提供的日志commons-logging.jar。
本文直接用IDEA创建一个spring项目,默认下载的是Spring-5.2.3.RELEASE。

3.相关概念

3.1 IoC

IoC(Inversion of Control),控制反转,别名叫做依赖注入(Dependency injection)


IOC的角色

IOC的主要作用是降低程序间的耦合(依赖关系),将程序所有依赖关系的管理都交给spring来维护。所谓的依赖关系是,在当前类中需要用到其他类的对象时,spring来为我们提供,我们不再需要去写具体的关系代码,只需要在配置文件中进行说明。这种依赖关系的维护也称为依赖注入

首先我们要理解程序间的耦合关系,即程序间的依赖关系,包含了类之间的依赖和方法间的依赖。

3.1.1耦合与解耦

以如下数据库连接代码为例,JDBC代码可参考前面的文章。

//1.注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());//如果没有此依赖类就会编译报错,即不能编译
//2.获取连接;
Connection conn=DriverManager.getConnection(url,user,password);
//3.获取操作数据库的预处理对象
sql="select * form account";
PreparedStatement pstm=conn.prepareStatement(sql);
//4.执行sql得到结果集
ResultSet rs=pstm.executeQuery();
//5.遍历结果集
columnLabel="name";
while(rs.next()){
    System.out.println(rs.getString(columnLabel));
}
//5.释放资源
rs.close();
pstm.close();
conn.close();

可以看到上述代码执行,最先依赖于com.mysql.jdbc.Driver(),如果工程中没有此依赖类整个程序会报错,无法进行编译。这种依赖关系就是耦合。
但是如果我们将注册驱动时的语句

DriverManager.registerDriver(new com.mysql.jdbc.Driver());

换成

Class.forName("com.mysql.jdbc.Driver")//此句没有依赖即为运行时依赖,非编译期依赖。

整个程序不会报错,可以进行编译,运行时才会报错找不到相关依赖。这种降低依赖关系即称为解耦。
在上述的例子中,我们通过使用反射来创建对象,从而避免使用new关键字。之后如果我们想通过class.forName来调用依赖,还要进行配置文件的读取,来获取要创建的对象的全限定类名。

3.1.2 IOC Demo

(1)Bean
Bean在计算机中被解释为可重用组件,用java语言编写的可重用组件被称为javaBean,它的范围大于实体类。并且它需要一个配置文件来配置,通过读取配置文件中的内容,反射创建对象。配置的内容包括(key,value),配置文件可以是xml也可以是Properties。在spring中主要用xml解析。

bean定义对应于组成应用程序的实际对象。通常,要定义:服务层对象,数据访问对象(DAO),表示对象(例如Struts Action实例),基础结构对象(例如Hibernate SessionFactories,JMS Queues等)。


bean definition

(2)Demo(new对象->工厂->Spring IOC的演变)
以一个简单的demo为例,创建一个学生类,设置其getter和setter,再创建相应的课程类HTMLCourse和JavaCourse课程类,学生类调用其接口。

//Student.java
package org.example.entity;
import org.example.factory.CourseFactory;
import org.example.instance.HTMLCourse;
import org.example.instance.ICourse;
import org.example.instance.JavaCourse;

public class Student {
    private int stuNo;
    private String stuName;
    private int stuAge;

//此处省略getter、setter

    public void learnJAVA(){
        ICourse course=new JavaCourse();
        course.learn();
    }
    public void learnHTML(){
        ICourse course=new HTMLCourse();
        course.learn();
    }
}
//ICourse.java
package org.example.instance;
public interface ICourse {
    void learn();
}

//JavaCourse.java
package org.example.instance;
public class JavaCourse implements ICourse {
    @Override
    public void learn() {
        System.out.println("学习java");
    }
}

//HTMLCourse.java
package org.example.instance;
public class HTMLCourse implements ICourse {
    @Override
    public void learn() {
        System.out.println("学习HTML");
    }
}

然后创建Test类,创建Student实例。运行测试实例我们可以得到“学习java”、“学习HTML”的输出。

//Test.java
public class Test {
    public void learnCourse(){
        Student student=new Student();
        student.learnJAVA();
        student.learnHTML();
    }

这是一个最简单的创建并调用对象的demo。但是我们发现如果有很多课程,我们要设置很多的课程类,如HTMLCourse、JavaCourse等。每次调用这些还需要new一个新的对象,非常零散,后期维护起来比较麻烦。所以我们就想到把new对象的方式放到工厂中。
那么我们设立一个工厂类CourseFactory,Student中再去调用课程的时候,直接根据课程name调用

//CourseFactory.java
public class CourseFactory {
    public static ICourse getCourse(String name){
        if (name.equals("JAVA")){
            return new JavaCourse();
        }
        else {
            return new HTMLCourse();
        }
    }
}

//Student.java
    public void learn(String name){
        ICourse course= CourseFactory.getCourse(name);
        course.learn();
    }

//Test.java
    public void learnCourseWithFactory(){
        Student student=new Student();
        student.learn("JAVA");
    }

这里的工厂是一个简单工厂,而Spring的IOC容器是一个超级工厂可以存放任何的对象,如demo中的课程、学生等。在简单的工厂中,我们在测试类想要应用还是需要自己new一个Student对象,但是应用Spring IOC可以直接从容器中获取,通过applicationContext.xml的方式读取。

//applicationContext.xml
<bean id="class" class="org.example.entity.Student">
        <property name="stuNo" value="2"/>
        <property name="stuName" value="lisi"/>
        <property name="stuAge" value="19"/>
</bean>
<bean id="javaCourse" class="org.example.instance.JavaCourse"></bean>
<bean id="htmlCourse" class="org.example.instance.HTMLCourse"></bean>

这样一来,我们可以直接通过读取此配置文件,对应其bean的id来调用相关内容。

    public static void learnCourseWithIOC(){
        ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student=(Student)context.getBean("class");
        student.learn("JAVA");
    }

(3)IOC容器原理
IOC(控制反转)
new一个关键字去创建对象的方式,是应用主动找资源的方式,资源独立性较差,有明显依赖关系。如果通过上述IOC容器中的getBean方法获取对象,应用断开了和资源的联系,由工厂提供应用资源,工厂控制着资源。换句话说,应用把new对象的权利给了IOC容器,能得到的Bean由工厂分配。所以叫控制反转。把创建对象的权利交给框架,降低计算机程序的耦合,但不能完全消除依赖。

DI(Dependency Injection,依赖注入)
后来这种IOC方式也被称为依赖注入(DI),即将属性值注入给属性(xml中的 <property name="stuNo" value="2"/>),然后属性注入给了bean(<bean id="class" class="org.example.entity.Student">),最后将Bean注入给了IOC容器。

Spring的IoC容器所起的作用,如图所示它会以某种方式加载Configuration Metadata(通常也就是XML格式的配置信息),然后根据这些信息绑定整个系统的对象,终组装成 一个可用的基于轻量级容器的应用系统。


IOC加载

Spring 的IoC容器实现以上功能的过程,基本上可以按照类似的流程划分为两个阶段,即容器启动阶段和Bean实例化阶段。容器启动阶段包含加在配置信息、分析配置信息、装备到BeanDefinition及其他后处理。Bean实例化阶段包含实例化对象、装配依赖、生命周期回调、对象其他处理、注册回调接口。

(4)IOC容器类型
SpringIOC有两种容器类型,一个是BeanFactory,另一个是ApplicationContext,ApplicationContext在BeanFactory的基础上构建提供其他高级特性,但同时会要求更多系统资源。

ApplicationContext有三个常用实现类:
(1)ClassPathXmlApplicationContext,可以加载类路径下的配置文件,要求配置文件必须在类路径下,不在的话加载不了
(2)FileSystemXmlApplicationContext,可以加载磁盘任意路径下的配置文件(必须有访问权限)
(3)AnnotationConfigApplicationContext,它用于读取注解创建容器

IOC容器的两个容器类型应用时的差别如下:
ApplicationContext:它在构建核心容器时,创建对象采取的策略是采用立即加载的方式,也就是说,只要一读取完配置文件就马上创建配置文件中配置的对象
BeanFactory:它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式,也就是说,什么时候根据Id获取对象了,什么时候才真正的创建对象。
ApplicationContext适用于单例对象(在实际应用中用的更多),BeanFactory适用于多例对象。

3.1.3创建Bean的方式

在3.1.2中说到,在spring中Bean主要用xml解析。xml的文件格式如下所示

XML配置文件格式

<beans>是XML配置文件中顶层的元素,它下面可以包含0或者1个<description>和多个 <bean>以及<import>或者<alias>
<beans>作为所有<bean>的“统帅”,它拥有相应的属性(attribute)对所辖的<bean>进行统一 的默认行为设置,包括如下几个。

default-lazy-init。其值可以指定为true或者false,默认值为false。用来标志是否对所 有的<bean>进行延迟初始化。
default-autowire。可以取值为no、byName、byType、constructor以及autodetect。默 认值为no,如果使用自动绑定的话,用来标志全体bean使用哪一种默认绑定方式。
default-dependency-check。可以取值none、objects、simple以及all,默认值为none, 即不做依赖检查。
default-init-method。如果所管辖的<bean>按照某种规则,都有同样名称的初始化方法的 话,可以在这里统一指定这个初始化方法名,而不用在每一个<bean>上都重复单独指定。
default-destroy-method。与default-init-method相对应,如果所管辖的bean有按照某种 规则使用了相同名称的对象销毁方法,可以通过这个属性统一指定。

而创建Bean标签主要有三种方式
(1)使用默认构造函数创建
在spring配置文件中使用Bean标签,配以id和class属性之后,且没有其他属性和标签时,采用的就是默认构造函数创建Bean对象,此时如果类中没有默认构造函数,则对象无法创建。
<bean id="accountService" class="com.service.AccountServiceImpl"></bean>

如果类是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数,就要采用第二种方式

(2)使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
<bean id="instanceFactory" class="com.Factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method=“getAccountService”></bean>

(3)使用工厂中的静态方法创建对象(使用某个类中的静态方式创建对象,并存入spring容器)
<bean id="accountService" class="com.factory.StaticFactory" factory-method="getAccountService"></bean>

id属性是标识单个bean定义的字符串。class属性定义bean的类型,并使用完全限定的类名。

除了基于xml的配置,也可以通过@Bean在@Configuration类中使用带注释的方法。这也称为基于注解的配置方法。

3.1.4 依赖注入的方式(Dependency Injection,DI)

在spring中,所有依赖关系(当前类对于其他类的对象的调用,由spring提供管理)的维护,即为依赖注入,依赖注入能注入的数据有三类,基本类型和String、其他bean类型(在配置文件中或者注释中配置过的bean)、复杂类型/几何类型。

依赖注入的方式有如下几类:即构造方法注入(constructor injection)、setter方法注入(setter injection)、p命名空间注入以及接口注入(interface injection)。但是接口注入方式较为死板,逐渐不被应用。

(1)构造方法注入
构造方法注入,被注入对象可以通过在其构造方法中声明依赖对象的参数列表, 让外部(通常是IoC容器)知道它需要哪些依赖对。但是构造方法注入要求类必须有构造函数。
构造注入方法设计到的标签是:constructor-arg,标签出现在bean标签的内部。

<bean id="" class="" >
    <constructor-arg name="name" value="test"></constructor-arg>
    <constructor-arg name="age" value="18"></constructor-arg>
    <constructor-arg name="birthday" value="1970-01-01"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>

//那么上面的birthday的字符串就可以改为对象形式如下,value不能表示引用,需要用ref
<constructor-arg name="birthday" ref="now"></constructor-arg>

标签中的属性包括:

type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引的位置是从0开始
name:用于指定给构造函数中指定名称的参数赋值  (较为常用)
//上述三个用于指定给构造函数中的哪个参数赋值,还要两个属性如下
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据,指的是spring的IOC核心容器中出现过的Bean对象

constructor-arg标签的顺序是与构造方法参数默认对应的,如果顺序不一致,则需要添加type/index/name具体指向。

(2)settler方法注入
对于JavaBean对象来说,通常会通过setXXX()和getXXX()方法来访问对应属性。这些setXXX()方 法统称为setter方法,所以,当前对象只要为其依赖对象所对应的属性添加setter 方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。set方法解决了构造注入的劣势,创建对象时没有明确的限制,可以直接使用默认构造函数。但是也有一定的弊端,即如果有某个成员必须有值,则set方法无法保证一定注入。3.1.2中的demo采用的就是setter方法注入。

    <bean id="class" class="org.example.entity.Student">
        <property name="stuNo" value="2"/>
        <property name="stuName" value="lisi"/>
        <property name="stuAge" value="19"/>
    </bean>

对于更复杂一点的注入,也称为几何类型注入,采用的也是setter方法,包含LIst结构或map结构等,
用于给List结构集合注入的标签:list\array\set、用于map结构集合注入的标签:map\props。结构相同的前提下,标签可以互换

<bean id="accountService3" class="com.servuce.impl.AccountServiceImpl3">
    <property name="myStrs">
        <array>
            <value>AAA</value>
            <value>BBB</value>
            <value>CCC</value>
        </array>
    </property>
    <property name="myList">
        <list>
            <value>AAA</value>
            <value>BBB</value>
            <value>CCC</value>
        </list>
    </property>
    <property name="mySet">
        <list>
            <value>AAA</value>
            <value>BBB</value>
            <value>CCC</value>
        </list>
    </property>
    <property name="myMap">
        <map>
            <entry key="testA" value="AAA"></entry>
            <entry key="testB">
                <value>AAA</value>
            </entry>
        </map>
    </property>
    <property name="myProps">
        <pros>
            <entry key="testC">CCC</entry>
        <pros>
    </property>
</bean>

p命名空间方式 则是在bean标签的最后加上,p:属性名="值"。

3.1.5 注解

使用注解定义bean,可以通过注解的形式将bean以及相应的属性值放入IOC容器。
创建一个StudentDaoImpl类,内容如下,在类前声明@Component("sudentDao")

//StudentDaoImpl .java
@Component("studentDao")
public class StudentDaoImpl implements IStudentDao{
    @Autowired //自动装配
    private IStudentDao studentDao;
    public void addStudent(Student student){
        System.out.println("增加学生");
    }
}

@Component这个注解可以理解为<bean id="studentDao" class="org.example.dao.StudentDaoImpl">,这样一来,我们就不需要再去xml中进行声明。@Component是最宽泛的注解,如果我们要进行细化,Dao层的注解为@Repository,service层的注解为@Service,控制器层的注解为@Controller@Autowired则可以指定是ByName寻找还是ByType寻找属性值。

3.2 AOP(面向方面编程)

OOP是面向对象编程,对于系统中普通的业务点,OOP可以很好地对其分解并使之模块化,但无法避免需求实现较为分散的问题,而AOP可以对类似于Logging和Security等系统需求进行模块化的组织,简化系统需求与实现之间的对比关系,而AOP(Aspect-Oriented Programming)翻译过来,就叫面向方面编程,可以使系统更具模块化。AOP中引入了Aspect的概念,而Aspect对于AOP就相当于Class对于OOP。AOP是对OOP的补充,对其有一定的“寄生”关系。

切面(Aspect):一个模切功能的模块化,这个功能可能横切多个对象/业务。如addMethod()方法
切入点(Pointcut):可以插入横切逻辑(addMethod())的方法,例如add()即为切入点
通知(Advice):切面在特定切入点处采取的操作。通知也分为:前置通知(Before Advice,切入点方法执行之前插入的通知)、后置通知(After Returning Advice)、异常通知(After Throwing Advice)、最终通知(After Finally Advice,无论是否抛出异常,切入点方法执行完毕的通知)、环绕通知(Around Advice,贯穿切入点方法执行的整个过程)

如果想把一个普通的类转变为含有特定功能的类,可以通过继承类、实现接口、注解、配置,这四种方式实现。
那么假如我们要时间一个前置通知,需要实现一个接口,首先要引入jar包(aopaliance.jar、aspectjweaver.jar),然后进行配置,最后编写内容(在执行add()方法前,自动执行一个方法,如before())

4. Spring web

如果使用Spring来做Web项目,需要Spring-java的六个相关jar和一个spring-web.jar,并且当tomcat启动时,web项目自动加载web.xml(WEB-INF中),需要通过监听器将SpringIOC容器初始化。然后IOC帮我们去处理对象。配置方式如下:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml,
                            classpath:applicationContext-*.xml
    </param-value>//指定不在web.xml中的配置
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

另外之前用Mybatis操作数据库,将数据库配置信息都写在了某个配置文件conf.xml中,现在都配置到Spring中。借助mybatis-spring.jar。之前conf.xml中的配置方式如下:

//sqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--    配置环境-->
    <environments default="mysql">
<!--        配置mysql环境-->
        <environment id="mysql">
<!--            配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
            <!-- 配置连接数据库的4个基本信息 -->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/javaweb"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
    <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
    <mappers>
        <mapper resource="org/example/dao/IUserDao.xml"/>
    </mappers>
</configuration>

现在写在springIoC中, 把<dataSource type="POOLED">后的部分改写,方式如下

//加载db.properties
<bean id="dbconfig" class="org.springframework.beans.factory.config.PreferencePlaceholderConfigurer">
    <property name="locations">
        <array>
            <value>classpath:db.properties</value>
        </array>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${driver}"></property>
    <property name="url" value="${url}"></property>
    <property name="username" value="${username}"></property>
    <property name="password" value="${password}"></property>
</bean>
//db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/javaweb
username=root
password=

如果想要用MyBatis,之前的使用方式如下,

public class MybatisTest {
    public static void main(String[] args) throws IOException {
        //1.读取配置文件
        Reader reader= Resources.getResourceAsReader("SqlMapConfig.xml");
        //2.创建SqlSessionFactor工厂
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
        //3.使用工厂生产SqlSession对象并创建Dao接口的代理对象
        //session=JDBC的connection
        SqlSession session=sqlSessionFactory.openSession();
        //4.使用代理对象执行方法
        String statement="org.example.dao.IUserDao.findAll";
        User person = session.selectOne(statement);
        System.out.println(person);
        //5.释放资源
        session.close();
    }
}

现在应用在Spring中,要创建MyBatis核心类SqlSessionFactory,改写如下,Spring产生Mybatis最终操作需要的动态mapper对象。

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
    //加载mybatis配置文件
    <property name="configLocation" ref="classpath:conf.xml"></property>
    //加载mapper.xml路径
    <property name="mapperLocation" value="/../../*.xml"></property>
</bean>
<bean id="Mappers" class="org.mybatis.spring.mapper.MapperScannerConfigure">
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    <property name="basePackage" ref="org.example.mapper,XX,XXX"></property>//扫描多个包,用逗号分隔
</bean>

5. Spring MVC

SpringMVC框架提供了MVC模型(Model、View、Controller),模型(Model)封装了应用程序数据,视图(View)负责渲染模型数据,控制器(Controller)负责处理用户请求并构建适当的模型,并将其传递给视图进行渲染。

5.1 SpringMVC项目流程

Servlet的发展从Struts1.X到Struts2.X然后到了SpringMVC。普通的servlet流程,请求Url-pattern,交给对应的servlet去处理,如果现在使用springmvc而不是普通的servlet,就需要配置一个Springmvc DispatcherServlet,将相应的请求拦截交给springMVC去处理。
首先启动tomcat的同时加载相应的配置文件,DispatcherServlet对象被创建,客户端发出请求,被DispatcherServlet拦截,然后查询HandlerMapping以调用相应的Controller。Controller返回相应的视图给DispatcherServlet,接着DispatcherServlet从ViewResolver请求定义的视图,最终展现在浏览器上。
Controller控制器,由注解@Controller标识,同时,@RequestMapping注解用于将URL映射到类或者特定处理程序方法。

5.2 SpringMVC配置

项目整体需要配置DispatcherServlet,在web.xml中。一般的项目以SSM为主,包括SpringMVC(控制视图)、Spring(类似Servlet功能)、Mybatis(操作数据),其整体的配置流程如下

//web.xml
<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>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern> //拦截全部请求,交给DispatcherServlet去处理
</servlet-mapping>

另外我们要设置解决中文乱码的过滤器和加载配置文件的监听器,让我们的Spring配置生效

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


  <!--配置Spring监听器,默认只加载WEB-INF目录下的applicationContext.xml配置文件-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!--设置配置文件路径-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

上面就是制作一个简单的SSM工程需要设置的web.xml全部的内容。这些主要是针对于Spring的设置,针对于SpringMVC,创建springmvc.xml,鉴于MVC主要控制视图层,要扫描相应的Controller注解。其配置内容如下

//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"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--开启注解扫描,只扫描Controller-->
    <context:component-scan base-package="org.example">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!--配置视图解析器-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/" />
        <property name="suffix" value=".jsp" />
    </bean>
    <!--过滤静态资源-->
    <mvc:resources mapping="/css/**" location="/css/" />
    <mvc:resources mapping="/js/**" location="/js/" />
    <mvc:resources mapping="/images/**" location="/images/" />
    <!--开启SpringMVC注解的支持-->
    <mvc:annotation-driven/>

</beans>

如果我们要将SpringMVC和数据库进行绑定,需要整合Mybatis框架,并声明相应的事务管理,在Spring的applicationContext.xml中配置如下:

//applicationContext.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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">


    <!--开启注解扫描,只处理service和dao-->
    <context:component-scan base-package="org.example">
        <!--不扫描controller-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

<!--    Spring整合Mybatis框架-->
<!--    配置连接池-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/javaweb?serverTimezone=GMT%2B8"/>
        <property name="username" value="root"/>
        <property name="password" value=""/>
    </bean>
<!--    配置SqlSessionFactory工厂-->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
    </bean>
<!--    配置AccountDao接口所在包-->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="org.example.dao" />
    </bean>

<!--    配置Spring框架声明式事务管理-->
<!--    配置事务管理器-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
<!--    配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
        <tx:attributes>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*" isolation="DEFAULT"/>
        </tx:attributes>
    </tx:advice>
<!--    配置AOP增强-->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* org.example.service.impl.*.*(..))"/>
    </aop:config>
</beans>

5.3 IDEA创建SSM工程(注册登录功能)

SSM(Spring MVC+Spring+Mybatis),即表现层、业务层、持久层。将三大部分组合在一起的整体思路是,由Spring为主导,整合另外两个。applicationContext.xml、springmvc.xml、web.xml整个项目的配置如同上一节。

(1)maven配置
这里以IDEA用Maven创建SSM工程为例。
首先建立一个maven项目,在下面这一步选择webapp,但是如果不想后续加载过慢,可以在next之后,添加键值对archetypeCatalog,internal,跳过相应加载。否则加载时间会很长。

建立SpringMVC工程
然后在生成的pom.xml中进行相关依赖的引入
//pom.xml
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>ssm02_17</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>ssm02_17 Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    <spring.version>5.2.3.RELEASE</spring.version>
    <mysql.version>5.7.24</mysql.version>
    <mybatis.version>3.4.5</mybatis.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.7.4</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.15</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.2</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>${mybatis.version}</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>
    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.2.2</version>
    </dependency>
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
      <type>jar</type>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
<!--      <scope>compile</scope>-->
    </dependency>
  </dependencies>

  <build>
    <finalName>ssm02_17</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

(2)具体工程
整个工程结构如下,resources中的xml内容和上一节相同,pom.xml配置如上,java代码附在后面,包含dao、service、controller三层。

工程结构
首先创建一个账户类Account
//Account
package org.example.domain;

import java.io.Serializable;

public class Account implements Serializable {
    private Integer id;
    private String username;
    private String password;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

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

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

然后编写数据查询接口AccountDao

package org.example.dao;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.example.domain.Account;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface AccountDao {

    @Select("select * from simpleusers where username=#{username}")
    public List<Account> findUser(Account account);

    @Insert("insert into simpleusers (username,password) values(#{username},#{password})")
    public void saveAccount(Account account);

    @Select("select * from simpleusers where username=#{username} AND password=#{password}")
    public List<Account> Login(Account account);
}

设置业务层接口AccountService

//AccountService
package org.example.service;

import org.example.domain.Account;
import java.util.List;

public interface AccountService {
    public void saveAccount(Account account);
    public List<Account> findUser(Account account);
    public List<Account> Login(Account account);
}

编写业务层接口具体的实现类

package org.example.service.impl;

import org.example.dao.AccountDao;
import org.example.domain.Account;
import org.example.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Override
    public void saveAccount(Account account) {
        accountDao.saveAccount(account);
    }

    @Override
    public List<Account> findUser(Account account) {
        return accountDao.findUser(account);
    }

    @Override
    public List<Account> Login(Account account) {
        return accountDao.Login(account);
    }
}

然后编写控制器,根据业务调用相关视图界面

package org.example.controller;

import org.example.domain.Account;
import org.example.service.AccountService;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@Controller
@RequestMapping("/account")
public class AccountController {
    @Autowired
    private AccountService accountService;


    //用于注册时判断用户是否存在
    @RequestMapping("/findUser")
    public String findUser(Account account,Model model){
        List<Account> list=accountService.findUser(account);
        if (list.isEmpty()){
            accountService.saveAccount(account);
            model.addAttribute("welcome","您已成功注册,请登录");
            return "login";
        }
        else {
            model.addAttribute("fail","用户已存在,请重新注册");
            return "register";
        }
    }

    @RequestMapping("/register")
    public String Register(Model model){
        return "register";
    }

    @RequestMapping("/test")
    public String Success(Model model){
        return "test";
    }

    //用于登录判断
    @RequestMapping("/login")
    public void Login(Account account,Model model, HttpServletRequest request, HttpServletResponse response) throws IOException {
        List<Account> list=accountService.Login(account);
        if (list.isEmpty()){
            response.sendRedirect(request.getContextPath());
        }
        else {
            response.sendRedirect(request.getContextPath()+"/account/test");
        }
    }

    //用于添加用户
    @RequestMapping("/save")
    public void save(Account account, HttpServletRequest request,HttpServletResponse response) throws IOException {
        accountService.saveAccount(account);
    }
}

几个简单的页面内容如下:

//login.jsp
<body>
<a href="register">未注册用户请先注册</a>
<form action="login" method="post">
    姓名:<input type="text" name="username" /><br/>
    密码:<input type="password" name="password" /><br/>
    <input type="submit" value="登录" /><br/>
</form>
</body>

//register.jsp
<body>
请注册:
<form action="findUser" method="post">
    姓名:<input type="text" name="username" /><br/>
    密码:<input type="password" name="password" /><br/>
    <input type="submit" value="注册" /><br/>
</form>

//test.jsp
<h3>登录成功,欢迎您</h3>

//index.jsp
<body>
<a href="account/register">未注册用户请先注册</a>
<form action="account/login" method="post">
    姓名:<input type="text" name="username" /><br/>
    密码:<input type="password" name="password" /><br/>
    <input type="submit" value="登录" /><br/>
</form>
</body>

这个简单的demo完成了用户注册、登录的功能,根据数据库中的内容进行匹配。具体的输入判断逻辑不包含在内。

参考文献

理论参考:《spring揭秘》
demo参考:https://www.bilibili.com/video/av47953244?from=search&seid=2782924437497092368

上一篇下一篇

猜你喜欢

热点阅读