一、Spring框架
1.1概述
其中最核心的是:IoC控制反转、DI依赖注入、Bean工厂、SpringAOP面向切面编程、事务控制。
Spring是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。
SpringMVC框架用来接受浏览器的请求和给浏览器做出响应
Mybatis框架用来和数据库交互
Spring框架主要用来完成中间的业务,主要的功能包括:IOC / DI
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
1. | 核心容器Spring Core | 核心容器提供Spring框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC)模式,将应用程序的配置和依赖性规范与实际的应用程序代码分开。 |
2. | Spring上下文 Spring Context | Spring上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。 |
3. | Spring AOP | 通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。可以很容易地使 Spring框架管理的任何对象支持AOP。Spring AOP模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。 |
4. | Spring DAO | JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。 |
5. | Spring ORM | Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括JDO、Hibernate和iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。 |
6. | Spring Web | Web上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以Spring 框架支持与 Jakarta Struts的集成。Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。 |
7. | Spring MVC框架 | MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。 |
Spring 框架的功能可以用在任何J2EE服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定J2EE服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同J2EE环境(Web或EJB)、独立应用程序、测试环境之间重用。
Spring以一种非侵入式的方式来管理你的代码,Spring提倡”最少侵入”,这也就意味着你可以适当的时候安装或卸载Spring ,但这点越来越模糊。
核心概念
模块 | 说明 | |
1. | BeanFactory | Spring内部使用,创建bean的工厂 |
2. | ApplicationContext | 外部应用程序调用,也成为spring容器 |
3. | IoC控制反转 Inversion of Control | 开发者在无需自己new对象,无需关心对象的创建过程 User user = new User(); 手动创建对象 User user = context.getBean(user); 容器创建对象 |
4. | DI依赖注入 Dependency Injection | 松耦合方式实现对象直接的依赖 |
5. | AOP面向切面编程 | 补充java面向对象的不足 |
1.2 三大核心组件的关系
Bean、Context、Core三大核心组件的关系:
Bean 包装的是 Object,而 Object 必然有数据,如何给这些数据提供生存环境就是 Context要解决的问题,对 Context 来说它就是要发现每个 Bean 之间的关系,为它们建立这种关系并且要维护好这种关系。所以 Context 就是一个Bean关系的集合,这个关系集合又叫 Ioc 容器,一旦建立起这个 Ioc 容器后 Spring 就可以为你工作了。Core 组件就是发现、建立和维护每个 Bean 之间的关系所需要的一些类的工具,从这个角度看来,Core 这个组件叫 Util 更能让你理解。
把Bean 比作一场演出中的演员的话,那 Context 就是这场演出的舞台背景,而 Core应该就是演出的道具了。只有他们在一起才能具备能演出一场好戏的最基本的条件。当然有最基本的条件还不能使这场演出脱颖而出,还要他表演的节目足够的精彩,这些节目就是 Spring 能提供的特色功能了。
主要jar组成
模块 | 说明 | |
1. | org.springframework.core | 核心工具包,其他包依赖此包 |
2. | org.springframework.beans | 核心,包括:配置文件,创建和管理bean等 |
3. | org.springframework.aop | 面向切面编程,提供AOP的实现 |
4. | org.springframework.context | 提供IoC功能上的扩展服务,此外还提供许多企业级服务的支持,邮件、任务调度、JNDI定位、EJB集成、远程访问、缓存以及多种视图层框架的支持 |
5. | org.springframework.web.mvc | 包含SpringMVC应用开发时所需的核心类 |
6. | org.springframework.transaction | 为JDBC、Hibernate、JDO、JPA提供一致的声明式和编程式事务管理 |
7. | org.springframework.web | 包含Web应用开发时所需支持类 |
8. | org.springframework.aspects | 提供对AspectJ框架的支持 |
9. | org.springframework.test | 对junit等测试框架的简单封装 |
10. | org.springframework.asm | 3.0后提供自己独立的,反编译 |
11. | org.springframework.context.support | Context的扩展支持,用于mvc方面 |
12. | org.springframework.expression | Spring表达式语言 |
13. | org.springframework.instument | 对服务器的代理接口 |
14. | org.springframework.jdbc | 对jdbc的简单封装 |
15. | org.springframework.jms | 为简化jms api的使用而做的简单封装 |
16. | org.springframework.orm | 整合第三方orm,如hibernate/mybatis |
17. | org.springframework.web.servlet | 增强servlet |
二、Spring框架两大核心:IoC和DI
2.1概述
1、IoC(Inversion of Control)简单来说就是将对象Object的创建的权力及对象的生命周期的管理过程交由Spring框架来处理,从此在开发过程中不在需要关注对象的创建和生命周期的管理,而是在需要的时候由Spring框架提供,这个由Spring框架管理对象创建和生命周期的机制称之为控制反转。
2、DI(Dependency Injection)在创建对象的过程中Spring可以依据对象的关系,自动把其它对象注入(无需创建对象,直接拿着使用)进来,这个过程称之为DI(Dependency Injection)依赖注入。
总结下Spring核心就干了两件事:
1. 创建对象
2. 设置对象的关联关系
2.2IOC(控制反转)
IOC(Inversion of Control),控制反转。
就是指将对象的创建,对象的存储(map),对象的管理(依赖查找,依赖注入)交给了spring容器。
--是指把new的过程交给Spring框架做 User u = 从框架里取
--创建新的maven的Module
2.2.1用xml实现IOC(知道不推荐使用)
--创建一个类
package cn.tedu.spring;
public class Hello {
public void get(){
System.out.println(123);
}
}
--创建一个xml,就是配置类的信息
--选中src里的resources-右键-new-xml configuration file-spring config-ok
--代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 这个文件是,spring的核心配置文件 -->
<!-- 把Hello类,交给spring容器来管理.IOC DI-->
<bean class="cn.tedu.spring.Hello" id="hello"></bean>
</beans>
--测试
--让框架帮我们new,我们直接从框架获取就行了
--代码
package cn.tedu.spring;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//用来测试
public class TestIOC {
public static void main(String[] args) {
//1,加载spring的核心配置文件--参数是xml文件的名字(xml在resources目录里)
ClassPathXmlApplicationContext spring =
new ClassPathXmlApplicationContext(
"spring-config.xml");
//2,从框架里获取对象--参数是xml文件里的bean标签的id值
//好处:spring容器里的bean都是默认单例的,只有一个实例
Object h = spring.getBean("hello");
//cn.tedu.spring.Hello@6950e31
System.out.println(h);
Hello hh = (Hello) h ; //用子类的扩展功能
hh.get();
}
}
这就是spring框架的IoC,控制反转。之前我们自己new出新类。new User();变成由一个初始化的xml配置文件来创建,也就是由spring容器来创建。遍历xml配置文件,读取到<bean>,获取到class属性的类的全路径,利用反射创建这个类。
在java范畴中万物皆Object,在Spring中万物皆Bean。Bean是Spring的核心、基础、根源。
2.2.2用注解实现IOC(重点)
--创建类(@Component)
package cn.tedu.spring;
import org.springframework.stereotype.Component;
@Component//交给spring框架管理(new)
public class Show {
public void get(){
System.out.println(456);
}
}
--创建核心配置文件spring-config.xml,让扫描所有的类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="cn.tedu.spring"/>
</beans>
package cn.tedu.spring;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext spring =
new ClassPathXmlApplicationContext(
"spring-config.xml");
Show a = (Show) spring.getBean("show");//注解实现IOC方法必须小写,规定。
System.out.println(a);//cn.tedu.spring.Animal2@1df82230
a.get();
//报错NoSuchBeanDefinitionException
//Hello类上没有使用@Component注解,spring容器不会new不会管理这个类
Demo a2 = (Demo) spring.getBean("demo");
System.out.println(a2);
}
}
2.2.3自己实现IOC
--封装Bean类,两个属性beanId(类名)+属性beanPath(类的全路径)
package cn.tedu.IOC;
public class Bean {
private String beanId;
private String beanPath;
public Bean(String beanId, String beanPath) {
this.beanId = beanId;
this.beanPath = beanPath;
}
public Bean() { }
public String getBeanId() {
return beanId;
}
public void setBeanId(String beanId) {
this.beanId = beanId;
}
public String getBeanPath() {
return beanPath;
}
public void setBeanPath(String beanPath) {
this.beanPath = beanPath;
}
}
--IOC的核心实现,new对象 - Map<类名,类的对象>
package cn.tedu.IOC;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MyIOC {
List<Bean> beans = new ArrayList<>();
public MyIOC() throws Exception {
//1,有哪些bean--工程里有很多类,spring都当做bean看待,都存入list查询效率要高
List<Bean> beans = new ArrayList<>();
//1.1,对象的初始化 -- 要创建对象时,早都准备好了很多bean
Bean b1 = new Bean("user","cn.tedu.IOC.User");
Bean b2 = new Bean("dept","cn.tedu.IOC.Dept");
Bean b3 = new Bean("order","cn.tedu.IOC.Order");
//1.2,把准备好的bean存入list,方便后面查的快
Collections.addAll(beans,b1,b2,b3);
//1.3,完成ioc的过程
init();
}
//2,给bean们创建对象--控制反转IOC
//准备map,存数据 -- key是类的名字 value是这个类的对象
Map<String,Object> map = new ConcurrentHashMap<>();
public void init() throws Exception {
//2.1,遍历list,获取每个bean
for (Bean b : beans) {
//2.2,获取每个bean身上的beanId作为key存入map
String key = b.getBeanId();
//2.3,获取每个bean身上的beanPath进行反射创建对象,作为value存入map
Object value = Class.forName(b.getBeanPath()).newInstance();
map.put(key,value);//{"user"=new User(),"dept"=new Dept()}
}
}
//3.getBean()获取对象
public Object getBean(String leiming){
return map.get(leiming);
}
}
--测试 - getBean("类名")去map里找value
package cn.tedu.Myioc;
public class TestMyIoc {
public static void main(String[] args) throws Exception {
MyIoc mine = new MyIoc();
Object o1 = mine.getBean("user");
System.out.println(o1);//cn.tedu.myioc.User@54bedef2
Object o2 = mine.getBean("dept");
System.out.println(o2);//cn.tedu.myioc.Dept@5caf905d
Object o3 = mine.getBean("order");
System.out.println(o3);// cn.tedu.myioc.Order@27716f4
}
}
2.2.4总结
--用Spring框架的IOC,只要把类上加@Component注解+配置扫描器
--我们模拟Spring的过程(4,自己实现IOC)多理解
2.3DI(依赖注入)
DI(Dependency Injection)依赖注入 。
相对于IoC而言,依赖注入(DI)更加准确地描述了IoC的设计理念。所谓依赖注入,即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。
基于IOC的控制反转的功能,又提供了DI依赖注入.使用注解@Autowired
用法
--用@Autowired注解实现对象间的依赖关系
2.3.1测试
--创建Student类
package cn.tedu.DI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Student {
String name = "王林";
@Autowired//相当于底层帮你给属性提供了get/set--自动装配
private Teacher teacher;
// public Teacher getTeacher() {
// return teacher;
// }
不加注解要get和set自己实现
// public void setTeacher(Teacher teacher) {
// this.teacher = teacher;
// }
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", teacher=" + teacher +
'}';
}
}
--创建Teacher类
package cn.tedu.DI;
import org.springframework.stereotype.Component;
@Component
public class Teacher {
String name = "白小纯";
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
'}';
}
}
--测试类
package cn.tedu.DI;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDI {
public static void main(String[] args) {
ClassPathXmlApplicationContext i = new
ClassPathXmlApplicationContext("spring-config.xml");
Student s = (Student)i.getBean("student");
// Teacher t1 = new Teacher();//不加注解自己实现的
// t1.name="苏铭";
// s.setTeacher(t1);
// System.out.println(s.getTeacher());
System.out.println(s);//加注解实现
Teacher t = (Teacher) i.getBean("teacher");
System.out.println(s);
System.out.println(t);
}
}
自己实现DI
package cn.tedu.DI;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("cn.tedu.di.Student");
Field[] fields = c.getDeclaredFields();//暴力反射方法
for (Field f:fields) {
Autowired a = f.getAnnotation(Autowired.class);
if (a!=null){//有注解
Object o = c.newInstance();//暴力反射提供new对象
f.setAccessible(true);//开启私有也可以访问的权限
f.set(o,new Teacher());//给属性设置值
}
}
}
}
2.3.2总结
面试IOC和DI
在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时可能需要多个对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new object() 这样的语法来将合作对象创建出来,这个合作对象是由自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个合作对象,就主动去创建,创建合作对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起,而使用了Spring之后就不一样了,创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题(你是什么时候生的,怎么生出来的我可不关心,能帮我干活就行),A得到Spring给我们的对象之后,两个人一起协作完成要完成的工作即可。
所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。
DI(依赖注入)其实就是IOC的另外一种说法,DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结:控制的什么被反转了?就是:获得依赖对象的方式反转了。
IoC是设计思想,IoC有三个核心:BeanFactory、反射、DI。BeanFactory利用反射实现对象的创建,DI实现对象关系管理。
自动装配
利用注解方式,我们只需要写@Autowired注解,底层就会去容器中找对应的对象,如果有获取到,反射调用其对应的set方法,设置。而这个调用过程都是自动,我们没有手工去写set方法。所以这个过程也称为自动装配。
三、拦截器Interceptor(注解@Configuration)
3.1概述
Springmvc提供的, 主要用于拦截用户请求并在方法执行前后添加功能.本质上也是AOP的思想. SpringMVC框架自动将配置的拦截器注入到所有的mapping中.
应用场景:
1、日志记录:可以记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等等。
2、权限检查:如登陆检测,进入处理器检测是否登陆,如果没有直接返回到登陆页面。
3、性能监控:可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间;
4、事务控制:对事务的提交回滚等,与业务无关的代码。
让程序员更关注业务代码,事务的处理/性能监测的代码 ,不必关心直接让拦截器配置就可以了.
3.2步骤加测试
步骤
--implements HandlerInterceptor
--方法执行前就调用的preHandle
--方法执行后就调用的postHandle
--方法都执行完后就调用的afterCompletion
package cn.tedu.controller;
//自定义拦截器
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component//交给spring进行ioc
public class MyInterceptor implements HandlerInterceptor {
long start;
@Override//执行前
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
start = System.currentTimeMillis();//计时开始
System.out.println("开始准备执行功能~~");
return true;//放行,继续执行后面的功能
}
@Override//执行后
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
long end = System.currentTimeMillis();//计时结束
System.out.println("方法执行了:"+(end-start)+"ms");
}
@Override//全部执行结束
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("资源已释放...");
}
}
--implements WebMvcConfigurer
--添加自定义的拦截器addInterceptors
package cn.tedu.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//把自定义的拦截器配置在容器中
@Configuration
public class Myconfig implements WebMvcConfigurer {
@Autowired//spring提供的注解,用来完成di--自动装配
private MyInterceptor myInterceptor;
@Override//重写接口里的方法,添加自定义拦截器
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor);
}
}
--测试
加拦截器前,只执行了业务代码
加拦截器后,在执行业务代码的前后加了一段特定的业务
package cn.tedu.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/con")
public class Controller {
@RequestMapping("/save")
public void save(){
System.out.println("save方法正在执行");
}
@RequestMapping("/get")
public void get(){
for (int i=0;i<10;i++){
System.out.print("save方法正在执行");
}
}
}
四、MVC项目
4.1项目结构
Spring用来管理项目中的所有Bean,需要使用注解@Component @Autowired @Service @Component等
Springmvc用来管理Controller层,需要使用的注解有@RestController @RequestMapping等。
4.2概述
Controller -> Service -> Dao
CarController CarService
CarServiceImpl
Car Car
4.3测试
创建Car类
package cn.tedu.pojo;
import org.springframework.stereotype.Component;
@Component//spring框架提供,用来ioc
public class Car {
private String name;
private String color;
private double price;
public Car(String name, String color, double price) {
this.name = name;
this.color = color;
this.price = price;
}
public Car() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", price=" + price +
'}';
}
}
创建CarService接口
package cn.tedu.service;
import cn.tedu.pojo.Car;
import org.springframework.stereotype.Component;
//用来实现mvc里的c层的优化
@Component
public interface CarService {
//接口里的方法都是抽象方法,而且都是public的
//会为简写的方法自动拼接public abstract
Car get();
}
创建CarServiceImpl实现类
package cn.tedu.service;
import cn.tedu.pojo.Car;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
//实现接口后,要重写接口里的抽象方法
@Service//作用和Component注解作用一样
public class CarServiceImpl implements CarService{
@Override
public Car get() {
Car c = new Car();
c.setName("jeep");
c.setColor("黑色");
c.setPrice(90.1);
return c;
}
}
创建CarController类
package cn.tedu.controller;
import cn.tedu.pojo.Car;
import cn.tedu.service.CarService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController//接受请求做出响应
@RequestMapping("/car2")
public class CarContorller2 {
@Autowired
private CarService carService;
@RequestMapping("/get")
public Car get(){
return carService.get();
}
}
MVC调用流程
请求的过程:从浏览器的地址栏敲了回车之后,直接访问了Controller层,Controller层会调用Service层,获取数据
响应的过程:把Service层return的数据返回给Controller层,Controller层再return给了浏览器展示
五、MyBatis持久层框架
5.1概述
MyBatis的前身就是iBatis,iBatis本是apache的一个开源项目,2010年5月这个项目由apahce sofeware foundation 迁移到了google code,并且改名为MyBatis。
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。
1、 简化JDBC的开发
2、 能够更好的完成ORM(对象关系映射)
是用来操作数据库的框架,是一个优秀的持久层框架
简化了JDBC的开发步骤,自动完成ORM映射(类里的属性值 和 表里的字段值 自动映射)
5.2内部组件结构图
--mybatis-config.xml 是mybatis的核心配置文件,用来配置事务,数据库的连接信息
--UserMapper.xml 是用来存储操作User表的大量的SQL
--SqlSessionFactory 是会话工厂,创建会话
--SqlSession 是会话,用来执行SQL(select()/insert()....)
5.3开发步骤
--准备数据库,准备表,和表里的记录
--添加mybatis的依赖jar包
--创建mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis的核心配置文件 配置了数据源,事务,... -->
<configuration>
<environments default="test">
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!--数据库的驱动-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--数据库的url-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai" />
<!--数据库的用户名-->
<property name="username" value="root"/>
<!--数据库的密码-->
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>
创建User类
package cn.tedu.pojo;
public class User {
private Integer id;
private String name;
private String addr;
private Integer age;
public User(Integer id, String name, String addr, Integer age) {
this.id = id;
this.name = name;
this.addr = addr;
this.age = age;
}
public User() {
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", addr='" + addr + '\'' +
", age=" + age +
'}';
}
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 String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
--创建UserMapper.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">
<!-- 是mybatis框架用来写SQL语句的文件,namespace用来作为文件的唯一标识-->
<mapper namespace="demons1">
<!--select用来表示要执行查询语句,
id属性是当前SQL的唯一标识.
resultType属性用来完成ORM映射,把查到的结果和指定类里的属性映射值
-->
<select id="getAll" resultType="cn.tedu.pojo.User">
select * from user
</select>
</mapper>
--修改核心配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis的核心配置文件 -->
<configuration>
<environments default="test">
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai" />
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--引入UserMapper.xml文件-->
<mappers>
<mapper resource="UserMapper.xml"></mapper>
</mappers>
</configuration>
--测试
--加载核心配置文件mybatis-config.xml
--创建会话工厂
--创建会话
--执行SQL
--处理返回值
package cn.tedu.mybatis;
import cn.tedu.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
//测试mybatis
public class Test {
public static void main(String[] args) throws IOException {
// 加载核心配置文件mybatis-config.xml
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 创建会话工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 创建会话
SqlSession session = factory.openSession();
// 执行SQL
// session.selectList(namespace的值.sql的id);
List<User> list = session.selectList("demons1.getAll");
// 处理返回值
for (User u : list) {
System.out.println(u);
}
}
}
六、Mybatis的接口开发
6.1概述
比XML的开发方式要好: sqlSession.selectList("hello.get");
用接口开发,SQL的定位不必拼串.
6.2步骤及测试
--准备核心配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis的核心配置文件 -->
<configuration>
<environments default="test">
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/
mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai" />
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--引入DeptMapper.xml文件-->
<mappers>
<mapper resource="DeptMapper.xml"></mapper>
</mappers>
</configuration>
--创建Dept类
package cn.tedu.pojo;
import org.springframework.stereotype.Component;
public class Dept {
private int id;
private String name;
private String loc;
//准备get和set,重写,无参、含参构造
}
--准备DeptMapper.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">
<!-- 这是映射文件。用来存大量的sql
是mybatis框架用来写SQL语句的文件,namespace用来作为文件的唯一标识
namespace的值=接口的全路径-->
<mapper namespace="cn.tedu.dao.DeptDao">
<!--select用来表示要执行查询语句,
id是接口里的方法名称,属性是当前SQL的唯一标识.
resultType属性用来完成ORM映射,把查到的结果和指定类里的属性映射值
-->
<select id="getAll" resultType="cn.tedu.pojo.Dept">
select * from dept
</select>
</mapper>
--准备DeptDao接口,提供方法
package cn.tedu.dao;
import cn.tedu.pojo.Dept;
import java.util.List;
//接口的全路径 = DeptMapper.xml里 namespace的值
//接口里的方法名 = DeptMapper.xml里 SQL的id值
public interface DeptDao {
List<Dept> getAll();//查询dept表的所有数据
Dept getOne();
Dept getTwo();
List<Dept> getLoc(String loc);
Dept getId(int id);
}
--测试
package cn.tedu.test;
import cn.tedu.dao.DeptDao;
import cn.tedu.pojo.Dept;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.transaction.support.ResourceTransactionDefinition;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
//测试类
public class TestDept {
public static void main(String[] args) throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 会话工厂SQLSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 会话SQLSession
SqlSession session = factory.openSession();
//获取DeptDao接口
DeptDao dao = session.getMapper(DeptDao.class);
List<Dept> list = dao.getAll();
// 解析结果集ORM(类和表 / 类里的属性和表里的字段)
for (Dept d : list) {
System.out.println(d);
}
System.out.println("***********************************************");
Dept d = dao.getOne();
System.out.println(d);
System.out.println("***********************************************");
Dept d1 = dao.getTwo();
System.out.println(d1);
System.out.println("***********************************************");
List<Dept> list1 = dao.getLoc("二区");
for (Dept pp1 : list1) {
System.out.println(pp1);
}
System.out.println("***********************************************");
Dept d2 = dao.getId(5);
System.out.println(d2);
}
}
6.3总结
mybatis-config.xml 要引入DeptMapper.xml
DeptMapper.xml 的namespace的值 = 接口的全路径
的SQL的id值 = 接口里方法的名字
DeptDao接口 的全路径 决定了 DeptMapper.xml 的namespace的值
里的方法名 决定了 DeptMapper.xml 的SQL的id值
6.4具体使用
6.4.1 别名:alias
在sqlMapConfig.xml配置,在映射文件中直接写对象名称即可
<typeAliases>
<typeAlias type="cn.mybatis.pojo.User" alias="User"/>
</typeAliases>
6.4.2 参数值:paramterType
指定参数类型,通常制定一个对象类型。
6.4.3 返回值:resultType
非常重要的东西,即完成ORM的映射关系所在。这里指定的cd.tedu.mybatis.domain.User代表把结果集转换成一个User对象实例。
6.4.4 返回值:resultMap
resultMap 用于对复杂对象结构时,对应的ResultMap结构名称
6.4.5 集合:List
在MyBatis中,大多默认采用List集合,声明集合参数或者返回值时,都有个奇怪的写法,本应写List<String>,但习惯只写集合元素的类型:String,大家切记。
6.4.6 #和$的区别
如果是整数都行,如果是字符串,#可以自动拼接字符串'',$只拼接值本身。
从安全性上考虑,能使用#尽量使用#来传参,因为这样可以有效防止SQL注入的问题。
<?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">
<!-- 这是映射文件。用来存大量的sql
是mybatis框架用来写SQL语句的文件,namespace用来作为文件的唯一标识
namespace的值=接口的全路径-->
<mapper namespace="cn.tedu.dao.DeptDao">
<!--select用来表示要执行查询语句,
id是接口里的方法名称,属性是当前SQL的唯一标识.
resultType属性用来完成ORM映射,把查到的结果和指定类里的属性映射值
-->
<select id="getAll" resultType="cn.tedu.pojo.Dept">
select * from dept
</select>
<!--mybatis练习查询id=100的,解析SQL需要的参数::: #{参数}
#和$的区别?如果是整数都行,如果是字符串,#可以自动拼接字符串'',$只拼接值本身
-->
<select id="getId" resultType="cn.tedu.pojo.Dept">
select * from dept where id = ${id}
</select>
<!--mybatis练习根据loc查,解析SQL需要的参数::: #{参数}-->
<select id="getLoc" resultType="cn.tedu.pojo.Dept">
select * from dept where loc = #{loc}
</select>
<!--mybatis练习根据dname查-->
<select id="getTwo" resultType="cn.tedu.pojo.Dept">
select * from dept where dname='开发'
</select>
<!--mybatis练习根据id查,getOne是接口里的方法名 -->
<select id="getOne" resultType="cn.tedu.pojo.Dept">
select * from dept where id=10
</select>
</mapper>
七、动态SQL
7.1概述
使用mybatis提供的各种标签实现SQL动态拼接
7.2分类
<sql> <include> sql标签用来提取SQL片段 ,include标签用来引用指定片段
<if> 判断
<where> 拼接过滤条件
<foreach> 循环
7.3测试
--创建Emp类
//封装emp表里 每个字段的值
public class Emp {
//属性类型必须和数据库里字段类型一致,属性名称必须和数据库里字段名称一致
private int id;
private String ename;
private String job;
private double mgr;
private Date hiredate;
private double sal;
private double comm;
private int deptno;
//准备get和set,tostring重写,无参、含参构造方法
}
--创建EmpMapper.xml文件,写SQL
<?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.tedu.dao.EmpDao">
<!--提取SQL片段,复用性高-->
<sql id="cols">
id,ename,job,mgr,sal,comm,hiredate,deptno
</sql>
<!--查询id是100的 或者200的 或者502 的记录-->
<select id="getIds" resultType="cn.tedu.pojo.Emp">
select <include refid="cols"/>
from emp
where id in(
<!-- /*foreach标签完成遍历,
collection值是固定写法,
separator数据间的分隔符,
item是取到的数据
*/-->
<foreach collection="demons" separator="," item="i">
#{i}
</foreach>
)
</select>
<!--查询id=200 ename=tony的记录 /*
去除多余的and 当<if test="id != 0">时,当前=null*/-->
<select id="getIdName" resultType="cn.tedu.pojo.Emp">
select <include refid="cols"/>
from emp
<where>
<if test="id != null">
id = #{id}
</if>
<if test="ename!=null">
and ename= #{ename}
</if>
</where>
</select>
<!--根据id获取数据-->
<select id="getId" resultType="cn.tedu.pojo.Emp">
<!-- /* include引用了指定的SQL片段 */-->
select <include refid="cols"/>
from emp where
<if test="id != null"><!-- /*用来判断*/-->
id=#{id}
</if>
</select>
<!--获取所有数据-->
<select id="getAll" resultType="cn.tedu.pojo.Emp">
select <include refid="cols"/> from emp
</select>
</mapper>
--创建EmpDao接口,写方法
package cn.tedu.dao;
import cn.tedu.pojo.Emp;
import java.util.List;
public interface EmpDao {
List<Emp> getAll();
Emp getId(int id);
// Emp getIdName(int id,String ename);
List<Emp> getIdName(Emp emp);
List<Emp> getIds(List demons);
}
--创建测试类
package cn.tedu.dao;
import cn.tedu.pojo.Emp;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestEmp {
public static void main(String[] args) throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
EmpDao emp = session.getMapper(EmpDao.class);
List<Emp> list = emp.getAll();
for (Emp e : list) {
System.out.println(e);
}
System.out.println("*********************************************");
Emp id = emp.getId(100);
System.out.println(id);
System.out.println("*********************************************");
// Emp IdName = emp.getIdName(200,"tony");
// System.out.println(IdName);
Emp IdName = new Emp();
IdName.setId(200);
IdName.setEname("tony");
List<Emp> list1 = emp.getIdName(IdName);
for(Emp f:list1){
System.out.println(f);
}
System.out.println("*********************************************");
List list2 = new ArrayList();
Collections.addAll(list2,100,200,502);
List<Emp> list3 = emp.getIds(list2);
for (Emp f2 : list3) {
System.out.println(f2);
}
}
}
八、ResultMap(不常用)
8.1概述
当发起一条查询的SQL语句时,通常都会查出结果,
结果想要和哪个类做ORM映射,直接给
select标签配置resultType属性就可以了.
当出现了特殊情况时(当表里的字段名和类里的属性名不一样),select标签配置resultMap属性。
8.2步骤
--创建UserInfo类
public class UserInfo {
private int id;
private String userName;
private String userAddr;
private int userAge;
//get和set,tostring,无参、含参构造方法
}
--创建UserInfoMapper.xml,写SQL
<?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.tedu.dao.UserInfoDao">
<!--resultType只能完成简单的ORM(表里的字段名和类里的属性名一样)
UserInfo{id=1, userName='null', userAddr='null', userAge=0}
<select id="getAll" resultType="cn.tedu.pojo.UserInfo">
-->
<!-- 用来解决 属性名和字段名 不一样
id 是唯一标识
type 想要把结果封装给哪个类
UserInfo{id=1, userName='韩梅梅', userAddr='上海', userAge=20}
-->
<resultMap id="userInfoRM" type="cn.tedu.pojo.UserInfo">
<!-- id是专门用来描述表里的主键信息 result描述表里的普通字段
column用来指定表里的字段名
property用来指定类中的属性名
-->
<id property="id" column="id"></id>
<result property="userName" column="user_name"></result>
<result property="userAddr" column="user_addr"></result>
<result property="userAge" column="user_age"></result>
</resultMap>
<!--resultMap 用来完成特殊情况的处理-->
<select id="getAll" resultMap="userInfoRM">
select * from user_info
</select>
</mapper>
--创建UserInfoDao接口,写方法
package cn.tedu.dao;
import cn.tedu.pojo.UserInfo;
import java.util.List;
public interface UserInfoDao {
List<UserInfo> getAll();
}
--创建测试类
package cn.tedu.dao;
import cn.tedu.pojo.UserInfo;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class TestUserInfo {
public static void main(String[] args) throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
UserInfoDao info = session.getMapper(UserInfoDao.class);
List<UserInfo> list = info.getAll();
for (UserInfo all : list) {
System.out.println(all);
}
}
}
8.3优化ResultMap
8.3.1开启自动映射
--在核心配置文件中mybatis-config.xml文件中加一个配置信息(放在最上面)
<settings>
<!--开启驼峰规则-->
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
--在指定的resultMap标签中,添加autoMapping属性
<resultMap id="userInfoRM" type="cn.tedu.pojo.UserInfo" autoMapping="true">
九、利用SpringBoot整合SSM
9.1项目结构
9.2代码SSM
十、拓展
10.1.SpringMVC源码分析
--前端控制器DispatcherServlet:接受请求(url和方法的匹配规则)和做出响应
--处理器映射器HandlerMapping:就是根据url找到对应的类对应的方法
--处理器适配器HandlerAdaptor:就是干活的(Controller-Service-Dao-db)
--视图解析器ViewResolver:就是准备网页上要展示数据的格式
--视图渲染View:给浏览器响应,在浏览器上做展示
10.2.前后端访问流程图
10.3.JDBC和MyBatis的区别
JDBC是java提供了一套专门用于和数据库对接的api,java.sql.*,其规范了如何和数据库进行对接,实现由各数据库厂商进行各自的实现和扩展。学习JDBC重点在学习如何使用其api。
MyBatis框架是轻量级封装了JDBC,我们已经看不到这些api,连接connection、语句preparedstatement、结果集ResultSet,而关注的是mybatis框架体系如何去使用,一旦写好,我们关注的是java对象。
10.4.XML和接口方式的区别
MyBatis提供了两种操作数据库的方式,一种是通过xml映射文件,一种是通过java的接口类。按面向对象方式更加推荐接口方式,但如果复杂的多表映射,仍然需要使用xml映射文件的ResultMap方式实现。
接口只是假象,其底层仍然是通过xml实现,好不容易实现了一套方式,怎忍丢掉呢?可以做个测试就知道它底层怎么实现的?把xml中的sql删除,它就玩不转了。
10.5. 接口方式怎么找到xml执行的
SqlSession的getMapper方法找到类,通过反射可以获取到类的全路径(包名.类名),相加后就定位到某个xml的命名空间namespace,在根据调用的方法去找到xml中某个标签的id属性。从而实现价值接口,调用接口的方法而间接找到xml中的标签,通过解析xml获取这个标签的内容,从而获取到sql语句。