SSM框架简介
SSM框架,是Spring + Spring MVC + MyBatis的缩写,这个是继SSH之后,目前比较主流的Java EE企业级框架,适用于搭建各种大型的企业级应用系统。 我们先大概的回顾一下吧。
1.Spring简介
Spring是一个开源框架,Spring是于2003年兴起的一个轻量级的Java开发框架,由Rod Johnson在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。 简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
A.控制反转(IOC)是什么呢?
IOC:控制反转也叫依赖注入。利用了工厂模式将对象交给容器管理,你只需要在spring配置文件总配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类(假设这个类名是A),分配的方法就是调用A的setter方法来注入,而不需要你在A里面new这些bean了。
[注意]:面试的时候,如果有条件,画图,这样更加显得你懂了
B.面向切面(AOP)又是什么呢?
首先,需要说明的一点,AOP只是Spring的特性,它就像OOP一样是一种编程思想,并不是某一种技术,AOP可以说是对OOP的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
[简单点解释],比方说你想在你的biz层所有类中都加上一个打印‘你好,AOP’的功能这你经可以用aop思想来做,你先写个类写个方法,方法经实现打印‘你好,AOP’让后你Ioc这个类 ref=“biz.*”让每个类都注入。
2.Spring MVC简介
Spring MVC属于Spring Framework的后续产品,已经融合在Spring Web Flow里面,它原生支持的Spring特性,让开发变得非常简单规范。Spring MVC 分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。
3.MyBatis简介
MyBatis本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。MyBatis是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。可以这么理解,MyBatis是一个用来帮你管理数据增删改查的框架。
先看一下项目的结构吧
说了这么多,现在回归正题,SSM框架里面有很多繁杂的配置,一共有三个
1.web.xml
2.spring.xml
3.mvc-servlet.xml
下面将会依次详细介绍三个配置文件的配置方法以及配置的含义
1.web.xml
web.xml是ssm项目当中最重要的一个配置文件,当服务启动时会首先加载web.xml这个文件,里面包括了对前端控制器、乱码等的配置
下面贴上代码,代码当中有详细的注释解释每个配置的意思
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!--spring的配置-->
<!--==================================================-->
<!-- 配置srping的位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<!-- 配置监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--springmvc需要配置的-->
<!--==================================================-->
<!-- 配置springmvc的核心控制器 -->
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 解决乱码的配置 -->
<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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 请求method支持 put和delete必须添加过滤器 -->
<filter>
<filter-name>myFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--<!– 启用druid的监控功能 –>-->
<servlet>
<servlet-name>statViewServlet</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
<init-param>
<param-name>loginUsername</param-name>
<param-value>admin</param-value>
</init-param>
<init-param>
<param-name>loginPassword</param-name>
<param-value>admin</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>statViewServlet</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
</web-app>
2.spring.xml
spring.xml的作用是为了给spring进行配置
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<!-- 扫描cn -->
<context:component-scan base-package="cn">
<!-- spring是bean的容器(spring用来操做 service+repository)controler是springmvc操作的 所以排除-->
<!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />-->
</context:component-scan>
<!-- 扫描jdbc文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--
所有数据库操作的源头 实现自接口DataSouce DriverManagerDataSource
还有很多连接池继承DataSouce DriverManagerDataSource 实现了很多子连接池 比如c3p0 dbcp druid
这里我们就用阿里的druid,因为这个连接池有一个监控功能
-->
<!-- 连接数据库 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${url}"></property>
<property name="driverClassName" value="${driverClass}"></property>
<property name="username" value="${username1}"></property>
<property name="password" value="${password1}"></property>
<!-- 默认初始化的连接个数 -->
<property name="initialSize" value="1"></property>
<!-- 最大允许的连接个数 -->
<property name="maxActive" value="200"></property>
<!-- 最大的等待人数 -->
<property name="maxIdle" value="100"></property>
<!-- 开启sql统计功能 -->
<property name="filters" value="stat"></property>
</bean>
<!-- 事务管理器 不再使用jdbc的commit和rollback 必须由事务管理器提供 配置事物管理器必须要注入dataSource-->
<bean id="tm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 定义事物里面的通知-->
<tx:advice id="txAdvise" transaction-manager="tm">
<tx:attributes>
<!--tx:method的作用是拦截指定方法开头的 然后对他进行事物处理 -->
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<!--read-only="true" 意思是除了上面拦截的以外 其他的都不拦截-->
<tx:method name="*" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 定义切点(搜索条件) aop:pointcut 意思相当于跟if差不多 满足条件的就会进来
表示式 execution(返回值 包.类.方法(参数 ...代表任意多个参数))
id 表示切点的名字
-->
<aop:pointcut expression="execution(* cn.et.service.*.*(..))" id="myPointCut" />
<!-- 关联切点和事务管理器 将事物跟切点连接起来-->
<aop:advisor advice-ref="txAdvise" pointcut-ref="myPointCut" />
</aop:config>
<!-- 集成mybatis -->
<!-- 实例化一个SqlSessionFactory工厂-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--指定实体文件所在的位置-->
<property name="typeAliasesPackage" value="cn.et.model"/>
<!--指定mapper.xml文件所在的位置-->
<property name="mapperLocations" value="classpath:mapper/*.xml" />
</bean>
<!-- 扫描mybatis的接口映射文件 -->
<bean id="scannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.et.mapper"></property>
</bean>
</beans>
3.mvc-servlet.xml
mvc-servlet.xml是为了配置springMVC
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
">
<!-- 扫描cn -->
<context:component-scan base-package="cn">
<!-- pringmvc主要操作控制层、视图层所以给不相关的层次的注解排除-->
<!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>-->
<!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
</context:component-scan>
<!--如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。-->
<mvc:default-servlet-handler/>
<!-- 引用返回对象 响应json的消息转换器 -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html</value>
<value>application/x-www-form-urlencoded</value>
</list>
</property>
</bean>
<!-- 配置返回对应解析成json的消息转换器 -->
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html</value>
<value>application/x-www-form-urlencoded</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
</beans>
小提示
关于配置文件这个东西,在初学各种框架的时候非常让人头疼,一不小心配置错误,项目就运行不了。所以不如直接从网上找个小项目的源码把代码删除,直接用别人的配置来做练习,在框架能熟练运用之后再慢慢学习配置文件的问题。
据本人亲自整合,在jar包导入完全的时候,上面的这些配置文件可直接使用(数据库连接参数被删除,包名和一些路径的配置根据自己项目来进行更改)
到此我们的SSM环境也就搭建好了,现在就要去实现业务的代码了
1.model层是实体类,属性对应数据库表的每个字段,并且我们使用驼峰规则命名属性(这里很重要,字段userName和UserName在封装时差别很大,会映射不到mapper层的xml上),我们一贯的做法会让model序列化,这里虽然没有并发的情况,但是这是一种良好的做法。代码如下:
package cn.et.model;
public class Student {
private String id;
private String name;
private String age;
private String sex;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
2.mapper层,这里实现和数据库直接交互的接口,@Repository的注解目的是持久化数据层。在这里为了同时将mybatis注解写sql跟xml写sql表现出来,我有的接口用的是注解实现的有的接口用的是xml实现的代码如下:
package cn.et.mapper;
import cn.et.model.Student;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface StudentMapper {
/**
* 添加
* @param student
*/
void insertStudent(@Param("student") Student student);
/**
* 删除
* @param id
*/
@Delete("delete from student where sid=#{id}")
void deleteStudent(@Param("id") String id);
/**
* 修改
* @param student
*/
void updateStudent(@Param("student") Student student);
/**
* 查询所有
* @return
*/
@Results({
@Result(column = "sid",property = "id"),
@Result(column = "sname",property = "name"),
@Result(column = "sage",property = "age"),
@Result(column = "ssex",property = "sex")
})
@Select("select * from student")
List<Student> studentList();
/**
* 根据id查询单个
* @param id
* @return
*/
@Results({
@Result(column = "sid",property = "id"),
@Result(column = "sname",property = "name"),
@Result(column = "sage",property = "age"),
@Result(column = "ssex",property = "sex")
})
@Select("select * from student where sid=#{id}")
Student studentById(@Param("id") String id);
/**
* 根据条件查询
* @param student
* @return
*/
List<Student> studentQuery(@Param("student") Student student);
}
xml类代码:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.et.mapper.StudentMapper" >
<resultMap id="studentMap" type="cn.et.model.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="age" column="sage"/>
<result property="sex" column="ssex"/>
</resultMap>
<insert id="insertStudent">
INSERT INTO student (sid,sname,sage,ssex) VALUES (#{student.id},#{student.name},#{student.age},#{student.sex})
</insert>
<update id="updateStudent">
UPDATE student
<set>
<if test="student.name !=null and student.name !=''">
sname=#{student.name},
</if>
<if test="student.age !=null and student.age !=''">
sage=#{student.age},
</if>
<if test="student.sex !=null and student.sex !=''">
ssex=#{student.sex},
</if>
</set>
WHERE sid =#{student.id}
</update>
<select id="studentQuery" resultMap="studentMap">
select * from student
<where>
<if test="student.name !=null and student.name !=''">
and sname like '%${student.name}%'
</if>
<if test="student.age !=null and student.age !=''">
and sage=#{student.age}
</if>
<if test="student.sex !=null and student.sex !=''">
and ssex=#{student.sex}
</if>
</where>
</select>
</mapper>
3.service层,这是用来处理业务的,代码如下:
package cn.et.service;
import cn.et.model.Student;
import java.util.List;
public interface StudentService {
/**
* 添加
* @param student
*/
void insertStudent(Student student);
/**
* 删除
* @param id
*/
void deleteStudent(String id);
/**
* 修改
* @param student
*/
void updateStudent(Student student);
/**
* 查询所有
* @return
*/
List<Student> studentList();
/**
* 根据id查询单个
* @param id
* @return
*/
Student studentById(String id);
/**
* 根据条件查询
* @param student
* @return
*/
List<Student> studentQuery(Student student);
}
4.serviceImpl层是service的实现类,@Service注解告诉Spring这是个业务层,Service中声明了几个接口,我们并没有去真正去实现,但是有人想为什么不直接写个实现类,反而要加一个Service层呢?这个问题,这要归结到代码复用性和维护性问题,如果你直接写一个实现,当你增加新的功能时就要在一大堆代码中是添加修改,这是非常耗时的操作,所以接口和实现分离有利于维护,代码如下:
package cn.et.service.impl;
import cn.et.mapper.StudentMapper;
import cn.et.model.Student;
import cn.et.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
StudentMapper studentMapper;
public void insertStudent(Student student) {
studentMapper.insertStudent(student);
}
public void deleteStudent(String id) {
studentMapper.deleteStudent(id);
}
public void updateStudent(Student student) {
studentMapper.updateStudent(student);
}
public List<Student> studentList() {
List<Student> students = studentMapper.studentList();
return students;
}
public Student studentById(String id) {
return studentMapper.studentById(id);
}
public List<Student> studentQuery(Student student) {
return studentMapper.studentQuery(student);
}
}
5.控制层Controller
使用@Controller注解,然后使用@AutoWired导入service层,因为service中的方法是我们使用到的,controller通过接收前端传过来的参数进行业务操作,在返回一个指定的路径或者数据表。RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。@responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据,需要注意的呢,在使用此注解之后不会再走试图处理器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。上面讲的这些都是Spring MVC知识,如果你看不懂请百度Spring MVC流程控制,基础知识我就不多废话。具体代码如下:
package cn.et.controller;
import cn.et.model.Student;
import cn.et.service.StudentService;
import cn.et.tool.CommonResponse;
import cn.et.tool.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class StudentController {
@Autowired
StudentService studentService;
@RequestMapping("insertStudent")
public Response insertStudent(Student student){
studentService.insertStudent(student);
return Response.success();
}
@RequestMapping("deleteStudent")
public Response deleteStudent(String id){
studentService.deleteStudent(id);
return Response.success();
}
@RequestMapping("updateStudent")
public Response updateStudent(Student student){
studentService.updateStudent(student);
return Response.success();
}
@RequestMapping("studentList")
public Response studentList(){
List<Student> students = studentService.studentList();
return new CommonResponse<List<Student>>(students);
}
@RequestMapping("studentById")
public Response studnetById(String id){
Student student = studentService.studentById(id);
return new CommonResponse<Student>(student);
}
@RequestMapping("studentQuery")
public Response studentQuery(Student student){
List<Student> students = studentService.studentQuery(student);
return new CommonResponse<List<Student>>(students);
}
}
至此我们的后台代码就告一段落了,如有疑问的小伙伴给我留言。。。。。。。。