Spring快速入门
容器,顾名思义,简单来说,就是放对象,拿对象。Spring容器就是管理放进容器中的对象的。
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.itcast.dao.Impl.UserDaoImpl"></bean>
默认情况下是通过反射的无参数构造创建实例对象,因此class对应的类必须保证有无参数构造
</beans>
public class SpringTest {
@Test
//测试bean的scope属性
public void testScope(){
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao1 = (UserDaoImpl)app.getBean("userDao");
UserDao userDao2 = (UserDaoImpl)app.getBean("userDao");
System.out.println(userDao1); //com.itcast.dao.Impl.UserDaoImpl@1c3a4799
System.out.println(userDao2); //com.itcast.dao.Impl.UserDaoImpl@1c3a4799
}
}
singleton
默认值,单例的,容器中只存在一个对象
public class SpringTest {
@Test
//测试bean的scope属性
public void testScope(){
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao1 = (UserDaoImpl)app.getBean("userDao");
UserDao userDao2 = (UserDaoImpl)app.getBean("userDao");
System.out.println(userDao1); //com.itcast.dao.Impl.UserDaoImpl@67205a84
System.out.println(userDao2); //com.itcast.dao.Impl.UserDaoImpl@7d0587f1
}
}
prototype
多例的,容器中存在多个对象,每次getBean都会返回一个新的对象
Bean标签范围之scope的取值
-
当取值为singleton时
Bean的实例化个数:1个 Bean的实例化时机:当Spring的核心文件被加载时,实例化配置的Bean实例 ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");执行后 Bean的生命周期: 对象创建:当应用加载,创建容器时,对象就被创建了 对象运行:只要容器存在,对象一直活着 对象销毁:当应用卸载,销毁容器时,对象就被销毁了
-
当取值是proptype时
Bean的实例化个数:多个 Bean的实例化时机:当调用getBean()方法时实例化Bean 对象创建:当使用对象时,创建新的对象实例,getBean()方法 对象运行:只要对象在使用中,就一直活着 对象销毁:当对象长时间不用,被java的垃圾回收机制回收
Bean生命周期配置
在Bean标签中配置 <bean id="userDao" class="com.itcast.dao.Impl.UserDaoImpl" scope="singleton" init-method="init" destroy-method="destory"></bean>
- init-method:指定类中的初始化方法名称
- destory-method:指定类中销毁方法名称
为什么init方法执行比构造方法晚?
因为init方法的执行要依靠对象的调用,所以要先创建完对象,然后调用init方法
控制台不出来销毁方法的执行?
public class UserDemo {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDaoImpl = (UserDaoImpl)app.getBean("userDao");
userDaoImpl.save();
}
}
因为是还没有来的及打印,函数就结束了,如果想看到destory方法执可以在后面添加app.close();
Bean实例化的三种方式
无参数构造方法实例化,默认
工厂静态方法实例化
<bean id="userDao" class="com.itcast.factory.StaticFactory" factory-method="getUserDao"></bean>
通过getBean()方法获取id找到com.itcast.factory.StaticFactory,通过factory-method找到静态方法返回对象,不需要有工厂对象再调用方法
工厂实例方法实例化
<bean id="factory" class="com.itcast.factory.DynamicFactory"></bean>
<bean id="userDao" factory-bean="factory" factory-method="getUserDao"></bean>通过getBean()获取对应的id(userDao) 通过factory-bean对应的属性值获取factory对应的类对象,通过类对象调用factory-method对应的方法getUserDao
Bean的依赖注入分析
public class UserDemo {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext app1 = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDaoImpl = (UserDaoImpl)app.getBean("userDao");
userDaoImpl.save();
}
}
public class UserDaoImpl implements UserDao {
public UserDaoImpl(){
System.out.println("实例创建...");
}
public void init(){
System.out.println("初始化方法...");
}
public void destory(){
System.out.println("销毁方法...");
}
@Override
public void save() {
System.out.println("Hello Spring");
}
}
控制台输出:
实例创建...
实例创建...
Hello Spring
原因:加载了两次applicationContext.xml配置文件,在Spring容器中创建了两个UserDao实例,调用了两次构造方法.
Bean的依赖注入方式
怎么将UserDao注入到UserService内部?(把一个对象注入到另一个对象内部)
-
构造方法
有参数构造注入 public class UserServiceImpl implements UserService { private UserDao userDao; public UserServiceImpl(UserDao userDao) { this.userDao = userDao; } public UserServiceImpl() { //自己书写有参构造后不会为此生成无参构造,因此要手动书写 } /*public void setUserDao(UserDao userDao) { this.userDao = userDao; }*/ //在容器内部将UserDao对象通过set方法注入UserService对象 @Override public void save() { userDao.save(); } } 配置: <bean id="userDao" class="com.itcast.dao.Impl.UserDaoImpl"></bean> <bean id="userService" class="com.itcast.service.impl.UserServiceImpl"> <constructor-arg name="userDao" ref="userDao"></constructor-arg> </bean> 通过id-"userService"获取userServiceImpl对象,对象创建的方式是通过有参数构造的方式创建,constructor-arg中name是参数的名称,ref="userDao"中的userDao才是指向id为userDao,创建UserDaoImpl对象
-
set方法
public class UserServiceImpl implements UserService { private UserDao userDao;//定义属性 public void setUserDao(UserDao userDao) { this.userDao = userDao; } //在容器内部将UserDao对象通过set方法注入UserService对象 @Override public void save() { userDao.save(); } } applicationContext.xml配置内容: <bean id="userDao" class="com.itcast.dao.Impl.UserDaoImpl"></bean> <bean id="userService" class="com.itcast.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> <!--name属性值是所在类中set方法的去掉set后的值,大写变小写,ref引用谁?引用所需要的对象--> </bean> p命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下:首先需要引入p命名空间: xmlns:p="http://www.springframework.org/schema/p" 其次,需要修改注入方式: <bean id="userService" class="com.itcast.service.impl.UserServiceImpl" p:userDao-ref="userDao"></bean> //p后面的是函数名称,属性值是id
在Spring的配置文件中进行配置告诉Spring要把容器中的一个对象注入到另外一个对象中
Bean的依赖注入的数据类型
除了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入
注入数据的三种数据类型
-
普通数据类型
通过setter注入 public class UserDaoImpl implements UserDao { private String username; private int age; public UserDaoImpl(){ System.out.println("实例创建..."); } public void setUsername(String username) { this.username = username; } public void setAge(int age) { this.age = age; } public void init(){ System.out.println("初始化方法..."); } public void destory(){ System.out.println("销毁方法..."); } @Override public void save() { System.out.println(username+"==="+age); System.out.println("Hello Spring"); } } 配置: <bean id="userDao" class="com.itcast.dao.Impl.UserDaoImpl"> <property name="username" value="zhangsan"></property> <property name="age" value="18"></property> </bean> 一般通过setter方法注入使用较多 通过构造函数配置 public class UserDaoImpl implements UserDao { private String username; private int age; public UserDaoImpl(){ System.out.println("实例创建..."); } public UserDaoImpl(String username,int age){ this.username=username; this.age=age; } @Override public void save() { System.out.println(username+"==="+age); System.out.println("Hello Spring"); } } 配置: <bean id="userDao" class="com.itcast.dao.Impl.UserDaoImpl"> <constructor-arg name="username" value="lisi"></constructor-arg> <constructor-arg name="age" value="20"></constructor-arg> </bean>
-
引用数据类型
-
集合数据类型
通过setter注入 public class UserDaoImpl implements UserDao { private List<String> list; private Map<String, User> map; private Properties properties; private String username; private int age; public UserDaoImpl(){ System.out.println("实例创建..."); } public UserDaoImpl(String username,int age){ this.username=username; this.age=age; } public void setList(List<String> list) { this.list = list; } public void setMap(Map<String, User> map) { this.map = map; } public void setProperties(Properties properties) { this.properties = properties; } @Override public void save() { System.out.println(username+"==="+age); System.out.println(list); System.out.println(map); System.out.println(properties); System.out.println("Hello Spring"); } } 配置如下: <bean name="user1" class="com.itcast.domain.User"> <property name="username" value="zhangsan"></property> <property name="age" value="18"></property> </bean> <bean name="user2" class="com.itcast.domain.User"> <property name="username" value="wangwu"></property> <property name="age" value="20"></property> </bean> <bean name="user3" class="com.itcast.domain.User"> <property name="username" value="lisi"></property> <property name="age" value="28"></property> </bean> <bean id="userDao" class="com.itcast.dao.Impl.UserDaoImpl"> <property name="list"> <list> <value>aaa</value> <value>bbb</value> <value>ccc</value> </list> </property> <property name="map"> <map> <entry key="k1" value-ref="user1"></entry> <entry key="k2" value-ref="user2"></entry> <entry key="k3" value-ref="user3"></entry> </map> </property> <property name="properties"> <props> <prop key="p1">aaa</prop> <prop key="p2">bbb</prop> <prop key="p3">ccc</prop> </props> </property> </bean> <bean id="userService" class="com.itcast.service.impl.UserServiceImpl"> <constructor-arg name="userDao" ref="userDao"></constructor-arg> </bean> 其中map中的user通过setter方法注入的 通过构造函数注入 public class UserDaoImpl implements UserDao { private List<String> list; private Map<String, User> map; private Properties properties; private String username; private int age; public UserDaoImpl(){ System.out.println("实例创建..."); } public UserDaoImpl(String username,int age){ this.username=username; this.age=age; } public UserDaoImpl(List<String> list,Map<String,User> map,Properties properties){ this.list=list; this.map=map; this.properties=properties; } @Override public void save() { System.out.println(username+"==="+age); System.out.println(list); System.out.println(map); System.out.println(properties); System.out.println("Hello Spring"); } } 配置如下: <bean name="user1" class="com.itcast.domain.User"> <property name="username" value="zhangsan"></property> <property name="age" value="18"></property> </bean> <bean name="user2" class="com.itcast.domain.User"> <property name="username" value="wangwu"></property> <property name="age" value="20"></property> </bean> <bean name="user3" class="com.itcast.domain.User"> <property name="username" value="lisi"></property> <property name="age" value="28"></property> </bean> <bean id="userDao" class="com.itcast.dao.Impl.UserDaoImpl"> <constructor-arg name="list"> <list> <value>aaa</value> <value>bbb</value> <value>ccc</value> </list> </constructor-arg> <constructor-arg name="map"> <map> <entry key="k1" value-ref="user1"></entry> <entry key="k2" value-ref="user2"></entry> <entry key="k3" value-ref="user3"></entry> </map> </constructor-arg> <constructor-arg name="properties"> <props> <prop key="p1">p</prop> <prop key="p2">pp</prop> <prop key="p3">ppp</prop> </props> </constructor-arg> </bean> <bean id="userService" class="com.itcast.service.impl.UserServiceImpl"> <constructor-arg name="userDao" ref="userDao"></constructor-arg> </bean> 注入时ref中的属性值通过Bean中的id属性值找到UserDaoImpl类,通过里面的配置找到对应的构造函数创建对象
引入其他配置文件(分模块开发)
为什么要分模块开发?
实际开发中,Spring的配置内容非常多,导致Spring配置繁杂,体积很大,所以,可以将部分配置拆解到其它配置文件中(例如可以按照业务逻辑拆解,用户模块、商品模块等或者是根据层去拆,如dao层、service层等),拆完之后文件很多,但最终加载的还是applicationContext.xml文件,所以需要将其引入到applicationContext.xml文件中,主文件加载,分文件跟着加载。
语法如下:
<import resource="applicationContext-xxx.xml"/>
例如:创建两个配置文件,如:applicationContext-user.xml和applicationContext-product.xml配置文件,将其引入applicationContext.xml,即:
<bean id="userService" class="com.itcast.service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
<import resource="applicationContext-user.xml"/>
<import resource="applicationContext-product.xml"/>
知识要点
Spring的重点配置
<bean>标签
id属性:在容器中Bean实例的唯一标识,不重复
class属性:要实例化的Bean全限定名,全类名
scope属性:Bean的作用范围,常用的时Singleton(默认)和prototype
<property>标签:属性注入,通过setter方法注入
name属性:属性名称,是setter方法名称去掉set后的单词首字母小写
value属性:注入的普通属性值(普通数据类型)
ref属性:注入的对象引用值(引用数据类型)
<list>标签:注入的是集合数据类型
<map>标签:注入的是集合数据类型
<properties>标签:注入的是集合数据类型
<constructor-arg>标签:属性注入,通过构造函数注入,name的值是构造函数中参数的名称
<import>标签:导入其它的Spring的配置文件
ApplicationContext的实现类
-
ClassPathXmlApplicationContext
从类的根路径下加载配置文件推荐使用
-
FileSystemXmlApplicationContext
从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置
-
AnnotationConfigApplicationContext
当使用注解配置容器对象使,需要使用此类来创建spring容器。它用来读取注解
Ctrl+D复制一行,ctrl+Y删除一行
数据源(连接池)的作用
- 数据源(连接池)是提高程序性能出现的
- 事先实例化数据源,初始化部分连接资源
- 使用连接资源时从数据源中获取
- 使用完毕后将连接资源归还个数据源
数据源的开发步骤
-
导入数据源的坐标和数据库驱动坐标
-
创建数据源对象
-
设置数据源的基本连接数据(url,驱动,用户名,密码等…)
-
使用数据源获取连接资源和归还连接资源
例子: public class DataSourceTest { //1. 导入数据源的坐标和数据库驱动坐标 @Test //手动测试创建c3p0数据源 public void testC3p0() throws PropertyVetoException, SQLException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); //2. 创建数据源对象 //3. 设置数据源的基本连接数据(url,驱动,用户名,密码等...) dataSource.setDriverClass("com.mysql.cj.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/bookstore?serverTimezone=CST&CharacterEncoding=utf-8"); dataSource.setUser("root"); dataSource.setPassword("123456"); Connection connection = dataSource.getConnection(); System.out.println(connection); //4. 使用数据源获取连接资源和归还连接资源 connection.close(); } }
用war包将项目打包上传到服务器上时只有class文件,所以改需要不能更改代码,只能通过配置文件更改
applicationContext.xml文件命名空间详解另有xml文件详解
为什么需要命名空间?(从语法上区别标签和属性,使xml处理器区分它们)
因为xml这种标记语言具有极好的扩展性,当我们同时访问多分xml文档时,可能会在同一份的xml文档中出现多个同名的标签和属性,而这些标签和属性意义又是完全不同的,遇到这种情况如果我们不从语法上提供区别,xml处理器将无法区分它们
例如:有一个文档,文档保存图书的信息,同时包含作者的详细信息
<book> 表示图书的信息
<title>西游记</title> 表示书名
<author> 表示作者信息
<name>吴承恩</name> 表示作者名称
<title>先生</title> 表示作者的头衔称号
</author>
</book>
但是book中的title和author中的title是两个不同的意思,靠程序不能区分两个title的含义
通过前缀区别,比如:定义一个规则,每个book下的元素使用一个前缀,而author下的使用另外一个前缀,通过不同前缀来区分不同标签,前缀就如同java中的包名,这个前缀就是命名空间
例子:<b:book> b类型的book
<b:title>西游记</b:title> b类型的title
<a:author> a类型的author
<a:name>吴承恩</a:name> a类型的name
<a:title>先生</a:title> a类型的title
</a:author>
</b:book>
这样相同的title就分为了两种不同类型,a类型和b类型
但是这里的命名空间只是通过名字来区别不同的标签,并没有说明名字具体代表什么意义,应该出现在哪并没有说明约束(Spring配置文件使用Schema约束),(xml文件是来存储数据的,没有约束的话数据没有可读性),并且前缀是否会重复呢?
如何解决前缀是否重复?
可以通过像java中包名名机制命名规则解决,就是uri地址,所以这里标签名可以是用uri地址作为前缀(http://www.atguigu.com/a)title,
http://www.atguigu.com/a并不是真正的地址目的只是确保前缀唯一
xml如何使用命名空间
在文档中使用xml命名空间,标签名变成限定名(前缀和标签名)
<b:book xmlns:b="http://www.atguigu.com/xml/b"/>b就是我们声明的命名空间,b相当于是http://www.atguigu.com/xml/b,xmlns:属性值是b=http://www.atguigu.com/xml/b,一旦使用b这个前缀就代表这个标签是属于http://www.atguigu.com/xml/b"这个命名空间下的元素
xml提供了默认的命名空间,这种没有指定前缀的命名空间就会作为页面中元素的默认命名空间,除非在标签中使用其他命名空间的前缀,否则解析器都会认为元素是在默认命名空间下存在。一个文档中只能有一个默认的命名空间
Spring中的配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 默认命名空间
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 这是一个特殊的命名空间,它已经定义好了4个标签分别是 xsi:type、xsi:nil、xsi:schemaLocation、xsi:noNamespaceSchemaLocation ,这几个属性只有声明了 xsi 命名空间后才能使用,这里我们只讲我们用到的 xsi:schemaLocation 这个属性
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>xsi:schemaLocation:这个标签的值分为两个部分,对应的是两个 uri 地址,使用空格将它们分开,空格前是 xsd 文件的目标命名空间,空格后是 xsd 文件的所在物理位置,它的的作用是引用 xsd 文件来校验指定命名的格式.它代表的意思是 引用外部的 http://www.springframework.org/schema/beans/spring-beans.xsd 约束文件来对 http://www.springframework.org/schema/beans 这个名称空间里的元素进行对应的语法、规范约束.
xmlns:context=“http://www.springframework.org/schema/context”
xsi:schemaLocation="
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
"
解释:context这个前缀代表命名空间http://www.springframework.org/schema/context,这个命名空间遵循约束https://www.springframework.org/schema/context/spring-context.xsd,命名空间和前缀需要更改
Spring配置数据源
抽取jdbc配置文件
applicationContext加载jdbc.properties配置文件获得连接信息
- 首先引入context命名空间和约束路径:
-
命名空间:xmlns:context=“http://www.springframework.org/schema/context”
-
约束路径:http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
配置: <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPollDataSource"> <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> 利用了spring-el表达式 @Test //通过applicationContext.xml文件加载jdbc.properties文件来创建c3p0数据源 public void testXml2() throws Exception { ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); DataSource dataSource = (DataSource)app.getBean("dataSource"); Connection connection = dataSource.getConnection(); System.out.println(connection); connection.close(); }
-
Spring注解开发
Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,注解代替xml配置文件可以简化配置,提高效率
- Spring的原始注解,主要是替代的配置
注解 | 说明 |
---|---|
@Component | 使用在类上用于实例化Bean,并不能知道属于那一层 |
@Controller | 使用在web层类上用于实例化Bean |
@Service | 使用在service层类上用于实例化Bean |
@Repository | 使用在dao层类上用于实例化Bean |
@Autowired | 使用在字段上用于根据类型依赖注入 |
@Qualifier | 结合@Autowired一起使用用于根据名称进行依赖注入 |
@Resource | 使用相当于@Autowired+@Qualifier,按照名称进行注入 |
@Value | 注入普通属性 |
@Scope | 标注Bean的作用范围 |
@PostConstruct | 使用在方法上标注该方法是Bean的初始化方法 |
@PreDestroy | 使用在方法上标注该方法是Bean的销毁方法 |
使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法
@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器,这个接下来就会讲到。
<context:component-scan base-package="包名"></context:component-scan> --注解的组件扫描
快速入门
package com.dyf.dao.impl;
import com.dyf.dao.UserDao;
import org.springframework.stereotype.Component;
/**
* @Author:DYF
* @Description: Date:Created in 10:00 2022/3/26
*/
@Component("userDao")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("save running...");
}
}
package com.dyf.dao;
/**
* @Author:DYF
* @Description: Date:Created in 10:00 2022/3/26
*/
public interface UserDao {
void save();
}
package com.dyf.service.impl;
import com.dyf.dao.UserDao;
import com.dyf.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
/**
* @Author:DYF
* @Description: Date:Created in 10:02 2022/3/26
*/
@Component("userService")
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userDao")
private UserDao userDao;
//这里只写Autowired而不写Qualifier的话,将根据数据类型从Spring容器中进行匹配的,但是当容器中出现多个Bean时,不知道匹配哪一个,添加上Qualifier告诉容器再按照名称进行匹配
public void setUserDao(UserDao userDao) {
this.userDao = userDao; //setter方法可以省掉不写
}
@Override
public void save() {
userDao.save();
}
}
package com.dyf.service;
/**
* @Author:DYF
* @Description: Date:Created in 10:02 2022/3/26
*/
public interface UserService {
void save();
}
package com.dyf.web;
import com.dyf.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author:DYF
* @Description: Date:Created in 10:09 2022/3/26
*/
public class UserController {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) app.getBean("userService");
userService.save();
}
}
配置文件:
<!--配置组件扫描-->
<context:component-scan base-package="com.dyf"/>
注解开发步骤:1.在对应位置写注解,2.在配置文件中配置组件去扫描哪个包以及子包下的包。
@Component注解对应于@Controller、@Service、@Repository注解来说后三个注解可以知道属于的是哪一层
@Autowried和@Qualifier注解
如果只是按照数据类型从Spring容器中进行匹配进行注入,只用写Autowried,但是容器中有多个相同类型的Bean时就会不知到匹配哪一个如果是按照id值从容器中进行匹配的,需要Qualifier结合Autowired一起使用
@Resource相当于@Autowried和@Qualifier注解
@Resource(name="id名称")
@Value注解(普通属性注入):
使用方法:
@value(${键名}):直接从容器中找key,找到就把值赋给对应的变量
@Scope注解:
@Scope("prototype")以及@Scope("singleton")
@PostConstruct和@PreDestory
示例如下:
@PostConstruct
public void init(){
System.out.println("Service对象的初始化方法");
}
@PreDestory
public void destory(){
System.out.println("Service对象的销毁方法");
}
Spring的新注解
使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:
- 非自定义的Bean的配置:,如数据源的配置
- 加载properties文件的配置:context:property-placeholder
- 组件扫描的配置:context:component-scan
- 引入其他文件:
<import>
注解 | 说明 |
---|---|
@Configuration | 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解 |
@ComponentScan | 用于指定Spring在初始化容器时要扫描的包。作用和在Spring的xml配置文件中的<context:component-scan base-package=“包名”/>一样 |
@Bean | 使用在方法上,标注将该方法的返回值存储到Spring容器中 |
@PropertySource | 用于加载properties文件中的配置 |
@import | 用于导入其他配置类 |
步骤如下:
- 创建Spring的核心配置类config包下的SpringConfiguration类,在类上写上注解@configuration
- 在核心配置类上加上@ComponentScan注解用于指定Spring在初始化容器时要扫描的包
- 在方法上使用@Bean注解标注,用于将当前方法的返回值存储到Spring容器中
- 在核心配置文件上写上注解@PropertySource用于加载properties文件中的配置
- 在核心配置类上写上注解@Import(“类名.class”),从核心配置类中引入其他配置
- 加载核心配置类
ApplicationContext app = new AnnotationConfigApplicationContext(类名.class)
@Import()导入后,这个类就注入到容器中了
快速入门
package com.dyf.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
/**
* @Author:DYF
* @Description: Date:Created in 18:55 2022/3/26
*/
@PropertySource("classPath:jdbc.properties")
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean("dataSource") //将当前对象的返回值以指定名称存储到Spring容器中
public DataSource getDataSource() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setUser(password);
return dataSource;
}
}
package com.dyf.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* @Author:DYF
* @Description: Date:Created in 18:24 2022/3/26
*/
@Configuration
@ComponentScan("com.dyf")
@Import(JdbcConfig.class)//数组
public class SpringConfiguration {
}
package com.dyf.dao.impl;
import com.dyf.dao.UserDao;
import org.springframework.stereotype.Component;
/**
* @Author:DYF
* @Description: Date:Created in 10:00 2022/3/26
*/
@Component("userDao")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("save running...");
}
}
package com.dyf.service.impl;
import com.dyf.dao.UserDao;
import com.dyf.service.UserService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Author:DYF
* @Description: Date:Created in 10:02 2022/3/26
*/
@Component("userService")
public class UserServiceImpl implements UserService {
//@Autowired
//@Qualifier("userDao")
@Resource(name = "userDao")
private UserDao userDao;
@Value("${jdbc.driver}")
private String driver;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
System.out.println(driver);
userDao.save();
}
}
package com.dyf.web;
import com.dyf.config.SpringConfiguration;
import com.dyf.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @Author:DYF
* @Description: Date:Created in 10:09 2022/3/26
*/
public class UserController {
public static void main(String[] args) {
//ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = (UserService) app.getBean("userService");
userService.save();
}
}
Spring整合Junit
原始Junit测试Spring的问题
在测试类中每个测试方法都有以下两行代码:
ApplicationContext app = new ClassPathXmlApplicationContext("bean.xml");
IAccountService as = app.getBean("accountService",IAccountService.class);
这两行代码的作用是获取容器,如果不写的话,出现空指针异常,所以不能轻易删除
如何解决?
1.让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉他
2.将需要进行测试Bean直接在测试类中进行注入
步骤
-
导入Spring集成Junit的坐标
导入Spring集成Junit的坐标 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.5.RELEASE</version> </dependency> 导入Junit的坐标 <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency>
-
使用@Runwith注解替换原来的运行期
-
使用@ContextConfiguration指定配置文件或者配置类
-
使用@Autowired注入需要测试的对象
-
创建测试方法进行测试
测试代码如下: package com.dyf.test; import com.dyf.config.SpringConfiguration; import com.dyf.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.sql.DataSource; /**
* @Author:DYF
* @Description: Date:Created in 22:59 2022/3/26
*/
@RunWith(SpringJUnit4ClassRunner.class) //指定SpringJunit4ClassRunner执行测试
//@ContextConfiguration("classpath:applicationContext.xml") //指定配置文件的位置,通过配置文件测试
@ContextConfiguration(classes={SpringConfiguration.class}) //指定配置类,通过注解测试
public class SpringJunitTest {
@Autowired
private UserService userService;
@Autowired
private DataSource dataSource;
@Test
public void test1(){
userService.save();
System.out.println(dataSource);
}
}
Spring与Web环境的集成
ApplicationContext应用上下文的获取方式
通过ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");获取应用上下文
应用上下文对象通过new ClasspathXmlApplicationContext(Spring配置文件)方式获取的,但在web层上不同的servlet中每次从容器中获得Bean是都要编写new ClasspathXmlApplicationContext(Spring配置文件),弊端是配置文件加载多次,应用上写文对象创建多次
解决:使用监听器监听servletContext的创建,然后将创建的上下文对象放入servletContext域中供其他servlet使用(服务器一启动就会创建servletContext对象,servletContextListener监听器执行,初始化方法执行)
ServletContextListener:监听ServletContext对象的创建和销毁
* 方法:
* void contextDestroyed(ServletContextEvent sce) :ServletContext对象被销毁之前会调用该方法
* void contextInitialized(ServletContextEvent sce) :ServletContext对象创建后会调用该方法
* 步骤:
1. 定义一个类,实现ServletContextListener接口
2. 复写方法
3. 配置
1. web.xml
<listener>
<listener-class>cn.itcast.web.listener.ContextLoaderListener</listener-class>
</listener>
* 指定初始化参数<context-param>
2. 注解:
* @WebListener
解决步骤:
-
在listenser包下创建ContextLoaderListener实现ServletContextListener接口,实现contextInitialized初始化方法
package com.dyf.listener; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /**
* @Author:DYF
* @Description: Date:Created in 9:10 2022/3/29
*/
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//将创建的上下文对象放入ServletContext域中
ServletContext servletContext = servletContextEvent.getServletContext();
servletContext.setAttribute("app",app);
System.out.println("spring容器创建完毕...");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
- 在web.xml配置监听器
<listener>
<listener-class>com.dyf.listener.ContextLoaderListener</listener-class>
</listener>
- 在其他位置通过servletContext获取上下文对象
代码优化(原理):
- 上述的代码中获取applicationContext.xml文件在代码中,将代码写死了,不利于后期代码维护,通过在web.xml文件中配置如下代码,将applicationContext.xml配置到全局初始化参数中去,通过servletContext获取指定名称的参数
<!--全局初始化参数-->
<context-param>
<param-name>contextConfiglocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
package com.dyf.listener;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* @Author:DYF
* @Description: Date:Created in 9:10 2022/3/29
*/
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
//读取web.xml中的全局参数
ServletContext servletContext = servletContextEvent.getServletContext();
String contextConfiglocation = servletContext.getInitParameter("contextConfiglocation");
ApplicationContext app = new ClassPathXmlApplicationContext(contextConfiglocation);
//将创建的上下文对象放入ServletContext域中
servletContext.setAttribute("app",app);
System.out.println("spring容器创建完毕...");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
- 通过
servletContext.setAttribute("属性名",属性值)
将创建的上下文对象放入ServletContext域中, 但是web层中通过servletContext.getAttribute("属性名")
获取上下文对象时属性名称不一定好记,所以将获取上下文对象的方式转换为工具类使用,这样方便对象的获取,代码如下:
package com.dyf.listener;
import org.springframework.context.ApplicationContext;
import javax.servlet.ServletContext;
/**
* @Author:DYF
* @Description: Date:Created in 21:21 2022/3/29
*/
public class WebApplicationContextUtils {
public static ApplicationContext getWebApplicationContext(ServletContext servletContext){
return (ApplicationContext) servletContext.getAttribute("app");
}
}
以上是手动获取上下文对象的代码优化,但是可以通过Spring提供获取应用上下文的工具:Spring提供了一个监听器ContextLoaderListener就是对上述功能的封装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象
步骤:
在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)
<!--导入spring-web坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!--全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--配置监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
使用WebApplicationContextUtils获得应用上下文对象ApplicationContext
package com.dyf.web;
import com.dyf.service.Impl.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author:DYF
* @Description: Date:Created in 12:40 2022/3/27
*/
public class UserServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
//ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");
//ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext); //使用WebApplicationContextUtils获取
UserServiceImpl userService = (UserServiceImpl) app.getBean("userService");
userService.save();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}
SpringMVC的简介
概述:SpringMVC是一种基于java的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于SpringFrameWork的后续产品,已经融合在Spring Web Flow中,通过一套注解,让一个简单的java类称为处理请求的控制器,而无需实现任何接口。同时还支持RESTful编程风格的请求
步骤:
导入SpringMVC坐标
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.5.RELEASE</version> </dependency>
配置SpringMVC核心控制器DispatcherServlet
<!--配置SpringMVC的前端控制器--> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup><!--服务器启动时就加载servlet创建对象--> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> <!--/后面不用加任何内容,表示缺省的url,当url跟所有servlet的url不匹配的时候会与其进行匹配--> </servlet-mapping>
创建Controller类和视图页面
package com.dyf.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * @Author:DYF * @Description: Date:Created in 20:37 2022/3/30 */ @Controller public class UserController { @RequestMapping("/quick") public String sava(){ System.out.println("Controller sava running..."); return "success.jsp"; //跳转的视图 } }
<%-- Created by DYF Date: 2022/3/30 Time: 20:39 --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>Success</h1> </body> </html>
使用注解配置Controller类中业务方法的映射地址(见上图)
配置SpringMVC核心文件spring-mvc.xml
创建spring-mvc.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 http://www.springframework.org/schema/context/spring-context.xsd "> <!--Controller的组件扫描--> <context:component-scan base-package="com.dyf.controller"/> </beans>
只是配置了spring-mvc.xml文件,但是应该加载spring-mvc.xml文件,模仿着applicationContext.xml在全局初始化参数中配置的方式,在配置springMVC前端控制器时,通过设置servlet的初始化参数的方式加载配置文件,所以配置如下:
<!--配置SpringMVC的前端控制器--> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param><!--servlet初始化参数--> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup><!--服务器启动时就加载servlet创建对象--> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
客户端发起请求测试(启动服务测试)
SpringMVC是通过Servlet实现的,客户端发送请求,Tomcat内核首先接受客户端请求,接着封装代表请求的的req以及代表响应的resp,然后调用请求资源,如果是使用SpringMVC将会调用其共有行为的Servlet,内部调用特有行为的资源(共有行为中可以包括请求数据,指派视图等),共有行为一般是框架充当(SpringMVC使用servlet)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ut520GgO-1654180943168)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220330220601445.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dWQnGPra-1654180943169)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220330220907962.png)]
SpringMVC的组件解析
SpringMVC的执行流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QCSMC0RH-1654180943170)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220402090808205.png)]
- 客户端发请求,请求到项目中找DispatcherServlet(前端控制器,主要负责调度组件)
- 前端控制器根据请求查询Handler,请求HandlerMapping这个组件(处理器映射器,对请求进行解析,知道最终要找谁),执行完工作之后就返回一连串资源的地址(返回处理器执行链HandlerExecutionChain,内部封装着要执行的很多资源的顺序)
- 前端控制器拿到了处理器执行链,请求执行Handler,调用处理器适配器(HandlerAdaptor,负责调度要被执行的资源),请求处理器(Handler,就是自己写的Controller),处理器响应ModelAndView对象给处理器适配器
- 处理器适配器返回ModelAndView给前端控制器,前端控制器拿到之后调用视图解析器(ViewResolver,负责从ModelAndView中把视图对象解析出来),视图解析器返回视图View对象给前端控制器,对视图对象进行渲染
- 把内容返回给客户端
文字描述:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fF4kOFiR-1654180943171)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220402091010683.png)]
SpringMVC的注解解析
@RequestMapping:用于建立请求URL和处理请求方法之间的对应关系
作用位置:
- 类上,请求URL的第一级访问目录。此处不写的话,就相当于应用的根目录
- 方法上,请求URL的第二级访问目录,与类上的使用@RequestMapping标注的一级目录一起组成访问虚拟路径
package com.dyf.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Author:DYF
* @Description: Date:Created in 20:37 2022/3/30
*/
@Controller
//请求的url是http://localhost:8080/xxx/quick 项目的虚拟目录是"/"
@RequestMapping("/xxx")
public class UserController {
//请求的url是http://localhost:8080/quick
@RequestMapping("/quick")
public String sava(){
System.out.println("Controller sava running...");
return "success.jsp"; //跳转的视图,这里第一次访问时出现了404 not found,提示文.件[/xxx/success.jsp] 未找到,原因是success.jsp是相对路径,相对于当前路径http://localhost:8080/xxx,结果为http://localhost:8080/xxx/success.jsp,而success.jsp路径应为/success.jsp,所以访问不到404,所以应为return "/success.jsp"
}
}
参数:
- value:用于指定请求的url,它和path属性的作用是一样的
- method:用于指定请求的方式
- params:用于指定限制请求参数的条件,支持简单的表达式,要求请求参数的key和value必须和配置的一摸一样
package com.dyf.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @Author:DYF
* @Description: Date:Created in 20:37 2022/3/30
*/
@Controller
//请求的url是http://localhost:8080/xxx/quick
@RequestMapping("/xxx")
public class UserController {
//请求的url是http://localhost:8080/quick
@RequestMapping(value = "/quick",method = RequestMethod.GET,params={"username"})
public String sava(){
System.out.println("Controller sava running...");
return "/success.jsp"; //跳转的视图
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pgxRqIrI-1654180943171)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220402102624820.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B29Y2Brh-1654180943172)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220402102707183.png)]
<context:component-scan base-package="com.dyf">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter> 包括的过滤器,扫描com.dyf包下的Controller注解
<context:exclude-filter></context:exclude-filter> 排除的过滤器
</context:component-scan>
SpringMVC的XML配置解析
-
视图解析器
SpringMVC有默认的组件配置,默认组件都是DispatcherServlet.properties配置文件中配置的,该配置文件地址org/springframework/web/servlet/DispatcherServlet.properties,该文件中配置了默认的视图解析器,如下:
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
在org.springframework.web.servlet.view.InternalResourceViewResolver代码中可以发现其调用了父类的setter和getter方法,观看其父类(UrlBasedViewResolver)源代码
public static final String REDIRECT_URL_PREFIX = "redirect:"; 重定向url前缀 public static final String FORWARD_URL_PREFIX = "forward:"; 转发url前缀(默认值)
通过return “url地址” 可以通过redirect: 和 forward: 指定是重定向还是转发到指定地址
private String prefix = ""; 视图名称前缀以及其setter方法 private String suffix = ""; 视图名称后缀以及其setter方法 --通过在springMVC.xml文件中可以通过配置为其赋值,就可以省略视图前缀以及后缀的书写
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 http://www.springframework.org/schema/context/spring-context.xsd "> <!--Controller的组件扫描--> <context:component-scan base-package="com.dyf.controller"/> <!--配置内部资源视图解析器--> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--/jsp/success.jsp--> <property name="prefix" value="/jsp/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
return 中视图名称的前缀和后缀就可以省略
package com.dyf.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Author:DYF
* @Description: Date:Created in 20:37 2022/3/30
*/
@Controller
//请求的url是http://localhost:8080/xxx/quick
@RequestMapping("/xxx")
public class UserController {
//请求的url是http://localhost:8080/quick
@RequestMapping(value = "/quick")
public String sava(){
System.out.println("Controller sava running...");
return "success"; //跳转的视图
}
}
SpringMVC的数据响应方式
-
页面跳转
-
直接返回字符串
此种方式会将字符串与视图解析器的前后缀拼接后跳转
转发可以访问WEB-INF下的资源,重定向不可以
-
通过ModelAndView对象返回
@RequestMapping("/quick2") public ModelAndView sava2(){ /* * Model模型 封装数据 * View视图 展示数据 * */ ModelAndView modelAndView = new ModelAndView(); //设置模型数据 modelAndView.addObject("username","dyf"); //相当于放入request域中 //设置视图名称 modelAndView.setViewName("forward");//这里视图名称和返回值是String时的写法相同 return modelAndView; }
@RequestMapping("/quick3") public ModelAndView sava3(ModelAndView modelAndView){ //这里相当与spring-mvc维护的,自动注入了modelAndView对象 modelAndView = new ModelAndView(); //设置模型数据 modelAndView.addObject("username","2123"); //相当于放入request域中 //设置视图名称 modelAndView.setViewName("forward"); return modelAndView; }
@RequestMapping("/quick4") public String sava4(Model model){ //这里相当与spring-mvc维护的,自动注入了modelAndView对象 model.addAttribute("username","zs"); return "forward"; }
@RequestMapping("/quick5") public String sava5(HttpServletRequest request){ //方法是spring-mvc调用的,参数通过spring-mvc注入, // 在springmvc接受参数的时候,尽量不要使用基本数据类型,使用其对应的对象类型 request.setAttribute("username","张三"); return "forward"; } //不常用
-
-
回写数据
-
直接返回字符串
Web基础阶段,客户端访问服务器端,如果想直接回写字符串作为响应体返回的话,只需要使用reponse.getWriter().print(“hello world”)即可
- 通过SpringMVC框架注入的response对象,使用reponse.getWriter().print(“hello world”)回写数据,此时不需要视图跳转,业务方法返回值为void
@RequestMapping("/quick6") public void sava6(HttpServletResponse response) throws IOException { //response是springMVC自动注入 response.getWriter().print("hello"); }
- 将需要回写的字符串直接返回,但此时需要通过
@ResponseBody
注解告诉SpringMVC框架,返回的字符串不是跳转是直接在http响应体中返回。
@RequestMapping("/quick7") @ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应 public String sava7(){ return "哈哈"; //中文乱码 }
//响应json格式的数据 @RequestMapping("/quick8") //使用json类型响应数据 @ResponseBody public String sava8() throws JsonProcessingException { User user = new User(); user.setName("张三"); //中文乱码 user.setAge(18); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(user); return json; }
-
返回对象或集合
通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器配置消息转换参数,指定使用jackson进行对象或集合的转换,因此需要在spring-mvc.xml中进行配置代替注解处理器和适配器的配置,默认地集成jackson进行对象或集合的json格式字符串的转换
@RequestMapping("/quick9") @ResponseBody //期望SpringMVC自动将User转换成json格式的字符串, // 所以通过声明告诉其怎么转---处理器适配器HandlerAdapter // 配置HandlerAdapter的setMessageConverters(消息转换器)方法 public User sava10(){ User user = new User(); user.setName("张三"); user.setAge(18); //中文乱码解决 return user; }
<!--配置处理适配器--> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean> </list> </property> </bean>
在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。使用
<mvc:annotation-driven>
自动加载RequestMappingHandlerMapping(处理器映射器)和RequestMappingHandlerAdapter(处理器适配器),可用在SpringMVC.xml配置文件<!--配置mvc的注解驱动--> <mvc:annotation-driven/> 代替配置处理适配器 作用 * Spring MVC用来提供Controller请求转发,json自动转换等功能,默认会帮助我们注册默认处理请求,参数和返回值的类 * 默认底层会集成jackson进行对象或集合的json格式字符串的转换,自动加载RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter(处理适配器) * 在配置文件中使用<mvc:annotation-driven/> 代替注解处理器和适配器的配置
-
SpringMVC获得请求数据
服务器端要获得请求的参数,又是还需要进行数据的封装,SpringMVC可以接受如下类型的参数
-
基本类型参数(Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配)
@RequestMapping("/quick10") @ResponseBody //告知SpringMVC框架 不进行视图跳转 直接进行数据响应,但这里是void所以响应的响应体中无数据(如果改变返回参数的类型,将会将数据响应到响应体中),如果去掉会进行页面跳转,所以该注解不能去掉 public void sava10(String username,Integer age){ //尽量使用对象类型声明参数 System.out.println(username); System.out.println(age); }
-
POJO类型参数(简单的javabean,例如User,Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配)
@RequestMapping("/quick11") @ResponseBody public void sava11(User user){ System.out.println(user); //请求参数的名称与参数中的属性对应,Spring帮助其封装到参数中,中文乱码 }
-
数组类型参数(Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配)
@RequestMapping("/quick12") @ResponseBody public void sava12(String[] args){ System.out.println(Arrays.asList(args)); }
-
集合类型参数(借助对象进行封装,对象的属性中有集合,也就是获取集合参数时,要将集合参数包装到一个POJO中)
@RequestMapping("/quick13") @ResponseBody public void sava13(VO vo){ //借助对象进行集合的封装 System.out.println(vo); }
//创建VO类 public class VO { private List<User> userList; //对应的集合属性 public List<User> getUserList() { return userList; } public void setUserList(List<User> userList) { this.userList = userList; } @Override public String toString() { return "VO{" + "userList=" + userList + '}'; } }
<%-- Created by DYF Date: 2022/4/15 Time: 17:11 --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="${pageContext.request.contextPath}/xxx/quick13" method="post"> <%--Vo对象中userlist属性中第一个User对象的username,age--%> <input type="text" name="userList[0].name"/><br/> <input type="text" name="userList[0].age"/><br/> <%--Vo对象中userlist属性中第二个User对象的username,age--%> <input type="text" name="userList[1].name"/><br/> <input type="text" name="userList[1].age"/><br/> <button type="submit">提交</button> </form> </body> </html> 通过form.jsp测试,有中文乱码
<!--配置过滤器解决乱码--> <filter> <filter-name>encoding</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>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
当使用ajax提交时,可以指定contentType为json形式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装
<%@page pageEncoding="utf-8" language="java" contentType="text/html; utf-8" %> <html> <head> <title>ajax</title> <script src="${pageContext.request.contextPath}/js/jquery-1.4.2.min.js"></script> <%--<script src="js/jquery-1.4.2.min.js"></script>--%> </head> <body> <script> var userList = new Array(); userList.push({ name:"张三", age:15 }); userList.push({ name:"李四", age:18 }); $.ajax({ type:"POST", url:"${pageContext.request.contextPath}/xxx/quick14", data:JSON.stringify(userList), contentType:"application/json;charset=utf-8" }); </script> </body> </html>
@RequestMapping("/quick14") @ResponseBody public void sava14(@RequestBody List<User> userList) { System.out.println(userList); }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oAz1P4mV-1654180943173)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220415194544873.png)]
如果不进行配置,将出现错误,访问不到静态资源文件
静态资源的访问权限问题,通过在spring-mvc.xml中配置 <mvc:resources mapping="/js/**" location="/js/"/> 在框架中开放资源的访问权限,mapping是映射地址 <mvc:default-servlet-handler/>访问资源时找不到对应的RequestMapping对应的地址时,交给原始容器处理(这里是Tomcat)
为什么访问不到,因为DispatcherServlet配置的url是“/”,所以当url没有匹配到对应的servlet后会匹配DispatcherServlet,对于jquery-1.4.2.min.js,DispatcherServlet会匹配RequestMapping,匹配不到,所以访问不到
先对ajax.jsp进行请求,之后请求js文件,最后请求quick14
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4jBQs0mB-1654180943173)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220415194918278.png)]
请求数据乱码问题
当post请求时,会出现乱码,可以设置一个过滤器来进行编码的过滤
<!--配置全局过滤的filter-->
<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
当请求的参数名称与Controller的业务方法参数名称不一致时,就需要@requestParam注解显示绑定
@RequestMapping("/quick15")
@ResponseBody
public void sava15(@RequestParam("name") String username){ //如果发送的请求url中参数是name只有通过注解才能为username注入值,否则username的值是null
System.out.println(username);
}
注解的参数如下:
- value:与请求参数名称,就是上面的”name“,方法中的参数与请求中的参数通过value值建立关系
- required:指定的请求参数是否必须,默认true,提交时没有此参数则报错
- defaultValue:当没有请求参数时,使用指定的默认值赋值,当设置值后就可以不设置required了
@RequestMapping("/quick15")
@ResponseBody
public void sava15(@RequestParam(value = "name",required = false,defaultValue = "张三") String username){
System.out.println(username);
}
获得Restful风格的参数
Restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制。
Restful风格的请求是使用"url+请求方式"表示一次请求
- GET:获取资源
- POST:新建资源
- PUT:更新资源
- DELETE:删除资源
@RequestMapping(value="/quick16/{username}",method="get")
@ResponseBody
public void sava16(@PathVariable(value = "username") String name){
System.out.println(name);
}
//{}中的username对应value的值,解析完url中的值后通过对应关系注入到参数中,中文乱码
自定义类型转化器
就是由SpringMVC提供,将客户端的提交的字符串转换成需要的参数类型,但不是所有的数据类型都提供了转换器,没有提供的需要自己定义转换器
步骤:
- 定义转换器类实现Converter(SpringMVC提供的)接口
package com.dyf.converter;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author:DYF
* @Description: Date:Created in 10:12 2022/4/16
*/
public class DateConverter implements Converter<String, Date> {
//泛型中的String是转换之前的类型,Date是转换之后的类型
public Date convert(String dateStr) {
System.out.println(dateStr);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = simpleDateFormat.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
- 在配置文件中声明转换器
<!--声明转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.dyf.converter.DateConverter"></bean>
</list>
</property>
</bean>
- 在
<annotation-driven>
中引用转换器
<!--配置mvc的注解驱动-->
<mvc:annotation-driven conversion-service="conversionService"/> 使用指定的转换器完成转换
SpringMVC中获得Servlet相关API
支持使用原始ServletAPI对象作为控制器方法的参数进行注入
- HttpServletRequest
- HttpServletResponse
- HttpSession
@RequestMapping("/quick18")
@ResponseBody
public void sava18(HttpServletRequest request, HttpServletResponse response, HttpSession session){
System.out.println(request); //org.apache.catalina.connector.RequestFacade@1ce69831
System.out.println(response); //org.apache.catalina.connector.ResponseFacade@437e4948
System.out.println(session); //org.apache.catalina.session.StandardSessionFacade@45588e3d
}
SpringMVC获得请求头
- @RequestHeader
使用@RequestHeader可以获得请求头信息,相当于request.getHeader(name)
属性如下:
- value:请求头名称
- required:是否必须携带此请求头,默认值是true,没有此请求头将报错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZJOhNq1i-1654180943173)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220416110128296.png)]
@RequestMapping("/quick19")
@ResponseBody
public void sava19(@RequestHeader(value = "User-Agent",required = false) String user_agent){
//value中的值要与请求头中的key保持一致
System.out.println(user_agent);
}
-
@CookieValue
使用@CookieValue可以获得指定Cookie的值
属性如下:
- value:指定cookie的名称
- required:是否必须携带此cookie
@RequestMapping("/quick20")
@ResponseBody
public void sava20(@CookieValue(value = "JSESSIONID") String jsessionid){
System.out.println(jsessionid);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OuI0NwM4-1654180943174)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220416112148299.png)]
value的值要与Cookie的值中的key一致
文件上传
form表单中的enctype="multipart/form-data"是什么意思?
form表单中的enctype=“multipart/form-data“什么意思?_夜阑卧听风吹雨,铁马冰河入梦来的博客-CSDN博客
文件上传客户端
<%@ page language="java" contentType ="text/html; utf-8" pageEncoding="utf-8" %>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/xxx/quick21" method="post" enctype="multipart/form-data">
名称:<input type="text" name="username"/><br/>
文件:<input type="file" name="upload"/><br/>
<button type="submit">提交</button>
</form>
</body>
</html>
<!--
1.表单项type="file"
2.表单提交的方式是post
3.表单的enctype属性是多部分表单形式,enctype="multipart/form-data"
-->
enctype="application/x-www-form-urlencoded"时,form表单的正文内容是:key=value&key=value&…
当form表单的enctype取值是multipart/form-data时,请求正文内容就变成多部份形式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gq2kgLQb-1654180943174)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220416144845013.png)]
步骤:
导入filupload和io坐标
<!--SpringMVC底层封装了fileupload插件--> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.2.1</version> </dependency>
配置文件上传解析器
<!--在SpringMVC配置文件中配置--> <!--配置文件上传解析器--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--上传文件的总大小--> <property name="maxInMemorySize" value="5000"/> <!--上传单个文件的大小,单位时byte,字节,超过会有异常--> <property name="maxUploadSizePerFile" value="5000"/> <!--上传文件的编码类型--> <property name="defaultEncoding" value="utf-8"/> </bean>
编写文件上传代码
@RequestMapping("/quick21") @ResponseBody public void sava21(String username, MultipartFile upload){ //这里的参数名称需要和表单中的name名称一致 System.out.println(username); //获得上传文件的名称 String filename = upload.getOriginalFilename(); System.out.println(filename); try { upload.transferTo(new File("C:\\Users\\86187\\Desktop\\" + filename)); } catch (IOException e) { e.printStackTrace(); } }
多文件上传
- 设置多个类型为MultipartFile类型的参数
- 设置MultipartFile类型的数组,表单name名称与MultipartFile类型数组的名字一致
@RequestMapping("quick22")
@ResponseBody
public void sava22(String username,MultipartFile[] uploadFile) throws IOException {
System.out.println(username);
for (MultipartFile multipartFile : uploadFile) {
String originalFilename = multipartFile.getOriginalFilename();
multipartFile.transferTo(new File("C:\\Users\\86187\\Desktop\\" + originalFilename));
}
}
JDBCTemplate基本使用
是spring框架提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。spring框架提供了操作模板类。例如:操作关系数据的JdbcTemplate和HibernateTemplate,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等。
-
JdbcTemplate开发步骤
-
导入spring-jdbc和spring-tx坐标
<!--导入JdbcTemplate的依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.8.RELEASE</version> </dependency> <dependency><!--与事务相关--> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.8.RELEASE</version> </dependency>
-
创建数据库表和实体
-
创建JdbcTemplate对象
-
执行数据库操作
@Test //测试JdbcTemplate开发步骤 public void test1() throws PropertyVetoException, SQLException { //创建数据源 ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass("com.mysql.cj.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/practice?serverTimezone=UTC"); dataSource.setUser("root"); dataSource.setPassword("123456"); JdbcTemplate jdbcTemplate = new JdbcTemplate(); //设置数据源 jdbcTemplate.setDataSource(dataSource); String sql = "insert into account(name,money) values(?,?)"; //执行操作 int row = jdbcTemplate.update(sql,"zhangsan",500); System.out.println(row); }
-
Spring产生JdbcTemplate对象
<!--配置数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/practice?serverTimezone=UTC"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--Jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
@Test
public void test2(){
ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate jdbcTemplate = (JdbcTemplate) application.getBean("jdbcTemplate");
String sql = "insert into account(name,money) values(?,?)";
int i = jdbcTemplate.update(sql, "lisi", 790);
System.out.println(i);
}
抽取Jdbc.properties文件
- 创建jdbc.properties文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/practice?serverTimezone=UTC
jdbc.user=root
jdbc.pwd=123456
- 在applicationContext.xml文件中加载jdbc.properties
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
">
<!--加载jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据源对象-->
<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.user}"/>
<property name="password" value="${jdbc.pwd}"/>
</bean>
<!--Jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
常用操作-更新操作
package com.dyf.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @Author:DYF
* @Description: Date:Created in 13:47 2022/4/17
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcTemplateCRUDTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
//测试更新操作
public void testUpdate(){
jdbcTemplate.update("update account set money=? where name=?",10000,"lisi");
}
@Test
//测试删除操作
public void testDelete(){
jdbcTemplate.update("delete from account where name = ?","zhangsan");
}
}
常规操作-查询操作
@Test
//聚合查询
public void testQueryCount(){
Long count = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
System.out.println(count);
}
@Test
//查询一个结果
public void testQueryOne(){
Account account = jdbcTemplate.queryForObject("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), "lisi");
System.out.println(account);
}
@Test
//查询所有结果
public void testQuery(){
List<Account> list = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
System.out.println(list);
}
要导航到抽象方法的【实现】,将插入符号置于其用法或声明中的名称,然后按【Ctrl+Alt+B】
SpringMVC拦截器(interceptor)
作用:类似于Servlet开发中的过滤器Filter,对处理器进行预处理和后处理,将拦截器按一定顺序联结成一条链,这条链称为拦截器链(interceptor chain),在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用,拦截器也是AOP思想的具体体现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ehRmpTId-1654180943175)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220503104010016.png)]
自定义拦截器
- 创建拦截器类实现HandlerInterceptor接口
- 配置拦截器(spring-mvc.xml)
- 测试拦截器的拦截效果
package com.dyf.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @Author:DYF
* @Description: Date:Created in 15:30 2022/5/3
*/
public class MyInterceptor1 implements HandlerInterceptor {
//在目标方法执行之前 执行 在这里是show 方法,对应的路径是/target
public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception {
System.out.println("PreHandle.....");
return true; //false不放行,true是放行
}
//在目标方法执行之后,视图对象(ModelAndView对象)返回之前执行,方法执行完成,但是页面还没有接收到视图中的数据
public void postHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("PostHandle.....");
}
//在流程都执行完毕后执行
public void afterCompletion(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("AfterCompletion.....");
}
}
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--对哪些资源执行拦截操作-->
<mvc:mapping path="/**"/>
<!--/**的意思是所有文件夹及里面的子文件夹-->
<!--/*是所有文件夹,不含子文件夹-->
<bean class="com.dyf.interceptor.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Th80qSbr-1654180943175)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220504083929115.png)]
preHandle方法中的request,response参数可以用来获得请求的参数,或者转发或者重定向(return false),postHandle方法可以获取ModelAndView对象,对其进行修改,当preHandle方法的返回值为true时才会执行Interceptor中的其它方法
拦截器链
拦截器执行顺序和配置有关
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--对哪些资源执行拦截操作-->
<mvc:mapping path="/**"/>
<bean class="com.dyf.interceptor.MyInterceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<!--对哪些资源执行拦截操作-->
<mvc:mapping path="/**"/>
<bean class="com.dyf.interceptor.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
执行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ziwjncH4-1654180943175)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220504094005136.png)]
<!--配置对哪些资源不进行拦截-->
<mvc:exclude-mapping path="/user/login"/>
<mvc:exclude-mapping path="/plugins/**"/>
<mvc:exclude-mapping path="/css/**"/>
<mvc:exclude-mapping path="/img/**"/>
SpringMVC的异常处理
系统中异常包括两类:编译时异常和运行时异常,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生。
系统的Dao、Service、Controller出现的都通过throws Exception向上抛出,最后由SpringMVC前端控制器交给异常处理器进行异常处理。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uGqqTmIW-1654180943176)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220505115032590.png)]
异常处理的方式
- 使用SpringMVC提供的简单异常处理器SimpleMappingExceptionResolver
- 例子:角标越界异常,跳转到指定页面,异常与跳转页面的映射关系
- 实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器
SimpleMappingExceptionResolver实现如下:
<!--只需要在springMVC中配置-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="error"/> <!--默认错误视图,当下面的map中的键值对都不匹配时作用-->
<property name="exceptionMappings">
<map>
<entry key="com.dyf.exception.MyException" value="error"/>
<entry key="java.lang.ClassCastException" value="error"/>
</map>
</property>
</bean>
自定义异常处理步骤
- 创建异常处理器类实现HandlerExceptionResolver
package com.dyf.resolver;
import com.dyf.exception.MyException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Author:DYF
* @Description: Date:Created in 12:39 2022/5/5
*/
public class MyExceptionResolver implements HandlerExceptionResolver {
/*
* 参数Exception异常对象
* 返回值ModelAndView:跳转的错误视图信息
* */
@Override
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;
}
}
- 配置异常处理
<!--在springMVC中配置-->
<!--自定义异常处理器-->
<bean class="com.dyf.resolver.MyExceptionResolver"/>
-
编写异常页面
-
测试异常跳转
Spring的AOP简介
- 什么是AOP?
是Aspect Oriented Programming的缩写,意思是面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发的效率(将代码在运行时期间通过AOP结合在一起完成功能)。
-
AOP的作用及其优势
- 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
- 优势:减少重复代码,提高开发效率,并且便于维护
-
AOP的底层实现
AOP的底层是通过Spring提供的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,再去调用目标对象的方法,从而完成功能的增强
-
AOP的动态代理技术
- JDK代理:基于接口的动态代理技术
- cglib代理:基于父类的动态代理技术
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yosHafj0-1654180943177)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220505150148698.png)]
左图代理对象基于目标接口动态生成,目标接口有什么方法,目标对象中就有什么方法,所以代理对象中就有什么方法,但是代理对象是基于目标接口生成,所以完成功能扩充(目标对象实现接口)
右图为目标对象动态生成子对象,子对象和父对象中的方法一致,但是子对象的方法和功能比父对象功能强大,所以完成功能扩充(但不是继承)
//JDK代理
package com.dyf.proxy.jdk;
/**
* @Author:DYF
* @Description: Date:Created in 14:48 2022/5/6
*/
public class Advice {
public void before(){
System.out.println("前置增强...");
}
public void after(){
System.out.println("后置增强...");
}
}
package com.dyf.proxy.jdk;
/**
* @Author:DYF
* @Description: Date:Created in 14:47 2022/5/6
*/
public class Target implements TargetInterface{
@Override
public String show() {
System.out.println("sava running...");
return "123";
}
}
package com.dyf.proxy.jdk;
/**
* @Author:DYF
* @Description: Date:Created in 14:47 2022/5/6
*/
public interface TargetInterface {
public String show();
}
package com.dyf.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Author:DYF
* @Description: Date:Created in 14:50 2022/5/6
*/
public class ProxyTest {
public static void main(String[] args) {
//创建目标对象
Target target = new Target();
//获取增强对象,用于增强目标对象
Advice advice = new Advice();
//返回值 是动态生成的代理对象
TargetInterface targetInterface = (TargetInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(), //目标对象类加载器
target.getClass().getInterfaces(), //目标对象相同的接口字节码对象数组
new InvocationHandler() {
//调用代理对象的任何方法,实质执行的都是invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//第一个参数是代理对象,第二个是方法对象,第三个是参数
//前置增强
advice.before();
String str = (String) method.invoke(target,args);
System.out.println(str); //这里的返回值是目标对象的
//后置增强
advice.after();
return "123"; //返回给调用者的值,可以对目标对象的返回值进行加强
}
}
);
//调用代理对象的方法
String str = targetInterface.show();
System.out.println(str);
}
}
<!--cglib的动态代理-->
<!--导入jar包,已经集成进spring中了,在spring-core下-->
package com.dyf.proxy.cglib;
/**
* @Author:DYF
* @Description: Date:Created in 16:51 2022/5/6
*/
public class Advice {
public void before(){
System.out.println("前置增强...");
}
public void after(){
System.out.println("后置增强...");
}
}
package com.dyf.proxy.cglib;
/**
* @Author:DYF
* @Description: Date:Created in 14:47 2022/5/6
*/
public class Target{
public void show() {
System.out.println("sava running...");
}
}
package com.dyf.proxy.cglib;
import com.dyf.proxy.jdk.Advice;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Author:DYF
* @Description: Date:Created in 16:53 2022/5/6
*/
public class ProxyTest {
public static void main(String[] args) {
//目标对象
Target target = new Target();
//增强对象
Advice advice = new Advice();
//基于cglib
//1.创建增强器
Enhancer enhancer = new Enhancer();
//2.设置父类(目标对象)
enhancer.setSuperclass(Target.class);
//3.设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
advice.before();//执行前置
Object invoke = method.invoke(target,args);
advice.after();//执行后置
return invoke;
}
});
//4.创建代理对象
Target proxy = (Target) enhancer.create();
proxy.show();
}
}
Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后只用编写需要部分的代码,最后通过配置完成指定目标方法的增强
AOP的相关概念
- Target(目标对象):代理的目标对象,要增强的对象
- Proxy(代理对象):一个类被AOP增强后,产生的结果代理类
- Joinpoint(连接点):那些被拦截到的方法,拦截到的方法就是可以被增强的方法
- Pointcut(切入点):切入点是指要对哪些JoinPoint进行拦截的定义(真正增强的方法/被增强的方法)
切入点的范围小于连接点的范围
- Advice(通知/增强):拦截到Joinpoint之后要做的事情就是通知(增强业务逻辑的方法)
- Aspect(切面):是切入点和通知的结合(目标方法和增强)
- Weaving(织入):把切入点和通知结合的过程
AOP开发明确的事项
-
需要编写的内容
- 编写核心业务代码(目标类的目标方法)
- 编写切面类,切面类中有通知(增强功能的方法)
- 在配置文件中,配置织入关系,将哪些通知与哪些切点结合
-
AOP技术实现的内容
Spring框架监控切入点方法的执行,一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别(前置通知、后置通知…),在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行,实现逻辑增强。
AOP底层使用哪种代理方式
根据目标类是否实现接口来决定采用哪种代理的方式(JDK代理、cjlib代理)
基于xml的AOP开发
- 导入AOP的相关坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
</dependency>
- 创建目标接口和目标类(内部有切点)
package com.dyf.aop;
/**
* @Author:DYF
* @Description: Date:Created in 14:47 2022/5/6
*/
public interface TargetInterface {
public String show();
}
package com.dyf.aop;
/**
* @Author:DYF
* @Description: Date:Created in 14:47 2022/5/6
*/
public class Target implements TargetInterface {
@Override
public String show() {
System.out.println("sava running...");
return "123";
}
}
- 创建切面类(内部有增强方法)
package com.dyf.aop;
/**
* @Author:DYF
* @Description: Date:Created in 21:49 2022/5/6
*/
public class MyAspect {
public void before(){
System.out.println("前置增强");
}
}
- 将目标类和切面类的对象创建权交给spring
<!--目标对象-->
<bean id="target" class="com.dyf.aop.Target"/>
<!--切面对象-->
<bean id="myAspect" class="com.dyf.aop.MyAspect"/>
- 在applicationContext.xml中配置织入关系
<!--配置织入:哪些目标方法需要被哪些增强,前置增强,后置增强...-->
<aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--切面:切点+通知-->
<!--前置增强-->
<aop:before method="before" pointcut="execution(public String com.dyf.aop.Target.show())"></aop:before>
</aop:aspect>
</aop:config>
<!--applicationContext.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--目标对象-->
<bean id="target" class="com.dyf.aop.Target"/>
<!--切面对象-->
<bean id="myAspect" class="com.dyf.aop.MyAspect"/>
<!--配置织入:哪些目标方法需要被哪些增强,前置增强,后置增强...-->
<aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--切面:切点+通知-->
<!--前置增强-->
<aop:before method="before" pointcut="execution(public String com.dyf.aop.Target.show())"></aop:before>
</aop:aspect>
</aop:config>
</beans>
- 测试代码
package com.dyf.test;
import com.dyf.aop.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @Author:DYF
* @Description: Date:Created in 22:05 2022/5/6
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
private TargetInterface target;
@Test
public void test(){
target.show();
}
}
切点表达式的写法
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用*代表任意
- 包名与类名之间一个点.代表当前包下的类,两个点…表示当前包及其子包下的类
- 参数列表可以使用两个点…表示任意个数,任意类型的参数列表
通知的类型
名称 | 标签 | 说明 |
---|---|---|
前置通知 | <aop:before> | 配置前置通知,指定增强的方法在切入点方法之前执行 |
后置通知 | <aop:after-returning> | 配置后置通知,指定增强的方法在切入点方法之后执行 |
异常抛出通知 | <aop:throwing> | 用于配置异常抛出通知,指定增强的方法在出现异常时执行 |
最终通知 | <aop:after> | 用于配置最终通知,无论增强方式执行是否有异常都会执行 |
环绕通知 | <aop:around> | 用于配置环绕通知,指定增强的方法在切入点方法之前和之后都执行 |
增强不会对增强增强,对于* com.dyf.aop.
*
.*
(…)非增强类的方法进行增强
package com.dyf.aop;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* @Author:DYF
* @Description: Date:Created in 21:49 2022/5/6
*/
public class MyAspect {
public void before(){
System.out.println("前置增强");
}
public void afterReturning(){
System.out.println("后置增强");
}
//Proceeding JoinPoint: 正在执行的连接点===切点
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强....");
//切点方法
pjp.proceed();
System.out.println("环绕后增强....");
}
public void afterThrowing(){
System.out.println("异常抛出异常...");
}
public void after(){
System.out.println("最终增强...");
}
}
package com.dyf.aop;
/**
* @Author:DYF
* @Description: Date:Created in 14:47 2022/5/6
*/
public class Target implements TargetInterface {
@Override
public String show() {
int i = 1/0;
System.out.println("sava running...");
return "123";
}
}
package com.dyf.test;
import com.dyf.aop.HeHe;
import com.dyf.aop.MyAspect;
import com.dyf.aop.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @Author:DYF
* @Description: Date:Created in 22:05 2022/5/6
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
private TargetInterface target;
@Autowired
private MyAspect myAspect;
@Autowired
private HeHe hehe;
@Test
public void test(){
target.show();
}
@Test
public void test1(){
myAspect.before();
}
@Test
public void test2(){
hehe.hehe();
}
}
package com.dyf.aop;
/**
* @Author:DYF
* @Description: Date:Created in 14:47 2022/5/6
*/
public interface TargetInterface {
public String show();
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--目标对象-->
<bean id="target" class="com.dyf.aop.Target"/>
<!--切面对象-->
<bean id="myAspect" class="com.dyf.aop.MyAspect"/>
<bean id="heHe" class="com.dyf.aop.HeHe"/>
<!--配置织入:哪些目标方法需要被哪些增强,前置增强,后置增强...-->
<aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--切面:切点+通知-->
<!--前置增强
<aop:before method="before" pointcut="execution(public String com.dyf.aop.Target.show())"></aop:before>
<!–后置增强–>
<aop:after-returning method="afterReturning" pointcut="execution(* com.dyf.aop.*.*(..))"></aop:after-returning>-->
<!--环绕增强-->
<aop:around method="around" pointcut="execution(* com.dyf.aop.*.*(..))"></aop:around>
<!--异常抛出增强-->
<aop:after-throwing method="afterThrowing" pointcut="execution(* com.dyf.aop.*.*(..))"></aop:after-throwing>
<!--最终增强-->
<aop:after method="after" pointcut="execution(* com.dyf.aop.*.*(..))"></aop:after>
</aop:aspect>
</aop:config>
</beans>
package com.dyf.aop;
/**
* @Author:DYF
* @Description: Date:Created in 19:14 2022/5/7
*/
public class HeHe {
public void hehe(){
System.out.println("hehe...");
}
}
切点表达式的抽取
在配置中多个通知的切点表达式可能是一样的,可以将切点表达式抽取,在增强中使用pointcut-ref属性代替pointcut属性来引用抽取后的切点表达式
<!--抽取切点表达式-->
<aop:pointcut id="myPointcut" expression="execution(* com.dyf.aop.*.*(..))"></aop:pointcut>
<aop:around method="around" pointcut-ref="myPointcut"/>
<aop:after method="after" pointcut-ref="myPointcut"/>
基于注解的AOP开发
- 创建目标接口和目标类
- 创建切面类
- 将目标类和切面类的对象创建全交给spring
package com.dyf.anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
/**
* @Author:DYF
* @Description: Date:Created in 21:49 2022/5/6
*/
@Component("myAspect")
public class MyAspect {
public void before(){
System.out.println("前置增强");
}
public void afterReturning(){
System.out.println("后置增强");
}
//Proceeding JoinPoint: 正在执行的连接点===切点
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强....");
//切点方法
pjp.proceed();
System.out.println("环绕后增强....");
}
public void afterThrowing(){
System.out.println("异常抛出异常...");
}
public void after(){
System.out.println("最终增强...");
}
}
package com.dyf.anno;
import org.springframework.stereotype.Component;
/**
* @Author:DYF
* @Description: Date:Created in 14:47 2022/5/6
*/
@Component("target")
public class Target implements TargetInterface {
@Override
public String show() {
//int i = 1/0;
System.out.println("sava running...");
return "123";
}
}
- 在切面类中使用注解配置织入关系
package com.dyf.anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/**
* @Author:DYF
* @Description: Date:Created in 21:49 2022/5/6
*/
@Component("myAspect")
@Aspect //标注当前myAspect是一个切面类
public class MyAspect {
public void before(){
System.out.println("前置增强");
}
@AfterReturning("execution(* com.dyf.anno.*.*(..))") //配置织入关系,注解的名字是为了可读性强
public void afterReturning(){
System.out.println("后置增强");
}
//Proceeding JoinPoint: 正在执行的连接点===切点
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强....");
//切点方法
pjp.proceed();
System.out.println("环绕后增强....");
}
public void afterThrowing(){
System.out.println("异常抛出异常...");
}
public void after(){
System.out.println("最终增强...");
}
}
- 在配置文件中开启组件扫描和AOP的自动代理
<!--在applicationContext.xml中-->
<!--开启组件扫描-->
<context:component-scan base-package="com.dyf.anno"/>
<!--开启AOP的自动代理-->
<aop:aspectj-autoproxy/>
- 测试
注解通知的类型
@通知注解(“切点表达式”)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0po5br45-1654180943177)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220507224051104.png)]
切点表达式的抽取
在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后在增强注解中引用
package com.dyf.anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @Author:DYF
* @Description: Date:Created in 21:49 2022/5/6
*/
@Component("myAspect")
@Aspect //标注当前myAspect是一个切面类
public class MyAspect {
/*
定义方法,使用@Pointcut注解定义切点表达式
* */
@Pointcut("execution(* com.dyf.anno.*.*(..))")
public void myPoint(){};
@Before("MyAspect.myPoint()") //进行引用
public void before(){
System.out.println("前置增强");
}
@AfterReturning("myPoint()") //进行引用
public void afterReturning(){
System.out.println("后置增强");
}
//Proceeding JoinPoint: 正在执行的连接点===切点
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强....");
//切点方法
pjp.proceed();
System.out.println("环绕后增强....");
}
public void afterThrowing(){
System.out.println("异常抛出异常...");
}
public void after(){
System.out.println("最终增强...");
}
}
编程式事务控制相关对象
-
PlatformTransactionManager
是接口类型,是spring的事务管理器,里面提供了我们常用的操作事务的方法,不同的Dao层技术则有不同的实现类,例如:Dao层技术是jdbc或mybatis时:org.springframework.jdbc.datasource.DataSourceTransactionManager,Dao层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l53SHKRL-1654180943178)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220510225457440.png)]
-
TransactionDefinition
事务的定义信息对象,内部封装控制事务的参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N1Sitow0-1654180943178)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220510230704152.png)]
2.1 事务的隔离级别:
- ISOLATION_DEFAULT(默认事务)
- ISOLATION_READ_UNCOMMITTED(读未提交)
- ISOLATION_READ_COMMITTED(读已提交)
- ISOLATION_REPEATABLE_READ(可重复读)
- ISOLATION_SERIALIZABLE(串行化)
2.2 事务的传播行为
解决业务方法调用业务方法时之间事务统一性的问题,如A业务方法调用B业务方法,A业务和B业务事先都进行事务控制,A调B时可能会出现事务重复或者统一的行为
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f0ti2Lwl-1654180943178)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220510233217072.png)]
- TransactionStatus
接口提供的是事务具体的运行状态,内部维护在不同的时间点事务的状态信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B9MBtMT2-1654180943179)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220511083646436.png)]
TransactionStatus = TransactionDefinition + PlatformTransactionManager,后两个需要声明,前一个不需要
基于XML的声明式事务控制
Spring的声明式事务顾名思义就是采用声明的方式来处理事务。这里的声明,就是在配置文件中声明,用在spring配置文件中声明式的处理事务来代替代码式的处理事务
声明式事务处理的作用
- 事务管理不侵入开发的组件。事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果将系统层面的和业务层面的代码写在一起,耦合死了,如果想要改变事务管理策划的话,只需要在定义文件中重新配置即可(业务代码跟事务控制通过配置的方式松耦合,在代码运行时,业务逻辑又能完成对应的事务控制,就是AOP思想,具体业务是切点,事务是通知,通过配置将切点和通知进行织入)
- 在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便
Spring声明式事务控制底层就是AOP
基于XML的声明式事务控制的实现
package com.dyf.controller;
import com.dyf.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author:DYF
* @Description: Date:Created in 14:19 2022/5/11
*/
public class AccountController {
public static void main(String[] args){
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = (AccountService) app.getBean("accountService");
accountService.transfer("tom","lucy",500);
}
}
package com.dyf.dao;
/**
* @Author:DYF
* @Description: Date:Created in 9:24 2022/5/11
*/
public interface AccountDao {
public void out(String outMan,double money);
public void in(String inMan,double money);
}
package com.dyf.dao.impl;
import com.dyf.dao.AccountDao;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* @Author:DYF
* @Description: Date:Created in 9:24 2022/5/11
*/
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void out(String outMan, double money) {
jdbcTemplate.update("update account set money = money - ? where name = ?",money,outMan);
}
public void in(String inMan, double money) {
jdbcTemplate.update("update account set money = money + ? where name = ?",money,inMan);
}
}
package com.dyf.domain;
/**
* @Author:DYF
* @Description: Date:Created in 9:18 2022/5/11
*/
public class Account {
private String name;
private double money;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"name='" + name + '\'' +
", money=" + money +
'}';
}
}
package com.dyf.service.impl;
import com.dyf.dao.AccountDao;
import com.dyf.service.AccountService;
/**
* @Author:DYF
* @Description: Date:Created in 14:14 2022/5/11
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void transfer(String outMan, String inMan, double money) {
accountDao.out(outMan,money);
int i = 1 / 0;//手动制造异常
accountDao.in(inMan, money);
}
}
package com.dyf.service;
/**
* @Author:DYF
* @Description: Date:Created in 14:14 2022/5/11
*/
public interface AccountService {
public void transfer(String outMan,String inMan,double money);
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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 http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">
<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.user}"/>
<property name="password" value="${jdbc.pwd}"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="accountDao" class="com.dyf.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<!--目标对象,内部的方法就是切点-->
<bean id="accountService" class="com.dyf.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--通过dataSource的connection控制事务-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--通知 事务通知-->
<!--1.引入tx命名空间-->
<!--2.配置事务通知-->
<!--平台事务管理器,通过配置告诉springDao层使用了什么技术-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--设置事务的属性信息-->
<tx:attributes>
<!--哪些方法被增强-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置织入-->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.dyf.service.impl.*.*(..))"/>
</aop:config>
<context:property-placeholder location="classpath:jdbc.properties"/>
</beans>
平台事务管理器的实现依据Dao的技术
<tx:method>
代表切点方法的事务参数的配置,如:
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
- name:切点方法名称
- isolation:事务的隔离级别
- propogation:事务的传播行为
- timeout:超时时间
- read-only:是否只读
基于注解的声明式事务控制
package com.dyf.controller;
import com.dyf.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author:DYF
* @Description: Date:Created in 14:19 2022/5/11
*/
public class AccountController {
public static void main(String[] args){
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = (AccountService) app.getBean("accountService");
accountService.transfer("tom","lucy",500);
}
}
package com.dyf.service;
/**
* @Author:DYF
* @Description: Date:Created in 14:14 2022/5/11
*/
public interface AccountService {
public void transfer(String outMan, String inMan, double money);
}
package com.dyf.service.impl;
import com.dyf.dao.AccountDao;
import com.dyf.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
/**
* @Author:DYF
* @Description: Date:Created in 14:14 2022/5/11
*/
@Service("accountService")
@Transactional(isolation = Isolation.REPEATABLE_READ) //当前这个类下的所有方法都用这个指定事务控制的参数
public class AccountServiceImpl implements AccountService {
@Autowired
@Qualifier("accountDao")
private AccountDao accountDao;
@Transactional(isolation = Isolation.DEFAULT)//当类上和方法上的都有@Tranactional注解时,就近选择
public void transfer(String outMan, String inMan, double money) {
accountDao.out(outMan,money);
int i = 1 / 0;//手动制造异常
accountDao.in(inMan, money);
}
}
package com.dyf.dao;
/**
* @Author:DYF
* @Description: Date:Created in 9:24 2022/5/11
*/
public interface AccountDao {
public void out(String outMan, double money);
public void in(String inMan, double money);
}
package com.dyf.dao.impl;
import com.dyf.dao.AccountDao;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
/**
* @Author:DYF
* @Description: Date:Created in 9:24 2022/5/11
*/
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Resource(name="jdbcTemplate")
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void out(String outMan, double money) {
jdbcTemplate.update("update account set money = money - ? where name = ?",money,outMan);
}
public void in(String inMan, double money) {
jdbcTemplate.update("update account set money = money + ? where name = ?",money,inMan);
}
}
package com.dyf.domain;
/**
* @Author:DYF
* @Description: Date:Created in 9:18 2022/5/11
*/
public class Account {
private String name;
private double money;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"name='" + name + '\'' +
", money=" + money +
'}';
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<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.user}"/>
<property name="password" value="${jdbc.pwd}"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--通过dataSource的connection控制事务-->
<property name="dataSource" ref="dataSource"/>
</bean>
<context:property-placeholder location="classpath:jdbc.properties"/>
<context:component-scan base-package="com.dyf"/>
<!--事务的注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
- 使用@Transactional在需要进行事务控制的类或方法上修饰,注解可用的属性同XML配置方式,如:隔离级别,传播行为等
- 注解使用在类上,该类下的所有方法都是用同一台注解参数配置
- 使用在方法上,不同的方法可以采用不同的事务参数配置
- XML配置文件中要开启事务的注解驱动
<tx:annotation-driven/>
Mybatis
原生jdbc开发存在的问题如下:
- 数据库连接创建、释放资源造成系统资源浪费从而影响系统性能
- sql语句在代码中硬编码,造成代码不易维护,实际应用sql变换的可能较大,sql变动需要改变java代码
- 查询操作时,需要手动将结果集中的数据手动封装到实体中。插入操作时,需要手动将实体的数据设置到sql语句的占位符位置
解决方案:
- 使用数据库连接池初始化连接资源
- 将sql语句抽取到xml配置文件中
- 使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TEX6O2FR-1654180943179)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220512114044376.png)]
快速入门
MyBatis开发步骤:
- 添加MyBatis的坐标
- 创建数据表
- 编写数据表对应的实体类
- 编写映射文件xxxMapper.xml(主要写的是sql语句)
- 编写核心文件SqlMapConfig.xml(主要是MyBatis框架的核心配置)
- 编写测试类
<!--添加坐标-->
<!--myBatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
/**
数据表对应的实体类
*/
package com.dyf.domain;
/**
* @Author:DYF
* @Description: Date:Created in 12:16 2022/5/12
*/
public class User {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
<!--映射文件-->
<?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="userMapper">
<select id="findAll" resultType="com.dyf.domain.User">
select * from user
</select>
</mapper>
<!--核心文件-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置数据源环境-->
<environments default="development">
<environment id="development">
<transactionManager type="jdbc"></transactionManager>
<dataSource type="pooled">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--加载映射文件-->
<mappers>
<mapper resource="com/dyf/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
/**
核心文件
*/
package com.dyf.test;
import com.dyf.domain.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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 13:59 2022/5/30
*/
public class MyBatisTest {
@Test
public void test1() throws IOException {
//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作,参数命名空间+id
List<User> userList = sqlSession.selectList("findAll");
//打印数据
System.out.println(userList);
//释放资源
sqlSession.close();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-874nHgYK-1654180943180)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220530143800353.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SUXviug1-1654180943180)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220530155458146.png)]
<insert id="save" parameterType="com.dyf.domain.User">
insert into user values(#{id},#{name},#{password})
<!--这里是实体的属性名-->
</insert>
@Test
public void test2() throws IOException {
User user = new User();
user.setId(0); //如果id是0,主键依然自增
user.setName("tom");
user.setPassword("abc");
System.out.println(user);
InputStream resourceStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.insert("userMapper.save",user); //mybatis事务默认不提交
//更、删、改提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
修改数据
<update id="update" parameterType="com.dyf.domain.User">
update user set username = #{name}, password = #{password} where id = #{id}
</update>
@Test
public void test3() throws IOException {
User user = new User();
user.setId(2);
user.setName("lucy");
user.setPassword("abc");
//1.获取核心配置文件
InputStream resourceInputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceInputStream);
//3.获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//4.进行操作
int update = sqlSession.update("userMapper.update", user);
System.out.println(update);
//4.1提交事务
sqlSession.commit();
//5.释放资源
sqlSession.close();
}
删除数据
<delete id="delete" parameterType="java.lang.Integer">
delete from user where id = #{id}
<!--只传递一个参数并且是简单类型时,#{}中可以任意起名,但是需要可读性好-->
</delete>
@Test
public void test4() throws IOException {
InputStream resourceInputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceInputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
int update = sqlSession.update("userMapper.delete", 11); //返回值中0是没有数据可以删除,1是有数据可以删除
System.out.println(update);
sqlSession.commit();
sqlSession.close();
}
MyBatis的核心配置文件概述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HRe6lzrU-1654180943180)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531084935327.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ikZwqdRI-1654180943181)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531085936018.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xY3js4Hd-1654180943181)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531090823793.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKp735GO-1654180943181)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531091038067.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y7MXfkTg-1654180943181)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531092444735.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vTsmVPWp-1654180943182)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531094041365.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eYR3Ik1G-1654180943182)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531094137688.png)]
MyBatis的相应API
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NXXVMbdo-1654180943182)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531100917855.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9T4xLDGF-1654180943183)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531100954527.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4vTTg0V5-1654180943183)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531101855440.png)]
MyBatis的Dao层实现
-
传统开发方式
编写UserDao接口
package com.dyf.dao; import com.dyf.domain.User; import java.io.IOException; import java.util.List; /** * @Author:DYF * @Description: Date:Created in 10:58 2022/5/31 */ public interface UserMapper { public List<User> findAll() throws IOException; }
实现接口
package com.dyf.dao.impl; import com.dyf.dao.UserMapper; import com.dyf.domain.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; /** * @Author:DYF * @Description: Date:Created in 11:06 2022/5/31 */ public class UserMapperImpl implements UserMapper { public List<User> findAll() throws IOException { InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); List<User> list = sqlSession.selectList("userMapper.findAll"); return list; } }
调用
-
代理开发方式
-
介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g4dQjoAZ-1654180943184)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531114312762.png)]
-
编写UserMapper接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xIjaeuMY-1654180943184)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531114636668.png)]
-
实现
package com.dyf.dao; import com.dyf.domain.User; import java.util.List; /** * @Author:DYF * @Description: Date:Created in 10:58 2022/5/31 */ public interface UserMapper { public List<User> findAll(); public List<User> findById(int id); }
<?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="com.dyf.dao.UserMapper"> <select id="findAll" resultType="user"> select * from user </select> <select id="findById" parameterType="int" resultType="user"> select * from user where id = #{id} </select> </mapper>
package com.dyf.serviceTest; import com.dyf.dao.UserMapper; import com.dyf.domain.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; /** * @Author:DYF * @Description: Date:Created in 11:09 2022/5/31 */ public class ServiceTest { public static void main(String[] args) throws IOException { InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = build.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> all = mapper.findAll(); System.out.println(all); List<User> list = mapper.findById(2); System.out.println(list); } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yw3Fwnkn-1654180943184)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531150756836.png)]
-
MyBatis映射文件深入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r1n2WxRc-1654180943185)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531151255261.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Il71Faz2-1654180943185)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531154337978.png)]
实现
package com.dyf.mapper;
import com.dyf.domain.User;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 15:27 2022/5/31
*/
public interface UserMapper {
public List<User> findByCondition(User user);
}
<?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="com.dyf.mapper.UserMapper">
<select id="findByCondition" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=0">
and id = #{id} <!--and 会被where标签处理 如果是where标签中没有写语句 where 会省略-->
</if>
<if test="username!=null">
and username = #{username}
</if>
<if test="password!=null">
and password = #{password}
</if>
</where>
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--加载外部文件-->
<properties resource="jdbc.properties"></properties>
<!--设置类型别名-->
<typeAliases>
<typeAlias type="com.dyf.domain.User" alias="user"/>
</typeAliases>
<!--数据源环境-->
<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>
<!--加载映射文件-->
<mappers>
<mapper resource="com/dyf/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
package com.dyf.test;
import com.dyf.domain.User;
import com.dyf.mapper.UserMapper;
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.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 15:31 2022/5/31
*/
public class MapperTest {
@Test
public void test1() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//模拟条件查询
User user = new User();
user.setId(3);
user.setUsername("zhangsan");
user.setPassword("abc");
List<User> userList = mapper.findByCondition(user);
System.out.println(userList);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s6l1dzns-1654180943186)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531160028380.png)]
//模拟数据
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
List<User> userList1 = mapper.findByIds(list);
System.out.println(userList1);
<select id="findByIds" parameterType="list" resultType="user">
select * from user
<where>
<foreach collection="list" open="id in(" item="iddd" close=")" separator=","> <!--如果传入的参数类型是List,collection的默认属性值是list-->
#{iddd}
</foreach>
</where>
</select>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YsVTHSqu-1654180943186)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531164704505.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H3CLXBEC-1654180943186)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531173121743.png)]
预处理语句中设置参数,还是从结果集中取出一个值,会用到类型处理器,简单点来说,我们在mapper映射器编写sql语句的时候,mapper映射器会自动帮我们进行JDBC(数据库字段需要的)类型和Java类型的转换。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-up9FgqXc-1654180943187)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531173855183.png)]
package com.dyf.handler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
/**
* @Author:DYF
* @Description: Date:Created in 19:38 2022/5/31
*/
public class DateTypeHandler extends BaseTypeHandler<Date> {
//将java类型转换为数据库需要的类型
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
System.out.println("参数i:" + i);
System.out.println("参数jdbcType:" + i);
preparedStatement.setLong(i,date.getTime()); //i是处理参数的位置
}
//将数据库类型转换为java类型
//s是处理参数字段名称
//resultSet 是结果集
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
System.out.println("resultSet:" + resultSet);
System.out.println("String s:" + s);
long aLong = resultSet.getLong(s);
Date date = new Date(aLong);
return date;
}
//将数据库中类型转换为java类型
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
System.out.println("resultSet:" + resultSet);
System.out.println("i : " + i);
long aLong = resultSet.getLong(i);
Date date = new Date(aLong);
return date;
}
//将数据库中类型转换为java类型
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
long aLong = callableStatement.getLong(i);
Date date = new Date(aLong);
return date;
}
}
<!--注册类型处理器-->
<typeHandlers>
<typeHandler handler="com.dyf.handler.DateTypeHandler"/>
</typeHandlers>
package com.dyf.test;
import com.dyf.domain.User;
import com.dyf.mapper.UserMapper;
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.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
/**
* @Author:DYF
* @Description: Date:Created in 18:48 2022/5/31
*/
public class MybatisTest {
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//执行保存
User user = new User();
user.setUsername("ceshi");
user.setPassword("abc");
Date date = new Date();
System.out.println(date);
user.setBirthday(date);
mapper.save(user);
sqlSession.commit();
sqlSession.close();
}
@Test
public void test2() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findById(11);
System.out.println(user);
sqlSession.commit();
sqlSession.close();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sxnmce4U-1654180943187)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220531210610244.png)]
<!--分页助手-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.5</version>
</dependency>
<!--配置插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/> <!--指定的方言,不同数据库有不同的方言-->
</plugin>
</plugins>
@Test
public void test3() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//设置分页相关参数
PageHelper.startPage(1,3);
List<User> userList = mapper.findAll();
for (User user: userList) {
System.out.println(user);
}
}
//获得与分页相关参数
PageInfo<User> pageInfo = new PageInfo<User>(userList);
System.out.println("当前页:" + pageInfo.getPageNum());
System.out.println("每页显示条数:" + pageInfo.getPageNum());
System.out.println("总条数:" + pageInfo.getTotal());
System.out.println("上一页:" + pageInfo.getPrePage());
System.out.println("下一页:" + pageInfo.getNextPage());
System.out.println("是否是第一页:" + pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:" + pageInfo.isIsLastPage());
System.out.println("获得数据:" + pageInfo.getList()); //Page{pageNum=1, pageSize=3, startRow=0, endRow=3, total=11, pages=4, reasonable=false, pageSizeZero=false}
MyBatis的多表操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XmFRKOar-1654180943187)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220601224144241.png)]
操作
package com.dyf.domain;
import java.util.Date;
/**
* @Author:DYF
* @Description: Date:Created in 21:27 2022/6/1
*/
public class Orders {
private int id;
private Date ordertime;
private double total;
//当前订单属于哪个用户
private User user;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getOrdertime() {
return ordertime;
}
public void setOrdertime(Date ordertime) {
this.ordertime = ordertime;
}
public double getTotal() {
return total;
}
public void setTotal(double total) {
this.total = total;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "Orders{" +
"id=" + id +
", ordertime=" + ordertime +
", total=" + total +
", user=" + user +
'}';
}
}
package com.dyf.domain;
/**
* @Author:DYF
* @Description: Date:Created in 21:19 2022/6/1
*/
public class User {
private int id;
private String username;
private String password;
private long birthday;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public long getBirthday() {
return birthday;
}
public void setBirthday(long birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", birthday=" + birthday +
'}';
}
}
package com.dyf.mapper;
import com.dyf.domain.Orders;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 21:30 2022/6/1
*/
public interface OrdersMapper {
public List<Orders> findAll();
}
package com.dyf.mapper;
/**
* @Author:DYF
* @Description: Date:Created in 21:19 2022/6/1
*/
public interface UserMapper {
}
<?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="com.dyf.mapper.OrdersMapper">
<resultMap id="orderMap" type="orders">
<!--
这里的type是类型,这里写orders是因为起了自定义别名,
完整应该是com.dyf.domain.Orders
-->
<!--
手动指定字段与实体属性的映射关系
column:数据库表字段名称
property:实体的属性名称
主键比较特殊使用id标签,其他的使用result标签
-->
<id column="id" property="id"></id> <!--property是type对应的属性名-->
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
<!--
第一种方式
<result column="uid" property="user.id"></result>
uid字段封装到type的user属性的内部的id属性
<result column="username" property="user.username"></result>
<result column="password" property="user.password"></result>
<result column="birthday" property="user.birthday"></result>
-->
<!--
第二种方式
property属性是type(orders)对应的user属性名称
javaType属性是user属性的类型是User(com.dyf.domain.User)
-->
<association property="user" javaType="user">
<id column="uid" property="id"></id>
<!--跟user中的属性名进行匹配-->
<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"> <!--resultMap的值与上面resultMap标签的id值对应-->
select * from orders o, user u where o.uid = u.id;
</select>
</mapper>
<?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="com.dyf.mapper.UserMapper">
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--加载外部文件-->
<properties resource="jdbc.properties"></properties>
<!--自定义别名-->
<typeAliases>
<typeAlias type="com.dyf.domain.Orders" alias="orders"/>
<typeAlias type="com.dyf.domain.User" alias="user"/>
</typeAliases>
<!--配置数据源环境-->
<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>
<!--加载映射文件-->
<mappers>
<mapper resource="com/dyf/mapper/UserMapper.xml"/>
<mapper resource="com/dyf/mapper/OrdersMapper.xml"/>
</mappers>
</configuration>
package com.dyf.test;
import com.dyf.domain.Orders;
import com.dyf.mapper.OrdersMapper;
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.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 21:46 2022/6/1
*/
public class TestMyBatis {
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
List<Orders> list = mapper.findAll();
for (Orders orders:list) {
System.out.println(orders);
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rPaOBFyV-1654180943188)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220602092637713.png)]
package com.dyf.mapper;
import com.dyf.domain.User;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 21:19 2022/6/1
*/
public interface UserMapper {
public List<User> findAll();
}
<?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="com.dyf.mapper.UserMapper">
<resultMap id="userMapper" type="user">
<id column="id" 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="ordersList" ofType="orders">
<!--封装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="userMapper">
SELECT *,o.id oid FROM USER u left JOIN orders o ON u.id = o.uid
</select>
</mapper>
@Test
public void test2() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.findAll();
for (User user:list) {
System.out.println(user);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z3QGqee6-1654180943188)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220602095425659.png)]
package com.dyf.domain;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 21:19 2022/6/1
*/
public class User {
private int id;
private String username;
private String password;
private long birthday;
//描述当前用户存在哪些订单
private List<Orders> ordersList;
//描述当前用户有哪些角色
private List<Role> roleList;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public long getBirthday() {
return birthday;
}
public void setBirthday(long birthday) {
this.birthday = birthday;
}
public List<Orders> getOrdersList() {
return ordersList;
}
public void setOrdersList(List<Orders> ordersList) {
this.ordersList = ordersList;
}
public List<Role> getRoleList() {
return roleList;
}
public void setRoleList(List<Role> roleList) {
this.roleList = roleList;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", birthday=" + birthday +
", ordersList=" + ordersList +
", roleList=" + roleList +
'}';
}
}
package com.dyf.mapper;
import com.dyf.domain.User;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 21:19 2022/6/1
*/
public interface UserMapper {
public List<User> findAll();
public List<User> findUserAndRoleAll();
}
<?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="com.dyf.mapper.UserMapper">
<resultMap id="userMapper" type="user">
<id column="id" 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="ordersList" ofType="orders">
<!--封装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="userMapper">
SELECT *,o.id oid FROM USER u left JOIN orders o ON u.id = o.uid
</select>
<resultMap id="userRoleMap" type="user">
<!--user信息-->
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></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="findUserAndRoleAll" resultMap="userRoleMap">
SELECT * FROM sys_user u LEFT JOIN sys_user_role ur ON u.id = ur.userId LEFT JOIN sys_role r ON ur.roleId = r.id;
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--加载外部文件-->
<properties resource="jdbc.properties"></properties>
<!--自定义别名-->
<typeAliases>
<typeAlias type="com.dyf.domain.Orders" alias="orders"/>
<typeAlias type="com.dyf.domain.User" alias="user"/>
<typeAlias type="com.dyf.domain.Role" alias="role"/>
</typeAliases>
<!--配置数据源环境-->
<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>
<!--加载映射文件-->
<mappers>
<mapper resource="com/dyf/mapper/UserMapper.xml"/>
<mapper resource="com/dyf/mapper/OrdersMapper.xml"/>
</mappers>
</configuration>
@Test
public void test3() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.findUserAndRoleAll();
for (User user:list) {
System.out.println(user);
}
sqlSession.close();
}
MyBatis的注解开发
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LPIvtkvx-1654180943189)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220602103555224.png)]
package com.dyf.domain;
/**
* @Author:DYF
* @Description: Date:Created in 10:49 2022/6/2
*/
public class User {
private int id;
private String username;
private String password;
private String birthday;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", birthday='" + birthday + '\'' +
'}';
}
}
package com.dyf.mapper;
import com.dyf.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 10:50 2022/6/2
*/
public interface UserMapper {
@Insert("insert into user values(#{id},#{username},#{password},#{birthday})")
public void save(User user);
@Update("update user set username = #{username},password = #{password} where id = #{id}")
public void update(User user);
@Delete("delete from user where id = #{id}")
public void delete(int id);
@Select("select * from user where id = #{id}")
public User findById(int id);
@Select("select * from user")
public List<User> findAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--加载外部文件-->
<properties resource="jdbc.properties"></properties>
<!--自定义别名-->
<typeAliases>
<typeAlias type="com.dyf.domain.User" alias="user"/>
</typeAliases>
<!--配置数据源环境-->
<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>
<!--加载映射文件-->
<!--<mappers>
<mapper resource="com/dyf/mapper/UserMapper.xml"></mapper>
</mappers>-->
<!--加载映射关系-->
<mappers>
<!--指定接口所在的包,相当于扫包-->
<package name="com.dyf.mapper"/>
</mappers>
</configuration>
package com.dyf.test;
import com.dyf.domain.User;
import com.dyf.mapper.UserMapper;
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.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 10:59 2022/6/2
*/
public class MyBatisTest {
UserMapper mapper;
SqlSession sqlSession;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = build.openSession();
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testSave(){
User user = new User();
user.setUsername("张三");
user.setPassword("123");
mapper.save(user);
}
@Test
public void testUpdate(){
User user = new User();
user.setId(15);
user.setUsername("张四");
user.setPassword("1234");
mapper.update(user);
}
@Test
public void testDelete(){
mapper.delete(15);
}
@Test
public void testFindById(){
User user = mapper.findById(2);
System.out.println(user);
}
@Test
public void testFindAll(){
List<User> userList = mapper.findAll();
for (User user : userList) {
System.out.println(user);
}
}
@After
public void testAfter(){
sqlSession.commit();
sqlSession.close();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IIVCm84k-1654180943189)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220602123318909.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NVo1m2iY-1654180943190)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220602123403545.png)]
- 一对一查询
package com.dyf.mapper;
import com.dyf.domain.Orders;
import com.dyf.domain.User;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 14:17 2022/6/2
*/
public interface OrdersMapper {
/*
第一种方式:两张表一起查
@Select("select *,o.id oid from orders o,user u where o.uid = u.id")
//代替的标签是resultMap,这里的写法相当于映射文件中一对一中的第一种写法
@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 = "user.username"),
@Result(column = "password",property = "user.password")
})
public List<Orders> findAll();
*/
//第二种方式:分表查,先查出orders表中的信息,然后将orders表中的uid与user表中
//的id相等查出user表中的信息
@Select("select * from orders")
//代替的标签是resultMap
@Results({
@Result(column = "id",property = "id"),
@Result(column = "ordertime",property = "ordertime"),
@Result(column = "total",property = "total"),
@Result(
javaType = User.class,//要封装的实体的数据类型
property = "user",//要封装的属性名称
column = "uid",//根据哪个字段查询user表数据
/**
*
* @Select("select * from user where id = #{id}")
* public User findById(int id);
* */
//select 属性 代表查询哪个接口的方法获得数据
//one 和@one是一对一的查询语法
one = @One(select = "com.dyf.mapper.UserMapper.findById")
//从orders表中查询出orders中的信息,调用指定的方法根据column指定的列名从user表中查询出
//相关信息,将信息封装到javaType指定的实体中,最后将实体封装到property指定的属性中
)
})
public List<Orders> findAll();
}
package com.dyf.mapper;
import com.dyf.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 10:50 2022/6/2
*/
public interface UserMapper {
@Insert("insert into user values(#{id},#{username},#{password},#{birthday})")
public void save(User user);
@Update("update user set username = #{username},password = #{password} where id = #{id}")
public void update(User user);
@Delete("delete from user where id = #{id}")
public void delete(int id);
@Select("select * from user where id = #{id}")
public User findById(int id);
@Select("select * from user")
public List<User> findAll();
}
package com.dyf.test;
import com.dyf.domain.Orders;
import com.dyf.mapper.OrdersMapper;
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.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 10:59 2022/6/2
*/
public class MyBatisTest1 {
OrdersMapper mapper;
SqlSession sqlSession;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = build.openSession();
mapper = sqlSession.getMapper(OrdersMapper.class);
}
@Test
public void testFindAll(){
List<Orders> ordersList = mapper.findAll();
for (Orders orders : ordersList) {
System.out.println(orders);
}
}
@After
public void testAfter(){
sqlSession.commit();
sqlSession.close();
}
}
- 一对多查询
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tS7XtfjH-1654180943190)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220602150618161.png)]
package com.dyf.mapper;
import com.dyf.domain.Orders;
import com.dyf.domain.User;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 14:17 2022/6/2
*/
public interface OrdersMapper {
@Select("select * from orders where uid = #{id}")
public List<Orders> findByUid(int id);
}
package com.dyf.mapper;
import com.dyf.domain.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 10:50 2022/6/2
*/
public interface UserMapper {
@Insert("insert into user values(#{id},#{username},#{password},#{birthday})")
public void save(User user);
@Update("update user set username = #{username},password = #{password} where id = #{id}")
public void update(User user);
@Delete("delete from user where id = #{id}")
public void delete(int id);
@Select("select * from user where id = #{id}")
public User findById(int id);
@Select("select * from user")
public List<User> findAll();
@Select("select * from user")
@Results({
@Result(id = true,column = "id",property = "id"), //id=true表示是个主键
@Result(column = "username",property = "username"),
@Result(column = "password",property = "password"),
@Result(
javaType = List.class, //这里封装数据的类型是List,不是Orders
//javaType = Orders.class,
column = "id",
property = "ordersList",
many = @Many(select = "com.dyf.mapper.OrdersMapper.findByUid")
)
})
public List<User> findUserAndOrdersAll();
}
package com.dyf.test;
import com.dyf.domain.User;
import com.dyf.mapper.UserMapper;
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.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 10:59 2022/6/2
*/
public class MyBatisTest2 {
UserMapper mapper;
SqlSession sqlSession;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = build.openSession();
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testFindAll(){
List<User> userList = mapper.findUserAndOrdersAll();
for (User user : userList) {
System.out.println(user);
}
}
@After
public void testAfter(){
sqlSession.commit();
sqlSession.close();
}
}
- 多对多查询
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zy7us39L-1654180943191)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220602154456332.png)]
package com.dyf.domain;
import java.util.Date;
/**
* @Author:DYF
* @Description: Date:Created in 21:27 2022/6/1
*/
public class Orders {
private int id;
private Date ordertime;
private double total;
//当前订单属于哪个用户
private User user;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getOrdertime() {
return ordertime;
}
public void setOrdertime(Date ordertime) {
this.ordertime = ordertime;
}
public double getTotal() {
return total;
}
public void setTotal(double total) {
this.total = total;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "Orders{" +
"id=" + id +
", ordertime=" + ordertime +
", total=" + total +
", user=" + user +
'}';
}
}
package com.dyf.domain;
/**
* @Author:DYF
* @Description: Date:Created in 9:56 2022/6/2
*/
public class Role {
private int id;
private String roleName;
private String roleDesc;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleDesc() {
return roleDesc;
}
public void setRoleDesc(String roleDesc) {
this.roleDesc = roleDesc;
}
@Override
public String toString() {
return "Role{" +
"id=" + id +
", roleName='" + roleName + '\'' +
", roleDesc='" + roleDesc + '\'' +
'}';
}
}
package com.dyf.domain;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 10:49 2022/6/2
*/
public class User {
private int id;
private String username;
private String password;
private String birthday;
//描述当前用户具有的订单
private List<Orders> ordersList;
//描述当前用户具有的角色
private List<Role> roleList;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public List<Orders> getOrdersList() {
return ordersList;
}
public void setOrdersList(List<Orders> ordersList) {
this.ordersList = ordersList;
}
public List<Role> getRoleList() {
return roleList;
}
public void setRoleList(List<Role> roleList) {
this.roleList = roleList;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", birthday='" + birthday + '\'' +
", ordersList=" + ordersList +
", roleList=" + roleList +
'}';
}
}
package com.dyf.mapper;
import com.dyf.domain.Orders;
import com.dyf.domain.User;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 14:17 2022/6/2
*/
public interface OrdersMapper {
/*
第一种方式:两张表一起查
@Select("select *,o.id oid from orders o,user u where o.uid = u.id")
//代替的标签是resultMap,这里的写法相当于映射文件中一对一中的第一种写法
@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 = "user.username"),
@Result(column = "password",property = "user.password")
})
public List<Orders> findAll();
*/
//第二种方式:分表查,先查出orders表中的信息,然后将orders表中的uid与user表中
//的id相等查出user表中的信息
@Select("select * from orders")
//代替的标签是resultMap
@Results({
@Result(column = "id",property = "id"),
@Result(column = "ordertime",property = "ordertime"),
@Result(column = "total",property = "total"),
@Result(
javaType = User.class,//要封装的实体的数据类型
property = "user",//要封装的属性名称
column = "uid",//根据哪个字段查询user表数据
/**
*
* @Select("select * from user where id = #{id}")
* public User findById(int id);
* */
//select 属性 代表查询哪个接口的方法获得数据
//one 和@one是一对一的查询语法
one = @One(select = "com.dyf.mapper.UserMapper.findById")
//从orders表中查询出orders中的信息,调用指定的方法根据column指定的列名从user表中查询出
//相关信息,将信息封装到javaType指定的实体中,最后将实体封装到property指定的属性中
)
})
public List<Orders> findAll();
@Select("select * from orders where uid = #{id}")
public List<Orders> findByUid(int id);
}
package com.dyf.mapper;
import com.dyf.domain.Role;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 15:58 2022/6/2
*/
public interface RoleMapper {
@Select("select * from sys_user_role ur,sys_role r where ur.roleId = r.id and ur.userId = #{userId}")
public List<Role> findRoleById(int userId);
}
package com.dyf.mapper;
import com.dyf.domain.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 10:50 2022/6/2
*/
public interface UserMapper {
@Insert("insert into user values(#{id},#{username},#{password},#{birthday})")
public void save(User user);
@Update("update user set username = #{username},password = #{password} where id = #{id}")
public void update(User user);
@Delete("delete from user where id = #{id}")
public void delete(int id);
@Select("select * from user where id = #{id}")
public User findById(int id);
@Select("select * from user")
public List<User> findAll();
@Select("select * from user")
@Results({
@Result(id = true,column = "id",property = "id"), //id=true表示是个主键
@Result(column = "username",property = "username"),
@Result(column = "password",property = "password"),
@Result(
javaType = List.class, //这里封装数据的类型是List,不是Orders
//javaType = Orders.class,
column = "id",
property = "ordersList",
many = @Many(select = "com.dyf.mapper.OrdersMapper.findByUid")
)
})
public List<User> findUserAndOrdersAll();
@Select("select * from sys_user")
@Results({
@Result(id = true,column = "id",property = "id"),
@Result(column = "username",property = "username"),
@Result(column = "password",property = "password"),
@Result(
javaType = List.class,
column = "id",
property = "roleList",
many = @Many(select = "com.dyf.mapper.RoleMapper.findRoleById")
)
})
public List<User> findUserAndRoleAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--加载外部文件-->
<properties resource="jdbc.properties"></properties>
<!--自定义别名-->
<typeAliases>
<typeAlias type="com.dyf.domain.User" alias="user"/>
</typeAliases>
<!--配置数据源环境-->
<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>
<!--加载映射文件-->
<!--<mappers>
<mapper resource="com/dyf/mapper/UserMapper.xml"></mapper>
</mappers>-->
<!--加载映射关系-->
<mappers>
<!--指定接口所在的包,相当于扫包-->
<package name="com.dyf.mapper"/>
</mappers>
</configuration>
SSM框架整合
原始整合方式见itheima_ssm
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M5cfBVPt-1654180943191)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220602210953401.png)]
package com.dyf.controller;
import com.dyf.domain.Account;
import com.dyf.service.AccountService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 16:42 2022/6/2
*/
@Controller
@RequestMapping("/account")
public class AccountController {
@Autowired
private AccountService accountService;
//保存
@RequestMapping(value = "/save",produces = "text/html;charset=utf-8") //指定响应数据的数据类型,也可以指定编码
@ResponseBody
public String save(Account account){
accountService.save(account);
return "保存成功";
}
//查询
@RequestMapping(value = "/findAll",produces = "text/html;charset=utf-8")
@ResponseBody
public String findAll(){
List<Account> accountList = accountService.findAll();
ObjectMapper objectMapper = new ObjectMapper();
String s = null;
try {
s = objectMapper.writeValueAsString(accountList);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return s;
}
}
package com.dyf.domain;
/**
* @Author:DYF
* @Description: Date:Created in 16:36 2022/6/2
*/
public class Account {
private int id;
private String name;
private double money;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
package com.dyf.mapper;
import com.dyf.domain.Account;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 16:37 2022/6/2
*/
public interface AccountMapper {
public void save(Account account);
public List<Account> findAll();
}
package com.dyf.service;
import com.dyf.domain.Account;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 16:40 2022/6/2
*/
public interface AccountService {
public void save(Account account);
public List<Account> findAll();
}
package com.dyf.service.impl;
import com.dyf.domain.Account;
import com.dyf.mapper.AccountMapper;
import com.dyf.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author:DYF
* @Description: Date:Created in 16:41 2022/6/2
*/
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
public void save(Account account){
accountMapper.save(account);
}
@Override
public List<Account> findAll() {
return accountMapper.findAll();
}
}
<?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="com.dyf.mapper.AccountMapper">
<!--插入数据-->
<insert id="save" parameterType="Account">
insert into account1 values(#{id},#{name},#{money})
</insert>
<!--查找数据-->
<select id="findAll" resultType="account">
select * from account1
</select>
</mapper>
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--组件扫描-->
<context:component-scan base-package="com.dyf">
<!--排除controller的扫描-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--加载外部文件-->
<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-->
<!--SqlSessionFactoryBean是spring提供的,mybatis-spring包提供的SqlSessionFactory的实现类-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!--加载mybatis核心文件-->
<property name="configLocation" value="classpath:sqlMapConfig-spring.xml"></property>
</bean>
<!--加载(扫描)mapper所在包,为mapper创建实现类,把实现放入容器中-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dyf.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:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.dyf.service.impl.*.*(..))"></aop:advisor>
</aop:config>
</beans>
jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username = root
jdbc.password = 123456
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--组件扫描-->
<context:component-scan base-package="com.dyf.controller"></context:component-scan>
<!--注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--内部资源解析器-->
<bean id="resolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--<property name="prefix" value=""></property>-->
<property name="suffix" value=".html"></property>
</bean>
<!--开放静态资源访问权限-->
<mvc:default-servlet-handler/>
</beans>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--定义别名-->
<typeAliases>
<!--<typeAlias type="com.dyf.domain.Account" alias="account"></typeAlias>-->
<package name="com.dyf.domain"/>
<!--
domain包下的实体的别名是,例子:com.dyf.domain.Account,
别名就是Account或account
-->
</typeAliases>
</configuration>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>保存账户信息</title>
</head>
<body>
<h1>保存账户信息表单</h1>
<form action="" method="post">
用户名称:<input type="text" name="name" id="username"/><br/>
账户金额:<input type="text" name="money" id="money"/><br/>
<input type="button" value="保存" onclick="save()"/><br/>
</form>
<button onclick="findAll()">展示账户数据列表</button>
</body>
</html>
<script src="js/jquery-1.4.2.min.js"></script>
<script>
function save(){
let name = $('#username').val()
let money = $('#money').val()
$.ajax({
type: "POST",
url: "account/save",
data: {name,money},
success: function(msg){
alert(msg);
},
error:function (error){
alert(error)
}
});
}
function findAll(){
location.href = "accountList.html"
}
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>编写列表页面</title>
</head>
<body>
<h1>展示账户数据列表</h1>
<table border="1" id="accountList">
<tr>
<th>账户id</th>
<th>账户名称</th>
<th>账户金额</th>
</tr>
</table>
</body>
</html>
<script src="js/jquery-1.4.2.min.js"></script>
<script>
$(function(){
$.post('account/findAll',function(date){
console.log(date)
let accountList = ''
console.log(date.length)
for (let i = 0; i < date.length; i++){
accountList += '<tr> <th>'+ date[i].id +'</th> <th>'+date[i].name+'</th> <th>'+date[i].money+'</th> </tr>'
}
$("#accountList").append(accountList)
},"json")
})
</script>
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--配置监听器-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置字符过滤器-->
<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>
</web-app>
“dataSource”>
<!--加载(扫描)mapper所在包,为mapper创建实现类,把实现放入容器中-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dyf.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:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.dyf.service.impl.*.*(..))"></aop:advisor>
</aop:config>
```
jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username = root
jdbc.password = 123456
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--组件扫描-->
<context:component-scan base-package="com.dyf.controller"></context:component-scan>
<!--注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--内部资源解析器-->
<bean id="resolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--<property name="prefix" value=""></property>-->
<property name="suffix" value=".html"></property>
</bean>
<!--开放静态资源访问权限-->
<mvc:default-servlet-handler/>
</beans>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--定义别名-->
<typeAliases>
<!--<typeAlias type="com.dyf.domain.Account" alias="account"></typeAlias>-->
<package name="com.dyf.domain"/>
<!--
domain包下的实体的别名是,例子:com.dyf.domain.Account,
别名就是Account或account
-->
</typeAliases>
</configuration>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>保存账户信息</title>
</head>
<body>
<h1>保存账户信息表单</h1>
<form action="" method="post">
用户名称:<input type="text" name="name" id="username"/><br/>
账户金额:<input type="text" name="money" id="money"/><br/>
<input type="button" value="保存" onclick="save()"/><br/>
</form>
<button onclick="findAll()">展示账户数据列表</button>
</body>
</html>
<script src="js/jquery-1.4.2.min.js"></script>
<script>
function save(){
let name = $('#username').val()
let money = $('#money').val()
$.ajax({
type: "POST",
url: "account/save",
data: {name,money},
success: function(msg){
alert(msg);
},
error:function (error){
alert(error)
}
});
}
function findAll(){
location.href = "accountList.html"
}
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>编写列表页面</title>
</head>
<body>
<h1>展示账户数据列表</h1>
<table border="1" id="accountList">
<tr>
<th>账户id</th>
<th>账户名称</th>
<th>账户金额</th>
</tr>
</table>
</body>
</html>
<script src="js/jquery-1.4.2.min.js"></script>
<script>
$(function(){
$.post('account/findAll',function(date){
console.log(date)
let accountList = ''
console.log(date.length)
for (let i = 0; i < date.length; i++){
accountList += '<tr> <th>'+ date[i].id +'</th> <th>'+date[i].name+'</th> <th>'+date[i].money+'</th> </tr>'
}
$("#accountList").append(accountList)
},"json")
})
</script>
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--配置监听器-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置字符过滤器-->
<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>
</web-app>