admin 管理员组

文章数量: 887021

SSM

SSM

IOC (Inverse Of Control:反转控制)

Spring IOC 实现原理 - 简书 (jianshu.com)

注意javajdk版本的区别 必须相等

Sping 5

spring的配置文件

Bean标签范围配置

当scope的取值为singleton时

Bean的实例化对象个数:1个

Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例

Bean的声明周期:

  • 对象创建:当应用加载,创建容器时,对象就被创建了
  • 对象运行:主要容器在,对象就一直活着
  • 对象销毁:当应用卸载,销毁容器时,对象就被销毁了

当scope的取值为prototype时

Bean的实例化对象个数:多个

Bean的实例化时机:当调用getBean()方法时实例化Bean

Bean的声明周期:

  • 对象创建:当使用对象时,创建新的对象实例
  • 对象运行:主要对象在使用中,就一直活着
  • 对象销毁:当对象长时间不用时,被Java的垃圾回收站回收
<!-- scope 默认为singleton单例的  prototype多例的  配置的对象取决于scope调用的UserDaoImpl 的无参构造scope为singleton时,创建bean的时机是加载xml配置文件的时候scope为prototype时,创建bean的时机是每次getBean的时候-->
Bean实例化三种方式
  • 无参构造方法实例化
<!-- 无参构造   -->
<!--    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" scope="singleton"></bean>-->
  • 工厂静态方法实例化
<!--静态工厂创建的无参构造    -->
<!--    <bean id="userDao" class="com.itheima.factory.StaticFactory" factory-method="getUserDao"></bean>-->
  • 工厂实例方法实例化
<!--    实例的工厂-->
<!--    <bean id="factory" class="com.itheima.factory.DynamicFactory"></bean>-->
<!--    <bean id="userDao" factory-bean="factory" factory-method="getUserDao"/>-->
Bean的依赖注入(DI)

有两种注入Bean的方式

  • set

  • 构造器方法的注入 —不用写set方法

注入数据的三种数据属性

  • 普通数据类型
<!--    普通属性的注入 使用value    --><bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"><property name="username" value="zhangsan"/><property name="age" value="18"/></bean>
  • 引用数据类型

  • 集合数据类型

    <!-- 集合类型的注入   --><bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"><property name="strList"><list><value>aaa</value><value>bbb</value><value>ccc</value></list></property><property name="userMap"><map><entry key="u1" value-ref="user1"></entry><entry key="u2" value-ref="user2"></entry></map></property><property name="properties"><props><prop key="p1">ppp1</prop><prop key="p2">ppp2</prop><prop key="p3">ppp3</prop></props></property></bean><bean id="user1" class="com.itheima.domain.User"><property name="name" value="tom"/><property name="addr" value="beijing"/></bean><bean id="user2" class="com.itheima.domain.User"><property name="name" value="lucy"/><property name="addr" value="tianjin"/></bean>
    
引入其他配置文件
<!--    引用其他模块的配置文件--><import resource="applicationContext-user.xml"/>
Spring的重点配置

标签:

  • id属性:在容器中Bean实例的唯一标识,不允许重复
  • class属性:要实例化Bean的全限定名
  • scope属性:bean的作用范围,常用的是Singleton(默认)和rototype
  • 标签:属性注入
    • name属性:属性名称
    • value属性:注入的普通属性值
    • ref属性:注入的对象引用值(在一个bean中注入另一个bean)
    • 标签:注入集合属性
    • < map> 标签:注入键值对属性
    • 标签 :注入资源属性
    • 标签
    • 标签:导入其他的Spring的分文件

SpringAPI

ApplicationContext的继承体系 读取xml配置文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D3VyEqPm-1652271393849)(C:\Users\29769\AppData\Roaming\Typora\typora-user-images\image-20220410173610216.png)]

ApplicationContext的实现类
  1. ClassPathXmlApplicationContext 它是从类的根路径下加载配置文件推荐使用这种
  2. FileSystemXmIApplicationContext 它是从磁盘路径上加載配置文件,配置文件可以在磁盘的任意位置。
  3. AnnotationConfigApplicationContext 当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

Spring配置数据源

数据源(连接池)的作用
  • 数据源(连接池)是提高程序性能出现的
  • 事先实列化数据源,初始化部分连接资源
  • 使用连接资源时从数据源中获取
  • 使用完毕将连接资源还给数据源

常见的数据源(连接池):DBCP C3P0 BoneCP Druid等

数据源的开发步骤
  • 导入数据源的坐标和数据库驱动坐标
  • 创建数据源对象()
  • 设置数据源的基本连接数据(数据库的账号密码的配置)
  • 使用数据源获取连接资源和归还连接资源(close的方法关闭数据库的连接)

使用properties来配置数据库的基本信息

基本的抽取的配置文件 方便解耦

在resources资源包下创建jdbc.properties—里面写的是数据库的配置

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/game
jdbc.username=root
jdbc.password=root

根据Spring配置数据源

bean标签里的Class是创建数据源对象的方法地址

需要在对set方法注入参数(<property name=“set方法后的参数名(需要第一个字母小写)” )

在方法里有两种getbean的方式

首先读取Spring的xml配置文件:ApplicationCaotext app = new ClassPathXmlApplicationContext("applicationCaotext.xml ")

  • 根据bean的id去获取 app.getBean(dataSource)
  • 根据bean的属性去获取 app.getBean(DataSource.class) bean标签的id首字母大写加.class

抽取jdbc的配置文件

在Spring的bean标签里加载其他properties的配置文件内容

获取properties的value用spring的表达式${key(指的是properties里的key)}

Spring的注解开发

Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率

Spring原始注解主要是代替****的配置

组件扫描 让Spring来识别对应包下的注解

Spring注解配置

userService需要用到userDao里的save方法 原本是userDao放到了Spring容器里,但现在在Service里调用到userDao里的save方法

需要把userService也放到容器里,将userDao注入到service里更加简便

@Componment(“userDao”)放在方法的头部 来表示此方法放入spring容器里

引号里的字符串相当于xml文件里Bean标签的id spring根据包去扫描对应的id来寻找对应的注入

来实线对应的bean标签的创建

@Autowired

@Qualifier(userDao)

相当于properties (set的方法名)和ref(“注谁?”)

这是告诉spring Dao 注入到Service

在使用xml配置的方法的时候必须要写对象的set的方法

而在使用注解的方式则可以省略写set方法 因为spring会调用反射技术为其赋值 也可以省略**@Qualifier(userDao)的注解 因为@Autowired按照数据类型**从Spring容器中进行匹配,找到对应的bean类型放入,但是只能如果有多个相同类型的bean则不能匹配

@Qualifier(userDao)按照id的值从容器中进行匹配的,但是必须结合**@Autowired**一起使用

按照数据类型注入:@Autowired

按照id值注入:@Autowired+@Qualifier(userDao)

@Resource(“userDao”) = @Autowired+@Qualifier(“userDao”) 按照id注入

@Value(”aaaa“)

private String driver --------->driver = aaaa

Spring新注解

SpringConfiguration Spring的核心配置类

DataSourceConfiguration是加载数据源的配置类------>存放数据库账号密码等数据

但是Spring的核心配置类只能有一个 所以需要在SpringConfiguration 引用外部类

使用@import(需要引用的外部类的名字.calss)标签来引用

如果需要加载多个则@import({需要引用的外部类的名字.calss,XXX,XXXX})

测试使用注解的核心配置类来使用spring容器

Spring集成junit

JUnit - 概述_w3cschool

什么是 JUnit?

JUnit 是一个 Java 编程语言的单元测试框架。JUnit 在测试驱动的开发方面有很重要的发展,是起源于 JUnit 的一个统称为 xUnit 的单元测试框架之一。

JUnit 促进了“先测试后编码”的理念,强调建立测试数据的一段代码,可以先测试,然后再应用。这个方法就好比“测试一点,编码一点,测试一点,编码一点……”,增加了程序员的产量和程序的稳定性,可以减少程序员的压力和花费在排错上的时间。

ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) app.getBean("userDao");

获取spring容器上的对象就要getbean ,然而每次都要写

所以:

  • 让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它

  • 将需要进行测试Bean直接在测试类中进行注入

    集成步骤
    • 导入Spring集成Junit的坐标

    • 使用@Runwith注解替换原来的运行期

    • 使用@ContextConfiguration指定配置文件和配置类

    • 使用@Autowired注入需要进行测试的对象

    • 创建测试方法进行测试

配置文件集成测试

全注解集成指定Spring的核心配置类

Spring集成web环境

  • 首先创建了web包下的UserServlet,在类里读取Spring的配置文件 获取spring容器的bean对象 userService来调用Dao层的UserDaoImpl的save方法

  • 在web.xml的文件下添加配置源

<servlet><servlet-name>UserServlet</servlet-name><servlet-class>com.itheima.web.UserServlet</servlet-class>
</servlet><!--映射-->
<servlet-mapping><servlet-name>UserServlet</servlet-name><url-pattern>/userServlet</url-pattern>
</servlet-mapping>
  • 在tomcat上部署项目
1.1ApplicationContext应用上下文获取方式(监听器)

应用上下文对象是通过new ClassPathXmlApplicationContext(spring配置文件)方式获取的,但是每次从容器在获得Bean时都要编写new ClassPathXmlApplicationContext(spring配置文件),这样的弊端是配置文件加载多次,应用上下文对象多次创建。所以我们需要只要应用上下文对象spring容器创建一次,web的组件谁想用谁去拿就行了

---------用监听器来创建

在Web项目中,可以使用ServletContextListener监听Web应用的启动,我们可以在Web应用启动时,就加 我Spring的配置文件,创建建应用上下文对象ApplicationContext, 在将其存储到最大的域sqsvletContext 中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。

ContextLoaderListener类

@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {ServletContext servletContext = servletContextEvent.getServletContext();//读取web.xml中的全局参数String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");//获取spring上下文对象ApplicationContext app = new ClassPathXmlApplicationContext(contextConfigLocation);//将spring上下文对象存储到ServletContext域中//上下文对象存入到ServletContext域中 名字是耦合死的 在运用的时候需要解耦方便写servletContext.setAttribute("app",app);System.out.println("spring容器创建完毕------init.......");}

需要在web.xml里配置 让服务器能够识别

<!--配置监听器-->
<listener><listener-class>com.itheima.listener.ContextLoaderListener</listener-class>
</listener>
<servlet><servlet-name>UserServlet</servlet-name><servlet-class>com.itheima.web.UserServlet</servlet-class>
</servlet>

这里获得上下文对象名字是写死的不方便读取,需要进行解耦

 ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");

创建WebApplicationContextUtils类来返回上下文对象的名字

public class WebApplicationContextUtils {/*** 解耦 上下文对象 名字* @param servletContext* @return*/public static ApplicationContext getWebApplicationContext(ServletContext servletContext){return (ApplicationContext)servletContext.getAttribute("app");}
}

在读取上下文对象的时候进行解耦了

        //解除固定的app的名字用工具类来获取 解耦
//        ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);UserService userService = app.getBean(UserService.)
1.2 Spring提供获取应用上下文的工具

上面的分析不用手动实现,Spring提供了一个监听器ContextLoaderListener就是对上述功能的封装,该监 听器内部加载Spring配置文件,创建应用上下文对象,并存储到Servletcontextt中,提供了一个客户端工 具WebApplicationContextUtils供使用者获得应用上下文对象

如何使用

  • 在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)
  • 使用WebApplicationContextUtils获取应用上下文对象ApplicationContext

SpringMVC

2.1 SpringMVC概述

SpringMVC是一种基于Java 的实现 MVC 设计模型的请求驱动类型的轻量级 web 框架属于 SpringFrameWork的后续产品,已经融合在 Spring Web Fiow中

SpringMVC已经成为目前最主流的MVC框架之一,并且随者Spring3.0的发布,全面超越Struts2,成为最优秀的MVC框架。

它通过一套注解,让一个简单的Java 类成为处理请求的控制器,而无须实现任何接口。同时 它还支持 RESTful 编程风格的请求。

SpringMVC 快速入门 开发步骤
  • 导入SpringMVC相关坐标
  • 配置SpringMVC核心控制器DispathcerServlet
  • 创建Controller类和视图
  • 使用注解配置Controller类中的业务方法的映射地址
  • 配置SpringMVC核心文件spring-mvc.xml
  • 客户端发起请求测试

从代码上分析的流程图

逻辑上的流程图

SpringMVC的组件解析

3.1 SpringMVC的执行流程

  • 用戶发送请求至前端控制器DispatcherServlet.
  • DispatcherServlet收到请求调用Handler Mapping处理器映射器。
  • 处理器映射器找到具体的处理器(可以根据xmi配置、注解进行查找),生成处理器对象及勿 器拦截器(如果 有则生成)一并返回给DispatcherServlet.
  • DispatcherServlet调用HandlerAdapter处理器适配器。
  • HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)
  • Controller执行完成后返回ModelAndView
  • HandlerAdapter将Controller的执行结果ModelAndView返回给DispatcherServlet
  • DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
  • ViewReslover解析后返回具体View。
  • DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户
SpringMVC注解解析

@RequestMapping:用于建立请求URL和处理请求方法之间的对应关系

  • 类上,请求URL的第一级访问目录,此处不写的话,就相当于应用的根目录
  • 方法上,请求URL的第二级访问目录,与类上的使用@RequestMapping标注的一级目录一起组成访问的虚拟路径

属性:

  • value:用于指定请求的URL。他和path属性的作用是一样的
  • method:用于指定请求的方式
  • params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样

列如:

  • params = {“accountName”},表示请求参数必须有accountName
  • params = {“money!100”},表示请求参数中money不能是100
    参数必须携带username

@RequestMapping(value"/quick",method=RequestMethod.GET.params={“username”})

SpringMVC组件扫描

SpringMVC的xml的配置解析

SpringMVC的相关组件
  • 前端控制器 DispatcherServlet
  • 处理器映射器:HandlerMapping
  • 处理器适配器:HandlerAdapter
  • 处理器:Handler
  • 视图解析器:View Resolver
  • 视图:View
Spring MVC的注解和配置

请求映射注解:@RequestMapping

视图解析器配置: REDIRECT_URL_PREFIX = “redirect.” FORWARD URL PREFIX = “forward:”

前缀:prefix=“”

后缀:suffix=“”

SpringMVC的数据响应

页面跳转
直接返回字符串

此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转

通过ModelAndView对像返回
@RequestMapping("/quick5")
public String save4(HttpServletRequest request){/*Model:模型 作用封装数据*/request.setAttribute("username","MBM");return "/success.jsp";
}@RequestMapping("/quick4")
public String save4(Model model){/*Model:模型 作用封装数据*/model.addAttribute("username","MsM");return "/success.jsp";
}/*** 传参的modelAndView spring发现需要为它传递参数* 从而在spring容器里拿出modelAndView 传递过来* @param modelAndView* @return*/
@RequestMapping("/quick3")
public ModelAndView save3(ModelAndView modelAndView){/*Model:模型 作用封装数据View: 视图 作用参数数据*///添加模型数据modelAndView.addObject("username","MVM");//设置视图名称modelAndView.setViewName("/success.jsp");return modelAndView;
}@RequestMapping("/quick2")
public ModelAndView save2(){/*Model:模型 作用封装数据View: 视图 作用参数数据*/ModelAndView modelAndView = new ModelAndView();//添加模型数据modelAndView.addObject("username","itcast");//设置视图名称modelAndView.setViewName("/success.jsp");return modelAndView;
}//请求地址 http://localhost:8080/user/quick
@RequestMapping("/quick")
public String save(){System.out.println("controller save running....");//转发默认省略了forward// forward:/success.jsp//重定向默认省略了redirect// redirect:/success.jspreturn "/success.jsp";
}
回写数据
直接返回字符串

1.通过传入参数一样由Spring容器给方法传入HttpServletRespone对象来在页面上回写字符串、

@RequestMapping("/quick6")
public void save6(HttpServletResponse response) throws IOException {response.getWriter().write("hello,heima");
}

2.将需要回写的字符串直接返回,但此时需要通过@ResponseBody注解告知SpringMVC框架,方法返回的字符串不是跳转是直接在http响应体中返回

@RequestMapping("/quick7")
@ResponseBody
public String save7() throws IOException {return "hello,heima·";
}
返回对象或集合

原本利用JSON转换工具转换成JSON格式字符串对象再返回

@RequestMapping("/quick9")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public String save9() throws IOException {User user = new User();user.setName("lisi");user.setAge(30);//使用JSON的转换工具将对象转换成JSON格式的字符串返回ObjectMapper objectMapper = new ObjectMapper();String json = objectMapper.writeValueAsString(user);return json;
}

现在有Spring-mvc的配置更加方便能传递

首先在Spring-mvc的xml文件下配置

<!--配置处理器映射器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"><property name="messageConverters"><list><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/></list></property>
</bean>

可以使用mvc注解驱动更加方便的注入

<!--配置mvc注解驱动代替上面的处理器映射器-->
<mvc:annotation-driven/>

而调用的时候直接返回User类型的对象即可 在页面上返回对象或集合

@RequestMapping("/quick10")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
//期望SpringMVC将user转换成JSON格式的字符串格式返回
public User save10() throws IOException {User user = new User();user.setName("lisi");user.setAddr("nmmdw");user.setAge(30);return user;
}

SpringMVC获得请求数据

  • 基本类型参数
  • POJO类型参数
  • 数组类型参数
  • 集合类型参数
获得基本类型参数

user/quick11?username=zhangsan&age=11

控制台直接打印 zhangsan 11

@RequestMapping("/quick11")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public void save11(String username,int age) throws IOException {System.out.println(username);System.out.println(age);
}
获得POJO类型参数

Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数会自动映射匹配。

封装一个实体对象就是在一次数据返回的时候SpringMVC会自动的去匹配和请求参数属性一致的属性进行写入,最后封装成一个对象属性

user/quick11?username=zhangsan&age=11&addr=beijing

控制台会打印对应的实体类属性

@RequestMapping("/quick12")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public void save12(User user) throws IOException {System.out.println(user);
}
获得数组类型参数

Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数会自动映射匹配。

/user/quick13?str=aaaa&str=bbbb&str=cccc

控制台会打印数组数据

@RequestMapping("/quick13")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public void save13(String []str) throws IOException {System.out.println(Arrays.asList(str));
}
获得集合类型参数
1.使用POJO进行包装
页面form表单上获取对象集合数据

封装成一个对象集合,首先创建一个对象集合类,来get和set集合对象,这种是通过页面的form表单来获取相应属性

public class VO {private List<User> userList;public List<User> getUserList() {return userList;}public void setUserList(List<User> userList) {this.userList = userList;}@Overridepublic String toString() {return "VO{" +"userList=" + userList +'}';}
}

通过表单上来获取对象的属性,从而写入到对象集合中去

首先得访问对应的页面将数据填入到表单中提交事务后

<form action="${pageContext.request.contextPath}/user/quick14" method="post">
<%--表明第一个User对象的username age addr--%><input type="text" name="userList[0].username"><br/><input type="text" name="userList[0].age"><br/><input type="text" name="userList[0].addr"><br/><input type="text" name="userList[1].username"><br/><input type="text" name="userList[1].age"><br/><input type="text" name="userList[1].addr"><br/><input type="submit" value="提交">
</form>

在控制台打印对象集合

@RequestMapping("/quick14")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public void save14(VO vo) throws IOException {System.out.println(vo);
}
2.Ajax提交,无需使用POJO封装

当使用Ajax提交时,可以指定contentType为json形式,那么在方法参数位置使用@RequestBody可以直接接收集合数据无需使用POJO进行包装

在获取资源jquery-3.3.1.js的时候需要静态读取----在Spring-mvc.xml里添加

开发静态资源的访问

<mvc:resources mapping="/js/" location=“/js/”/>**

才能使用静态资源

<head><title>Title</title><script src="${pageContext.request.contextPath}/js/jquery-3.3.1.js"></script><script>var userList = new Array();userList.push({username:"zhangsan",age:18,addr:"beijing"});userList.push({username:"zhanger",age:17,addr:"sandong"});$.ajax({type:"POST",url:"${pageContext.request.contextPath}/user/quick15",data:JSON.stringify(userList),contentType:"application/json;charset=utf-8"})</script>
</head>

在使用ajax发送请求后,将对象数据封装在userList里一起传递到后端,这样就可以直接接收集合数据

@RequestBody将请求体的数据直接封装到参数位置的参数里

结果会在控制台打印对象集合数据

@RequestMapping("/quick15")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public void save15(@RequestBody List<User> userList) throws IOException {System.out.println(userList);
}

静态资源的访问

<!--开放静态资源的访问-->
mapping:对应访问服务端的地址 找谁
location:具体资源所在的目录 那个目录下的资源
<mvc:resources mapping="/js/**" location="/js/"/><!--如果spring-mvc找不到对应的静态资源则交由spring容器来寻找    -->
<mvc:default-servlet-handler/>

请求数据乱码问题

可以设置一个过滤器来进行编码的过滤

<!--全局过滤的filter-->
<!--防止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>

参数绑定注解@RequestParam

当请求的参数名称与Controllerd的业务方法参数不一致时,就需要通过@RequestParam注解显示的绑定

user/quick16?name=zhangsan 绑定username 一样可以在控制台打印数据

@RequestMapping("/quick16")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public void save16(@RequestParam("name") String username) throws IOException {System.out.println(username);
}
  • @RequestParam的required属性默认为ture:当请求提交的时候必须携带对应的参数否则报错required = false时可以不用携带对应的参数,不会报错
  • @RequestParam的defaultValue属性 设置defaultValue = manian 的时候即使发送请求的时候没有携带参数信息,但会defaultValue的默认值,即username = manian
3.获得Restful风格的参数

Restful是一种软件架构风格,设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更加简洁,更有层次,更易于实现缓存机制等。

Restful风格的请求是使用“url+请求方式”表示一次请求目的的,HTTP协议里面四个表示操作方式的动词如下:

  • GET:用于获取资源
  • POST:用于新建资源
  • PUTl:用于更新资源
  • DELETE:用于删除资源

列如:

  • /user/1 GET: 得到id = 1的user
  • /user/1 DELETE: 删除id = 1的user
  • /user/1 PUTl: 更新id = 1的user
  • /user/1 POST: 新增id = 1的user
//访问地址:localhost:8080/user/quick17/zhangsan

上述url地址/user/1中的1就是要获得的请求参数, 在SpringMVC中可以使用占位符进行参数绑定。地址/user/1可以写成 user/ id)。占位符(id}对应的就是1的值。在业务方法中我们可以使用**@PathVariable注解进行占位符的匹配获取工作**。

//访问地址:localhost:8080/user/quick17/zhangsan
@RequestMapping("/quick17/{username}")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public void save17(@PathVariable("username") String username) throws IOException {System.out.println(username);//打印zhangsan
}
自定义类型转换器
  • SpringMVC 默认己经提供了一些常用的类型转换器,例如客户端提交的字符串转换成in型进行爹数设置
  • 但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自 定义转换器。

自定义类型转换器的开发步骤:

1.定义转换器类实现Converter接口

将时间yyyy-MM-dd类型转换成format类型,自己实现接口

public class DateConverter implements Converter<String,Date> {public Date convert(String dataStr) {SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");Date date = null;try {date = format.parse(dataStr);} catch (ParseException e) {e.printStackTrace();}return date;}
}

2.在配置文件中声明转换器

这个配置相当于一个bean对象,声明一个bean丢到容器中,进行类的地址读入

<!--声明日期转换器  -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"><property name="converters"><list><bean class="com.itheima.converter.DateConverter"></bean></list></property>
</bean>

3.在中引用转换器

<!--配置mvc注解驱动代替上面的处理器映射器-->
<!--配置指定的转换器    -->
<mvc:annotation-driven conversion-service="conversionService"/>
获得Servlet相关API

SpringMVC支持使用原始ServletAPI对象作为控制器方法参数的注入,常用对象如下:

  • session
  • HttpServletResponse
  • HttpServletRequest
//获得servlet对应的API 使用参数注入的方法即可
@RequestMapping("/quick19")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public void save19(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {System.out.println(request);System.out.println(response);System.out.println(session);
}
获得请求头
@RequestHeader

使用@@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)

@RequestHeader的注解属性如下:

  • value:请求头的名称
  • required:是否必须携带此请求头
@RequestMapping("/quick20")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public void save20(@RequestHeader(value = "User-Agent",required = false) String user_agent) throws IOException {System.out.println(user_agent);
}
@CookieValue

使用**@CookieValue**可以指定Cookie的值

@CookieValue注解属性如下:

  • value:请求头的名称
  • required:是否必须携带此cookie
@RequestMapping("/quick21")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public void save21(@CookieValue(value = "JSESSIONID",required = false) String jsessionID) throws IOException {System.out.println(jsessionID);
}
文件上传
1.文件上传客户端三要素
  • 表单项type = “file”
  • 表单的提交方式是post
  • 表单的enctype属性是多部分表单形式,及enctype=“multipart/form-data”
2.文件上传原理

JSP页面

<form action="${pageContext.request.contextPath}/user/quick22"method="post" enctype="multipart/form-data">名称<input type="text" name="username"><br/>文件<input type="file" name="upload"><br/><input type="submit" value="提交">
</form>
  • 当form表单修改为多部分表单时,request.getParameter()将失效。
  • enctype="application/x-www-from-urlencoded’'时,form表单的正文内容格式是:key=value&key=value&key=value
  • 当form表单的enctype取值为multipart/form-data时,请求正文内容就变成多部分部分:
2.1单文件上传步骤

1.导入fileupload和io坐标

<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.3</version>
</dependency><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version>
</dependency>

2.配置文件解析器

<!--配置文件上传的解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!--上传文件总大小--><property name="maxInMemorySize" value="5000000"></property><!--上传文件编码--><property name="defaultEncoding" value="UYF-8"></property>
</bean>

3.编写文件上传代码

//MultipartFile 会上传的文件封装成一个对象 里面的参数
//(MultipartFile upload)必须和页面代码上类型为file的名字一致 文件<input type="file" name="upload">
@RequestMapping("/quick22")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public void save22(String username, MultipartFile upload) throws IOException {System.out.println(username);System.out.println(upload);
}
//将文件另存为本机新建文件下
upload.transferTo(new File("D:\\upload\\"+originalFilename));
2.3多文件上传

当页面file类型的name是不同的时候 在后端代码接收的时候,有多少个文件,就写多少个MultipartFile且名字要对应

<form action="${pageContext.request.contextPath}/user/quick22"method="post" enctype="multipart/form-data">名称<input type="text" name="username"><br/>文件1<input type="file" name="upload"><br/>文件2<input type="file" name="upload1"><br/>文件3<input type="file" name="upload2"><br/><input type="submit" value="提交">
</form>
public void save22(String username, MultipartFile upload,MultipartFile upload1,MultipartFile upload2) throws IOException {
System.out.println(username);
String originalFilename = upload.getOriginalFilename();
upload.transferTo(new File("D:\\upload\\"+originalFilename));
String originalFilename1 = upload1.getOriginalFilename();
upload1.transferTo(new File("D:\\upload\\"+originalFilename1));
String originalFilename2 = upload2.getOriginalFilename();
upload2.transferTo(new File("D:\\upload\\"+originalFilename2));
}

当页面file类型的name相同的时候,传递到后台的文件是一个数组,需要用数组来接收 用循环来读出

<form action="${pageContext.request.contextPath}/user/quick22"method="post" enctype="multipart/form-data">名称<input type="text" name="username"><br/>文件1<input type="file" name="upload"><br/>文件2<input type="file" name="upload"><br/>文件3<input type="file" name="upload"><br/><input type="submit" value="提交">
</form>
//MultipartFile 会上传的文件封装成一个对象
@RequestMapping("/quick22")
@ResponseBody   //告知SpringMVC框架该方法不进行视图跳转 直接进行数据响应
public void save22(String username, MultipartFile[] upload) throws IOException {System.out.println(username);for (int i = 0;i<upload.length;i++){//获得文件名称String originalFilename = upload[i].getOriginalFilename();//将文件另存为本机新建文件下upload[i].transferTo(new File("D:\\upload\\"+originalFilename));}
}

Spring Jdbc Template基本使用

1.1 JdbcTemplate概述

它是spring框架中提供的一个对象,是对原始繁琐的JDBC API对象的简单封装spring框架为我们提供了很多的操作模板类。

例如:操作关系型数据的JdbcTemplate和Hibernate Template,操作nosal数据库的Redis Template,操作消息队列的JmsTemplate等等

1.2 JdbcTemplate开发步骤

因为是数据库的操作需要在pom文件里导入mysql和c3p0的驱动jar包才能调用对应的类

  • 导入spring-jdbc和spring-tx的坐标
  • 创建数据库表和实体
  • 创建JdbcTemplate对象
    • 一种是读取properties.xml的方式获取
    • 一种是在spring容器里读取properties.xml填充对象
  • 执行数据库操作
    • 更新操作:JdbcTemplate.update(sql,params)---->(params是sql语句中有几个占位符就填充几个参数)
    • 查询操作:JdbcTemplate.query(sql,Mapper.params)---->查询多个对象(JavaBean) 返回的是一个数组 JdbcTemplate.queryForObject(sql,Mapper.params)------>查询的不是一个对象或者JavaBean 只是一个简单类型 返回简单类型
1.3 Spring产生JdbcTemplate对象

我们可以将JdbcTemplate的创建权交给Spring,将数据源DateSource的创建权也交给Spring,在Spring容器内部将数据源DataSource注入到JdbcTemplate模板对象中

基于spring容器来抽取properties

<!--加载jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties"/><!--创建数据源对象将其注入到spring容器里    -->
<!--数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${jdbc.driver}"/><property name="jdbcUrl" value="${jdbc.url}"/><property name="user" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/>
</bean><!--jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/>
</bean>

注解形式注入的JdbcTemplate对象

@Autowired//从Spring容器里拿出JdbcTemplate自动注入
private JdbcTemplate jdbcTemplate;

xml文件方式读取的注入

ApplicationContext app  = new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate jdbcTemplate = app.getBean(JdbcTemplate.class);

Spring练习环境搭建

1.1 Spring环境搭建步骤

  • 创建工程 (Project&Module)
  • 导入静态页面(见资料sp页面)
  • 导入需要坐标(见资料中的pom.xm)
  • 创建包结构 (controller. service、dao. domain, utils)
  • 导入数据库脚本(见资料testsal)
  • 创建POJ0类(见资料Userjava和Role java) 创建配置文件 (applicationContext.xml spring-mvcxmlu idbapropertiesulog4j.properties)

实现页面的角色管理 即在页面上显示数据库表sys_role全部信息

首先创建web层的控制器Controller来实现页面上个按钮的功能

(需要将其注入到spring容器里获得bean对象—这里使用的是注解形式:@Controller)

一个功能对应一个相应的方法返回对应的数据

@Controller//web层 注解方式产生bean存入spring容器 需要包扫描
public class RoleController

但是必须进行包扫描才能让spring来将其存入spring容器里

<!--4.组件扫描 扫描controller-->
<context:component-scan base-package="com.itheima.controller"/>

ModelAndView的模型对象(—>显示roleService返回的对象数组)和跳转到新的相应界面

由此分析需要调用roleService里返回对应数据库表数据的操作

而roleService需要使用roleDao的findAll方法来查询表中所有的角色信息 因此需要将roleService和roleDao都注入到spring容器里

这里使用的是配置文件的形式注入,这里因为roleService调用了roleDao的方法最后实现是roleService来实现,所以需要将roleDao注入roleService里

<!--配置RoleService 在 配置roleService里 组装roleDao -->
<bean id="roleService" class="com.itheima.service.impl.RoleServiceImpl"><!--引用roleDao 属性 调用roleDao内的对应方法     --><property name="roleDao" ref="roleDao"/>
</bean><!--配置RoleDao  将roleDao注入到spring容器  -->
<bean id="roleDao" class="com.itheima.dao.impl.RoleDaoImpl"><!--标签注入引用jdbcTemplate模板属性 操作数据库 --><property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>

具体实现:

roleDao使用jdbcTemplate模板前需要创建jdbcTemplate模板类 setJdbcTemplate里的参数是spring容器自动填充(因为在spring的配置文件里配置了setJdbcTemplate对象)

//创建jdbc模板类
private JdbcTemplate jdbcTemplate;public void setJdbcTemplate(JdbcTemplate jdbcTemplate){this.jdbcTemplate = jdbcTemplate;
}

roleDao使用jdbcTemplate模板操作数据库返回表对象信息

/*** 返回查询sys_role表的所有成员信息* @return roleList*/
public List<Role> findAll() {List<Role> roleList = jdbcTemplate.query("select * from sys_role", new BeanPropertyRowMapper<Role>(Role.class));return roleList ;
}

roleService调用roleDao的findAll方法 由此返回数据给web端

public List<Role> list() {List<Role> roleList = roleDao.findAll();return roleList;
}

所以ModelAndView接收并设置模型对象 最后携带模型跳转到对应界面将数据显示

@RequestMapping("/list")
private ModelAndView list(){ModelAndView modelAndView = new ModelAndView();List<Role> roleList = roleService.list();//设置模型对象modelAndView.addObject("roleList",roleList);//设置视图  向哪跳转modelAndView.setViewName("role-list");return modelAndView;
}

新建角色方法也差不多

在新建user关系中 一个user可能对应多个role 又因为是使用id值进行匹配的 id值是有数据库自动产生的 所以这里的id值需要去获取之后才能对角色表和人物关系表进行操作

获得数据库自动产生的id是使用jdbcTemplate模板的方法 但只要知道就行(mybatis代替)

因为一个user可能对应多个role 即一个userID可以对应多个roleID 所以在给关系表添加数据的时候,需要循环遍历对应user的roleID的数组 将其一 一对应的添加到数据库里

public void saveUserRoleRel(Long userId, Long[] roleIds) {//一个user对应多个role 一个user有多个roleIdfor (Long roleId : roleIds) {jdbcTemplate.update("insert into sys_user_role values (?,?)",userId,roleId);}
}

删除角色操作

根据页面传输携带的userId值来删除对应user的角色再删除user

  • 因为有关系型数据表 首先要删除和外表有连接的数据 sys_user_role内user和role的多对多关系
  • 再删除user表的user数据

创建用户登录界面

判断用户有没有登录从而访问界面 首先利用拦截器线判断session域中有没有user对象 如果有则可以访问页面 如果没有这跳转到登录页面

首先从页面获得username和password传输到登录按钮提交的对应方法 然后userService调用userDao层查询username和password是否对应如果对应则封装成一个对象 然后保存到session域中 供拦截器检测 然后重定向到登录页面 值得注意的是如果username和password不匹配,jdbcTemplate操作数据库会抛出异常 需要接收

public User findByUsernameAndPassword(String username, String password) throws EmptyResultDataAccessException {User user = jdbcTemplate.queryForObject("select * from sys_user where username = ? and password = ?",new BeanPropertyRowMapper<User>(User.class),username,password);return user;
}
@RequestMapping("/login")
public String login(String username, String password, HttpSession session){User user = userService.login(username,password);if (user!=null){//登陆成功 将user存储到session域中session.setAttribute("user",user);return "redirect:/index.jsp";}return "redirect:/login.jsp";
}

SpringMVC拦截器

拦截器(interceptor)的作用

SpringMVC的拦截器类似于Servlet开发中的过滤器Filter,用于处理器进行预处理后处理

将拦截器按一定的顺序连结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法和字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现

拦截器和过滤器的区别

拦截器说明

开发步骤:

  • 创建拦截器实现HandlerLnterceptor接口

  • 配置拦截器(在spring-mvc.xml里 配置拦截器 拦截器的执行的先后顺序是由在xml配置的顺序决定 下面的拦截器线执行)

  • <!--配置拦截器-->
    <mvc:interceptors><mvc:interceptor><!--对哪些资源执行拦截操作--><mvc:mapping path="/**"/><bean class="com.itheima.interceptor.MyInterceptor2"/></mvc:interceptor><mvc:interceptor><!--对哪些资源执行拦截操作--><mvc:mapping path="/**"/><bean class="com.itheima.interceptor.MyInterceptor1"/></mvc:interceptor>
    </mvc:interceptors>
    
  • 测试拦截效果

SpringMVC异常处理机制

系統中异常包括两类:预期异常和运行时异第RuntimeException, 前者通过捕获异常从而获取异常信息,后 者主要通过规范代码开发!测试等手段减少运行时异常的发生

系統的Dao Service Controller出现都通过throws Exception向上拋出,最后由SpringMVC前端控制器交 由异常处理器进行异常处理,如下图:

异常处理的两种方式

  • 使用SpringMVC提供的简单异常处理器SimpleMappingExceptionResolver

  • 实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器

<!--配置简单的异常映射处理器--><bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--        <property name="defaultErrorView" value="error"/>-->  默认异常跳转页面<property name="exceptionMappings">  可以自己规定什么异常 到什么页面处理<map><entry key="ClassCastException" value="error1"/><entry key="MyException" value="error2"/></map></property></bean>

自定义异常处理步骤

  • 创建异常处理器类实现HandlerExceptionResolver
  • 配置异常处理器
  • 编写异常页面
  • 测试异常跳转

方法中传入的 Exception e 代表了当前的异常是什么形式的

public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {ModelAndView modelAndView = new ModelAndView();if (e instanceof MyException){modelAndView.addObject("info","自定义的异常");}else if (e instanceof ClassCastException){modelAndView.addObject("info","类转换异常");}modelAndView.setViewName("error");return modelAndView;
}

SpringAOP(Aspect Oriented Programming:面向切面编程)

Spring 框架的 AOP_w3cschool

什么是AOP

AOP 为 Aspect Oriented Programming 的縮写,意思为面向切面编程, 是通过预编译方式和运行期动态代理实现程序功能的統一维护的一种技术

AOP 是 OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一 -个重界内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的名个部分进行抽离,从而使得业务逻辑各部分之间的耦合度降低,提育程序 的可重用性,同时提高了开发的效率

代理模式:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的媒介

  • 静态代理代理类在编译时期就生成
  • 动态代理代理类则是在Java运行时动态生成 动态代理有分为JDK代理和CGLib代理

AOP的作用及其优势

  • 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
  • 优势:减少重复代码,提高开发效率,并且便于维护

AOP的底层实现

实际上,AOP的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技木动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

AOP的动态代理技术

常用的动态代理技术

  • JDK代理:基于接口的动态代理技术
  • cglib代理:基于父类的动态代理技术

JDK代理

public static void main(String[] args) {//目标对象 里面包含了一个save方法final Target target = new Target();//增强对象 想在save方法前或后执行的操作Advice advice = new Advice();//返回值 就是动态生成的代理对象TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(//目标对象的类加载器target.getClass().getClassLoader(),//目标对象相同的接口字节码对象数组target.getClass().getInterfaces(),new InvocationHandler() {//调用代理的任何方法实质执行的都是invoke方法@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//前置增强advice.before();//执行目标方法Object invoke = method.invoke(target, args);//后置增强advice.afterReturning();return invoke;}});proxy.save();
}

执行结果

Cglib代理

public static void main(String[] args) {//目标对象final Target target = new Target();//增强对象Advice advice = new Advice();//返回动态代理的对象 基于cglib//1.设置增强器Enhancer enhancer = new Enhancer();//2.设置父类enhancer.setSuperclass(Target.class);//3.设置回调enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//执行前置advice.before();Object invoke = method.invoke(target, args);//执行目标//执行后置advice.afterReturning();return invoke;}});//4.创建代理对象Target proxy = (Target) enhancer.create();proxy.save();
}

AOP相关概念

Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,井通过配置的方式完成指定目标的方法增强。

在正式讲解AOP 的操作之的,我们必须理解AOP 的相关术语,常用的术语如下:

  • Target (目标对象):代理的目标对象
  • Proxy(代理): 个类被AOP织入增强后,就产生一个结果代理类
  • Joinpoint(连接点(方法)):所谓连接点是指那些被拦截到的点(方法)。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点 ------可以被增强的方法称为连接点
  • Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义------真正被增强的方法称为切入点
  • Advice(通知/增强):所谓通知是指拦截到Joinpoint之后要做的事情就是通知----对方法进行操作(添加额外功能)的方法叫做通知/增强
  • Aspect(切面):是切入点和通知的结合
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而Aspect采用编译时期织入和类装载期织入--------切点和通知结合的过程

AOP开发明确的事项

1.编写的内容

  • 编写核心业务代码(目标类的目标方法)
  • 编写切面类,切面类中有通知(增强功能方法)
  • 在配置文件中,配置织入关系,即将哪些通知和哪些切入点进行结合

2.AOP技术实现的内容

Spring框架监控切入点方法的执行。一旦监控到切入点方法被执行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整代码的逻辑运行。

3.AOP底层使用哪种代理方式

在spring在,框架会根据模板类是否实现了接口来决定采用哪种动态代理的方式

  • JDK代理:基于接口的动态代理技术
  • cglib代理:基于父类的动态代理技术

基于XML的AOP开发

快速入门

  • 导入 AOP 相关坐标
  • 创建目标接口和目标类 (内部有切点)
  • 创建切面类(内部有增强方法)
  • 将目标类和切面类的对象创建权交给 spring
  • 在applicationContextxml中配置织入关系
  • 测试代码
<!--配置目标对象-->
<bean id="target" class="com.itheima.aop.Target"></bean><!--配置切面对象-->
<bean id="myAspect" class="com.itheima.aop.MyAspect"></bean><!--配置织入:告诉spring框架 哪些方法(切点)需要进行哪些增强(前置 后置。。)-->
<aop:config><!--声明切面--><aop:aspect ref="myAspect"><!--抽取切片表达式--><aop:pointcut id="myPointcut" expression="execution(public void *.itheima.aop.*.*(..))"/><!--切面:切点+通知--><!--            <aop:before method="before" pointcut="execution(public void com.itheima.aop.Target.save())"/>--><!--            <aop:before method="before" pointcut="execution(public void *.itheima.aop.*.*(..))"/>--><!--            <aop:after-returning method="afterReturning" pointcut="execution(public void *.itheima.aop.*.*(..))"/>--><!--            <aop:around method="around" pointcut="execution(public void *.itheima.aop.*.*(..))"/>--><!--            <aop:after-throwing method="afterThrowing" pointcut="execution(public void *.itheima.aop.*.*(..))"/>--><!--            <aop:after method="after" pointcut="execution(public void *.itheima.aop.*.*(..))"/>--><aop:around method="around" pointcut-ref="myPointcut"/><aop:after method="after" pointcut-ref="myPointcut"/></aop:aspect>
</aop:config>

XML配置的AOP详解

1.切点表达式的写法

execution([修饰符])返回值类型 包名.类名.方法名(参数)

  • 访问修饰符可以省略
  • 返回值类型,包名,类名,方法名可以使用星号*代表任意
  • 包名和类名之间一个点.代表了当前包下的类,两个点…表示当前包及其子包下的类
  • 参数列表可以使用两个点…表示任意两个数,任意类型的参数列表

列如

通知的类型

切点表达式的抽取

基于注解的AOP开发

快速入门

  • 创建目标接口和目标类 (内部有切点)
  • 创建切面类(内部有增强方法) 使用@Aspect标注前面类
@Component("MyAspect")
@Aspect //标注当前的MyAspect是一个切面类
public class MyAspect
  • 将目标类和切面类的对象创建权交给 spring
  • 在切面类中使用注解注入关系
  • 在配置文件在开启组件扫描和AOP的自动代理(让AOP的各种通知注解让spring识别)
<!--开启组件扫描-->
<context:component-scan base-package="com.itheima.anno"/><!--AOP的自动代理    -->
<aop:aspectj-autoproxy/>
  • 测试代码

注解通知的类型

切点表达式的抽取

@After("pointcut()")
public void after(){System.out.println("最终增强.....");
}//定义切点表达式
@Pointcut("execution(public void *.itheima.anno.*.*(..))")
public void pointcut(){
}

事务控制

编程式事务控制的相关对象





基于XML的声明式事务控制

2.1什么是声明式事务控制

Spring 的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明 ,用在 Spring配置文件中声明式的处理事务来代著代码式的处理事务

声明式事务处理的作用

  • 事务管理不侵入开发的组件。具体来说,业务逻指对款就不会意识到正在事务篇理之中,事实上也应该如 此,因为事务管理是属于系统层面的服务,而不是业务逻指的-部分,如果想要放安事务管理第划的话 也只需要 件中更新配置即可
  • 不需婴事务管理的时候,只要任设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编泽这样维护起来极其方便

注意:Spring声明式事务控制底层就是AOP

声明式事务控制的实现

声明式事务控制明确事项:

  • 谁是切点?-------业务方法
  • 谁是通知?-------事务控制
  • 配置切面? 切点+通知的控制
声明式事务控制的配置要点
  • 平台事务管理配置
  • 事务通知的配置
  • 事务aop织入的配置
<!--目标对象  内部的方法就是切点-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"/>
</bean><!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/>
</bean><!--通知  事务的增强-处理事务  transaction-manager 平台事务管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager"><!--设置事务的属性信息 --><tx:attributes><tx:method name="*"/></tx:attributes>
</tx:advice<!--配置事务的AOP织入    --><aop:config><!--spring专门为事务的增强提供了配置advisor 不同与aop里的切面aspect--><!--配置pointcut也可以实现代替--><aop:pointcut id="txPointcut" expression="execution(* com.itheima.service.impl.*.*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/></aop:config>

其中tx:method代表切点方法的事务参数的配置,;例如:

<!--name(方法名)="*" 不写就是全默认 全部方法 isolation-管理级别  propagation-传播行为 timeout-失效时间  read-only-是否只读 -->
<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false"/>
  • name:切点方法名称
  • isolation:事务隔离的级别
  • propogation:事务的传播行为
  • timeout:超时时间
  • read-only:是否只读

基于注解的声明式事务控制

注解声明式事务控制的配置要点

在Service方法下

  • 事务通知的配置(@Transactional(注解配置))

  • 在AccountServiceImpl方法头添加**@Transactional(isolation = Isolation.DEFAULT)**代表该Service下的切面类都共用这个原则

  • @Transactional(isolation = Isolation.DEFAULT)
    public class AccountServiceImpl implements AccountServic
    
  • 而在Service下的切面类头上使用,利用就近原则切面类使用头上的原则

  • @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
    public void transfer
    
  • 平台事务管理器配置(xml)

必须配置注解形式的平台事务管理器 不然无法执行相应注解形式的事务 事务注解驱动的配置<tx:annotation-driven/>

<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/>
</bean><!--事务的注解驱动 配置平台事务管理器-->
<tx:annotation-driven transaction-manager="transactionManager"/>
注解配置声明式事务控制解析
  • 使用@Transactional在需要进行事务控制的类或者是方法上修饰,注解可用的属性同xml配置方式,例如:隔离级别,传播行为等
  • 注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置
  • 使用在方法上,不同的方法可以采用不同的事务参数配置
  • xml配置文件在要开启事务的注解驱动<tx:annotation-driven/>

Mybatis

原始jdbc开发存在的问题如下:

  • ①数据库连接创建、释放烦繁造成系统资源浪费从而影响系统性能
  • ② sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql交化的可能较大,sql 交动需要改变java代码。
  • ③查询操作时,需安手动将结果集中的数据手动封装到实体中。插入操作时,需要手动将实体的数据设置到sq语句的占位符位置

应对上述问题给出的解决方案:

  • 使用数据库连接池初始化连接资源
  • 将sql语句抽取到xm配置文件中
  • 使用反射、内省等底层技术,自动将实体与表进行属性气字段的自动映射

什么是MyBatis

  • mybatis是一个优秀的基于java的持久层框架,它内部封装了 jdbc,使开发者只需要关汪sql语句本身,而不需要花费精力 去处理加载驱动、 创建连接、创建statement等繁杂的过程。
  • mybatis通过xml或注解的方式将要执行的各种 statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句。
  • 最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM思想解決了实体和数据库映射的问题,对dbc进行了 封装,屏蔽了jdbc api底层访问细节,使我们不用与dbc api 打交道,就可以完成对数据库的持久化操作。

基本的MyBatis开发步骤

  • 添加MyBatis坐标
  • 创建表结构
  • 编写对应的实体类
  • 编写映射文件Mapper.xml
  • 编写核心文件SqlMapConfig.xml
  • 测试

MyBatis的映射文件概述

增删改查操作的Mybais映射配置

//模拟user对象
User user = new User();
user.setUsername("tom");
user.setPassword("abc");//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session过工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作
sqlSession.insert("userMapper.save",user);
//提交事务
sqlSession.commit();
//释放资源
sqlSession.close();

参数可能会有多个类型

  • resultType 查询的结果的参数类型 是查询完封装的结果类型
  • parameterType 增加 删除 修改 需要获得一个什么类型的参数
<!--查询  resultType="com.itheima.domain.User 查询完毕后封装成一个resultType类型返回  -->
<select id="findAll" resultType="com.itheima.domain.User">select * from user
</select><!--插入 插入操作是必须获得的参数是一个parameterType对象才能向数据库进行操作 -->
<insert id="save" parameterType="com.itheima.domain.User">insert into user values(#{id},#{username},#{password})</insert>
<!--更新    -->
<update id="update" parameterType="com.itheima.domain.User">update user set username=#{username},password = #{password} where id = #{id}
</update><!--删除  parameterType只需要接收一个int类型的参数 删除  -->
<update id="delete" parameterType="java.lang.Integer">delete from user where id = #{id}
</update>

MyBatis核心配置文件概述

mybatis – MyBatis 3 | 配置

environments (数据源环境配置)标签

environments 支持多配置数据库环境

其中,事务管理器 (transactionManager)

  • 类型有两种 JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
  • MANAGED: 这个能置几乎没做什么, 它以我不是安款圖茶一个连孩,品是记容器大管理車参8要个生品居期ttA)EE 应用服务器的上下文)。默认情况下它会关闭连接,然而一些容器井不希望这样,因此需要将closeConnection属性设置 为false 来阻止它默认的关闭行为。

其中,数据源 (dataSource)类型有三种:

  • UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
  • POOLED:这种数据源的实现利用 “池”的概念将JDBC连接对象组织起来,
  • JNDI:这个数据源的实现是为了能在如EJB 或应用服务器这类容器中使用,容器可以集中或在外部配省数据源,然后放置 一个JNDI上下文引用.

mappers 标签

加载映射文件 加载方式有以下几种

mappers 下的mapper标签 可以配置多个mapper引用多个文件 用mapper映射

  • 使用相对于类路径的资源引用,例如: 引用resource资源引用对应文件的相对路径

了解后三者

  • 使用完全限定资源定位符(URL),例如 在磁盘哪个位置寻找文件
  • 使用映射器接口实现类的完全限定类名,例如: 在注解方式下加载类
  • 将包内的映射器接口全部注册为映射器,例如 使用package相当于扫包 将该包内的所有映射器接口均注册为映射器
properties(加载外部properties文件)标签

实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,在mybatis的配置文件李使用properties标签可以加载额外配置的properties文件

<!--引用外部资源文件    -->
<properties resource="jdbc.properties"></properties>
<!--数据源的环境-->
<environments default="development"><environment id="development"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment>
</environments>
typeAliase(设置类型别名)标签

类型别名是为Java类型设置一个短的名字。 自定义类的别名

<!--根据dtd mybatis规范  <typeAliases>标签必须放在  <properties>后 在 <environments>前-->
<!--自定义别名 将实体类起别名为user 一般情况下在使用mybatis操作数据库的sql语句下使用的类型是parameterType="com.itheima.domain.User" 绝对路径 起别名之后可以直接使用别名-->
<typeAliases><typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>
</typeAliases>

Mybatis的相应API

SqlSession工厂构建器SqlSessionFactoryBuilder

常用API:SqlSessionFactory build(InputSream inputStream)

通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象

//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session过工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

其中Resources工具类,这个类在org.apache.ibatis.io包中。Resource类帮助你从类路径下,文件系统或一个web URL在加载资源文件

而有了工厂对象 就可以造对象

sqlSessionFactory工厂对象可以创建会话对象SqlSession实例

有常用的以下两种

  • openSession():会默认开启一个事务,但是事务不会自动提交,也就意味着需要手动提交该事务,更新操作数据才会持久化保存到数据库中
  • openSession(boolean autoCommit):参数为是否自动提交,如果设置为true,那么不需要手动提交事务

MyBatis的DAO层实现

代理开发方式

1.代理开发方式介绍 采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。

Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis框架根据接口定义创建接 口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法,

Mapper 投口开发需妥道后以下规范:

1、 Mapper.xml文件中的namespace与mapper接口的全限定名相同

2、Mapper接口方法名和Mapper.xml中定义的每个statement的sid相同

3、Mapper孩口方法的箱入參数类型和mapperxml中定义的每个sql的parameterType的类型相同

  1. Mapper接口方法的输出参数类型和mapperxml中定义的每个sqI的resultType的类型相同

<mapper namespace="com.itheima.dao.UserMapper"><!--查询操作--><select id="findAll" resultType="user">select * from user</select><!--根据id进行查询--><select id="findById" parameterType="int" resultType="user">select * from user where id=#{id}</select></mapper>
public interface UserMapper {public List<User> findAll() throws IOException;User findById(int id);
}
public class ServiceDemo {public static void main(String[] args) throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> all = mapper.findAll();User user = mapper.findById(1);System.out.println(user);System.out.println(all);}
}

代理的方式不需要自己去实现接口内的方法 根据和UserMapper.xml里的sql语句返回值类型 和 输入类型和DAO层的接口类型一样,则mybatis会自动去填充 不需要直接实现接口内的方法

MyBatis映射文件深入

动态Sql语句概述

MyBatis的映射文件在,前面我们的SQL都是比较简单的,有时候业务逻辑复杂时,我们的SQL是动态变化的,此时前面学习在我们的SQL就不能满足需求了。

sql片段抽取

<!--sql语句抽取-->
<sql id="selectUser">select * from user</sql>
  • if
<select id="findByCondition" parameterType="user" resultType="user"><include refid="selectUser"></include><where> <-- where标签相当于1=1 则where标签会根据没有条件动态的查询 --!><if test="id!=0">and id=#{id}</if><if test="username!=null">and username=#{username}</if><if test="password!=null">and password=#{password}</if></where>
</select>
  • choose(when, otherwise)

  • trim(where, set)

  • foreach

  • <select id="findByIds" parameterType="list" resultType="user"><include refid="selectUser"></include>   ids in(?,?,?,?)<where><foreach collection="list" open="id in(" close=")" item="id" separator=",">#{id}</foreach></where>
    </select>
    
typeHandlers标签 (配置自定义类型处理器)

无论是MyBatis在预处理句(PreparedStatement)在设置一个参数时,还是从结果集中取出的一个值时,都会用类型处理器将获取的值以合适的方式转换成Java类型。

也可以重写类型处理器或者创建自己的类型处理器来处理不支持的或非标准的类型。

具体实现 org.apache.ibatis.type.TypeHandler接口,或继承一个很便利的类 org.apache.ibatis.type.TypeHandler,然后可以选择地将它映射到一个JDBC类型。例如:一个Java中的Date数据类型,在存入数据库的时候存成1970年至今的毫秒数,取出来时转换成Java的Date。即Java的Date与数据库的varchar毫秒值之间的转换

开发步骤:

  • 定义转换类继承类BaseTypeHandler
  • 覆盖四个未实现的方法,其中setNonNullParameter为Java程序设置数据库的回调方法,getNullableResult为查询时mysql的字符串类型转换成Java的Type类型的方法
  • 在MyBatis核心配置文件中进行注册
  • 测试

关于MyBatisX爆红 是因为给类定义别名的时候 在其他模块定义了相同的别名

原来我定一个两个user类,在不同的modul下 需要修改 别名

plugins标签 (配置MyBatis插件)(分页插件)

MyBatis可以使用第三方插件对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据

开发步骤:

  • 导入通用PageHelper的坐标

  • <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>3.7.5</version>
    </dependency><dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>0.9.1</version>
    </dependency>
    
  • 在mybatis核心配置文件中配置PageHelper插件

    <!--配置分页助手插件-->
    <plugins><plugin interceptor="com.github.pagehelper.PageHelper"><!--指定方言            --><property name="dialect" value="mysql"></property></plugin>
    </plugins>
    
  • 测试

//设置分页相关参数  当前页 每页显示的数目
PageHelper.startPage(1, 3);
//保存对象
List<User> userList = mapper.findAll();
for (User user : userList) {System.out.println(user);
}

MyBatis的多表操作

表的关系:
一对一 (一个订单对应一个user)
    <resultMap id="orderMap" type="order"><!--手动指定字段与实体属性的映射关系column:数据表的字段名称property:实体的属性名称配置两个表的数据在一个表上能查询出来两个表中有相同名称的字段,需要自己重新定义字段名称 对应实体类上的属性名称resultMap 键值对类型 为下文sql语句配置属性 返回的类型为指定查询的实体类属性type="order" 是该类起的别名--><id column="oid" property="id"></id><result column="orderTime" property="orderTime"></result><result column="total" property="total"></result>
<!--        <result column="uid" property="user.id"></result>-->
<!--        <result column="username" property="user.username"></result>-->
<!--        <result column="password" property="user.password"></result>-->
<!--        <result column="birthday" property="user.birthday"></result>--><!--property:当前实体属性名称(private User user)javaType:当前(order)中的属性类型(User) 但是在配置文件里起别名为useres--><association property="user" javaType="useres"><id column="uid" property="id"></id><result column="username" property="username"></result><result column="password" property="password"></result><result column="birthday" property="birthday"></result></association></resultMap><select id="findAll" resultMap="orderMap">select *, o.oid oid from order o,user u where o.uid = u.id</select>
一对多 (一个user可以有多个订单数据 所有将订单数据封装在集合里)
<!--配置一对多 user可以用于多个订单 订单是一个List集合集合有对应的订单数据-->
<resultMap id="UserMap" type="useres"><id column="uid" property="id"></id><result column="username" property="username"></result><result column="password" property="password"></result><result column="birthday" property="birthday"></result><!--配置集合信息property:集合名称ofType:当前集合中的数据类型    --><collection property="orderList" ofType="order"><!--封装集合数据--><id column="oid" property="id"></id><result column="orderTime" property="orderTime"></result><result column="total" property="total"></result></collection>
</resultMap><select id="findAll" resultMap="UserMap">select *, o.id oidfrom user u,order owhere u.id = o.uid
</select>
多对多(一个user可能对应多个角色 而一个角色又可以对应多个user)

多对多查询的需求:查询用户同时查询该用户的所有角色

多对多关系 需要新建一个表

<resultMap id="userRoleMap" type="useres"><!--配置user的信息--> <id column="userId" property="id"></id><result column="username" property="username"></result><result column="password" property="password"></result><result column="birthday" property="birthday"></result><!--配置user内部的roleList信息--><collection property="roleList" ofType="role"><id column="roleId" property="id"></id><result column="roleName" property="roleName"></result><result column="roleDesc" property="roleDesc"></result></collection>
</resultMap><select id="findUserAndRole" resultMap="userRoleMap">select *from user u,role r,userrole urwhere u.id = ur.userIdand ur.roleId = r.id
</select>

MyBatis注解开发

注解开发越来越流行,使用注解形式减少了xml配置 但是值得注意的是 对应简单的操作建议使用注解形式 而对应逻辑比较复杂的操作还是建议使用xml的形式

MyBatis常用注解
  • @Insert:实现新增
  • @Update:实现更新
  • @Select:实现查询
  • @Result:实现结果封装
  • @Results:可以与@Result一起使用,封装多个结果集
  • @One:实现一对一结果集封装
  • @Many:实现一对多结果集封装
MyBatis的注解实现复杂映射开发

实现复杂关系映射之前我们可以在映射文件中通过配置来实现,使用注解开发后,我们可以使用@Results注解,@Result注解,@One注解组合完成复杂关系的配置

使用注解形式开发查询订单的对应信息和订单用户信息

一对一的两种查询方式(注解实现)
//两张表一起查
@Select("select *, o.oid oid from order o,user u where o.uid = u.id")
@Results({@Result(column = "oid",property = "id"),@Result(column = "orderTime",property = "orderTime"),@Result(column = "total",property = "total"),@Result(column = "uid",property = "user.id"),@Result(column = "username",property = "useres.username"),@Result(column = "password",property = "useres.password"),})
List<Order> findAll();
############################################################################################################
// 先查一张表再查另一张表@Select("select *, o.oid oid from order o,user u where o.uid = u.id")@Results({@Result(column = "oid",property = "id"),@Result(column = "orderTime",property = "orderTime"),@Result(column = "total",property = "total"),@Result(property = "user",//要封装的属性名称column = "uid", //跟据哪个字段去查询user表的数据javaType = User.class,//要封装的实体类型//select属性 代表查询哪个接口的方法获得数据//根据查询user接口里查询user id 的方法返回id 给column 从而获得user数据one = @One(select = "查询user表的id的方法")-----@Select("select * from user where id=#{id}")-----User findById(int id))})List<Order> findAll();

注解形式相比与xml方式更加方便 查询结果和作用相同

    <resultMap id="orderMap" type="order"><!--手动指定字段与实体属性的映射关系column:数据表的字段名称property:实体的属性名称配置两个表的数据在一个表上能查询出来两个表中有相同名称的字段,需要自己重新定义字段名称 对应实体类上的属性名称resultMap 键值对类型 为下文sql语句配置属性 返回的类型为指定查询的实体类属性--><id column="oid" property="id"></id><result column="orderTime" property="orderTime"></result><result column="total" property="total"></result>
<!--        <result column="uid" property="user.id"></result>-->
<!--        <result column="username" property="user.username"></result>-->
<!--        <result column="password" property="user.password"></result>-->
<!--        <result column="birthday" property="user.birthday"></result>--><!--property:当前实体属性名称(private User user)javaType:当前(order)中的属性类型(User) 但是在配置文件里起别名为useres--><association property="user" javaType="useres"><id column="uid" property="id"></id><result column="username" property="username"></result><result column="password" property="password"></result><result column="birthday" property="birthday"></result></association></resultMap><select id="findAll" resultMap="orderMap">select *, o.oid oid from order o,user u where o.uid = u.id</select>
一对多的查询(注解实现)
@Select("select * from user")
@Results({//id=true 表明该列是主键@Result(id=true, column = "id",property = "id"),@Result(column = "username",property = "username"),@Result(column = "password",property = "password"),@Result(property = "orderList",//要封装的属性名称column = "id", //跟据哪个字段去查询user表的数据javaType = List.class,//要封装的实体类型//select属性 代表查询哪个接口的方法获得数据//根据查询order接口里查询user id 的方法返回user id 给column 从而获得user对应的order表数据many = @Many(select = "查询order表的user id的方法")-------@Select("select * from order where uid=#{uid}")-------List<Order> findByUid(int uid))})
List<User> findAll();
多对多的查询(注解实现)

存在一个中间表

一个user可能对应多个角色 而一个角色又可以对应多个user

多对多查询的需求:查询用户同时查询该用户的所有角色

@Select("select * from user")
@Results({//id=true 表面该列是主键@Result(id=true, column = "id",property = "id"),@Result(column = "username",property = "username"),@Result(column = "password",property = "password"),@Result(property = "roleList",//要封装的属性名称column = "id", //跟据哪个字段去查询user表的数据javaType = List.class,//要封装的实体类型//select属性 代表查询哪个接口的方法获得数据//根据查询UserAndOrder接口里查询user id 的方法返回与role id对应user id 给column// 从而获得user对应的role表数据many = @Many(select = "查询UserAndRole表的user id的方法")----@Select("select * from UserAndRole ur,role r where ur.roleId=r.id and ur.userId=#{id}")----List<Role> findByUid(int uid);)})
List<User> findUserAndRole();

SSM框架整合

原始整合方式

配置Spring的核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns=""xmlns:xsi=""xmlns:aop=""xmlns:tx=""xmlns:context=""xsi:schemaLocation="
.xsd

.xsd

.xsd

.xsd"><!--组件扫描 扫描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></beans>
配置mybatis的核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN"".dtd">
<configuration><!--加载properties文件--><properties resource="jdbc.properties"></properties><!--定义别名--><typeAliases><typeAlias type="com.itheima.domain.Account" alias="account"></typeAlias><!--扫包的方式将所有的实体都起别名为包下的实体名-->
<!--        <package name="com.itheima.domain"></package>--></typeAliases><!--环境--><environments default="development"><environment id="development"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></dataSource></environment></environments><!--加载映射--><mappers><mapper resource="com/itheima/mapper/AccountMapper.xml"></mapper>
<!--        <package name="com.itheima.mapper"></package>--></mappers></configuration>
配置srping——mvc的核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns=""xmlns:mvc=""xmlns:context=""xmlns:xsi=""xsi:schemaLocation="
.xsd

.xsd

.xsd"><!--组件扫描  主要扫描controller--><context:component-scan base-package="com.itheima.controller"></context:component-scan><!--配置mvc注解驱动--><mvc:annotation-driven></mvc:annotation-driven><!--内部资源视图解析器--><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><!--开发静态资源访问权限--><mvc:default-servlet-handler></mvc:default-servlet-handler></beans>
配置对应的mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"".dtd">
<mapper namespace="com.itheima.mapper.AccountMapper"><insert id="save" parameterType="account">insert into ssm values(#{id},#{name},#{money})</insert><select id="findAll" resultType="account">select * from ssm</select>
</mapper>
!🔴 🐎

在配置mapper映射文件的时候在resources目录下创建包和配置文件的格式为:com/itheima/mapper/xxxxxx.xml 中间不能用点来分割 否则在运行时会找不到资源文件

起别名的方式有两种 但是在用web界面的情况下 最好使用根源目录的配置方式 不要使用包扫描的方式 否则页面会丢失

 <!--定义别名--><typeAliases><typeAlias type="com.itheima.domain.Account" alias="account"></typeAlias><!--扫包的方式将所有的实体都起别名为包下的实体名-->
<!--        <package name="com.itheima.domain"></package>--></typeAliases>

Spring整合Mybatis

将数据源和外部资源文件交给spring来配置

将session工厂的配置交给spring :工厂里需要引入数据源和mybatis的其他配置

由spring创建 声明式事务控制

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns=""xmlns:xsi=""xmlns:aop=""xmlns:tx=""xmlns:context=""xsi:schemaLocation="
.xsd

.xsd

.xsd

.xsd"><!--组件扫描 扫描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><!--加载propeties文件--><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><!--配置数据源信息--><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><!--配置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创建实现类   会为mapper创建实现放入容器里-->   <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.itheima.mapper"></property></bean><!--声明式事务控制--><!--平台事务管理器--><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></beans>
@Service("accountService")
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountMapper accountMapper;@Overridepublic void save(Account account) throws IOException {//由spring来创建工厂对象 简化了代码accountMapper.save(account);}@Overridepublic List<Account> findAll() throws IOException {//由spring来创建工厂对象 简化了代码return accountMapper.findAll();}
}

扫描mapper所在的包 为mapper创建实现类 会为mapper创建实现放入容器里 所以才能将AccountMapper注入到容器里

SSM的整体架构

参考:ssm的架构及整合说明 - twoheads - 博客园 (cnblogs.com)

一般来说 ssm中

ssm框架里需要的配置文件有:

spring容器的配置文件:

applicationContext.xml通常包括的内容有:

  • 组件扫描(主要是扫描整个项目包下的spring注解 但需要排除controller(spring-mvc的web层的控制器)因为这个需要spring_mvc的配置文件自己扫描)

  • 数据源(配置数据库的信息)

  • 配置sessionFactory 配置会话工厂 将工厂的创建注入到spring容器里简化service层每次使用sessionFactory 的代码

  • <!--配置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的包后在spring容器里注入了mapper的对应实体类 可以直接使用mapper对象来调用方法

  • @Autowired
    private AccountMapper accountMapper;@Override
    public void save(Account account) throws IOException {//由spring来创建工厂对象 简化了代码accountMapper.save(account);}
    
  • <!--mapper扫描器 扫描mapper所在的包 为mapper创建实现类-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.itheima.mapper"></property>
    </bean>
    
  • 配置事务控制 在原有的操作上进行增强操作或者识别

整合springmvc配置文件

  • 配置处理器映射器

  • <mvc:annotation-driven></mvc:annotation-driven>
    
  • 组件扫描(扫描controller层 对应的mvc注解)

  • 视图解析器

  • 适配器

  • 开放静态资源访问权限

mybatis配置文件

主要的数据源配置到spring配置文件中 将数据放在spring容器里更方便获取

包括了少量文件配置是为了给domain实体类器别名 和批量包扫描包下对应的实体类

<!--定义别名--><typeAliases><typeAlias type="com.itheima.domain.Account" alias="account"></typeAlias>
<!--        <package name="com.itheima.domain"></package>--></typeAliases>

web.xml文件

  • contextConfigLocation配置springmvc加载的配置文件(配置处理器映射器、适配器等等)springmvc前端控制器
  • spring 监听器
  • 乱码过滤器(请求方式post 或者 get 方式的在web页面返回中文字符)

本文标签: SSM