Spring与Spring MVC
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,跳过相应加载。否则加载时间会很长。
然后在生成的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