01_SpringloC&DI
1.Spring概述
1.1Spring是什么(理解)
Spring是分层的Java SE/EE应用 full-stack 轻量级开源框架,以 loC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核。
提供了展现层SpringMVC和持久层Spring JDBCTemplate以及业务层事务管理众多的企业级应用技术,还能整合开源时间众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架
1.2Spring发展历程(了解)
Rod Johnson(Spring 之父)
Expert One-to-One J2EE Design and Development(2002)
阐述了J2EE使用EJB开发设计的优点及解决方案
Expert One-to-One J2EE Development without EJB(2004)
阐述了J2EE开发不使用EJB的解决方式(Spring 雏形)
2017年9月份发布了Spring的最新版本Spring5.0 通用版(GA)
1.3Spring的优势(理解)
1)方便解耦,简化开发
通过Spring提供的loC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度耦合。用户也不必再为单例模式类、属性文件解析等这些的需求编写代码,可以更专注于上层的应用。
2)AOP编程的支持
通过Spring的AOP功能,方便进行面向切面编程,许多不容易用传统OOP实现的功能可以通过AOP轻松实现。
3)声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量。
4)方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
5)方便集成各种优秀框架
Spring对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的支持。
6)降低JavaEE API的使用难度
Spring对javaEE API(如JDBC、javaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。
7)Java源码是经典学习范例
Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对java设计模式灵活运用以及对java技术的高深造诣。它的源代码无疑是java技术的最佳实践的范例。
1.4Spring的体系结构(了解)
2.Spring快速入门
2.1Spring程序开发步骤
导入Spring开发的基本包坐标
编写Dao接口和实现类
创建Spring核心配置文件
在Spring配置文件中配置UserDaoImpl
使用Spring的API获得Bean实例
2.2导入Spring开发的基本包坐标
<properties>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
</dependencies>
2.3 编写Dao接口和实现类
public interface UserDao{
public void save();
}
public class UserDaoImpl implements UserDao{
@Override
public void save(){
sout("UserDao save method running….")
}
}
2.4创建Spring核心配置文件
在类路径下(resource)创建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"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
2.5在Spring配置文件中配置UserDaoImpl
<?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.itheima.dao.impl.UserDaoImpl"></bean>
</beans>
2.6使用Spring的API获得Bean实例
@Test
public void test1{
Application applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao=(UserDao)applicationContext.getBean("userDao");
userDao.save();
}
2.7知识要点
Spring的开发步骤
导入坐标
创建Bean
创建applicationContext.xml
在配置文件中进行配置
创建ApplicationContext对象getBean
3. Spring配置文件
3.1 Bean标签基本配置
用于配置对象交由Spring来创建。
默认情况下它调用的是类中无参构造函数,如果没有无参构造函数则不能创建成功。
基本属性:
-
id:Bean实例在Spring容器中的唯一标识
-
class:Bean的全限定名称
3.2 Bean标签范围配置
scope:指对象的作用范围,取值如下:
| singleton | 默认值,单例的 |
| prototype | 多例的 |
| request | web项目中,Spring创建一个Bean的对象,将对象存入到request域中 |
| session | web项目中,Spring创建一个Bean的对象,将对象存入到session域中 |
| global session | web项目中,应用在portlet环境,如果没有porlet环境那么globalSession相当于session |
当scope的取值为singleton时
Bean的实例化个数:一个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例(applicationContext)
Bean的生命周期:
-
对象创建:当应用加载,创建容器时,对象就被创建了
-
对象运行:只要容器在,对象一直活着
-
对象销毁:当应用卸载,销毁容器时,对象就被销毁了
当scope的取值为prototype时
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
-
对象创建:当使用对象时,创建新的对象实例
-
对象运行:只要对象在使用中,就一直活着
-
对象销毁:当对象长时间不用时,被java的垃圾回收器回收了
3.3 Bean生命周期配置(当生成和销毁时会执行相应的方法)
-
init-method:指定类中的初始化方法名称
-
destory-method:指定类中销毁方法名称
3.4 Bean实例化三种方式
-
无参构造方法实例化
-
工厂静态方法实例化
-
工厂实例方法实例化
使用无参构造方法实例化
它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败
public interface UserDao{
public void save();
}
public class UserServiceImpl implements UserService {
public void save() {
ApplicationContext app= new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao1 = (UserDao) app.getBean("userDao");
}
} <bean id="userDao" class="com.itheima.impl.UserDaoImpl"></bean> public class SpringText {
@Test
//测试scope
public void text1(){
ApplicationContext app= new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao1 = (UserDao) app.getBean("userDao");
System.out.println(userDao1);
}
}
工厂静态方法实例化
工厂的静态方法返回Bean实例
public class staticFactoy{
public static UserDao createUserDao(){
return new UserDaoImpl();
}
} <bean id="userDao" class="com.itheima.factory.staticFactory" factory-method="createUserDao"></bean>
工厂实例方法实例化
工厂的非静态方法返回Bean实例(先创建出工厂类,然后再工厂方法)
public class DynamicFactoy(){
public UserDao createUserDao(){
return new UserDaoImpl();
}
} <bean id="factory" class="com.itheima.factory.DynamicFactoy"></bean>
<bean id="userDao" factory-bean="factory" factory-method="createUserDao"></bean>
3.5Bean的依赖注入入门
创建UserService,UserService内部在调用UserDao的save()方法
public class UserServiceImpl implements UserService{
@Override
public void save(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao=(UserDao) applicationContext.getBean("userDao");
}
}
将UserServiceImpl的创建权交给Spring
<bean id="userService" class="com.itheima.impl.UserServiceImpl"></bean>
从Spring容器中获得UserService进行操作
ApplicationContext applicationContext=new ClassPathXmlApplication("applicationContext.xml");
UserService userService=(UserService)applicationContext.getBean("userService");
userService.save();
3.6 Bean的依赖注入分析
目前UserService实例和UserDao实例都存在与Spring容器中,当前的做法是在容器外部获得UserService实例和UserDao实例,然后在程序中进行结合。
因为UserService和UserDao都在Spring容器中,而最终程序直接使用的是UserService,所以可以在Spring容器中,将UserDao设置到UserService内部。
3.7Bean的依赖注入概念
依赖注入(Dependency Injection):它是Spring框架核心IOC的具体实现。
在编写程序时,通过控制反转,把对象的创建权交给了Spring,但是代码中不可能出现没有依赖的情况。
IOC解耦只是降低他们的依赖关系,但不会消除。例如:业务层依然会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用Spring之后,就让Spring来维护了。
简单来说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
3.8 Bean的依赖注入方式
怎么将UserDao注入到UserService内部呢?
-
构造方法
-
set方法
set方法
在UserServiceImpl中添加setUserDao方法
public class UserServiceImpl implements UserService{
private UserDao userDao;
public void setUserDao(UserDao userDao){
this.userDao=userDao;
}
@Override
public void save(){
userDao.save();
}
}
配置Spring容器调用set方法进行注入
<bean id="userDao" class="com.itheima.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.itheima.impl.UServiceImpl">
<property name="userDao" ref="userDao"></property>
<!–name的userDao是setUserDao后面小写userDao 而ref的userDao是上面配置的userDao–>
</bean>
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) app.getBean("userService");
userService.save();
}
}
第二种Set依赖注入
p命名空间本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下:
首先需要引入命名空间:
其次,需要修改注入方式
<bean id="userService" class="com.itheima.Service.impl.UserServiceImpl" p:userDao-ref="userDao"></bean>
构造方法注入
创建有参构造
public class UserServceImpl implements UserService{
private UserDao userDao;
public UserServceImpl(){}
public UserServceImpl(UserDao userDao){
this.userDao=userDao;
}
@Override
public void save(){
userDao.save();
}
}
配置Spring容器调用有参构造时进行注入
<bean id="userDao" class="com.itheima.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.itheima.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
3.9Bean的依赖数据类型
上面的操作,都是注入引用Bean,除了对象的引用可以注入,普通数据类型、集合等可以在容器中进行注入。
注入数据的三种数据类型
-
普通数据类型
-
引用数据类型
-
集合数据类型
其中引用数据类型,此处就不在多说,之前都是对UserDao对象的引用进行注入,下面将以set方法注入为例,演示普通数据类型和集合数据类型的注入。
普通数据类型的注入
public class UserDaoImpl implements UserDao{
private String userName;
private int age;
public void setUserName(String userName){
this.userName=userName;
}
public void setAge(int age){
this.age=age;
}
public void save(){
sout(userName+"===="+age);
sout("save running….");
}
} <bean id="userDao" class="com.itheima.impl.UserDaoImpl">
<property name="userName" value="张三"></property>
<property name="age" value="15"></property>
</bean>
集合数据类型(List<String>)的注入
public class UserDaoImpl implements UserDao{
private List<String> strList;
public void setStrList(List<String> strList){
this.strList=strList;
}
public void save(){
sout(strList);
sout("UserDao save method running…");
}
} <bean id="userDao" class="com.itheima.dao.UserDaoImpl">
<property name="strList">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
</bean>
集合数据类型(List<User>)的注入
public class UserDaoImpl implements UserDao{
private List<User> userList;
public void setUserList(List<User> userList){
this.userList=userList;
}
public void save(){
sout(userList);
sout("UserDao save method running…");
}
} <bean id="u1" class="com.itheima.domain.User">
<property name="name" value="安安"/>
<property name="age" value="11"/>
</bean>
<bean id="u2" class="com.itheima.domain.User"/>
<bean id="userDao" class="com.itheima.impl.UserDaoImpl">
<property name="userList">
<List>
<bean class="com.itheima.domain.User">
<property name="name" value="qq"/>
<property name="age" value="22"/>
</bean>
<!–可在里面配置对象 可空可不空–>
<bean class="com.itheima.domain.User"/>
<ref bean="u1"/>
<ref bean="u2"/>
</List>
</property>
</bean>
集合数据类型(Map<String,User>)的注入
public class UserDaoImpl implements UserDao{
private Map<String,User> userMap;
public void setUserMap(Map<String,User> userMap){
this.userMap=userMap;
}
public void save(){
sout(userMap);
sout("UserDao save method running…");
}
} <bean id="u1" class="com.itheima.domain.User">
<property name="name" value="tom"/>
<property name="addr" value="beijing"/>
</bean>
<bean id="u2" class="com.itheima.domain.User">
<property name="name" value="lucy"/>
<property name="addr" value="tianjing"/>
</bean>
<bean id="userDao" class="com.itheima.impl.UserDaoImpl">
<property name="userMap">
<map>
<entry key="user1" value-ref="u1"/>
<entry key="user2" value-ref="u2"/>
</map>
</property>
</bean>
集合数据类型(Properties)的注入
public class UserDaoImpl implements UserDao{
private Properties properties;
public void setProperties(Properties properties){
this.properties=properties;
}
public void save(){
sout(properties);
sout("UserDao save method running…");
}
} <bean id="userDao" class="com.itheima.impl.UserDaoImpl">
<property name="properties">
<props>
<prop key="p1">aaa</prop>
<prop key="p2">bbb</prop>
<prop key="p3">ccc</prop>
</props>
</property>
</bean>
3.9 引入其他配置文件(分模块开发)
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载
<import resourse="applicationContext-xxx.xml"/>
3.10 总结
<bean>标签
-
id属性:在容器中Bean实例的唯一标识,不允许重复
-
class属性:要实例化的Bean的全限定名
-
scope属性:Bean的作用范围,常用是Singleton(默认)和prototype
<property>标签:属性注入
-
name属性:属性名称
-
value属性:注入的普通属性值
-
ref属性:注入的对象引用值
<list>标签
< map > 标签
<properties>标签
<constructor-arg>标签
<import>标签:导入其他的Spring的分文件
4 Spring相关API
4.1ApplicationContext的继承体系
applicationContext:接口类型,代表应用上下文,可以通过其实例获得Spring容器中的Bean对象
ClassPathXmlApplicationContext
它是从类的根路径下加载配置文件(根路径下的文件名) 推荐使用这种
(ApplicationContext application Context=new ClassPathXmlApplicationContext("applicationContext.xml"));
FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
AnnotationConfigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建spring容器。它用来读取注解。
4.3 getBean()方法使用
public Object getBean(String name)throws BeansException{
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
public <T> T getBean(Class<T> requiredType)throws BeansException{
assertBeanFactoryActive();
return getBeanFactory().getBean(requiredType);
}
其中,当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错。
第一种
代码如下:
UserService userService=(UserService)applicationContext.getBean(userService);
区别:
1)它是调用xml的id值进行声明对象,使用范围最好是多例模式下,因为单例模式另一种方法会报错
2)声明对象时需要强转对象,默认为Object
第二种
代码如下:
UserService userService=ApplicationContext.getBean(UserService.class);
区别:
1)它是直接调用类名进行声明对象,但多例模式下多个同类型的Bean时,会报错
2)声明对象时不需要强转对象
4.4 知识要点
Spring的重点API
ApplicationContext app=new ClasspathXmlApplicationContext("xml文件");
app.getBean("id");
app.getBean(Class)
02_SpringloC和DI注解开发
1. Spring配置数据源
1.1 数据源(连接池)的作用
-
数据源(连接池)是提高程序性能出现的
-
事先实例化数据源,初始化部分连接资源
-
使用连接资源时从数据源中获取
-
使用完毕后将连接资源归还数据源
常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid等
1.2 数据源的开发步骤
导入数据源的坐标和数据库驱动坐标
创建数据源对象
设置数据源的基本连接数据
使用数据源获取连接资源和归还连接资源
1.3 数据源的手动创建
导入C3P0和Druid的坐标 pom.xml
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.4</version>
</dependency>
导入mysql数据库驱动坐标
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
创建C3P0连接池
public class DataSourceTest {
@Test
//测试手动创建c3p0数据源
public void test1() throws Exception {
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/how2java?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimeZone=GMT");
dataSource.setUser("root");
dataSource.setPassword("admin");
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
}
创建Druid连接池
@Test
public void testDruid()throws Exception{
//创建数据源
DruidDataSource dataSource=new DruidDataSource();
//设置数据库连接参数
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/how2java?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimeZone=GMT");
dataSource.setUserName("root");
dataSource.setPassword("admin");
//获得连接对象
Connection connection=dataSource.getConnection();
sout(connection);
}
提取jdbc.properties配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/how2java?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimeZone=GMT
jdbc.username=root
jdbc.password=admin
读取jdbc.properties配置文件创建连接池
@Test
public void testC3P0ByProperties() throws Exception{
//加载类路径下的jdbc.properties
ResourceBundle rb=ResourceBundle.getBundle("jdbc");
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setDriverClass(rb.getString("jdbc.driver"));
dataSource.setJdbcUrl(rb.getString("jdbc.url"));
dataSource.setUser(rb.getString("jdbc.username"));
dataSource.setPassword(rb.getString("jdbc.password"));
Connection connection=dataSource.getConnection();
sout(connection);
}
1.3 Spring配置数据源
可以将DataSource的创建权交由Spring容器去完成
-
DataSource有无惨构造方法,而Spring默认就是通过无参构造方法实例化对象的
-
DataSource要想使用需要通过set方法设置数据库连接信息,而Spring可以通过set方法进行字符串注入
<bean id="dataSourse" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/how2java?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimeZone=GMT"/>
<property name="user" value="root"/>
<property name="password" value="admin"/>
</bean>测试从容器当中获取数据源
@Test
public void test04() throws SQLException {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSourse = (DataSource) applicationContext.getBean("dataSourse");
Connection connection = dataSourse.getConnection();
System.out.println(connection);
connection.close();
}
1.4抽取jdbc配置文件
applicationContext.xml加载jdbc.properties配置文件获得连接信息。
首先,需要引入context命名空间和约束路径:
-
命名空间:xmlns:context="Index of /schema/context"(复制beans那个 加context和改context)
-
约束路径:Index of /schema/context http://www.springframework.org/schema/context/spring-context.xsd
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSourse" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
@Test
public void test04() throws SQLException {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSourse = (DataSource) applicationContext.getBean("dataSourse");
Connection connection = dataSourse.getConnection();
System.out.println(connection);
connection.close();
}
1.5 知识要点
Spring容器加载properties文件
<context:propety-placeholder location="xx.properties"/>
<property name="" value="${key}"/>
2. Spring注解开发
2.1 Spring原始注解
Spring是轻代码而重配置的框架,配置笔记繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。
Spring原始注解主要是替代<Bean>的配置
| @Component | 使用在类上用于实例化Bean |
| @Controller | 使用在web层类上用于实例化Bean |
| @Service | 使用在service层类上用于实例化Bean |
| @Repository | 使用在dao层类上用于实例化Bean |
| @Autowired | 使用在字段上用于根据类型依赖注入 |
| @Qualifier | 结合@Autowired一起使用用于根据名称进行依赖注入 |
| @Resource | 相当于@Autowired+@Qualifiers,按照名称进行注入 |
| @Value | 注入普通属性 |
| @Scope | 标注Bean的作用范围 |
| @PostConstruct | 使用在方法上标注该方法是Bean的初始化方法 |
| @PreDestroy | 使用在方法上标注该方法是Bean的销毁方法 |
注意:
使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。
<!–注解的组件扫描–>
<context:component-scan base-package="com.itheima"></context:component-scan>
使用@Component给UserDao和UserService创建对象,并调用UserCtroller看看是否成功
@Component("userDao")
public class UserDaoImpl implements UserDao{
@Override
public void save(){
sout("save running…");
}
}
@Component("userService")
//@sScope("prototype") 多例模式和单例模式的注解
public class UserServiceImpl implements UserService{
//将userDao对象注入进去
//在xml配置对象那种是需要set方法和有参构造方法,而主键通过反射,所以不需要set方法
//@Autwired//按照数据类型从Spring容器中进行匹配的 看bean里面有没有跟userDao差不多的,单个可以使用,多个不建议使用
//@Qualifier("userDao")//是根据id值从容器中进行匹配的,但是主要此处要结合@Autwired使用
@Resourse(name="userDao")//@Resourse相当于@Autwired+@Qualifier
private UserDao userDao;
@Override
public void save(){
userDao.save();
}
@PostConstruct
public void init(){
sout("初始化");
}
@PreDestory
public void destory(){
sout("销毁");
}
}
<!–配置xmln的context和下面网页的context–>
<!–配置主键扫描–>
<context:component-scan base-package="com.itheima"/>
public class UserContoller{
main{
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService bean=app.getBean(UserService.class);
bean.save();
}
}
@Component是类中实例化对象,而@Service、@Repository、@Controller是在对应层的类实例化对象
注入普通属性
@Value("itcast")
private String name;
//name=itcast 较麻烦,但这个用法最好的体现如下 <!–加载外部的properties文件–>
<context:property-placeholder location="classpath:jdbc.properties"/> @Value("${jdbc.driver}")
private String driver;
//driver=com.mysql.cj.jdbc.Driver
2.2 Spring新注解
使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:
-
非自定义的Bean的配置:<bean>
-
加载properties文件的配置:<context:property-placeholder>
-
组件扫描的配置:<context:component-scan>
-
引入其他文件:<import>
| @Configuration | 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解 |
| @ComponentScan | 用于指定Spring在初始化容器时要扫描的包 作用和在spring的xml配置文件中的<context:component-scan base-package="com.itheima"/>一样 |
| @Bean | 使用在方法上,标注将该方法的返回值存储到到Spring容器中 |
| @PropertySource | 用于加载.properties文件中的配置 |
| @Import | 用于导入其他配置类 |
//该类是Spring的核心配置类
@Configuration
/*<!–配置主键扫描–>
<context:component-scan base-package="com.itheima"/>*/
@ComponentScan("com.itheima")
//<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classpath:jdbc.properties")
//配置过多过杂乱,可以写多个,然后注入在主配置文件中
@Import(DataSourceConfiguration.class)
public class SpringConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean(name="dataSource") //Spring会将当前方法的返回值以指定名称存储到Spring容器中
public DataSource getDataSource() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
}
}
public class UserContoller {
public static void main(String[] args) throws SQLException {
//ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext app=new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService bean = app.getBean(UserService.class);
bean.save();
DataSource bean1 = app.getBean(DataSource.class);
Connection connection = bean1.getConnection();
System.out.println(connection);
}
}
3. Spring集成Junit
3.1 原始Junit测试Spring的问题
在测试类中,每个测试方法都有一下两行代码:
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
IAccountService as=ac.getBean("accountService",IAccountService.class);
这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。
3.2上述问题解决思路
-
让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它
-
将需要进行测试Bean直接在测试类中进行注入
3.3 Spring集成Junit步骤
导入spring集成Junit的坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
</dependency>
使用@Runwith注解替换原来的运行期
使用@ContextConfiguration指定配置文件或配置类
使用@Autowired注入需要测试的对象
创建测试方法进行测试
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:applicationContext.xml")当用配置文件用这个
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringJunitTest {
@Autowired
private DataSource dataSource;
@Test
public void test02() throws SQLException {
System.out.println(dataSource.getConnection());
}
}
03_Spring的AOP
1. Spring的AOP简介
1.1 什么是AOP
AOP为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实训程序功能统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高的开发的效率。
1.2 AOP的作用及其优势
-
作用:在程序运行期间,在不修改源码的情况下对方进行功能增强
-
优势:减少重复代码,提高开发效率,并且便于维护
1.3 AOP的底层实现
实际上,AOP的底层是通过Spring提供的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。
1.4 AOP的动态代理技术
常用的动态代理技术
-
JDK代理:基于接口的动态代理技术
-
cglib代理:基于父类的动态代理技术
1.5 JDK的动态代理(用父类接口)
目标类接口
public interface TargetInterface{
public void method();
}
目标类
public class Target implements TargetInterface{
@Override
public void method(){
sout("Target running…");
}
}
动态代理代码
//创建目标对象
Target target=new Target();
//增强对象
Advice advice=new Advice();
//返回值 就是动态生成的代理对象
TargetInterface proxy= (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();//前置增强
Object invoke = method.invoke(target, args);//执行目标方法 返回代理对象
advice.afterReturning();//后置增强
return invoke;
}
}
);
调用代理对象的方法测试
//测试当调用接口的任何方法时,代理对象的代码都无序修改
proxy.method();
1.6cglib的动态代理
创建增强对象、目标对象
public class Advice {
public void before(){
System.out.println("前置增强…");
}
public void afterReturning(){
System.out.println("后置增强…");
}
} public class Target{
public void save() {
System.out.println("save running…");
}
public void init(){
System.out.println("啦啦啦啦啦");
}
}
调用代理对象的方法测试
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.afterReturning();
return invoke;
}
});
//4.创建代理对象
Target o = (Target) enhancer.create();
o.save();
o.init();
}
}
1.7 AOP相关概念
Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
在正式讲解AOP的操作之前,我们必须理解AOP的相关术语,常用的术语如下:
-
Target(目标对象):代理的目标对象(要被增强的方法对象)
-
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
-
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点(就是可以被增强的方法,但是还没有增强,方法范围广)
-
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
-
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知
-
Aspect(切面):是切入点和通过(引介)的结合
-
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
1.8AOP开发明确的事项
需要编写的内容
-
编写核心业务代码(目标类的目标方法)
-
编写切面类,切面类中有通知(增强功能方法)
-
在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
AOP技术实现的内容
Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
AOP底层使用那种代理方式
在spring中,框架会根据目标类是否实现了接口来决定采用那种动态代理的方式
1.9 知识要点!!
-
aop:面向切面编程
-
aop底层实现:基于JDK的动态代理和基于Cglib的动态代理
-
aop的重点概念:
Pointcut(切入点):被增强的方法
Advice(通知/增强):封装增强业务逻辑的方法
Weaving(织入):将切点与通知结合的过程
-
开发明确事项:
谁是切点(切点表达式配置)
谁是通知(切面类中的增强方法)
将切点和通知进行织入配置
2. 基于XML的AOP开发
2.1 快速入门
导入AOP相关坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
创建目标接口和目标类(内部有切点)
public interface TargetInterface {
public void save();
} public class Target implements TargetInterface {
@Override
public void save() {
System.out.println("save running…");
}
}
创建切面类(内部有增强方法)
public class MyAspect {
public void before(){
System.out.println("前置增强….");
}
public void after(){
System.out.println("后置增强….");
}
}
将目标类和切面类的对象创建权交给spring
<!–目标对象–>
<bean id="target" class="com.itheima.aop.Target"/>
<!–切面对象–>
<bean id="myAspect" class="com.itheima.aop.MyAspect"/>
在applicationContext.xml中配置织入关系
<!–配置织入:告诉spring框架,哪些方法(切点)需要进行哪些增强–>
<aop:config>
<!–声明切面–>
<aop:aspect ref="myAspect">
<!–切面,切点+通知–>
<aop:before method="before(前置方法名)" pointcut="execution(public void com.itheima.aop.Target.save())"/>
</aop:aspect>
</aop:config>
测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
private TargetInterface target;
@Test
public void test01(){
target.save();
}
}
2.2 XML配置AOP详解
切点表达式的写法
表达式语法:
execution([修饰符] 返回值类型 包型.类名.方法名(参数))
-
访问修饰符可以省略
-
返回值类型、包名、类名、方法名可以使用星号*代表任意
-
包名与类名之间一个点,代表当前包下的类,两个点..表示当前包及其子包下的类
-
参数列表可以使用两个点..表示任意个数,任意类型的参数列表
例如:
//增强指定方法
execution(public void com.itheima.aop.Target.method())
//增强aop包下的Target类的无返回值的全部方法
execution(void com.itheima.aop.Target.*(..))
//增强aop包下的全部类的全部方法
execution(* com.itheima.aop.*.*(..))
//增强aop所在包和子包下的全部类的全部方法
execution(* com.itheima.aop..*.*(..))
//增强整个该项目的类的方法
execution(* *..*.*(..))
通知的类型
通知的配置语法
<aop:通知类型 method="切面类中方法名" pointcut="切点表达式"></aop:通知类型>
| 前置通知 | < aop:before > | 用于配置前置通知。指定增强的方法在切入点方法之前执行 |
| 后置通知 | < aop:after-returning > | 用于配置后置通知。指定增强的方法在切入点方法之后执行 |
| 环绕通知 | < aop:around > | 用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行 |
| 异常抛出通知 | < aop:throwing > | 用于配置异常抛出通知。指定增强的方法在出现异常时执行 |
| 最终通知 | < aop:after > | 用于配置最终通知。无论增强方式执行是否有异常都会执行 |
public class MyAspect {
public void before(){ System.out.println("前置增强…."); }
public void afterRunning(){ System.out.println("后置增强…."); }
//Proceeding JoinPoint:正在执行的连接点===切点
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强…");
Object proceed = pjp.proceed();//切点方法
System.out.println("环绕后增强…");
return proceed;
}
public void afterThrowing(){ System.out.println("异常抛出增强…"); }
public void after(){ System.out.println("最终增强…"); }
} <!–配置织入:告诉spring框架,哪些方法(切点)需要进行哪些增强–>
<aop:config>
<!–声明切面–>
<aop:aspect ref="myAspect">
<!–切面,切点+通知–>
<!–<aop:before method="before" pointcut="execution(public void。com.itheima.aop.Target.save())"/>
<aop:after method="after" pointcut="execution(public void com.itheima.aop.Target.save())"/>–>
<aop:before method="before" pointcut="execution(* com.itheima.aop.*.*(..))"/>
<aop:after-returning method="afterRunning" pointcut="execution(* com.itheima.aop.*.*(..))"/>
<aop:around method="around" pointcut="execution(* com.itheima.aop.*.*(..))"/>
<aop:after-throwing method="afterThrowing" pointcut="execution(* com.itheima.aop.*.*(..))"/>
<aop:after method="after" pointcut="execution(* com.itheima.aop.*.*(..))"/> </aop:aspect>
</aop:config>
public class Target implements TargetInterface {
@Override
public void save() {
int i=1/0;
System.out.println("save running…");
}
}
切点表达式的抽取
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用pointcut-ref属性代替pointcut属性来引用抽取后的切点表达式
<aop:config>
<!–声明切面–>
<aop:aspect ref="myAspect">
<!–抽取切点表达式–>
<aop:pointcut id="myPointcut" expression="execution(* com.itheima.aop.*.*(..))"/>
<!–切面,切点+通知–>
<aop:around method="around" pointcut-ref="myPointcut"/>
<aop:before method="before" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
2.3 知识要点
-
aop织入的配置
<aop:config>
<aop:aspect ref="切面类">
<aop:before method="通知方法名称" pointcut="切点表达式"></aop:before>
</aop:aspect>
</aop:config> -
通知的类型:前置通知、后置通知、环绕通知、异常抛出通知、最终通知
-
切点表达式的写法:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
3. 基于注解的AOP开发
3.1 快速入门
基于注解的aop开发步骤:
创建目标接口和目标类(内部有切点)
public interface TargetInterface{
public void save();
} @Component("target")
public class Target implements TargetInterface{
@Override
public void save(){
sout("save running");
}
}
创建切面类(内部有增强方法)
@Component("myAspect")
@Aspect //标注当前MyAspect是一个切面类
public class MyAspect{
//配置前置通知
@Before("execution(* com.itheima.anno.*.*(..))")
public void before(){ sout("前置增强…") }
}
将目标类和切面类的对象创建权交给spring
给Target和MyAspect类加@Component
在切面类中使用注解配置织入关系
给切面类加@Aspect和@Before("execution(* com.itheima.anno..(..))")
在配置文件中开启组件扫描和AOP的自动代理
<!–注解扫描–>
<context:component-scan base-package="com.itheima.aop"/>
<!–aop的自动代理–>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest{
@Autowired
private TargetInterface target;
@Test
public void test1(){
target.method();
}
}
3.2 注解配置AOP详解
注解通知的类型
通知的配置语法:@通知注解("切点表达式")
| 前置通知 | @Before | 用于配置通知。指定增强的方法在切入点之前执行 |
| 后置通知 | @AfterReturning | 用于配置后置通知。指定增强的方法在切入点方法之后执行 |
| 环绕通知 | @Around | 用于配置环绕通知。指定增强的方法在切入点之前和之后都执行 |
| 异常抛出通知 | @After-Throwing | 用于配置异常抛出通知。指定增强的方法在出现异常时执行 |
| 最终通知 | @After | 用于配置最终通知。无论增强方式执行是否有异常都会执行 |
切点表达式的抽取
通xml配置aop一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后再增强注解中进行引用,具体如下:
@Component("myAspect")
@Aspect
public class MyAspect{
@Before("myPoint()")
public void before(){
sout("前置代码增强….")
}
@Pointcut("execution(execution(* com.itheima.aop.*.*(..))")
public void myPoint(){}
}
在配置文件也可以转换
<!–注解扫描–>
<context:component-scan base-package="com.itheima.aop"/>
<!–aop的自动代理–>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> @ContextConfiguration
//注解扫描
@ComponentScan("com.itheima.anno")
//aop的自动代理
@EnableAspectJAutoProxy
public class SpringConfig { }
3.3 知识要点
-
注解aop开发步骤
-
使用@Aspect标注切面类
-
使用@通知注解标注通知方法
-
在配置文件中配置aop自动代理< aop:aspectj-autoproxy >< /aop:aspectj-autoproxy >和注解扫描
-
通知注解类型
04_Spring jdbcTemplate基本使用
1. Spring jdbcTemplate基本使用
1.1 jdbcTemplate概述
它是spring框架中提供的一个对象,是对原始繁琐的jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的jdbcTemplate和HibernateTemplate,操作nosql数据库RedisTemplate,操作消息队列的Jms Template等等。
1.2 JdbcTemplate开发步骤
导入spring-jdbc和spring-tx坐标
创建数据库表和实体
创建JdbcTemplate对象
执行数据库操作
1.3 JdbcTemplate快速入门
导入坐标(要使用这个 不然容易报错)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
创建account表和实体
public class Account{
private String name;
private double money;
getter+setter
}
创建jdbcTemplate对象
执行数据库操作
public class JdbcTemplateTest {
@Test
public void test1() throws Exception {
//创建数据源对象
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC");
dataSource.setUser("root");
dataSource.setPassword("admin");
//设置数据源对象 知道数据库在哪
JdbcTemplate jdbcTemplate=new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
//执行操作
Object[] objects=new Object[]{"kiki",5000};
int tom = jdbcTemplate.update("insert into account values(?,?)",objects);
System.out.println(tom);
}
}
1.4 Spring产生jdbcTemplate对象
我们可以将jdbcTemplate的2创建权交给spring,将数据源datasource的创建权也交给spring,在spring容器内部将数据源DataSource注入到jdbcTemplate模板对象中,配置如下:
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
jdbc.username=root
jdbc.password=admin
applicationContext.xml
<?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"
>
<!–配置数据源对象–>
<!–要加载外部文件–>
<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.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!–jdbc模板对象–>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
1.5 jdbcTemplate的常用操作
操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class jdbcTemplateCRUDTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testUpdate(){
Object object[]=new Object[]{10000,"kiki"};
int update = jdbcTemplate.update("update account set money=? where name=?",object);
System.out.println(update);
}
@Test
public void testDelete(){
int tom = jdbcTemplate.update("delete from account where name=?", "tom");
System.out.println(tom);
}
//查询全部
@Test
public void testQueryALL(){
List<Account> accountList = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
System.out.println(accountList);
}
//查询kiki的账户,返回kiki的account对象
@Test
public void testQueryOne(){
Account kiki = jdbcTemplate.queryForObject("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), "kiki");
System.out.println(kiki);
}
//查询 返回普通类型对象 这里查询行数
@Test
public void testQueryCount(){
Long aLong = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
System.out.println(aLong);
}
}
1.6 知识要点
导入spring-jdbc和sprng-tx坐标(第一个封装了jdbc模板,第二个做事务)
创建数据库表和实体
创建dataSource和jdbcTemplate对象
执行数据库操作
-
增改删操作
jdbcTemplate.update(sql,params) params是Obejct[] 对应sql的问号
-
查询操作
查询全部
jdbcTemplate.query(sql,new Mapper<实体对象类>(实体对象类.class),params)
查询一个
jdbcTemplate.queryForObject(sql,Mapper,parame)
查询返回普通类型
jdbcTemplate.queryForObject(sql,class<T>)
05_声明式事务控制
1. 编程式事务控制相关对象
1.1 PlatformTransactionManager
PlatformTransactionManager接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法
| TransactionStatus getTransaction(TransactionDefination defination) | 获取事务的状态信息 |
| void commit(TransactionStatus status) | 提交事务 |
| void rollback(TransactionStatus status) | 回滚事务 |
注意:
PlatformTransactionManager是接口类型,不同的dao层技术则有不同的实现类,例如Dao层技术是jdbc或mybatis时:org.springframework.jdbc.datasource.DataSourceTransactionManager
Dao层技术是hibernate时:org,springframwork.orm.hibernate5.HibernateTransactionManager
1.2 TransactionDefinition
TransactionDefinition是事务的定义信息对象,里面有如下方法:
| int getIsolationLevel() | 获得事务的隔离级别 |
| int getPropogationBehavior() | 获得事务的传播行为 |
| int getTimeout() | 获得超时时间 |
| boolean isReadOnly() | 是否只读 |
事务隔离级别
设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读
-
ISOLATION_DEFAULT
-
ISOLATION_READ_UNCOMMITTED
-
ISOLATION_READ_COMMITTED
-
ISOLATION_REPEATABLE_READ
-
ISOLATION_SERIALAIZABLE
事务传播行为
-
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
(A调用B,B看A有没有事务,没有就新建,有就加入它)
-
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
(A调用B,B看看A有没有事务,有就一起用,没有就非事务运行)
-
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
-
REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起
-
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
-
MEVER:以非事务方式运行,如果当前存在事务,抛出异常
-
超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置
-
是否只读:建议查询时设置为只读
1.3 TransactionStatus
TransactionStatus接口提供的是事务具体的运行状态,方法介绍如下:
| boolean hasSavepoint() | 是否存储回滚点 |
| boolean isCompleted() | 事务是否完成 |
| boolean isNewTransaction() | 是否有新事务 |
| boolean isRollbackOnly() | 事务是否回滚 |
1.4 知识要点
编程式事务控制三大对象(要理解他们的作用)
-
PlatformTransactionManager(事务定义行为 事务管理对象 根据不同的数据库运用不同的包)
-
TransactionDefintion(事务定义对象 定义事务相关参数)
-
TransactionStatus(被动封装状态信息 不需要指定刻意去弄)
2.基于XML的声明式事务控制
2.1 什么是声明式事务控制
spring的声明式事务顾名思义就是采用声明的方式来处理事务,这里所说的声明,就是指在配置文件中声明,用在spring配置文件中声明式的处理事务来代替代码式的处理事务。
声明式事务处理的作用
-
事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可
-
在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需该表代码重新编译,这样维护起来极其方便
注意:Spring声明式事务控制底层就是AOP。
2.2 声明式事务控制的实现
声明式事务控制明确事项:
-
谁是切点?
-
谁是通知?
-
配置切面?
导包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
</dependencies>
配置文件,事务底层原理用到aop,属于需要引入aop和tx
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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 <!–载入外部文件–>
<context:property-placeholder location="jdbc.properties"/>
<!–配置数据源–>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="${jdbc.driver}" p:jdbcUrl="${jdbc.url}"
p:user="${jdbc.username}" p:password="${jdbc.password}"/>
<!–配置数据模板对象–>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSource"/>
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl" p:jdbcTemplate-ref="jdbcTemplate"/>
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" p:accountDao-ref="accountDao"/>
<!–配置平台事务管理器 告诉事务数据库是什么–>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" />
<!–通知 事务的增强–>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!–设置事务属性信息–>
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!–1.配置事务的aop织入–>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))"/>
</aop:config>
<!–2.事务的aop增强–>
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(* com.itheima.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"></aop:advisor>
</aop:config>
测试
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void out(String outMan, double money) {
jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
}
@Override
public void in(String inMan, double money) {
jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
}
} public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outMan, String inMan, double money) {
accountDao.out(outMan,money);
int i=1/0;//错误 查看数据库是否变化
accountDao.in(inMan,money);
}
} public class AccountController {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService=app.getBean(AccountService.class);
accountService.transfer("kiki","to",500);
}
}
2.3 切点方法的事务参数的配置
<!–1.配置事务的aop织入–>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))"/>
</aop:config>
其中,< tx:method >代表切点方法的事务参数的配置,例如:
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
name:切点方法名称
isolation:事务隔离级别
propogation:事务传播行为
timeout:超过时间
read-only:是否只读
2.4 知识要点
声明式是u我控制的配置要点
平台事务管理器配置
事务通知的配置
事务aop织入的配置
3. 使用注解配置声明式事务控制
3.1 使用注解配置声明式事务控制
xml配置(自定义配置可使用注解,非自定义建议xml配置)
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="${jdbc.driver}" p:jdbcUrl="${jdbc.url}"
p:user="${jdbc.username}" p:password="${jdbc.password}"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSource"/>
<!–注解扫描–>
<context:component-scan base-package="com.itheima"/>
<!–配置平台事务管理器–>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" />
<!–事务注解驱动–>
<tx:annotation-driven transaction-manager="transactionManager"/>
编写AccountDao
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void out(String outMan, double money) {
jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
}
@Override
public void in(String inMan, double money) {
jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
}
}
编写AccountService
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)
public void transfer(String outMan, String inMan, double money) {
accountDao.out(outMan,money);
accountDao.in(inMan,money);
}
}
配置applicationContext.xml配置文件
<!–注解扫描–>
<context:component-scan base-package="com.itheima"/>
<!–事务注解驱动–>
<tx:annotation-driven transaction-manager="transactionManager"/>
3.2 注解配置声明式事务控制解析
使用@Transactional在需要进行事务控制的类或是方法上修饰,注解可用的属性同xml配置方式,例如隔离级别、传播行为等。
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)
注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置。
使用在方法上,不同的方法可用采用不同的事务参数配置
xml配置文件中要开启事务的注解驱动
<tx:annotation-driven transaction-manager="transactionManager"/>
3.3 知识要点
注解声明式事务控制的配置要点
-
平台事务管理器配置(xml)
-
事务通知的配置(@Transaction注解配置)
-
事务注解驱动的配置< tx:annotation-driven/>
3-2 SpringMVC
01-springmvc快速入门、组件解析
1.Spring集成web环境
1.1 ApplicationContext应用上下文获取方式
应用上下文对象是通过new ClasspathXmlApplicationContext(spring配置文件)方式获取的,但是每次从容器中获得Bean时都要编写new ClasspathXmlApplicationContext(spring配置文件),这样的弊端是配置文件加载多次,应用上下文创建多次。
在web项目中,可以使用ServletContextListener监听web应用的启动,我们可以在web应用启动时,就加载spring的配置文件,创建应用上下文对象ApplicationContext,在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。
总结就是为了不让配置文件被加载太多次,减少重复代码,因此做一个listener直接初始化配置文件
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);
//将spring的应用上下文对象存储到ServletContext域当中
servletContext.setAttribute("app",app);
}
<listener>
<listener-class>com.itheima.listener.ContextLoaderListener</listener-class>
</listener>
<!–全局变量–>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
public class WebApplicationContextUtils {
public static ApplicationContext getApplicationContext(ServletContext servletContext){
//在servlet书写app时 万一对方不是app啥的,所以用一个工具类获取上下文对象
return (ApplicationContext) servletContext.getAttribute("app");
}
}
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
ServletContext servletContext = this.getServletContext();
//ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");
ApplicationContext app = WebApplicationContextUtils.getApplicationContext(servletContext);
UserService u = app.getBean(UserService.class);
u.save();
}
}
1.2 Spring提供获取应用上下文的工具
上面的分析不用手动实现,spring提供了一个监听器ContextLoaderListener就是对上述功能的封装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象。
所以我们需要做的只有两件事:
在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)
使用WebApplicationContextUtils获得应用上下文对象ApplicationContext
1.3 导入Spring集成web的坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.1</version>
</dependency>
1.4 配置ContextLoaderListener监听器
<listener>
<!–Spring的监听器–>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!–全局参数–>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
1.5 通过工具获得应用上下文对象
ServletContext servletContext = this.getServletContext();
WebApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
UserService u = app.getBean(UserService.class);
u.save();
1.6 知识要点
spring集成web环境步骤
配置ContextLoaderListener监听器
使用WebApplicationContextUtils获得应用上下文
2. SpringMVC简介
2.1 SpringMVC概述
SpringMVC是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级web框架,属于SpringFrameWork的后续产品,已经融合在Spring Web Flow中。
SpringMVC已经成为目前最主流的MVC框架之一,并且随着Spring3.0的发布,全面超越Struts2,成为最优秀的MVC框架。它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful编程风格的请求。
2.2 SpringMVC快速入门
需求:客户端发起请求,服务器端接收请求,执行逻辑并进行视图跳转。
开发步骤:
导入SpringMVC相关坐标
配置SpringMVC核心控制器DispathcerServlet
创建Controller类和视图页面
使用注解配置Controller类中业务方法的映射地址
配置SpringMVC核心文件spring-mvc.xml
客户端发起请求测试
导入Servlet和JSP坐标
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
在web.xml配置SpringMVC的核心控制器
<servlet>
<servlet-name>DisapatcherServlet</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>
spring-mvc.xml
<!–Controller组件扫描–>
<context:component-scan base-package="com.itheima.controller"/>
创建Controller和业务方法
@Controller
public class UserController {
@RequestMapping("/quick")
public String save(){
System.out.println("controller save running…");
return "success.jsp";
}
}
在webapp创建success.jsp,在首页后面输入/quick完成
2.4 知识要点
SpringMVC的开发步骤
导入SpringMVC相关坐标
配置SpringMVC核心控制器DispathcerServlet
创建Controller类和视图页面
使用注解配置Controller类中业务方法的映射地址
配置SpringMVC核心文件spring-mvc.xml
客户端发起请求测试
3.SpringMVC组件解析
3.1 SpringMVC的执行过程
用户发送请求至前端控制器DiapatcherServlet
DiapatcherServlet收到请求调用HandlerMapping处理器映射器
处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
DiapatcherServlet调用HandlerAdapter处理器适配器
HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)
Controller执行完成返回ModelAndView
HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
ViewReslover解析后返回具体View
DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DisapatcherServlet响应用户。
fdsdsd
3.2 SpringMVC组件解析
前端控制器:DispatcherServlet
用户请求到达前端控制器,它就相当于MVC模式中的C,DispatcherServlet是整个流程控制的中心,由它调用其他组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性。
处理器映射器:HandlerMapping
HandlerMapping负责根据用户请求找到Handler即处理器,SpringMVC提供了不同的映射器实现不同映射方式,例如配置文件方式,实现接口方式,注解方式等。
处理器适配器:HandlerAdapter
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
处理器:Handler
它就是我们开发中要编写的具体业务控制器。由DispatcherServlet把用户请求转发到Handler。由Handler对具体的用户请求进行处理。
视图解析器:View Resolver
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
视图:View
SpringMVC框架提供了很多的View视图类型的支持,包括:jstView、freemarkerView、pdfView等。最常用的视图就是jsp。一般情况下需要通过页面标签或页面模板技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体页面
3.3 注解解析
@RequestMapping
作用:用于建立URL和处理请求方法之间的对应关系
位置:
-
类上,请求URL的第一级访问目录。此处不写的话,就相当于应用的根目录
-
方法上,请求URL的第二级访问目录,与类上的使用@RequetMap标注的一级目录一起组成访问虚拟路径
@Controller
@RequestMapping("/user")
public class UserController{
//请求地址 Http://localhost:8080/user/quick
@RequestMapping("/quick")
public String save(){
System.out.println("controller save running…");
//加/则是在当前应用找该文件,不加就是在user目录下找该文件
return "/success.jsp";
}
} -
value:用于指定请求的URL,它和path属性的作用一样的(当只写value,可以省略value=,多个内容,要带上value=“/quick”)
-
method:用于指定请求的方式 例如:method=RequestMethod.GET
-
params:用于指定限制请求参数的条件。他指出简单的表达式。要求请求参数的key和value必须和配置的一模一样
例如:
-
params={"accountName"},表示请求参数必须有accountName
-
params={"money!100"},表示请求参数中的money不能是100
@Controller
@RequestMapping("/user")
public class UserController{
//请求地址 Http://localhost:8080/user/quick?username=xxx 才成功
@RequestMapping(value="/quick",method=RequestMethod.GET,params="username")
public String save(){
System.out.println("controller save running…");
//加/则是在当前应用找该文件,不加就是在user目录下找该文件
return "/success.jsp";
}
}
-
spring-mvc.xml注解扫描详解
mvc命名空间引入
命名空间:xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
约束地址:http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
组件扫描
SpringMVC基于spring容器,所以在进行springmvc操作时,需要将Controller存储到spring容器中,如果使用@Controller注解标注的话,就需要使用<context:component-scan base-package=“com.itheima.controller“/>进行组件扫描。
-
只是但扫描一个包的话可以写<context:component-scan base-package=“com.itheima.controller“/>
-
但要扫描全部包下的controller,会和application Context的扫描相撞,则下面是在那些包下扫描包下的哪些组件,exclude就是不扫描该组件的意思
<!–Controller组件扫描–>
<context:component-scan base-package="com.itheima">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
3.4 SpringMVC的XML配置解析
视图解析器
SpringMVC有默认组件配置,默认组件都是DispatcherServlet.properties配置文件(在spring=mvc包下的view)中配置的,该配置文件地址org/springframework/web/servlet/DispatcherServlet.properties,该文件中配置了默认的视图解析器,如下:
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
翻看该解析器源码,可以看到该解析器的默认设置,如下:
REDIRECT_URL_PREFIX = "redirect:" –重定向前缀
FORWARD_URL_PREFIX = "forward:" –转发前缀(默认值)
例如:
@Controller
@RequestMapping("/user")
public class UserController{
@RequestMapping("/quick")
public String save(){
System.out.println("controller save running…");
//加/则是在当前应用找该文件,不加就是在user目录下找该文件
//当返回值啥都没写,默认有forward: 且地址访问时地址不变
//地址:Http://localhost:8080/user/quick
//return "(forward:)/success.jsp";
//当写了redirect:在前面,则是重定向
//访问地址则会变成http://localhost:8080/success.jsp
return "redirect:/success.jsp";
}
}
prefix = ""; –视图名称前缀
suffix = ""; –视图名称后缀
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
则Controller返回值可以少写一点东西
@Controller
@RequestMapping("/user")
public class UserController{
@RequestMapping("/quick")
public String save(){
System.out.println("controller save running…");
return "success";
//会访问web下jsp目录下的success.jsp文件
}
}
3.5 知识要点
-
前端控制器:DispatcherServlet
-
处理器映射器:HandlerMapping
-
处理器:Handler
-
视图解析器:View Resolver
-
视图:View
SpringMVC的注解和配置
-
请求映射注解:@RequestMapping
-
视图解析器配置:
REDIRECT_URL_PREFIX = "redirect:"
FORWARD_URL_PREFIX = "forward:"
prefix = "";
suffix = "";
02-SpringMVC的请求和响应
1. SpringMVC的数据响应
1.1 SpringMVC的数据响应方式
页面跳转
-
直接返回字符串
-
通过ModelAndView对象返回
回写数据
-
直接返回字符串
-
返回对象或集合
1.2 页面跳转
返回字符串形式
直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转
@RequestMapping("/quick")
public String quickMethod(){
return "index";
} <property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp"/>
转发资源地址:/WEB-INF/views.jsp
返回带有前缀的字符串:
转发:forward:/WEB-INF/views/index.jsp
重定向:redirect:/index.jsp
返回ModelAndView对象
View是视图对象 也就是返回视图网页跳转效果
Model是模型对象 也就是在特定网页里面赋值,以id来区分
@RequestMapping("/quick2")
public ModelAndView save2(){
/*
* Model:模型 作用封装数据
* View:视图 作用展示数据
* */
ModelAndView modelAndView = new ModelAndView();
//设置模型对象
modelAndView.addObject("username","cxy");
//设置视图名称
modelAndView.setViewName("index");
return modelAndView;
} <html>
<head>
<title>helloWorld</title>
</head>
<body>
<h1>helloWorld!${username}</h1>
</body>
</html>
输入网页映射名,出现helloworld!cxy
也有不同的书写ModelAndView方法
@RequestMapping("/quick3")
public ModelAndView save3(ModelAndView modelAndView){
/*
* Model:模型 作用封装数据
* View:视图 作用展示数据
spring识别自己注入对象
* */
//设置模型对象
modelAndView.addObject("username","cxy3");
//设置视图名称
modelAndView.setViewName("index");
return modelAndView;
} @RequestMapping("/quick4")
public String save4(Model model){
/*
把ModelAndView拆开使用
* */
model.addAttribute("username","cxy4");
return "index";
}
向request域存储数据
在进行转发时,javaweb都是用request域中存储数据在jsp显示,那么使用Controoller在,有存储数据呢?(一般不使用,因为spring框架已经给你一个ModelAndView对象了,没有必要)
@RequestMapping("/quick")
public String quickMethod(HttpServletRequest request){
request.setAttribute("name","zhangsan");
return "index";
}
1.3 回写数据
直接返回字符串
Web基础阶段,客户端访问服务器端,如果想直接回写字符串作为响应体返回的话,只需要使用response.getWriter().print("Hello world")即可,那么在Controller中想直接回写字符串该怎样呢?
通过SpringMVC框架注入的response对象,使用response.getWriter().print("Hello world")回写数据,此时不需要视图跳转,业务方法返回值为void
@RequestMapping("/quick6")
public void save6(HttpServletResponse response) throws IOException {
response.getWriter().print("itcast");
}
将需要回写字符串直接返回,但此时需要通过@ResponseBody注解告知SpringMVC框架,这是使用response直接回写数据,而不是跳转页面,方法返回的字符串不是跳转是直接在http响应体中返回。
@RequestMapping("/quick7")
@ResponseBody //告知SpringMVC框架不是进行页面跳转,是直接进行数据响应
public String save7(){
return "hello itheima";
}
在异步项目中,客户端与服务器端往往要进行json格式字符串交互,此时我们可以手动拼接json字符串返回。
@RequestMapping("/quick8")
@ResponseBody
public String save8(){
return "{\\"name\\":\\"cxy\\",\\"age\\":18}";
}
上述方式手动拼接json格式字符串的方式很麻烦,开发中往往要将复杂的Java对象装换成json格式的字符串,我们可以使用web阶段学习过的json转换工具jackson进行转换,导入jackson坐标。
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.12.1</version>
</dependency>
通过jackson转换json格式字符串,回写字符串
@RequestMapping("/quick9")
@ResponseBody
public String save9() throws JsonProcessingException {
User user = new User();
user.setName("lisi");
user.setAge(39);
//使用json的转换工具将对象转换成json格式字符串在返回
ObjectMapper objectMapper=new ObjectMapper();
String s = objectMapper.writeValueAsString(user);
return s;
}
返回对象或集合
通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器消息转换参数,指定使用Jackson进行对象或集合的转换,因此需要在spring-mvc.xml进行如下配置:
<!– 配置处理器映射器–>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean> @RequestMapping("/quick10")
@ResponseBody
//期望SpringMVC自动将user转换成json格式的字符串
public User save10() throws JsonProcessingException {
User user = new User();
user.setName("lisi");
user.setAge(39);
return user;
}
在方法上添加@ResponseBody就可以返回json格式的字符串,但是这样配置比较麻烦,配置的代码比较多,因此,我们可以使用的注解驱动代替上述配置。
<!– mvc注解驱动–>
<mvc:annotation-driven/>
在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。使用<mvc:annotation-driven >自动加载RequestMapping(处理映射器)和RequestMappingHandlerAdapter(处理适配器),可用在Spring-xml.xml配置文件中使用<mvc:annotation-driven/ >替代注解处理器和适配器的配置。
同时使用<mvc:annotation-driven/ >默认底层就会集成jackson进行对象或集合的json格式字符串的转换。
1.4 知识要点
页面跳转
-
直接返回字符(视图跳转 是重定向还是转发)
-
通过ModelAndView对象返回
回写数据
-
直接返回字符串
-
返回对象或集合
2. springMVC获得请求数据
2.1 获得请求参数
客户端请求参数的格式是:name=value&name=value……
服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数:
-
基本类型参数
-
POJO类型参数
-
数组类型参数
-
集合类型参数
2.2 获得基本类型参数
Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8080/quick11?username=zhangsan&age=12
@RequestMapping("/quick11")
@ResponseBody
//期望SpringMVC自动将user转换成json格式的字符串
public void save11(String username,int age) throws JsonProcessingException {
System.out.println(username);
System.out.println(age);
}
2.3 获得POJO类型参数(实体)
Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8080/itheima_springmvc1/quick9?username=zhangsan&age=12
@RequestMapping("/quick12")
@ResponseBody
//期望SpringMVC自动将user转换成json格式的字符串
public void save12(User user) throws JsonProcessingException {
System.out.println(user);
}
则会在控制台打印出来
2.4 获得数组类型参数
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8080/quick13?strs=aaa&strs=bbb
@RequestMapping("/quick13")
@ResponseBody
//期望SpringMVC自动将user转换成json格式的字符串
public void save13(String[]strs) throws JsonProcessingException {
System.out.println(Arrays.asList(strs));
}
2.5 获得集合类型参数
获得集合参数时,要将集合参数包装到一个POJO才可以(就是将集合类型参数弄成获得pojo参数,将集合封装起来,在使用时要封装集合name和请求参数的name一致,然后被集合的对象的参数名称在小数点后一致)
<form action="${pageContext.request.contextPath}/quick14" method="post">
<%– 表明是第几个user对象的username age–%>
<input type="text" name="userList[0].name"><br/>
<input type="text" name="userList[0].age"><br/>
<input type="text" name="userList[1].name"><br/>
<input type="text" name="userList[1].age"><br/>
<input type="submit" value="提交">
</form>
public class VO {
private List<User> userList;
@Override
public String toString() {
return "VO{" +
"userList=" + userList +
'}';
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
}
@RequestMapping("/quick14")
@ResponseBody
//期望SpringMVC自动将user转换成json格式的字符串
public void save14(VO vo) throws JsonProcessingException {
System.out.println(vo);
}
当使用ajax提交时,可以指定contentType为json形式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装。
<head>
<title>Title</title>
<script src="${pageContext.request.contextPath}/js/jquery-3.3.1.js"></script>
<script>
var userList=new Array();
userList.push({name:"zhangsan",age:18});
userList.push({name:"lisi",age:20});
$.ajax({
type:"POST",
url:"${pageContext.request.contextPath}/quick15",
data:JSON.stringify(userList),
contentType:"application/json;charset=utf-8"
});
</script>
</head>
@RequestMapping("/quick15")
@ResponseBody
//期望SpringMVC自动将user转换成json格式的字符串
public void save15(@RequestBody List<User> userList) throws JsonProcessingException {
System.out.println(userList);
}
<!– 开放资源的访问–>
<mvc:resources mapping="/js/**" location="/js/"/>
注意:通过谷歌开发者工具抓包发现,没有加载到jquery文件,原因是SPringMVC的前端控制器DispatcherServlet的url-pattern配置的是/,代表对所有的资源都进行过滤操作,我们可以通过以下两种方式指定放行静态资源:
-
在spring-mvc.xml配置文件中指定放行的资源
前面是映射资源路径,后面是资源路径
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/img/**" location="/img/"/>
-
使用<mvc:default-servlet-handler/ >标签(不需要写多个静态资源,一行代码搞定)
2.6 请求数据乱码问题
当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>
2.7 参数绑定注解@requestParam
当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@ErquestParam注解显示的绑定。
Http://localhost:8080/quick14?name=zhangsan
<form action="${pageContext.request.contextPath}/quick14" method="post">
<input type="text" name="name"><br>
<input type="submit" name="提交"><br>
</form>
@RequestMapping("/quick14")
@ResponseBody
public void quickMethod14(@RequestParam("name") String username) throws IOException{
sout("username");
}
注解@RequestParam还有如下参数可以使用:
-
value:与请求参数名称
-
required:此在指定的请求参数是否必须包括,默认是true,提交时如果没有此参数就会报错
-
defaultValue:当没有指定请求参数时,则使用指定的默认值赋值
@RequestMapping("/quick14")
@ResponseBody
public void quickMethod14(@RequestParam(value="name",required=false,defaultValue="itcast") String username) throws IOException{
sout("username");
} Http://localhost:8080/quick14 不报错 打印itcast Http://localhost:8080/quick14?name 不报错 打印itcast Http://localhost:8080/quick14?name=lisi 打印lisi
2.8 获得Restful风格的参数
Restful是一种软件架构、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类得软件,基于这个风格设计得软件可以更简洁,更有层次,更易于实现缓存机制等。
Restful风格的请求是使用"url+请求方式"表示一次请求目的,HTTP协议里面四个表示操作方式的动词如下:
-
get:用于获取资源
-
post:用于新建资源
-
put:用于更新资源
-
delete:用于删除资源
例如:
-
/user/1 get:得到id=1的user
-
/user/1 delete:删除id=1的user
-
/user/1 put:更新id=1的user
-
/user/1 post:新增user
上述url地址/user/1中的1就是要获得的请求参数,在SpringMVC中可以使用占位符进行参数绑定。地址/user/1可以写成/user/{id},占位符{id}对应的就是1的值,在业务方法中我们可以使用@PathVarable注解进行占位符的匹配获取工作
2.9 自定义类型转换器
-
springMVC默认已提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置
-
但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。
自定义类型转换器的开发步骤:
定义转换器实现Converter接口
在配置文件中声明转换器
在< annotation-driven >中引用转换器
定义转换器实现Converter接口
public class DateConverter implements Converter<String,Date> {
@Override
public Date convert(String dateStr) {
//将日期的字符串转换成真正的日期对象返回
SimpleDateFormat format=new SimpleDateFormat("yyyy-mm-dd");
Date date=null;
try {
date = format.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.itheima.converter.DateConverter"/>
</list>
</property>
</bean>
在< annotation-driven >中引用转换器
<!– mvc注解驱动–>
<mvc:annotation-driven conversion-service="ConversionService"/>
2.10 获得Servlet相关API
SpringMVC支持使用原始的ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:
-
HttpServletRequest
-
HttpServletResponse
-
HttpSession
@RequestMapping("/quick16")
@ResponseBody
public void quickMethod16(HttpServletRequest request,HttpServletResponse response,HttpSession session){
System.out.println(request);
System.out.println(response);
System.out.println(session);
}
2.11 获得请求头
1. @RequestHeader
使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)
@RequestHeader注解的属性如下:
-
value:请求头的名称
-
required:是否必须携带此请求头
@RequestMapping("/quick19")
@ResponseBody
public void quickMethod19(@RequestHeader(value = "User-agent",required = false) String user_agent){
System.out.println(user_agent);
}
2. @CookieValue
使用@CookieValue可以获得指定Cookie的值
@CookieValue注解的属性如下:
-
value:指定cookie的名称
-
required:是否必须携带此cookie
@RequestMapping("/quick20")
@ResponseBody
public void quickMethod20(@CookieValue(value = "JSESSIONID") String session_id){
System.out.println(session_id);
}
2.12 文件上传
1.文件上传客户端三要素
-
表单项type=“file“
-
表单的提交方式是post
-
表单的enctype属性是多部分表单形式,及enctype=”multipart/form-data“
<form action="${pageContext.request.contextPath}/quick22" method="post" enctype="multipart/form-data">
名称:<input type="text" name="name"><br/>
文件:<input type="file" name="upload"><br/>
<input type="submit" name="提交">
</form>
2. 文件上传原理
-
当form表单修改为多部分表单时,request.getParameter()将失效
-
enctype="application/x-www-form-urlencoded"时,form表单的正文内容格式是:
key=value&key=value&key=value
-
当form表单的enctype取值为Mutilpart/form-data时,请求正文内容就变成多部分形式:
2.13 单文件上传步骤
导入fileupload和io坐标
配置文件上传解析器
编写文件上传代码
2.14 单文件上传实现
导入fileupload和io坐标
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
配置文件上传解析器
<!– 配置文件上传解析器–>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxInMemorySize" value="500000"/>
</bean>
编写文件上传代码
@RequestMapping("/quick22")
@ResponseBody
public void quickMethod22(String name, MultipartFile uploadFile){
System.out.println(name);
System.out.println(uploadFile);
//获取上传文件名称
String originalFilename = uploadFile.getOriginalFilename();
uploadFile.transferTo(new File("C:\\\\upload\\\\"+originalFilename));
}
2.15 多文件上传实现
多文件上传,只需要将页面修改为多个文件上传项,将方法参数MultipartFile类型修改为MultipartFile[]即可
<form action="${pageContext.request.contextPath}/quick22" method="post" enctype="multipart/form-data">
名称:<input type="text" name="name"><br/>
文件:<input type="file" name="uploadFile"><br/>
文件:<input type="file" name="uploadFile2"><br/>
<input type="submit" name="提交">
</form>
@RequestMapping("/quick21")
@ResponseBody
public void quickMethod21(String name,MultipartFile[] uploadFiles) throws IOException {
for (MultipartFile uploadFile : uploadFiles){
String originalFilename = uploadFile.getOriginalFilename();
uploadFile.transferTo(new File("C:\\\\upload\\\\"+originalFilename));
}
}
2.14 知识要点
MVC实现数据请求方式
基本类型参数
POJO类型参数
数组类型参数
集合类型参数
MVC获取数据细节
中文乱码问题
@RequestParam 和 @PathVariable
自定义类型转换器
获得Servlet相关API
@RequestHeader 和 @CookieValue
文件上传
03-SpringMVC拦截器及异常处理
1.1 拦截器(innterceptor)的作用
SpringMVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。
1.2 拦截器和过滤器的区别
| 使用范围 | 是servlet规范中的一部分,任何java web工程都可以使用 | 是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用 |
| 拦截范围 | 在url-pattern中配置了/*之后,可以对所有要访问的资源拦截 | 只会拦截访问的控制器方法,如果访问的是jsp,html,css,image或者js是不会进行拦截的 |
1.3 拦截器的快速入门
自定义拦截器很简单,只有如下三步:
创建拦截器类实现HandlerInterceptor接口
配置拦截器
测试拦截器的拦截效果
创建拦截器类实现HandlerInterceptor接口
public class MyInterceptor1 implements HandlerInterceptor {
//在目标方法执行之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
//返回false代表不放行(后面的方法都不能执行) 返回true则放行
return true;
}
//在目标方法执行之后视图返回之前执行
//modelandview可以获取 进行修改
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
//在流程都执行完毕后 执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
配置拦截器
<!– 配置拦截器–>
<mvc:interceptors>
<mvc:interceptor>
<!– 对哪些资源执行拦截操作–>
<mvc:mapping path="/**"/>
<bean class="com.itheima.interceptor.MyInterceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<!– 当要加多一个拦截器时–>
<mvc:mapping path="/xxxx/**"/>xxxx下的全部方法
<bean class="com.itheima.interceptor.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
测试拦截器的拦截效果
preHandle running… postHandle afterCompletion
1.5 拦截器方法说明
| preHandle() | 方法将在请求处理之前进行调用,该方法的返回值是布尔值Boolean类型的,当它返回false时,表示请求结束,后续的Intercetor和Controller都不会再执行;当返回值true时就会继续调用下一个Interceptor的preHandle方法 |
| postHandle() | 该方法是当前请求进行处理之后被调用,前提是preHandle方法的返回值为true时才能被调用,且它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作 |
| afterCompletion() | 该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行,前提是preHandle方法的返回值为true时才能被调用 |
1.6 知识要点
自定义拦截器步骤
创建拦截器类实现HandlerInterceptor接口
配置拦截器
测试拦截器的拦截效果
1.7 案例-用户登录权限控制
需求:用户没有登录的情况下,不能对后台菜单进行访问操作,点击菜单跳转到登录页面,只有用户登录成功后才能进行后台功能的操作。
08-异常处理机制
1. SpringMVC异常处理
1.1 异常处理的思路
系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生。
系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理,如下图:
1.2 异常处理两种方式
使用SpringMVC提供的简单异常处理器SimpleMappingExceptionResover
实现Spring的异常处理接口HandlerExceptionResover自定义自己的异常处理器
1.3 简单异常处理器SimpleMappingExceptionResover
做好前期工作,建立项目添加web,建好三层建构,配置pom并打包方式为war
applicationContext.xml
<context:component-scan base-package="com.itheima"/>
spring-mvc.xml
<!– 组件扫描–>
<context:component-scan base-package="com.itheima.controller"/>
<!–mvc注解驱动–>
<mvc:annotation-driven/>
<!– 配置视图解析器–>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!– 静态资源权限开放–>
<mvc:default-servlet-handler/>
<!– 配置异常处理器–>
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!– <property name="defaultErrorView" value="error"/> 报错跳到默认错误页面–>
<property name="exceptionMappings">
<map>
<entry key="java.lang.ClassCastException" value="error1"/>
<entry key="com.itheima.exception.MyException" value="error5"/>
</map>
</property>
</bean>
web.xml
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!– 前端控制器–>
<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>
<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>
public class DemoServiceImpl implements DemoService{
@Override
public void show1() {
System.out.println("抛出类型转换异常…");
Object str="shanghai";
Integer num=(Integer)str;
}
@Override
public void show2() {
System.out.println("抛出零异常…");
int i=1/0;
}
@Override
public void show3() throws FileNotFoundException {
System.out.println("文件找不到异常…");
InputStream in=new FileInputStream("C:/xxx/xxx");
}
@Override
public void show4() {
System.out.println("空指针异常…");
String str=null;
str.length();
}
@Override
public void show5() throws MyException {
System.out.println("自定义异常…");
throw new MyException();
}
} public class MyException extends Exception{
} @Controller
public class DemoController {
@Autowired
private DemoService demoService;
@RequestMapping("/show")
public String show() throws MyException {
System.out.println("show running…");
demoService.show5();
return "index";
}
}
1.4 异常处理步骤
创建异常处理器类实现HandlerExceptionResolver
配置异常处理器
编写异常页面
测试异常跳转
-
创建异常处理器类实现HandlerExceptionResolver
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
/*
* 参数Exception:异常对象
* 返回值ModelandView:跳转到错误视图信息
*
* */
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;
}
} -
配置异常处理器
<!– 自定义异常处理器–>
<bean class="com.itheima.resolver.MyExceptionResolver"/> -
编写异常页面
<h1>
${info}
</h1> -
测试异常跳转
@Controller
public class DemoController {
@Autowired
private DemoService demoService;@RequestMapping("/show")
public String show() throws MyException {
System.out.println("show running…");
demoService.show5();
return "index";
}
}
1.4 知识要点
异常处理方式
配置简单异常处理器SimpleMappingExceptionResovler
自定义异常处理器
异常处理步骤
创建异常处理类实现HandlerExceptionResolver
配置异常处理器
编写异常页面
测试异常跳转
3-3 Spring+SpringMVC综合练习
Spring练习
1. Spring练习环境搭建
1.1 Spring环境搭建步骤
创建工程(Project&Module)
导入静态页面(见资料jsp页面)
导入需要坐标(见资料中的pomxml)
创建包结构(controller、service、dao、domain、utils)
导入数据库脚本(见资料test.sql)
创建POJO类(见资料User.java和Role.java)
创建配置文件(applicationContext.xml、spring-mvc.xml、jbdc.properties、log4j.properties)
applicationContext加载jbdc.properties、书写数据源、数据库模板对象
spring-mvc注解驱动、静态资源权限开放、视图解析器
web.xml也要配置前端控制器和spring的监听器ContextLoaderListener
1.2 用户和角色的关系
一个用户可以拥有多个角色,一个角色可以有多个用户,是多对多的关系
2.1 角色列表的展示效果
2.2 角色列表的展示步骤分析
点击角色管理菜单发送请求到服务器端(修改角色管理菜单的url地址)
创建RoleController和showList()方法
创建RoleService和showList()方法
创建RoleDao方法和findAll()方法
将查询数据存储到Model中
转发到role-list.jsp页面进行展示
2.3 角色添加的效果
2.4 角色添加的步骤分析
点击列表页面新建按钮跳转到角色添加页面
输入角色信息,点击保存按钮,表单数据提交服务器
编写RoleController的save()方法
编写Roleservice的save()方法
编写RoleDao的save()方法
使用jdbcTemplate保存Role数据到sys_role
跳转回角色列表页面
@Override
public Long save(User user) {
//jdbcTemplate.update("insert into sys_user values (?,?,?,?,?)",null,user.getUsername(),user.getEmail(),user.getPassword(),user.getPhoneNum());
//创建PreparedStatementCreator
PreparedStatementCreator creator=new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
//使用原始jdbc完成有个PreparedStatement的组建
PreparedStatement preparedStatement = connection.prepareStatement("insert into sys_user values (?,?,?,?,?)", PreparedStatement.RETURN_GENERATED_KEYS);
preparedStatement.setObject(1,null);
preparedStatement.setString(2,user.getUsername());
preparedStatement.setString(3,user.getEmail());
preparedStatement.setString(4,user.getPassword());
preparedStatement.setString(5,user.getPhoneNum());
return preparedStatement;
}
};
//创建keyHolder
GeneratedKeyHolder keyHolder=new GeneratedKeyHolder();
jdbcTemplate.update(creator,keyHolder);
long userId = keyHolder.getKey().longValue();
return userId;//返回当前保存用户的id 该id是数据库自动生成的
}
3.1 用户列表的展示效果
3.2 用户列表的展示步骤分析
点击用户管理菜单发送请求到服务器端(修改用户管理菜单的url地址)
创建RoleController和showList()方法
创建RoleService和showList()方法
创建RoleDao和findAll()方法
使用JdbcTemplate完成查询操作
将查询数据存储到Model中
转发到user-list.jsp页面进行展示
3.3 用户添加的效果
3.4 用户添加的步骤分析
①点击列表页面新建按钮跳转到角色添加页面
②输入角色信息,点击保存按钮,表单数据提交服务器
③编写RoleController的save()方法
④编写RoleService的save()方法
⑤编写RoleDao的save()方法
⑥使用JdbcTemplate保存Role数据到sys_role
⑦跳转回角色列表页面
4.1 删除用户的效果
<script>
function delUser(userId) {
if (confirm("您确认要删除吗")){
location.href="${pageContext.request.contextPath}/user/del/"+userId;
}
}
</script>
onclick
<td class="text-center">
<a href="javascript:void(0);" onclick="delUser('${user.id}')" class="btn bg-olive btn-xs">删除</a>
</td>
@RequestMapping("/del/{userId}")
public String del(@PathVariable("userId") Long userId){
userService.del(userId);
return "redirect:/user/list";
}
3-4 MyBatis
01-Mybatis快速入门
1. Mybatis简介
1.1原始jdbc操作(查询数据)
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获得连接
Connection connection=DriverManager.getConnection(url,user,password);
//获得satement
PreparedStatement statement=connection.prepareStatement(sql);
//执行查询
ResultSet resultSet=statement.executeQuery();
//遍历结果集
while(resultSet.next()){
//封装实体
User user=new User();
user.setId(resultSet.getInt("id"));
user.setUserName(resultSet.getString("userName"));
user.setPassword(resultSet.getString("password"));
//user实体封装完毕
sout(user);
}
resultSet.close();
statement.close();
connection.close();
1.1 原始jdbc操作(插入数据)
//模拟实体对象
User user=new User();
user.setId(2);
user.setUserName("tom");
user.setPassword("lucy");
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获得连接
Connection connection=DriverManager.getConnection(url,user,password);
//获得statement
PreparedStatement statement=connection.preparedStatement(sql(?,?…));
//设置占位符参数(?)
statement.setInt(1,user.getId());
statement.setString(2,user.getUserName());
statement.setString(3,user.getPassword());
//执行更新操作
statement.executeUpdate();
//释放资源
statement.close();
connection.close();
1.2 原始jdbc操作的分析
原始jdbc开发存在的问题如下:
数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能
sql语句在代码中硬代码,造成代码不易维护,实际应用sql变化可能较大,sql变动需要改变Java代码(java耦合过度 代码重复多 不够灵活)
查询操作,需要手动将结果集中的数据手动封装到实体中,插入操作时,需要手动将实体的数据设置到sql语句的占位符位置
应对上述问题给出的解决方案:
使用数据库连接池初始化连接资源
将sql语句抽取到xml配置文件中
使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射
1.3 什么是Mybatis
mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql
最后mybatis框架执行sql并将结果映射为Java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。
重点:
mybatis是一个优秀的基于java的持久层框架
mybatis可以隐藏繁杂的api
我们只需要关注我们的sql语句,不需要关注加载驱动、创建连接、创建statement等繁杂的过程
mybatais可以完成实体对象与数据库映射关系
2. Mybatis的快速入门
2.1 Mybatis开发步骤
Mybatis官网地址:MyBatis 3 | Introduction – mybatis
Mybatis开发步骤:
添加Mybatis的坐标
创建user数据表
编写User实体类
编写映射文件UserMapper.xml
编写核心文件SqlMapConfig.xml
编写测试类
2.2 环境搭建
导入Mybatis的坐标和其他相关坐标
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
创建user数据表和编写User实体
编写UserMapper映射文件(在resources.com.itheima.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">
<!–namespace是为了分辨出哪些功能取名 resultType 返回的结果是什么类型进行声明–>
<mapper namespace="userMapper">
<select id="findAll" resultType="com.itheima.domain.User">
select * from user;
</select>
</mapper>
编写sqlMapConfig.xml(在resource目录下)
<?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?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>
</environment>
</environments>
<!– 加载映射文件–>
<mappers>
<mapper resource="com/itheima/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
编写测试代码
public class MybatisTest {
@Test
public void test01() throws IOException {
//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工程对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作 参数:namespace+id
List<User> users = sqlSession.selectList("userMapper.findAll");
System.out.println(users);
sqlSession.close();
}
}
2.4 知识小结
Mybatis开发步骤:
添加Mybatis的坐标(至少要有mybatis和mysql-connector)
创建user数据表
编写User实体类
编写映射文件UserMapper.xml
编写核心文件SqlMapConfig.xml
编写测试类
3. Mybatis的映射文件概述
<?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">
-
这个是映射文件DTD约束头,具有约束和提示作用
<mapper namespace="userMapper">
-
mapper是根标签,userMapper是命名空间,与下面语句的id一起组成查询的标识
<select id="findAll" resultType="com.itheima.domain.User">
-
select 查询操作 可选的还有insert、update、delete
-
resultType 查询结果对应的实体类型
<select id="findAll" resultType="com.itheima.domain.User">
select * from user;
</select>
-
要执行sql语句
4. Mybatis的增删改查操作
4.1 Mybatis的插入数据操作
1. 编写UserMapper映射文件
<!– 插入操作–>
<insert id="save" parameterType="com.itheima.domain.User">
insert into user values(#{id},#{userName},#{password})
</insert>
2. 编写插入实体User的代码
@Test
public void test02() throws IOException {
//模拟user对象
User user=new User();
user.setUserName("zhanananaaaa");
user.setPassword("aaaaaaaa");
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
int row = sqlSession.insert("userMapper.save",user);
System.out.println(row);
//mybatis执行更新操作 提交事务
sqlSession.commit();
sqlSession.close();
}
3. 插入操作注意问题
-
插入语句使用insert标签
-
在映射文件中使用parameterType属性值
-
Sql语句中使用#{实体属性名}方式引用实体的属性值
-
插入操作使用的API是sqlSession.insert("命名空间id,实体对象")
-
插入操作涉及数据库数据变化,所以要使用sqlSession对象显示的提交事务,即sqlSession.commit()
4.2 Mybatis的修改数据操作
1. 编写UserMapper映射文件
<!– 修改操作–>
<update id="update" parameterType="com.itheima.domain.User">
update user set userName=#{userName},password=#{password} where id=#{id}
</update>
2. 编写修改实体User的代码
@Test
public void test03() throws IOException {
//模拟user对象
User user=new User();
user.setId(11);
user.setUserName("vv");
user.setPassword("abc");
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
int row = sqlSession.update("userMapper.update",user);
System.out.println(row);
//mybatis执行更新操作 提交事务
sqlSession.commit();
sqlSession.close();
}
3. 修改操作注意问题
-
修改语句使用update标签
-
修改操作使用的API是sqlSession.update("命名空间.id",实体对象);
4.3 Mybatis的删除数据操作
1. 编写UserMapper映射文件
<!– 删除操作–>
<delete id="delete" parameterType="java.lang.Integer">
delete from user id=#{id}
</delete>
2. 测试代码
@Test
public void test04() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
int row = sqlSession.delete("userMapper.delete",1);
System.out.println(row);
//mybatis执行更新操作 提交事务
sqlSession.commit();
sqlSession.close();
}
3. 删除操作注意问题
-
删除语句使用delete标签
-
sql语句中使用#{任意字符串}方式引用传递的单个参数
-
删除操作使用的API是sqlSession.delete("命名空间.id",Object);
4.4 知识小结
增删查改标签:select、insert、update、delete
resultType是返回结果集 parameterType是占位符对象(实体类和普通类型类)
例子:
查询数据: List<User> userList = sqlSession.selectList("userMapper.findAll");
<select id="findAll" resultType="com.itheima.domain.User">
select * from User
</select>
5. Mybatis核心配置文件概述
5.1 Mybatis核心配置文件层级关系
-
configuration配置
-
properties属性
-
settings设置
-
typeAllassess类型别名
-
type Handlers类型处理器
-
objectFactory对象工厂
-
plugins插件
-
environments环境
-
environment环境变量
-
transactionManager事务管理器
-
dataSource数据源
-
-
-
databaseIdProvider数据库厂商标识
-
mappers映射器
-
5.2 Mybatis常用配置解析
1. environment标签
数据库环境的配置,支持多环境配置
sqlMapConfig.xml
<!– 数据源环境–>
<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?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>
</environment>
</environments>
| default | 在environments里面,代表指定默认的环境名称 |
| id | 指定当前环境的名称 |
| type="JDBC" | 指定事务管理类型是JDBC |
| type="POOLED" | 指定当前数据源类型是连接池 |
| <property name="driver" value="${jdbc.Driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="root"/> <property name="password" value="admin"/> | 数据源配置的基本参数 |
其中,事务管理器(transactionManager)类型有两种:
-
JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于数据源得到的连接来管理事务作用域(常用)
-
MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文)。默认情况下它回关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来阻止它默认的关闭行为。
其中,数据源(dataSource)类型有三种:
-
UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
-
POOLED:这种数据源的实现利用"池"的概念将JDBC连接对象组织起来
-
JNDI:这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。
2. mapper标签
该标签的作用是加载映射的,加载方式有如下几种:
-
使用相对于类路径的资源引用(常用),例如:< mapper resource="com/itheima/mapper/UserMapper.xml">
-
使用完全限定资源定位符(URL),例如:< mapper url="file:///var/mappers/AuthorMapper.xml"/>
-
使用映射器接口实现类的完全限定类名,例如:< mapper class="com/itheima/mapper/UserMapper">
-
将包内映射器接口实现全部注册为映射器,例如< mapper name="com/itheima/mapper">
3. Properties标签
实际开发中,习惯数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件
4. typeAliases标签
类型别名是为java类型设置一个短的名字。原来的类型名称配置如下
<select id="findAll" resultType="com.itheima.domain.User">(全限定名称)
select *from User
</select>
配置typeAliases,为com.itheima.domain.User定义别名为user(有顺序!!!)
<typeAliases>
<typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>
</typeAliases>
<select id="findAll" resultType="user">(user为别名)
select *from User
</select>
上面我们是自定义的别名,mybatis框架已经为我们设置好的一些常用的类型的别名
| string | String |
| long | Long |
| int | Integer |
| double | Double |
| boolean | Boolean |
| … | … |
5.3 知识小结
properties标签:该标签可以加载外部的properties文件
< properties resource="jdbc.properties"/>
typeAliases标签:设置类型别名
< typeAlias type="com.itheima.domain.User" alias="user"/>
mappers标签:加载映射配置
< mapper resource="com/itheima/mapper/UserMapper.xml"/>
environments标签:数据源环境配置标签(多环境)
< environment> < transactionManager> < dataSource>
6. Mybatis相应API
6.1 SqlSession工厂构建器SqlSessionFactoryBuilder
常用API:SqlSessionFactory build(InputStream inputStream)
通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象
String resource="sqlMapConfig.xml";
InputStream inputStream=Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
sqlSessionFactory factory=builder.build(inputStream);
其中,Resource工具类,这个类在org.apache.ibatis.io包中。Resources类帮助你从类路径下、文件系统或一个web URL中加载资源文件。
SqlSessionFactory有多个方法创建SqlSession实例。常用的有如下两个:
| openSession() | 会默认开启一个事务,但事务不会自动提交,也就以为着需要手动提交该事务,更新操作数据才会持久化到数据库中 |
| openSession(boolean autoCommit) | 参数是否自动提交,如果设置为true,那么不需要手动提交事务 |
6.3 SqlSession会话对象
SqlSession实例在Mybatis中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例的方法。执行语句的方法主要有:
<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)
操作事务的方法主要有:
void commit()
void rollback()
02-Mybatis的dao层实现原理
1. Mybatis的Dao层实现
1. 编写UserDao接口
public interface UserDao{
List<User> findAll() throws IOException;
}
2. 编写UserDaoImpl实现
public class UserMapperImpl implements UserMapper {
@Override
public List<User> findAll() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> userList = sqlSession.selectList("userMapper.findAll");
sqlSession.close();
return userList;
}
}
3. 测试传统方式
public class ServiceDemo {
public static void main(String[] args) throws IOException {
//创建dao层对象
UserMapper userMapper=new UserMapperImpl();
List<User> all = userMapper.findAll();
System.out.println(all);
}
}
1.2 代理开发方式
1. 代理开发方式介绍
采用Mybatis的代理开发方式实现DAO层的开发,这种方式是我们后面进入企业的主流。
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
Mapper.xml文件中的namespace与mapper接口的全限定名相同
Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
2. 编写UserMapper接口
3. 测试代理方式
public class ServiceDemo {
public static void main(String[] args) throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> all = userMapper.findAll();
System.out.println(all);
sqlSession.close();
}
}
1.3 知识小结
MyBatis的Dao层实现的两种方式:
-
手动对Dao进行实现:传统开发方式
-
代理方式对Dao进行实现
UserMapper userMapper=sqlSession.getMapper(UserMapper.class)
03_Mybatis映射文件深入(动态SQL)
1. Mybatis映射文件深入
1.1 动态sql语句
1. 动态sql语句概述
mybatis的映射文件中,前面我们的SQL都是比较简单的,有些时候业务逻辑复杂时,我们的SQL是动态变化的,此时在前面的学习中我们的SQL就不能满足要求了。
2. 动态SQL之< if>
我们根据实体类的不太取值,使用不同的SQL语句来进行查询。比如在id如果不为空时可以根据id查询,如果username不同空时还要加入用户名作为条件。这种情况在我们多条件组合查询中经常会碰到。
<select id="findByCondition" parameterType="user" resultType="user">
select * from User
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
</where>
</select>
3. 动态SQL之< foreach>
循环执行sql的拼接操作,例如:select * from user where id in(1,2,5)
public List<User> findByIds(List<Integer> ids);
<select id="findByIds" parameterType="list" resultType="user">
select * from user
<where>
<foreach collection="list" open="id" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
foreach标签的属性含义如下:
< foreach>标签用于遍历集合,它的属性:
-
collection:代表要遍历的集合元素,注意编写时不用写#{} 数组则为array 集合根据名称
-
open:代表语句的开始部分
-
close:代表结束部分
-
item:代表遍历集合的每个元素,生成的变量名
-
sperator:代表分隔符
1.2 SQL片段抽取
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的
<!– sql语句抽取–>
<sql id="selectUser">select * from user</sql>
<select id="findByCondition" parameterType="user" resultType="user">
<include refid="selectUser"></include>
<where>
<if test="id!=0">
#{id}
</if>
<if test="userName!=null">
#{userName}
</if>
<if test="password!=null">
#{password}
</if>
</where>
</select>
1.3 知识小结
Mybatis映射文件配置:
< select>:查询
< insert>:插入
< update>:修改
< delete>:删除
< where>:where条件
< if>:if判断
< foreach>:循环
< sql>:sql片段抽取
04_Mybatis核心配置文件深入
1.Mybatis核心配置文件深入
1.1typeHandler标签
无论是Mybatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成java类型。下表描述了一些默认的类型处理器(截取部分)。
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现org.apache.ibatis.type.TypeHandler接口,或继承一个很便利的类org.apache.ibatis.type.BaseTypeHandler,然后可以选择性地将它映射到一个JDBC类型。例如需求:一个java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。
开发步骤:
定义转换继承类BaseTypeHandler<T>
覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时mysql的字符串类型转换成java的type类型的方法
在Mybatis核心配置文件中进行注册
测试转换是否正确
public class DateTypeHandler extends BaseTypeHandler<Date> {
//将java的Date类型 转换成数据库需要的类型(int)
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
long time = date.getTime();
preparedStatement.setLong(i,time);
}
//将数据库中类型 转换成java类型
//string参数 要转换的字段名称
//Resultset 查询出的结果
@Override
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
//获得结果集中需要的数据(long)转换成Date类型 返回
long aLong = resultSet.getLong(s);
Date date = new Date(aLong);
return date;
}
//将数据库中类型 转换成java类型
@Override
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
//获得结果集中需要的数据(long)转换成Date类型 返回
long aLong = resultSet.getLong(i);
Date date = new Date(aLong);
return date;
}
//将数据库中类型 转换成java类型
@Override
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
//获得结果集中需要的数据(long)转换成Date类型 返回
long aLong = callableStatement.getLong(i);
Date date = new Date(aLong);
return date;
}
}
<!– 自定义注册类型处理–>
<typeHandlers>
<typeHandler handler="com.itheima.handler.DateTypeHandler"/>
</typeHandlers>
1.2 plugins标签
Mybatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
开发步骤:
导入通用PageHelper的坐标
在mybatis核心位置文件中配置PageHelper插件
测试分页数据获取
1.导入通用PageHelper的坐标
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
2. plugins标签
在mybatis核心配置文件中配置PageHelper插件
<!– 配置分页助手插件–>
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
4. 测试分页代码
5. plugins标签的其他参数
| pageInfo.getTotal() | 总条数 |
| pageInfo.getPages() | 总页数 |
| pageInfo.getPageNum() | 当前页 |
| pageInfo.getPageSize() | 每页显示长度 |
| pageInfo.isFirstPage() | 是否第一页 |
| pageInfo.isLastPage() | 是否最后一页 |
1.3 知识小结
Mybatis核心配置文件常用标签:
properties标签:该标签可以加载外部的properties文件
typeAliases标签:设置类型别名
environment标签:数据源环境配置标签
typeHandler标签:配置自定义类型处理器
plugins标签:配置Mybatis的插件
05_Mybatis的多表操作
1. Mybatis多表查询
1. 一对一查询的模型
用户和订单表的关系为,一个用户有多个订单,一个订单只从属一个用户
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
2. 一对一查询的语句
对应的sql语句:select * from orders o,user u where o.uid=u.id;
查询的结果如下:
3.创建Order和User实体
4.创建OrderMapper接口
public interface OrderMapper {
public List<Order> findAll();
}
5. 配置OrderMapper.xml
<mapper namespace="com.itheima.mapper.OrderMapper">
<resultMap id="orderMap" type="order">
<!– 手动指定字段与实体属性的映射关系
column:数据表的字段名称
properties:实体的属性名称
–>
<id 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"/>
<result column="Birthday" property="user.Birthday"/>
or
<!–property当前实体(order)中的属性名称 private User user
javaType:当前实体(order)中的属性的类型(User)–>
<association property="user" javaType="user">
<id column="uid" property="id"/>
<result column="userName" property="user.userName"/>
<result column="password" property="user.password"/>
<result column="Birthday" property="user.Birthday"/>
</association>
</resultMap>
<select id="findAll" resultMap="orderMap">
select *,o.id oid from orders o,user u where o.uid=u.id
</select>
</mapper>
1.2 一对多查询
1. 一对多查询的模型
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单
2. 一对多查询的语句
对应的sql语句:select *,o.id oid from user u ,orders o where u.id=o.uid
查询的结果如下:
3. 修改User实体
4.创建UserMapper接口
5.配置UserMapper.xml
<resultMap id="userMap" type="user">
<id column="uid" property="id"/>
<result column="userName" property="userName"/>
<result column="password" property="password"/>
<result column="Birthday" property="Birthday"/>
<!– 配置集合信息–>
<collection property="orderList" ofType="order">
<id column="oid" property="id"/>
<result column="ordertime" property="ordertime"/>
<result column="total" property="total"/>
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
select *,o.id oid from user u ,orders o where u.id=o.uid
</select>
6.测试结果
1.3 多对多查询
1. 多对多查询的模型
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询用户同时查询出该用户的所有角色
2. 多对多查询语句
对应的sql语句和结果:
3. 创建Role实体,修改User实体
4.添加UserMapper接口方法
5.配置UserMapper.xml
resultMap id="userRoleMap" type="user">
<id column="id" property="id"/>
<result column="userName" property="userName"/>
<result column="password" property="password"/>
<result column="Birthday" property="Birthday"/>
<collection property="roleList" ofType="com.itheima.domain.Role">
<id column="roleId" property="id"/>
<result column="roleName" property="roleName"/>
<result column="roleDesc" property="roleDesc"/>
</collection>
</resultMap>
<select id="findUserAndRoleAll" resultMap="userRoleMap">
select * from user u,sys_user_role ur,sys_role r where u.id=ur.userId and ur.roleId=r.id
</select>
1.4 知识小结
Mybatis多表配置方式:
一对一配置:使用< resultMap>做配置or使用< resultMap>+< association>做配置
一对多配置:使用< resultMap>+< collection>做配置
多对多配置:使用< resultMap>+< collection>做配置
06_Mybatis注解开发
1.Mybatis的注解开发
1.1Mybatis的常用注解
这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result一起使用,封装多个结果集
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
1.2 Mybatis的增删改查
我们先用xml配置方式完成简单的user表的增删改查
UserMapper.xml
<!– 查询单个–>
<select id="findById" parameterType="int" resultType="user">
select * from user where id=#{id}
</select>
<!– 查询全部–>
<select id="findAll" resultType="user">
select * from user
</select>
<!– 插入操作–>
<insert id="save" parameterType="user">
insert into user values (#{id},#{userName},#{password},#{Birthday});
</insert>
<!– 修改操作–>
<update id="update" parameterType="user">
update user set userName=#{userName},password=#{password} where id=#{id}
</update>
<!– 删除操作–>
<delete id="delete" parameterType="int">
delete from user where id=#{id}
</delete>
public interface UserMapper {
public User findById(int id);
public List<User> findAll();
public void save(User user);
public void update(User user);
public void delete(int id);
}
public class MybatisTest {
private UserMapper mapper;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testSave(){
User user=new User();
user.setUserName("lisa");
user.setPassword("123");
mapper.save(user);
}
@Test
public void testUpdate(){
User user=new User();
user.setId(17);
user.setUserName("wisi");
user.setPassword("123");
mapper.update(user);
}
@Test
public void testDelete(){
mapper.delete(5);
}
@Test
public void testFindById(){
User user = mapper.findById(17);
System.out.println(user);
}
@Test
public void testFindFindAll(){
List<User> userList = mapper.findAll();
System.out.println(userList);
}
}
修改Mybatis的核心配置文件,我们使用了注解替代的映射文件,所以我们只需要加载使用了注解的Mapper接口或者指定扫描包含映射关系的接口包
<!– 加载映射关系–>
<mappers>
<!– 指定接口所在的包–>
<package name="com.itheima.mapper"/>
</mappers>
public interface UserMapper {
@Select("select * from user where id=#{id}")
public User findById(int id);
@Select("select * from user")
public List<User> findAll();
@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);
}
1.3 Mybatis的注解实现复杂映射开发
实现复杂关系映射之前我们可以在映射文件中通过配置< resultMap>来实现,使用注解开发后,我们可以使用@Results注解,@Result注解,@One注解,@Many注解组合完成复杂关系的配置
| @Results | 代替的是标签< resultMap>该注解中可以使用单个@Result注解,也可以使用@Result集合。 使用格式:@Results({@Result(),@Result()})或@Results(@Result()) |
| @Result | 代替< id>标签和< Result>标签 @ Result中属性介绍: column:数据库的列名 property:需要装配的属性名 one:需要使用的@One注解(@Result(one=@One( ))) many:需要使用的@Many注解(@Result(many=@many( ))) |
| @One(一对一) | 代替了< assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。 @One注解属性介绍: select:指定用来多表查询的sqlmapper 使用格式:@Result(column="",property="",one=@One(select="")) |
| @Many(多对一) | 代替了< collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象集合。 使用格式:@Result(property="",column="",many=@Many(select="")) |
1.4 一对一查询
1. 一对一查询的模型
用户表和订单表的关系wei,一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询一个订单,于此同时查询出该订单所属的yonghu
2. 一对一查询语句
对应的sql语句:select *,o.id oid from orders o,user u where o.uid=u.id
查询的结果如下:
3.创建Order和User的实体(如上的笔记可知)
4.创建OrderMapper接口和使用注解配置
public interface OrderMapper {
@Select("select *,o.id oid from orders o,user u where o.uid=u.id")
@Results({
@Result(column = "oid", property = "id"),
@Result(column = "ordertime", property = "ordertime"),
@Result(column = "total", property = "total"),
@Result(column = "uid", property = "user.id"),
@Result(column = "userName", property = "user.userName"),
@Result(column = "password", property = "user.password"),
@Result(column = "Birthday", property = "user.Birthday"),
})
public List<Order> findAll();
}
public interface OrderMapper {
@Select("select * from orders")
@Results({
@Result(column = "id", property = "id"),
@Result(column = "ordertime", property = "ordertime"),
@Result(column = "total", property = "total"),
@Result(
property = "user",//要封装的属性名称
column = "uid",//根据那个字段去查询user表的数据
javaType = User.class, //要封装的实体类型
//select属性 代表查询那个接口的方法获得数据
one = @One(select = "com.itheima.mapper.UserMapper.findById")
)
})
public List<Order> findAll();
}
5.测试结果
1.5 一对多查询
1.一对多查询的模型
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单
2.一对多的查询语句
对应的sql语句:
select*from user;
select * from orders where uid=查询出用户的id;
3.修改实体(由上笔记可知)
4.创建UserMapper接口和使用注解
usermapper.java
@Select("select * from orders where uid=#{id}")
public List<Order> findByUid(int uid);
@Select("select * from user")
@Results({
@Result(column = "id",property = "id"),
@Result(column = "userName",property = "userName"),
@Result(column = "password",property = "password"),
@Result(column = "Birthday",property = "Birthday"),
@Result(
property = "orderList",
column = "id",
javaType = List.class,
many = @Many(select = "com.itheima.mapper.UserMapper.findByUid")
)
})
public List<User> findUserAndOrderAll();
6.测试结果
1.6多对多查询
1.多对多查询的模型
用户和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询用户同时查询出该用户的所有角色
与一对多差不多,区别就是要使用一个中间表
2.多对多查询语句
对应的sql语句:
select * from user;
select * from role r,user_role ur where r.id=ur.role_id and ur.user_id=用户的id
3.创建Role实体,修改User实体(由上面笔记可知)
4.添加UserMapper和RoleMapper的接口方法并注解
public interface RoleMapper {
@Select("select * from sys_user_role ur,sys_role r where ur.roleId=r.id and ur.userId=#{uid}")
public List<Role> findByUid(int uid);
}
@Select("select * from user")
@Results({
@Result(id = true,column = "id",property = "id"),
@Result(column = "userName",property = "userName"),
@Result(column = "password",property = "password"),
@Result(column = "Birthday",property = "Birthday"),
@Result(
property = "roleList",
javaType = List.class,
column = "id",
many =@Many(select = "com.itheima.mapper.RoleMapper.findByUid")
)
})
public List<User> findUserAndRoleAll();
5.测试结果
SSM整合
1.1准备工作
1.原始方式整合
2.创建Maven工程
3.导入Maven的坐标
<dependencies>
<!– spring相关–>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!– 数据库和mybatis–>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!– 测试–>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!– servlet和jsp–>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
</dependency>
<!– 数据源和标签库–>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.4</version>
</dependency>
</dependencies>
<build>
<finalName>itheima_ssm</finalName>
<pluginManagement><!– lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) –>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<!– see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging –>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
4.编写实体类
5.编写Mapper接口
public interface AccountMapper {
@Insert("insert into account values(#{id},#{name},#{money})")
public void save(Account account);
@Select("select * from account")
public List<Account> findAll();
}
6.编写Service接口 编写Service接口实现
public interface AccountService {
public void save(Account account);
public List<Account> findAll();
}
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Override
public void save(Account account) {
try {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
AccountMapper accountMapper=sqlSession.getMapper(AccountMapper.class);
accountMapper.save(account);
sqlSession.commit();
sqlSession.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public List<Account> findAll() {
List<Account> accountList=new ArrayList<Account>();
try {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
AccountMapper accountMapper=sqlSession.getMapper(AccountMapper.class);
accountList = accountMapper.findAll();
sqlSession.commit();
sqlSession.close();
} catch (IOException e) {
e.printStackTrace();
}
return accountList;
}
}
7.编写Controller
@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("findAll")
public ModelAndView findAll(){
ModelAndView modelAndView=new ModelAndView();
List<Account> accountList = accountService.findAll();
modelAndView.addObject(accountList);
modelAndView.setViewName("AccountList");
return modelAndView;
}
}
8.编写添加页面
<body>
<h1>添加账户信息表单</h1>
<form name="accountForm" action="${pageContext.request.contextPath}/account/save" method="post">
账户名称:<input type="text" name="name"><br>
账户金额:<input type="text" name="money"><br>
<input type="submit" value="保存"><br>
</form>
</body>
9.编写列表页面
<body>
<h1>展示账户数据列表</h1>
<table border="1">
<tr>
<th>账户id</th>
<th>账户名称</th>
<th>账户金额</th>
</tr>
<c:forEach items="${accountList}" var="account">
<tr>
<td>${account.id}</td>
<td>${account.name}</td>
<td>${account.money}</td>
</tr>
</c:forEach>
</table>
</body>
10.编写相应配置文件
-
Spring配置文件:applicationContext.xml
<!–组件扫描–>
<context:component-scan base-package="com.itheima">
<!– 排除controller扫描–>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan> -
SpringMVC配置文件:spring-mvc.xml
<!– mvc组件扫描–>
<context:component-scan base-package="com.itheima.controller"/>
<!– 配置mvc注解驱动–>
<mvc:annotation-driven/>
<!– 内部资源视图解析器–>
<bean id="InternalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/pages/" p:suffix=".jsp" />
<!– 开发静态资源访问权限–>
<mvc:default-servlet-handler/> -
Mybatis核心文件:sqlMapConfig.xml
<configuration>
<properties resource="jdbc.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.itheima.mapper"/>
</mappers>
</configuration> -
数据库连接信息文件:jdbc.properties
-
web.xml文件
<!– spring的监听器–>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!– springmvc前端控制器–>
<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>
<!– utf-8的乱码过滤器–>
<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>
<jsp-config>
<taglib>
<taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>
<taglib-location>/WEB-INF/lib/c.tld</taglib-location>
</taglib>
</jsp-config> -
日志文件:log4j.xml
1.2 Spring整合Mybatis
1.整合思路
2.将SqlSessionFactory配置到Spring容器中,且把数据源和jdbc文件声明放在容器中
applicationContext.xml
<!– 加载properties文件–>
<context:property-placeholder location="classpath:jdbc.properties"/>
<!– 配置数据源–>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="${jdbc.driver}" p:jdbcUrl="${jdbc.url}"
p:user="${jdbc.username}" p:password="${jdbc.password}"
/>
<!– 配置sessionFactory–>
<!– 加载mybatis核心文件–>
<bean id="SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
p:dataSource-ref="dataSource" p:configLocation="classpath:sqlMapConfig-spring.xml">
</bean>
3.扫描Mapper,让Spring容器产生Mapper实现类
applicationContext.xml
<!– 加载扫描mapper所在的包 为mapper创建实现类–>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:basePackage="com.itheima.mapper"/>
4.Service代码修改
@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() {
List<Account> accountList = accountMapper.findAll();
return accountList;
}
}
5.配置声明式事务控制
代表SqlSession的commit和close
<!– 声明式事务控制–>
<!– 平台事务管理器–>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"
/>
<!– 配置事务增强–>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!– 事务的AOP织入–>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))"/>
</aop:config>
网硕互联帮助中心





评论前必须登录!
注册