🛫更多ssm知识见SSM_面向CRUD编程专栏
🚕本博客总结自黑马程序员的ssm框架视频
🚒博主对于该知识尚在学习阶段
🚄如果发现存在问题请毫不吝啬的指出
🚀🚀扎哇太枣糕的博客主页🚀🚀
目录
🍺 Spring篇
🍕 初识Spring
🍔 xml配置文件
🍟 依赖、数据注入问题
🌭 Spring相关API
🍿 Spring配置数据源
🥓 Spring注解开发
🥯 Spring整合Junit
🥪 Spring集成web环境
🍹 SpringMVC篇
🥝 初识SpringMVC
🍉 SpringMVC之数据响应
🍊 SpringMVC之获取请求数据
🍒 springMVC之拦截器
🍓 springMVC之异常处理
☕ MyBatis篇
🍩 jdbcTemplate的基本使用
🍪 Spring AOP
🍰 使用AOP开发
🧁 spring的事务控制
🥧 初识MyBatis
🍫 MyBatis的dao层(mapper层)实现
🍿 MyBatis多表操作
🍮 MyBatis实现注解开发
🎉SSM框架整合
案例分析
框架结构
实现
🍺 Spring篇
🍕 初识Spring
Spring是分层的Java SE/EE阶段应用的full-stack轻量级开源框架,主要以两个部分IOC (Inverse Of Control 反转控制)和AOP (Aspect Oriented Programming 面向切面编程)为内核。所谓的full-stack就是说提供了web层的Spring MVC、DAO层的JDBC模板、业务层的事务管理等众多的企业级应用技术,除此之外还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。
了解完Spring是什么,接下来学习一下Spring的发展历程,Spring框架的前身是IBM提出的EJB,2005年Spring之父罗德·约翰森(Rod Johnson)发表Expert One-on-One J2EE Development without EJB一书被认为是Spring框架的雏形,再后来经过几年的发展就有了我们现在学习的Spring框架。
从高斯林和约翰森两位大佬的发型就能看出来,学Java必定是前途无量。
Spring体系结构
我们对Spring的学习是按照它的体系结构从下往上开始学习的
使用Spring进行快速入门开发
学习Spring之前
在使用Spring之前想要在UserDaoDemo的main方法里调用UserDaoImple类中的save方法,就要先使用多态创建对象就是通过new创建,再使用创建出来的UserDao对象调用save方法。这样做不好的地方就是:将创建对象写死(固定)了,编码过度耦合,代码里面创建的是什么对象就只能用什么对象。
学习Spring之后
使用Spring就可以通过xml配置文件实现解耦合,通过xml配置文件的配置和getBean方法的配合就可以动态的修改创建对象,具体步骤如下:
目录结构:
第一步:通过maven导入spring-context对应的依赖坐标
第二步:编写UserDao对应的接口和实现类
第三步:创建applicationContext.xml配置文件并配置id标识标识 UserDaoImpl类
第四步:在UserDemo中通过Spring的API获得Bean并调用方法
控制端运行结果
红色的是打印的日志信息而非报错
🍔 xml配置文件
Bean标签的配置
属性:
id:Bean在Spring容器中的唯一标识,在一个xml配置文件内部所有Bean标识的命名不可与之重复
class:Bean的全限名,指定这个id标识所对应的Bean类。Spring框架底层利用反射通过这个全限名对应Bean类的无参构造器创建对象。
scope:指的是对象创建的模式,有以下五中取值
- singleton:是scope属性的默认值,相当于饿汉单例模式,也就是说Bean对象只在使用xml配置文件创建Spring客户端对象的时候创建一次,之后使用getBean方法获取Bean对象的时候返回的都是之前创建好的Bean对象。singleton一共就在Spring的核心文件被加载时创建一次一个Bean对象存储在容器里,当应用卸载销毁容器时对象随之销毁。
- prototype:相当于多例设计模式,也就是说在每一次使用Spring客户端对象的getBean方法时都会创建一个新的Bean对象并返回。prototype是使用几次getBean方法就会创建几个Bean对象,当对象长时间不用的时候就会被java的垃圾回收机器回收对象也就销毁了。
- request:web项目中,Spring创建一个Bean对象并将其存入到request域中
- session:web项目中,Spring创建一个Bean对象并将其存入到session域中
- global session:web项目中,应用于portlet环境中,如果没有portlet环境的话就相当于session
init-method:指定Bean类中的初始化方法
destory-method:指定Bean类中的销毁方法
import标签
import标签是用来将其他配置文件的配置内容加载到这个配置文件中
Bean实例化的三种方法:
- 无参构造方法实例化(重点):无参构造方法其实就是我们上面讲的那些知识点
- 工厂静态方法实例化:通过工厂中的静态方法进行实例化,只需创建一个工厂类,类里面创建一个静态方法,方法返回一个无参构造器创建的Bean对象,再设置xml文件即可
- 工厂实例方法实例化:通过工厂中的实例方法进行实例化,与工厂静态方法的区别就是,用于返回一个无参构造器创建的Bean对象的方法是实例方法,xml配置文件也不一样,要更加复杂些。
🍟 依赖、数据注入问题
引出问题:
现在有这么一个情况,除了上面已经有的dao层之外,新建一个业务层service。service层的结构和dao层相似,也是一个接口和实现类。目录结构如下:
UserServiceImpl类和UserDaoImpl类也都通过xml配置文件的bean标签进行配置。
此时有一个需求,就是要在service层的UserServiceImpl类里的cast()方法中调用UserDaoImpl类的save()方法,实现思想就是在UserServiceImpl类里的cast()方法中使用之前UserDaoDemo的那一套进行调用UserDaoImpl类的save()方法
在新建的UserController类中使用那一套调用UserServiceImpl类里的cast()方法
这么一来,就可以在Spring容器外部将Dao组装到了Service 那么,有没有一种方式,可以在Spring容器内部就将Dao组装到了Service呢?有的,于是乎就出现了依赖注入。
依赖注入:
依赖注入(Dependency Injection)是Spring框架核心IOC(控制反转)的具体实现。在编程的时候通过控制反转就可以把对象的创建交给Spring 容器,IOC只是降低他们之间的依赖关系并不会消除依赖,service层仍会调用dao层的方法。那么,该如何将UserDao注入到UserService的内部呢?
依赖注入的两种方式:
set方法注入
就相当于将userDao对象通过setUserDao方法当做一个参数直接传给UserServiceImpl类中,当创建UserServiceImpl类的对象调用cast()方法时,cast()方法内部就会使用setUserDao方法传过来的userDao对象调用save()方法
具体setUserDao方法参数的userDao对象是如何创建的,这就要到xml配置文件中设置了
xml配置文件的另一种配置方式的写法,知道即可还是上面的<property>标签的可读性更加高
通过set方法注入的方式,就可以在Spring容器内部时就将Dao组装到了Service
set方法注入的关键就是,xml配置文件<property>标签将Dao组装到了Service,并在再service层的实现方法上用set方法接收dao层的对象,并使用对象对dao层进行操作。
构造方法注入
创建一个有参构造器用于接收UserDao对象
使用<constructor-arg>标签配置
⚠ set方法注入和构造器注入的方式其他都一样,就是接收UserDao的载体set注入是私有属性的setter方法、构造器注入是有参构造器,xml配置文件的标签set注入是<property>、构造器注入是<constructor-arg>
set方式注入其他类型数据
以上两种依赖注入的方式不只能用于注入对象,还可以用于注入普通数据类型、引用数据类型和集合数据类型,接下来就以set注入方式为例演示
普通数据类型
创建变量相应的setter方法
配置xml配置文件
spring客户端对象获取Bean对象,并调用save方法
数组、集合、propetries
propetries是Java的一个类以键值对的形式存储配置信息
setter方法
xml配置文件
spring客户端对象获取Bean对象,并调用save方法
总结(注入数据时配置文件的配置格式)
上图传作资源,如有用可自行下载:下载地址
🌭 Spring相关API
创建Spring客户端对象
创建Spring客户端对象主要是依靠多态的方式使用ApplicationContext接口的以下三个实现类:
ClassPathXmlApplicationContext类
这个类的参数使用的是相对路径,xml文件需要在项目下的resource文件夹下进行配置
FileSystemXmlApplicationContext类
这个类的参数使用的是绝对路径,xml文件可以在任何地方进行配置,但是缺点是一旦配置文件修改位置或者是将项目在其他电脑上运行的话基本上就是报错无法正常运行。
AnnotationConfigApplicationContext类
使用注解配置容器对象的使用此类创建Spring容器读取注解,先行了解,后续的学习再使用。在博客的最后一小块进行了介绍使用。
获取Bean对象
getBean(String id)
通过唯一id标识去创建bean对象,好处是id标识唯一确定一个bean类,不会创建错
getBean(Class<T> type)
通过bean类创建bean对象,坏处就是配置文件里的bean类不止一个,如果此时配置文件里bean类的标签不唯一就会抛NoUniqueBeanDefinitionExceptionBean定义不唯一异常
🍿 Spring配置数据源
数据源又被称为是连接池其出现的目的是为了提高程序的性能,数据源可以事先实例化源数据并初始化部分连接资源,使用连接资源的时候可以从数据源中获取,使用完毕之后再将连接资源还给数据源。常见的数据源有DBCP、C3P0、BoneCP、Druid等。
Druid数据源的创建方式有很多种:手动创建、使用配置文件解耦合、使用spring框架依赖注入并解耦合,他们的学习是层层递进的,要从手动开始最后引入spring框架这样便于加深理解。
手动创建Druid数据源
第一步:通过pom文件依赖坐标的方式导入两个数据源需要使用的jar包 第二步:创建数据源对象并设置基本连接信息,最后归还资源
将数据源对象的基本连接信息设置抽取出来到配置文件,实现解耦合
第一步:通过pom文件依赖坐标的方式导入两个数据源需要使用的jar包 (跟上一个相同)
第二步:配置配置文件,配置文件的后缀是properties,也就是使用键值对的形式存储配置信息
第三步:创建数据源对象并设置基本连接信息,最后归还资源
🔥使用Spring依赖注入的方式创建Druid数据源,并实现解耦合最终进化版本,以后的使用模板
第一步:导入spring-contest依赖和以上两种依赖
第二步:配置配置文件,存储数据源配置信息
第三步:创建applicationContext.xml配置文件并配置bean标签
第四步:创建数据源对象操作连接最后归还资源
🥓 Spring注解开发
Spring的原始注解
通过案例了解注解开发
注解开发的优势就在于进一步使用反射封装代码,简化xml配置文件的内容,使用注解就可以代替xml配置文件的bean标签配置。
在使用注解开发之前,xml文件里要使用UserDaoImpl类的bean标签创建对象,使用UserServiceImpl类的bean标签创建对象的同时将上一个bean标签创建的对象通过set方法依赖注入到这个bean标签里。
依赖注入的有关注解
@Component:通用版的@Repository注解,无论哪一层都能用,缺点就是辨识度不高,无法通过注解理清楚这是哪一层的业务
@Repository:Dao层的@Component注解用于实例化Bean,加上字符串参数相当于一个<bean>标签设置了唯一id标识。这个注解的作用相当于图中上面一行的<bean>标签。
@Controller:controller层的@Component注解用于实例化Bean
@Service: Service层的@Component注解用于实例化Bean,用法和意义同上一个
@Autowired: 用于依赖注入,加入注解后就说明下面的变量定义用到了依赖注入
@Qualifier:用于指定注入对象相当于ref属性
当然这种依赖注入还可以进一步简写。就是只保留@Autowired注解舍去@Qualifier注解,再将set方法的定义舍去。之所以这么做也能成功的原因是:xml配置文件里面只有一个这种类型的bean标签,此时只会唯一将该bean对象注入进去,不会出现像使用getBean方法时参数传class对象但是bean不唯一抛异常的问题。只使用一个@Autowired注解的话,需要保证只有一个同类型的bean被注入到spring容器中。不管是用哪种方式,使用几个注解,set方法的定义都可以省略不写,注解底层已经通过反射机制为我们创建好了。
@Resource:如果出现了xml配置文件里面不只有一个这种类型的bean标签的情况时,相当于下面这种情况一个类创建了两个标签,这个时候就用到了这个注解
除了使用注解之外,还要在xml文件使用标签扫描注解,让spring容器知道都使用了哪些注解并实现对应的功能
数据注入的注解
@Value:将基本数据类型和string字符串注给下面的变量
除了直接注入数据的形式,还能引用spring容器中的数据进行注入
其他注解
@Scope:与bean标签的scope属性意思相同
@PostConstruct:指明下面的方法是初始化方法
@PreDestory:指明下面的方法是销毁方法
新注解
注解开发的最终目的就是用注解取代xml配置文件的同时还不会降低代码的解耦合能力,使用上面的注解进行开发时还是无法替代全部的xml文件,依然有以下四种标签无法被注解所取代,需要使用新注解进行开发取代xml文件
- 非自定义的bean(就像前面Druid数据源配置的bean标签,第三方jar包给的bean无法取代)
- 加载properties文件的标签<context:property-placeholder>
- 组件扫描<context:component-scan>
- 引入其他配置文件的<import>
@Configuration:标示这是Spring容器的核心配置类,相当于xml配置文件
@ComponentScan:扫描包下注解,相当于<context:component-scan>
@Import:导包,相当于<import>
@property: 加载properties文件,相当于<context:property-placeholder>
@Bean:在注解里将返回值是对象的方法实例化Bean,也就是说将方法返回的对象注入到Spring容器中供调用,也相当于一个<bean>标签,只不过是用在方法上的。
由于xml文件此时已经被注解锁完全取代了,所以创建客户端对象的时候就不能再使用ClassPathApplicationContext类,要使用第三种方式AnnotationConfigApplicationContext类
🥯 Spring整合Junit
第一步:导入两个依赖坐标
第二步:加注解
@RunWith:替换原来的运行期
@ContextConfiguration:指定配置文件(xml配置)或者配置类(注解配置)
在使用spring整合Junit的时候遇到一个问题记录一下: spring-context和spring-test的依赖版本不同导致控制台抛出以下异常java.lang.NoClassDefFoundError: org/springframework/core/annotation/MergedAnnotations。解决方法:将两个以来坐标的版本改成一致即可。
Spring集成web需要学习使用IDEA创建一个web项目,并在IDEA配置tomcat服务器运行web项目,具体操作参考博客:关于黑马程序员最全SSM框架教程视频,P37集老师跳过的模块创建以及tomcat下载安装配置和运行等诸多问题
🥪 Spring集成web环境
ServletContextListener监听器
使用原始的new ClassPathXmlApplicationContext获取应用上下文对象的方式,每一次都会去加载这个xml配置文件创建应用上下文对象来获取Bean。于是为了降低这种频繁加载配置文件创建对象的损失,可以在web项目中使用ContextLoaderListener类实现ServletContextListener接口监听器来监听web应用的启动,web应用启动的时候就加载配置文件创建应用上下文对象并将其存储在servletContext域中,这样就可以在web应用的任意位置从域中获取应用上下文对象。
第一步:创建一个类充当ContextLoaderListener监听器,重写初始化方法将应用上下文对象存储在域中
第二步:通过域获取应用上下文对象
第三步:运行
对上述代码的两处优化
主要是servlet类中有两处耦合死了,可以借助全局化初始参数和工具类实现解耦合。
全局初始化参数(web.xml)
工具类
创建一个工具类WebApplicationContextUtils类传参servletcontext域对象返回app对象
Spring对ServletContextListener监听器的优化
通过前面对ServletContextListener监听器的讲解,过渡到Spring对ServletContextListener监听器的优化,Spring提供了一个ContextLoaderListener监听器其底层就是对上面写的代码的一系列封装,该监听器内部加载xml配置文件创建应用上下文对象并存储在ServletContext域中,提供一个客户端工具WebApplicationContextUtils供使用者获取应用上下文对象。
第一步:导入spring-web依赖坐标
第二步: 配置web.xml文件第三步:servlet类使用
🍹 SpringMVC篇
🥝 初识SpringMVC
SpringMVC是一种基于Java语言实现的MVC设计模型(Model View Controller)的请求驱动型的轻量级web框架,属于是SpringFrameWork的后续产品,已经融合在Spring Web Flow中。它是一套注解,让一个简单的Java类成为处理请求的控制器,无需使用任何接口,同时支持RESTful编程风格的请求。大致而言SpringMVC的模型就是下面的这张图
参照是上面的图总结出SpringMVC模式的开发步骤,以SpringMVC的快速入门案例为例
第一步:导入SpringMVC的spring-webmvc依赖,用于使用SpringMVC内部的前端控制器
第二步:web.xml文件中配置SpringMVC的前端控制器DispatcherServlet
第三步:创建Controller层、对应的Controller类和视图页面
第四步:使用注解配置Controller类中的方法的映射地址
第五步:创建并配置SpringMVC的核心文件spring-mvc.xml配置文件并在web.xml里将它的位置告知SpringMVC
第六步:客户端发起请求测试
点绿色小三脚运行web项目,如果控制台报错或者无反应的话可以考虑关了进行如下操作
像上述案例的SpringMVC执行过程如下
1、用户发送请求至前端控制器DispatcherServlet(前端控制器包含在spring-webmvc依赖中,导入之后才能正常使用)
2、 前端控制器DispatcherServlet收到请求之后调用处理映射器HandlerMapping解析请求资源
3、处理映射器HandlerMapping找到具体的处理器(根据xml配置、注解查找)、生成处理器对象以及处理器拦截器对象(如果有的话就生成)一并返回给前端控制器DispatcherServlet
4、前端控制器DispatcherServlet调用处理器适配器HandlerAdapter
5、处理器适配器HandlerAdapter经过适配调用具体的处理器Handler(也就是自己写的controller,也叫后端控制器)
6、处理器Handler(Controller)执行返回ModelAndView
7、处理器适配器HandlerAdapter将处理器Handler(Controller)返回的ModelAndView返回给前端控制器DispatcherServlet
8、前端控制器DispatcherServlet将ModelAndView传给视图解析器ViewReslover
9、视图解析器ViewReslover解析后返回具体的View
10、前端控制器DispatcherServlet根据View渲染视图
11、前端控制器DispatcherServlet返回响应给用户
知识点查漏补缺:
@RequestMapping:用于建立请求URL和请求方法之间的对应关系,众所周知URL的访问是分级的,如果@RequestMapping注解加在类上的话表示的是一级访问目录,如果@RequestMapping注解加在方法上的话就表示是二级目录与方法所在类的一级目录一起形成虚拟路径。注解的属性有:
- value:用于指定请求路径的url,默认直接给一个以/开头的字符串,而不用键值对的方式给value赋值
- method:用于指定请求的方式,都是枚举类型的值RequestMethod.常量名一般是GET或者POST
- params:限定请求参数的条件,支持简单的表达式,要求参数key和value必须和配置的一模一样
组件扫描:组件扫描在xml文件中配置,在配置组件扫描之前先引入命名空间
然后通过<context:component-scan>标签进行配置组件扫描,之前一直用的都是直接指定包名扫描包下面的注解
现在直接按照注解的类型进行扫描 配置视图解析器,使之拥有默认的前缀和后缀,也就是当将所有的.jsp文件都放在jap文件夹下的时候,可以指定重定向的网页路径的前缀和后缀使之可以直接跳转,不必在return的时候写过多的多级路径。
因为有上图两个set方法,所以在xml文件的配置时,很容易就联想到使用<property>标签的形式直接注入数据,于是有了下一步
设置了前缀字符串和后缀字符串之后,就可以下面的return字符串拼接成一个url路径对jsp文件夹下的.jsp文件进行映射了
🍉 SpringMVC之数据响应
响应方式之页面跳转
🔴返回字符串
请求转发(不加的话默认是请求转发):forward:前缀表示是请求转发,具体表现为地址不会变。是服务器跳转,相当于方法调用,在执行当前文件的过程中转向执行目标文件,一共就一个请求对象
请求重定向:redirect:前缀表示是请求重定向,具体表现为地址跳转。是客户端跳转,相当于客户端向服务端发送请求之后,服务器返回一个响应,客户端接收到响应之后又向服务端发送一次请求,一共是2次请求
🔴返回ModelAndView对象
可以通过ModelAndView对象和Model对象向request域中存放数据,并使用${}的形式取出数据
返回ModelAndView对象的三种方式
方式一:自己创建ModelAndView对象并返回
方式二:传参ModelAndView对象(SpringMVC创建)并返回
方式三: 传参Model对象(SpringMVC创建)并返回字符串
响应方式之回写数据
🔴直接返回普通字符串
加@ResponseBody注解,表示这里是返回字符串而不是进行页面跳转
🔴返回一个JSON字符串
第一步:新建一个User类封装数据
第二步:导入相关依赖(三个的版本号要一样),导依赖就要将依赖添加到目录lib千万别忘了
第三步:使用转换工具将对象转换成JSON字符串并返回
优化:使用xml文件配置SpringMVC的处理器适配器HandlerAdapter,设置消息转换器,也就是说设置之后不用创建JSON转换器,只要return一个对象就可以将其转换成JSON字符串。
靠这个set方法在xml文件进行数据注入,设置消息转换器
配置好了之后直接return对象就能返回一个JSON字符串
当然,以黑马这个视频主讲老师的讲解风格,这波可以说又是经典白学,因为这么多配置都可以使用一个标签代替
🍊 SpringMVC之获取请求数据
客户端请求参数的格式是:name:value&name=value……有的时候服务器要想获得请求的参数还需要对数据进行封装,SpringMVC就可以接收一下类型的数据:基本数据类型、POJO类型的数据、数组类型的数据、集合类型的数据
基本类型数据
当Controller中业务方法的参数名与请求参数的键一致时,参数会进行自动映射匹配,也就是说将该请求消息与这个方法相匹配。
POJO类型参数
当Controller中业务方法的参数类里的封装属性名(更准确应该说是set方法名去掉set)与请求参数的键一致时,参数会进行自动映射匹配,也就是说将该请求消息与这个方法相匹配。
数组类型参数
当Controller中业务方法的参数数组名与请求参数的键一致时,参数会进行自动映射匹配。
集合类型参数
要想获得集合参数的话,要将集合参数包装到一个POJO类型数据中才可以实现。当POJO类参数里的封装属性(也就是一个集合)与请求参数的集合名(也就是form表单里行的值)一致时,会将form表单与业务方法进行匹配。
请求数据乱码的问题
当使用POST请求的时候,数据会出现乱码问题,我们可以使用一个全局过滤器来进行编码的过滤。
在web.xml文件中配置全局过滤器
配置之后的结果就不乱码了
集合型还有一种直接使用集合不再使用POJO进行封装
第一步:在wabapp下创建一个js文件夹并导入jquery-2.2.3.min.js文件
第二步:开放资源,使js目录下的jquery-2.2.3.min.js文件可以被访问到,下面运行时说到这一次的集合请求参数一共有三次访问,如果不开放资源的话第二次访问会被打断,以至于无法进行第三次访问导致失败
上面的标签配置是指定jsp目录下的所有文件,有限定,下面的这个标签配置的是所有的静态资源,而无需指定是哪个文件夹,实际开发中使用的更多。
第三步:在jsp文件夹下创建一个jsp页面,使用Ajax进行发送一个集合数据
第四步:创建业务方法传list集合参数,并在参数前加@RequestBody注解
第五步:maven depoly并运行访问ajax.jsp
参数绑定
@RequestParam:在方法参数定义前使用,将参数与注解定义的字符串绑定到一起,在发送请求时对注解字符串的赋值就是对参数的赋值。
获取Restful风格参数
Restful就是一种架构风格或者说是设计风格,而不是一套标准,主要就是提供了一组设计原则和约束条件。Restful风格的请求就是使用“url + 请求方式”便是以此请求的目的,HTTP协议里表示操作方式的四个词分别是:GET获取资源、POST新建资源、PUT更新资源、DELECT删除资源。
参数获取
自定义类型转换器
第一步:定义一个转换器类实现Convert接口
第二步:声明转换器并在<annotation-driven>中引用转换器
获取请求头信息
众所周知,http请求包括:请求行、请求头、消息实体。那么,在MVC中如何获得请求头信息的呢?@RequestHeader注解和@CookieValue
文件上传并转存
当form表单修改为多部分表单时,即设置enctype="mulipart/form-data",用来获取url编码方式提交信息的API将无法再继续使用,比如说request.getParameter()。url编码方式即是设置enctype="application/x-www-form-urlencoded",此时会使用键值对的方式传参。
第一步:导入相关坐标
第二步:配置文件上传解析器 第三步:文件上传的代码
第四步:编写相应的jsp页面,提交的数据名要与上传方法的参数名相一致
🍒 springMVC之拦截器
SpringMVC的拦截器(Interceptor)相当于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。经拦截器按一定的顺序连接成一条链,这条链成为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段是,拦截器链中的拦截器就会按期之前定义的顺序被调用,拦截器也是AOP思想的具体体现。
自定义一个拦截器
第一步:创建一个拦截器类实现HandlerInterceptor接口
第二步:在spring-mvc.xml中配置拦截器
测试:
因为页面被拦截至自定义的拦截器,拦截器中的第一个方法返回的是false,故程序执行至此结束,不会继续执行,控制台就打印出一行信息。
拦截器案例:
如果url的参数param的值是yes就放行并将name属性的值改为"小陈",如果不为yes就不执行该请求页面,在自定义的拦截器中执行相应的方法,跳转至error.jsp页面。
拦截器链中方法的先后执行顺序:preHandle1方法-->preHandle2方法-->目标方法-->postHandle2方法-->postHandle1方法-->afterCompletion2方法-->afterCompletion1方法
🍓 springMVC之异常处理
系统中的异常包括两类:预期异常(无论编译时报不报异常都要处理)和运行时异常(编译时不报运行时报异常)。一般情况的业务开发下,异常就是使用throws Exception往上一层抛由上一层进行处理,在一个spring项目中就是系统的Dao抛向Service再抛向Controller最后抛向SpringMVC的前端控制器由异常处理器进行异常处理。
异常处理的两种方式:
使用SpringMVC提供的SimpleMappingException
自定义异常处理器实现HandlerExceptionResolver接口
第一步:创建自定义异常处理类并实现HandlerExceptionResolver接口
第二步:配置异常处理器
☕ MyBatis篇
🍩 jdbcTemplate的基本使用
jdbcTemplate是一个spring框架提供的对象,是对原始jdbcAPI对象的简单封装,之所以在这里学习jdbc模板是因为MyBatis框架是和jdbc模板功能相同的一个框架体现,这样就可以实现从jdbc模板向MyBatis框架进行过度。
jdbcTemplate的开发步骤
第一步:导入spring-jdbc和spring-tx依赖
第二步:创建数据库和实体类
第三步:创建jdbcTemplate对象并执行数据库操作
执行结果:
使用spring创建jdbcTemplate对象
第一步:导入相关依赖
第二步:编写配置文件和链接设置文件
第三步:创建对象并操作数据
使用jdbcTemplate对象进行CRUD操作
🍪 Spring AOP
aop也是Spring框架的知识,但是MyBatis声明式事务控制的底层使用的就是Spring AOP,于是我们把aop的知识放在这里进行学习。
AOP(Aspect Oriented Programming)意思是面向切面编程,是通过预编译的方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是函数式编程的一种衍生范型,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑个部分之间的耦合性降低,提高程序的可重用性,同时提高了开发的效率。
AOP可以在程序运行期间,在不修改源码的情况下对方法进行功能的增强。它的优势就是可以减少重复代码,提高开发效率,并且便于维护。
底层实现
AOP的底层通过Spring提供的动态代理技术实现,在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,再去调用目标对象的方法,从而完成功能的增强。
相关名词概念
- Target(目标对象):也就是被代理类对象
- Proxy(代理):一个类被AOP增强后return的对象就是代理类对象
- JoinPoint(连接点):每个被拦截到的方法就是一个连接点,这里点指的是方法,因为spring只支持方法类型的连接点
- Pointcut(切入点):需要被增强的每个方法就是一个切入点
- Advice(增强):用于定义功能增强方法
- Aspect(切面):切入点加增强就是切面
- Weaving(织入):将切入点的方法进行增强的过程就被称为织入,spring采用动态代理织入,而AspectJ采用编译器织入和类装载期织入。
常用的两种动态代理技术
- JDK代理:基于接口的动态代理技术,有接口时使用
- cglib代理:基于父类的动态代理技术,无接口时使用
JDK代理
第一步:创建一个公共接口
第二步:创建一个被代理类实现公共接口
第三步:创建一个功能增强类
第四步:测试类,利用反射创建代理类调用增强方法
测试结果:
cglib代理
第一步:导入相应的依赖jar包
由下图可知,cglib的包已经被封装到spring-core中它又被封装在spring-context中,故只需要导入spring-context的依赖就可以使用cglib
第二步:创建一个被代理类
第三步:创建一个功能增强类
第四步:测试类,利用反射创建代理类调用增强方法
测试结果:
🍰 使用AOP开发
AOP开发需要编写目标类的目标方法、编写切面类内置功能增强方法、配置配置文件将切入点与增强方法相结合。spring框架会监控切入点方法的执行,一旦监控到切入点方法执行就会动态的使用代理机制创建代理类对象,并在相应位置将增强与切入点进行织入。spring框架会根据目标类是否实现了接口来决定使用JDK和cglib中的哪一种动态代理模式
基于xml的AOP开发
第一步:导入相关依赖
第二步:创建一个公共接口和目标类
第三步:创建一个切面类内置增强方法
第四步:配置配置文件(创建对象的bean标签、配置织入关系)
测试类及结果:
⚠ 通知类型
⚠ 切点表达式的写法:
🔴访问修饰符可以省略不写🔴返回值类型、方法全限名里的具体项可以使用*代表任意🔴包名与类名之间一个点.代表当前包下的类,两个点..代表当前包及其子包下的类🔴参数可以使用两个点..代表任意(任意个数、任意类型)
举例如下:对照上述要求理解记忆
⚠ 切点表达式的抽取
⚠ 环绕式增强方法
基于注解的AOP开发
第一步:导入相关依赖
第二步:创建一个公共接口和目标类
第三步:创建一个切面类内置增强方法
第四步:配置配置文件(开启组件扫描、AOP自动代理)
测试类:
🧁 spring的事务控制
编程式事务控制三个对象
🔴平台事务管理器对象
PlatformTransactionManager接口是spring的事务管理器,他里面提供了我们常用的操作事务的方法。
🔴事务定义对象
TransactionDefinition内部封装控制事务的一些参数事务隔离级别
事务隔离性控制不好的情况下,会产生以下三个问题:脏读、不可重复度、幻读
一共有以下五种事务隔离级别
- ISOLATION_DEFAULT:默认
- ISOLATION_READ_UNCOMMITTED:读未提交
- ISOLATION_READ_COMMITTED:读已提交(解决脏读)
- ISOLATION_REPEATABLE_READ:可重复读(解决不可重复读)
- ISOLATION_SERIALIZABLE:串行化(解决三种问题,但是效率极低相当于锁表)
事务传播行为
一个事务方法调用另一个事务是会具有事务传播行为
🔴事务状态对象
TransactionStatus接口提供的是事务具体的运行状态
声明式事务控制
spring的声明事务控制就是采用生命的方式处理事务,这里说的声明就是指在配置文件中声明,用在spring配置文件中声明处理事物的方式代替代码式的处理事务。声明式事务控制可以将业务逻辑和事务管理相分离开,这样在不需要事务管理的时候,只要在设定文件上修改即可移除事务管理服务,无需改变代码方便维护。Spring声明式事务控制的底层就是AOP。
基于xml的声明式事务控制
dao层:定义转入转出方法
service层:实现转入转出的业务逻辑
controller层:调用service层的方法进行事务操作
如果xml文件中不进行aop织入进行事务增强的话,除数为零异常的抛出就会导致tom转出500而lucy并没有转入500,接下来进行事务控制配置
这样的话就会在抛异常的同时两人的账户余额都不会改变
基于注解的声明式事务控制
使用@Repository注解和@Service注解代替AccountDaoImpl和AccountServiceImpl的bean标签,使用@Transactional注解代替aop织入和事务增强设置
🥧 初识MyBatis
下面都是官方的解答有些过于的生硬,按我个人理解就是MyBatis框架就是用来代替jabcTmplate的一个巨巨巨好用的持久层框架,其底层封装的东西比较多,所以开发起来就比较快速。MyBatis是一个基于Java的持久层框架,它的内部封装了jdbc使得开发者只需要关注于SQL语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。MyBatis通过xml或者注解的方式将要执行的各种statement配置起来,并通过Java对象和statement中的SQL的动态参数进行映射生成需要执行的SQL语句。最后MyBatis框架执行SQL语句并将结果映射成Java对象并返回,采用ORM(将实体类与数据库表进行映射)思想解决了实体与数据库映射的问题,对jdbc进行封装屏蔽了jdbc底层访问的细节直接完成数据库持久化操作。
1.1 MyBatis的开发步骤
MyBatis后期结合spring框架进行开发还是有一些小不同的,以下的开发步骤知识为了快速入门而学习,由简入难的学习步骤更加有利于知识点进行掌握。
第一步:导入相关依赖
第二步:创建实体类
第三步:创建mapper映射文件
第四步:创建核心配置文件
测试类及结果:
1.2 mapper映射文件:
映射文件包含的内容
增删改查操作
原始的jdbc默认事务完成之后进行提交,MyBatis的事务默认完成之后不提交。所以当数据进行增删改操作时,必须使用sqlsession.commit()进行手动提交。或者调用openSession方法创建sqlSession对象的时候传参为true,意味着每次更改自动提交。
1.3 核心配置文件
environments标签
- transactionManager标签的Type值
- JDBC:直接使用JDBC的提交和回滚设置,依赖于从数据源得到的连接来进行事务的管理
- MANAGED:几乎不用,了解即可
- dataSource标签的Type值
- UNPOOLED:不使用连接池,数据库的连接随着使用创建和释放
- POOLED:使用连接池,创建一次,随用随取
- JNDI:几乎不用,了解即可
Properties标签
用于加载外部的properties文件,获得数据源参数信息${}进行引用,配合上面的DataSource标签进行使用
typeAliases标签
为java类定义一个别名,这样的话就可以在mapper映射文件中使用别名指定参数类型和结果类型了,方便操作。
⚠ 核心配置文件中那么多标签是有顺序的,如果不按照规定的顺序配置标签的话,就会导致文件报错!!!
⚠ 除了自定义别名以外,MyBatis框架已经为一些常用类型设置好了别名,比如int、string、long、double………
😲mappers标签
用于加载mapper映射文件,暂时只用学习相对路径加载的这一种
🍫 MyBatis的dao层(mapper层)实现
2.1 代理开发方式
Mapper代理开发方式只需要编写Mapper接口,由MyBatis框架根据接口中定义的方法通过反射创建出相应的代理对象和接口的实现类方法。要想完成以上开发,需要遵循以下规范:
- 接口的全限名和mapper映射文件中的namespace相同
- 接口中方法名和mapper映射文件中的每个mapper标签的子标签的id相同
- 接口中方法的参数类型和mapper映射文件中的每个mapper标签的子标签的parameterType相同
- 接口中方法的返回值和mapper映射文件中的每个mapper标签的子标签的resultType相同
开发步骤:
代理开发最主要的就是上面的四个规范,只要将理解并完成规范之后,开发就会异常的快且方便
第一步:导依赖坐标
第二步:编写实体类
第三步:编写核心配置文件和数据源配置的properties文件
第四步:对比着接口配置mapper映射文件
测试类及其测试结果:
2.2 映射文件进阶之动态sql
上面的这种SQL语句写法相当一一个精确匹配,但凡三个参数少任意一个都会造成这么一个问题,就是没有匹配的结果输出。但是一般的业务情况下如果参数少了一个的话就认为这个参数可以是任意值,也就相当于模糊匹配的概念。要想完成这个功能就需要使用动态SQL语句,也就是使用标签进行。
<where>和<if>标签
<if>标签实际上就是对test属性值的条件判断,只有当条件满足了才会将标签内部的语句拼接到上面的select语句中,如果都不满足就会查询所有,有几个条件满足都会拼接到where条件限制,where条件限制不固定就是动态SQL语句的由来。
<foreach>标签
<foreach>标签实际上就是可以拼接SQL语句,有前后的SQL语句成分,有中间的字段值(集合或数组)和分隔符
<sql>片段抽取标签
2.3 核心配置文件进阶
typeHandlers标签
一些情况下我们会有这样的一个业务需求:java中的数据类型与要存放到数据库中的类型不一致,或者数据库中的数据类型和我们需要获取的类型不一致,这时候就需要我们使用自定义的类型处理器了。typeHandlers标签就是用于在核心配置文件中对自定义的类型处理器进行注册使用,于是自定义类型处理器的使用就有两个比较重要的步骤定义处理器类和核心配置文件注册。
下面的案例就是java中是一个Date类型的数据,需要将date转成时间毫秒值存储到数据库中,从数据库中取出时还必须是Date类型。
定义处理器类
核心配置文件注册
plugins标签
MyBatis可以使用第三方的插件进行功能的拓展,plugins标签就是对拓展功能的注册
比如说使用分页插件查询数据库信息
第一步:导入分页插件的相关依赖
第二步:核心配置文件配置分页插件第三步:测试类中设置分页参数,并获取相关参数
核心配置文件中的标签配置是遵循以下顺序的,如果顺序改变的话会导致报错,且红框框住的是相比较而言重要的,且前面都讲过
🍿 MyBatis多表操作
表与表之间有三种对应关系:一对一、一对多、多对多,加下来让我们依次学习一下这三种关系该的表查询如何操作
一对一
一张订单唯一对应着一个用户
第一步:首先要有两个表和对应的实体类,两个表之间还要有一个外键进行表之间的连接
第二步:对应着mapper接口写mapper映射文件 第三步:配置核心配置文件 第四步:测试及结果
⚠ 还有一种映射关系的配置方式,使用<association>标签可读性更加好
多对一
一个用户对应着多张订单
第一步:首先要有两个表和对应的实体类,两个表之间还要有一个外键进行表之间的连接。仔细看,这个案例使用的表还是那两张表,但是实体类就不是上一个案例的实体类了。
第二步:对应着mapper接口写mapper映射文件
第三步:配置核心配置文件,这里的加载mapper映射文件和上一个案例不一样,其他配置都一样
第四步:测试及结果,这里只查询用户id为1的订单,查询出来的所有订单在后面进行拼接,无法全部展示
多对多
一个用户扮演多个角色,一个角色被多个用户扮演
第一步:首先要有三个表和除了连接表之外的实体类,两个表之间还要有一个连接表进行角色表和用户表之间的连接。
第二步:对应着mapper接口写mapper映射文件
第三步:配置核心配置文件,这里的别名role1不一样,其他配置都一样
第四步:测试及结果
学到这MyBatis基本上已经够用了,下面的注解开发只做了解,可以不学,注解开发与xml配置开发的不一样的地方就是,注解开发使用注解将mapper映射文件取而代之,在核心配置文件里mapper映射文件的加载换成包下注解扫描。
🍮 MyBatis实现注解开发
简单的CRUD注解开发
第一步:创建表和实体类、导入依赖 都不再演示
第二步:配置核心配置文件,加载配置文件、配置数据库、扫描包下注解
第四步:接口方法上加注解,省去mapper映射文件
第五步:和xml开发一样直接调用方法
多表查询的注解开发
一对一
一张订单唯一对应着一个用户
第一步:创建user、order表和实体类、导入依赖 都不再演示
第二步:配置核心配置文件,加载配置文件、配置数据库、扫描包下注解,和上面的一样不再演示
第三步:接口方法上加注解,省去mapper映射文件
第四步:和xml开发一样直接调用方法
⚠ 还有一种注解开发方法,和下面要学习的一对多的开发一样,提前了解一下
一对多
一个用户对应着多张订单
第一步:创建user、order表和实体类、导入依赖 都不再演示
第二步:配置核心配置文件,加载配置文件、配置数据库、扫描包下注解,和上面的一样不再演示
第三步:一对多和一对一的拓展方法一样,需要借助其它方法进行查询,在rolemapper创建一个通过id查询的方法
第四步:usermapper接口方法上加注解,省去mapper映射文件
第五步:和xml开发一样直接调用方法
多对多
一个用户扮演多个角色,一个角色被多个用户扮演
第一步:创建user、sys_user_role、sys_role表和User、Role实体类、导入依赖 都不再演示
第二步:配置核心配置文件,加载配置文件、配置数据库、扫描包下注解
第三步:一对多和一对一的拓展方法一样,需要借助其它方法进行查询,在ordermapper创建一个通过id查询的方法
第四步:usermapper接口方法上加注解,省去mapper映射文件
第五步:和xml开发一样直接调用方法
🎉SSM框架整合
在前面的ssm专栏知识的学习中,我们已经大致清楚了ssm框架有三个框架组合而成——spring、springMVC、MyBatis。这三个框架可已经节省了我们很多的开发时间,现在要做的就是将这三个框架整合到一起,让它们能更好的配合为我们节省更多的开发时间。
案例分析
由于本篇博客主要是为了讲解ssm框架的整合知识,所以准备了一个比较简单的案例。项目需要连接数据库实现两个功能:在页面上插入数据、查询数据并在页面上显示。
框架结构
ssm框架在项目src/main/java的目录下一般包含四个package,也就是我们常说的四层,从日常开发的顺序来分依次是domain层(也叫entity层)、mapper层(也叫dao层)、service层、controller层。复杂点的也会有五层,多出来一个until层,由于学习时间有限更复杂的情况就不知道了😓 除了domain层以外,其他三层都有相应的配置文件配合spring框架代码的使用,最后要在web.xml文件中将配置文件加载进去,否则配置文件相当于白配并不会生效。除了上面这种配置文件对应方式,还有一种方式就是将spring-xxx合成一个applicationContext.xml,这样可以使配置文件的数量变得更少些,但是对应关系没有上一种清晰,当然配置文件改变了web.xml文件也会跟着改变。本次案例就采用第二种对应方式进行讲解。
实现
项目的层级结构
如果不会创建一个web项目的话,参考往期博客进行创建【SSM面向CRUD编程专栏 3】关于黑马程序员最全SSM框架教程视频,P37集老师跳过的模块创建以及tomcat下载安装配置和运行等诸多问题
pom.xml
<dependencies><!--spring相关--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.5.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.7</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.0.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.0.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.0.5.RELEASE</version></dependency><!--servlet和jsp--><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version></dependency><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.0</version></dependency><!--mybatis相关--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency></dependencies>
domain层
数据库中account表对应的实体类,首先需要参考下图创建一个数据库表
domain层下创建一个Account类对应数据库中的各个字段
public class Account {private Integer id;private String name;private Double money;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;} }
mapper层
之前学过MyBatis的代理开发模式,使用这种模式进行开发就不再需要实现类了,但是需要满足以下四个条件
- mapper标签的namespace = 接口全限名
- 接口方法 = mapper字标签的id
- 接口方法参数类型 = mapper字标签的parameterType
- 接口方法返回值类型 = mapper字标签的resultType
mapper层创建一个AccountMapper接口,定义插入和查询功能的实现方法
public interface AccountMapper {public void save(Account account);public List<Account> findAll();}
创建AccountMapper.xml映射文件使用SQL语句对数据库进行操作
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.itheima.mapper.AccountMapper"><insert id="save" parameterType="account">insert into account values(#{id},#{name},#{money})</insert><select id="findAll" resultType="account">select * from account</select></mapper>
对数据库进行操作,要连接到数据库,在这里需要将数据库的配置抽取到jdbc.properties中方便日后修改(等于号右边的要配成自己数据库的值),又叫解耦合
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test jdbc.username=root jdbc.password=123456
有了数据库配置之后就需要在applicationContest.xml中配置数据源信息 ,当然需要先加载上面的数据库配置了,也就是常说的创建连接池,连接池的好处相信大家都知道了吧
<!--加载propeties文件--> <context:property-placeholder location="classpath:jdbc.properties"/><!--配置数据源信息--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${jdbc.driver}"></property><property name="jdbcUrl" value="${jdbc.url}"></property><property name="user" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property> </bean>
下面的service层需要使用到AccountMapper对象调用其中的方法,所以需要使用spring框架底层直接创建好对象之后service直接注入,这样的话就需要我们在applicationContest.xml中让spring框架知道它需要做这件事了
<!--配置sessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"></property><!--加载mybatis核心文件--><property name="configLocation" value="classpath:sqlMapConfig-spring.xml"></property> </bean><!--扫描mapper所在的包 为mapper创建实现类--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.itheima.mapper"></property> </bean>
数据库的操作又叫事务,既然有事务就要在applicationContest.xml对它进行声明式事务控制
<!--声明式事务控制--> <!--平台事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property> </bean><!--配置事务增强--> <tx:advice id="txAdvice"><tx:attributes><tx:method name="*"/></tx:attributes> </tx:advice><!--事务的aop织入--> <aop:config><aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))"></aop:advisor> </aop:config>
service层
service层有一个AccountService接口由于阐述业务需求,也就是需要完成哪些功能,一般来说它和AccountMapper接口一样
public interface AccountService {public void save(Account account);public List<Account> findAll();}
service层下有个Implpackage,又来放所有的接口实现类,比如说这个案例的AccountServiceImpl类。之前创建好的AccountMapper的对象就可以使用注解直接注入,然后调用它的方法
@Service("accountService") public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountMapper accountMapper;public void save(Account account) {accountMapper.save(account);}public List<Account> findAll() {return accountMapper.findAll();} }
这里使用了@Service注解,所以需要在applicationContest.xml对注解进行扫描,让框架知道应该创建相应的对象了
<!--组件扫描 扫描service和mapper--> <context:component-scan base-package="com.itheima"><!--排除controller的扫描--><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter> </context:component-scan>
controller层
controller层是与前端进行交互的层,决定了请求和响应的具体事项,下面先放代码再进行详解
@Controller @RequestMapping("/account") public class AccountController {@Autowiredprivate AccountService accountService;// 保存数据@RequestMapping(value = "/save", produces = "text/html;charset=UTF-8")@ResponseBodypublic String save(Account account){accountService.save(account);return "保存成功";}//查询数据@RequestMapping("/findAll")public ModelAndView findAll(){List<Account> accountList = accountService.findAll();ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("accountList",accountList);modelAndView.setViewName("accountList");return modelAndView;}}
这里使用了@controller注解,所以需要在SpringMVC.xml配置注解驱动并对注解进行扫描,让框架知道应该创建相应的对象了
<!--组件扫描 主要扫描controller--> <context:component-scan base-package="com.itheima.controller"/><!--配置mvc注解驱动--> <mvc:annotation-driven/>
@RequestMapping注解设置了方法的映射地址,前端访问该映射地址的时候会执行对应的方法,类上注解和方法上注解分别对应着两级不一样的映射路径,也就是说当前端发送/account/save请求时,就会调用save方法。produces属性是设置响应的编码格式以防返回给页面一个字符串的时候出现乱码
@ResponseBody注解标明返回的只是一个字符串,而不是进行页面的跳转,如果是页面跳转的话,需要开放静态资源(jsp页面)访问权限,还可以在SpringMVC.xml中配置视图解析器,给返回值加上前缀和后缀(因为开发时的前端页面都会放在一个统一的package下而且页面的后缀基本上都是jsp,于是就可以减少开发时的代码书写)
<!--开放静态资源访问权限--> <mvc:default-servlet-handler/><!--内部资源视图解析器--> <bean id="resourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/pages/"></property><property name="suffix" value=".jsp"></property> </bean>
存数据的jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>Title</title> </head> <body><h1>添加账户信息表单</h1><form name="accountForm" action="${pageContext.request.contextPath}/account/save" method="post">账户名称:<input type="text" name="name"><br>账户金额:<input type="text" name="money"><br><input type="submit" value="保存"><br></form> </body> </html>
查询数据对应的jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head><title>Title</title> </head> <body><h1>展示账户数据列表</h1><table border="1"><tr><th>账户id</th><th>账户名称</th><th>账户金额</th></tr><c:forEach items="${accountList}" var="account"><tr><td>${account.id}</td><td>${account.name}</td><td>${account.money}</td></tr></c:forEach></table> </body> </html>
web.xml文件
加载applicationContext.xml文件
<!--spring 监听器--> <context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value> </context-param> <listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
加载SpringMVC.xml文件,又叫前端控制器
<!--springmvc的前端控制器--> <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:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup> </servlet> <servlet-mapping><servlet-name>DispatcherServlet</servlet-name><url-pattern>/</url-pattern> </servlet-mapping>
配置全局过滤器用于解决POST请求乱码问题
<!--全局过滤器用于解决乱码问题--> <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>
至此,ssm的框架整合就告一段落了,每一位大国工匠都是从基础学起,之所以能够异于常人。除了天赋以外就是在夯实基础功之外,加之时间的沉淀和经验的积累。万丈高楼平地起,这就是框架开发的第一站。
上一篇:【SSM面向CRUD编程专栏 9】SSM框架整合
完结……