系列文章目录
快乐的一天从Ctrl+c,Ctrl+v开始。
文章目录
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、spring简介
Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
二、MVC设计模式
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
三、springmvc简介
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,可以选择是使用内置的 Spring Web 框架还可以是 Struts 这样的 Web 框架。
ORM程序技术
Object-Relationl Mapping,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。
ORM的出现充当了对象和数据库层次的桥梁作用。
优点:
提高我们的开发效率,开发人员技术参差不齐,大神写的sql优化非常好。
五、MyBatis简介
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
六、搭建配置spring
引入依赖
maven仓库查询网址:MavenRepository
spring基础包:
spring-core:Core模块主要包含Spring框架基本的核心工具类,Spring的其他组件要都要使用到这个包里的类,Core模块是其他组件的基本核心
spring-beans:包含访问配置文件、创建和管理bean以及进行IOC/DI操作相关的所有类
spring-context:Spring的上下文即IOC容器,通过上下文可以获得容器中的Bean
spring-expression:EL表达式语言用于在运行时查询和操纵对象
七、编写代码测试
```java org.springframework spring-core 5.2.13.RELEASE org.springframework spring-beans 5.2.13.RELEASE org.springframework spring-context 5.2.13.RELEASE org.springframework spring-expression 5.2.13.RELEASE ``` 刷新maven等待自动下载 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210413210816333.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81NTk0NjI4Ng==,size_16,color_FFFFFF,t_70#pic_center)官方给出的配置文件内容如下:<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd
">
<!-- bean definitions here -->
</beans>
把他复制到我们的配置文件后左上角会提示“Application context not configured for this file”,点击“Configure application context”,点击OK
编写代码测试
接口类
新建接口类
编写接口类
package services;
public interface UserService {
public void saveUser();
}
实现类
测试类
新建实现类
编写测试类main方法
public class DemoTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService service = (UserService) context.getBean("userService");
service.saveUser();
}
}
Alt+Enter键导包
测试结果
可以看到控制台打印输出 证明确实从容器中获取到了userService实例
八、 执行过程分析
BeanFactory
BeanFactory是基础类型的IOC容器,是管理bean容器的根接口,并提供了完整的IOC服务支持
简单来说BeanFactory就是一个管理Bean的工厂,它主要负责初始化各种Bean、调用生命周期等方法
ApplicationContext
ApplicationContext被称为应用上下文,是BeanFactory接口的子接口,在其基础上提供了其他的附加功能,扩展了BeanFactory接口
ClassPathXmlApplicationContext
ClassPathXmlApplicationContext是ApplicationContext的实现类,也在其基础上加了许多附加功能
该类从类路径ClassPath中寻找指定的XML配置文件,找到并完成对象实例化工作
其构造器源码如下:
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(
String[] configLocations,
boolean refresh,
@Nullable ApplicationContext parent)
throws BeansException {
super(parent);
// 加载项目中的Spring配置文件
setConfigLocations(configLocations);
if (refresh) {
// 刷新容器
refresh();
}
}
构造器的作用:
1.调用setConfigLocations方法加载项目中的Spring配置文件
2.调用refresh方法刷新容器(bean的实例化就在这个方法中)
refresh方法源码如下:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 准备容器刷新
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 准备bean工厂对象
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 加载配置文件中的所有bean标签
postProcessBeanFactory(beanFactory);
......
......
// Instantiate all remaining (non-lazy-init) singletons.
// 完成此上下文的bean工厂初始化,初始化所有剩余的单例bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 完成容器刷新
finishRefresh();
} catch (BeansException ex) {
......
} finally {
......
}
}
}
refresh方法的作用:
准备容器刷新
准备bean工厂对象
加载配置文件中的所有bean标签
完成bean工厂实例化
完成容器刷新
context.getBean()
context.getBean()方法是通过配置文件中声明的bean标签id属性获取容器内的实例
九、spring IOC&DI
IOC(Inversion of Control 控制反转)是面向对象编程中的一种设计模式
其最常见的方式叫做DI(Dependency Injection 依赖注入)
通过控制反转,将实例化对象的控制权,由手动的new变成了Spring框架通过反射机制实例化
需要使用的时候,依赖通过配置文件以及注解的方式注入到对象中
项目常用后端代码结构 如下图所示:
我们新建Maven项目名为“spring-ioc“,设置好Maven版本、配置文件以及Maven仓库
以查询User数据为例对比IOC的引入前后程序耦合性
引入IOC之前
引入IOC之前
代码实现
User模块实体类:User.java
package entity;
public class User {
private Integer id;
private String name;
private Integer gender;
// 省略getter&setter方法
}
User模块视图类:UserVo.java
package vo;
public class UserVo {
private Integer id;
private String name;
private Integer gender;
private String genderName;
// 省略getter&setter方法
public UserVo() {
}
public UserVo(User user) {
this.id = user.getId();
this.name = user.getName();
this.gender = user.getGender();
}
// 省略toString方法
}
User模块Dao层:UserDao.java
package dao;
public interface UserDao {
User getEntity(Integer id);
}
User模块Dao层实现类:UserDaoImpl.java
package dao.impl;
public class UserDaoImpl implements UserDao {
public User getEntity(Integer id) {
// 此处应该从数据库查询值 方便起见直接返回一个固定对象
User user = new User();
user.setId(1);
user.setName("Anne");
user.setGender(0);
return user;
}
}
User模块Service层:UserService.java
package services;
public interface UserService {
UserVo getVo(Integer id);
}
User模块Service层实现类:UserServiceImpl.java
package services.impl;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserVo getVo(Integer id) {
// 手动实例化Dao
userDao = new UserDaoImpl();
// 执行Dao层方法
User user = userDao.getEntity(id);
// 省略业务逻辑处理。。。
UserVo userVo = new UserVo(user);
userVo.setGenderName(userVo.getGender() == 0 ? "female" : "male");
return userVo;
}
}
引入IOC(XML)
要想使用SpringIOC首先需要导入Spring框架基础包并且添加Spring核心配置文件
将依赖交给Spring的beanFactory管理
<bean id="userDao" class="dao.impl.UserDaoImpl"/>
<bean id="userService" class="services.impl.UserServiceImpl"/>
<bean id="userController" class="controller.UserController"/>
User模块测试类:UserTest.java
读取配置文件刷新Spring容器
Controller由手动实例化改为从Spring容器拿取
把ApplicationContext传到Controller层继续使用
public class UserTest {
public static void main(String[] args) {
// 读取配置文件刷新Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 从Spring容器拿Controller
UserController userController = (UserController) context.getBean("userController");
// 执行Controller层方法,因为之后还需要用到context对象,故下传
UserVo userVo = userController.getVo(1, context);
System.out.println(userVo);
}
}
User模块Controller层:UserController.java
Service由手动实例化改为从Spring容器拿取
把ApplicationContext传到Service层继续使用
package controller;
public class UserController {
private UserService userService;
public UserVo getVo(Integer id, ApplicationContext context) {
// 从Spring容器拿Service
userService = (UserService) context.getBean("userService");
// 执行Service层方法,因为之后还需要用到context对象,故下传
return userService.getVo(id, context);
}
}
User模块Service层实现类:UserServiceImpl.java
Dao由手动实例化改为从Spring容器拿取
package services.impl;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserVo getVo(Integer id, ApplicationContext context) {
// 从Spring容器拿Dao
userDao = (UserDao) context.getBean("userDao");
// 执行Dao层方法
User user = userDao.getEntity(id);
// 省略业务逻辑处理。。。
UserVo userVo = new UserVo(user);
userVo.setGenderName(userVo.getGender() == 0 ? "female" : "male");
return userVo;
}
}
测试结果
测试结果如下图所示:
表示已经将所有的依赖由手动实例化改为从Spring容器拿取
缺点分析:
因为每一个类的实例化都需要一个bean标签,一个大型工程有很多类,配置文件的内容未免过于臃肿,维护成本高
解决方式
使用注解形式实现SpringIOC
XML改注解(IOC)
核心配置文件修改
context-component-scan标签Spring框架自定义的xml标签,通过base-package属性指明需要被自动扫描实例化的类所在位置
如下代码所示,我们在dao、services、controller下的类是需要扫描自动注入容器的
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
<!-- bean definitions here -->
<context:component-scan base-package="dao"/>
<context:component-scan base-package="services"/>
<context:component-scan base-package="controller"/>
</beans>
修改好后运行项目发现context.getBean()代码报错
说明不是在base-package下的所有类都会自动注入到容器,而是要搭配注解使用
常用注解介绍
@Component:一般用于通用组件类上使用的注解
@Service:一般用于业务逻辑层上使用的注解
@Controller:一般用于流程控制层上使用的注解
@Repository:一般用于数据持久层上使用的注解
依次添加注解,添加之后运行再次报错找不到bean
其实我们在添加注解后,Spring会默认给每个bean设置id,值为类名首字母改为小写
这次报错原因就是找不到名为”userService“的bean
解决办法就是在注解时设置bean的id,保证可以找到bean
测试结果如下
引入DI
上面所有的内容都是将对象放入Spring容器中
那么放入之后的使用呢,目前都是使用ApplicationContext拿取容器中的对象
接下来讲解如何使用注解实现依赖注入
常用注解介绍
@Autowired注解自动按照类型注入
会从容器中寻找符合依赖类型的实例,但是也有缺点:
因为时按照类型匹配,如果找不到匹配的实例也会抛出异常
如果容器中有多个匹配的类型也会抛出异常,需要指定引入的实例id
@Qualifier注解作用是在按照类型注入的基础之上,再按照Bean的id注入。所以如果是使用了@Autowire注解自动注入,但是容器中却有多个匹配的实例,可以搭配此注解,指定需要注入的实例id
@Resource注解作用是指定依赖按照id注入,还是按照类型注入。当只使用注解但是不指定注入方式的时候,默认按照id注入,找不到再按照类型注入。
代码实现
User模块Controller层:UserController.java
package controller;
@Controller
public class UserController {
// 改为自动注入
@Autowired
private UserService userService;
public UserVo getVo(Integer id, ApplicationContext context) {
// 执行Service层方法,因为之后还需要用到context对象,故下传
return userService.getVo(id, context);
}
}
User模块Dao层实现类:UserDaoImpl.java
去除指定bean id,改为默认bean id(userDaoImpl)
package dao.impl;
// 改为默认bean id“userDaoImpl”
@Repository
public class UserDaoImpl implements UserDao {
public User getEntity(Integer id) {
// 此处应该从数据库查询值 方便起见直接返回一个固定对象
User user = new User();
user.setId(1);
user.setName("Anne");
user.setGender(0);
return user;
}
}
测试结果
测试结果如下图所示
表示
@Autowired注解已将UserService依赖自动注入UserController
@Qualifier注解已指定UserDao依赖的bean id,并使用@Autowired注解自动注入UserServiceImpl
十、springAOP
代理模式
Spring的核心特性就是IOC和AOP,之前整理了SpringIOC,这篇文章就来写一下SpringAOP(Aspect Oriented Programming),即:面向切面编程
面向切面编程是指通过预编译和运行期动态代理的方式实现在不修改源代码的情况下给程序动态统一添加功能的一种技术
在学习SpringAOP之前我们需要了解什么是代理模式
文中提到的基于接口的JDK动态代理与基于子类的CGLib动态代理两种动态代理的方式都是实现SpringAOP的基础
在spring中,虽然引入了AspectJ的语法,但是他本质上使用的是动态代理的方式,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式
如果目标对象有接口,优先使用JDK 动态代理,如果目标对象没有接口,则使用CGLib动态代理
简单转账功能
新建Maven项目名为“spring-aop“,设置好Maven版本、配置文件以及Maven仓库
准备数据
# 删除spring_aop数据库
drop database if exists spring_aop;
# 创建spring_aop数据库
create database spring_aop;
# 使用spring_aop数据库
use spring_aop;
# 创建account表
create table account (
id int(11) auto_increment primary key,
accountNum varchar(20) default NULL,
money int(8) default 0
);
# 新增数据
insert into account (accountNum, money) values
("622200001",1000),("622200002",1000);
导包
导入Spring基础包
导入操作数据库、连接数据库、测试需要的包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.13.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
核心配置文件
配置自动扫包
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
<!-- bean definitions here -->
<context:component-scan base-package="dao"/>
<context:component-scan base-package="services"/>
<context:component-scan base-package="utils"/>
</beans>
配置数据源
<!--配置QueryRunner-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--连接数据库的必备信息-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_aop"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
代码编写
数据库连接工具类:ConnectionUtils.java
package utils;
@Component
public class ConnectionUtils {
private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
@Autowired
private ComboPooledDataSource dataSource;
/**
* 获得当前线程绑定的连接
*
* @return
*/
public Connection getThreadConnection() {
try {
// 看线程是否绑了连接
Connection conn = tl.get();
if (conn == null) {
// 从数据源获取一个连接
conn = dataSource.getConnection();
// 和线程局部变量 绑定
tl.set(conn);
}
// 返回线程连接
return tl.get();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 把连接和当前线程进行解绑
*/
public void remove() {
tl.remove();
}
}
Account模块实体类:Account.java
package dao;
public interface AccountDao {
/**
* 更新
*
* @param account
*/
void updateAccount(Account account);
/**
* 根据编号查询账户
*
* @param accountNum
* @return 如果没有结果就返回null,如果结果集超过一个就抛异常,如果有唯一的一个结果就返回
*/
Account findAccountByNum(String accountNum);
}
Account模块Dao层实现类:AccountDaoImpl.java
package dao.impl;
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
// 数据库查询工具类
@Autowired
private QueryRunner runner;
// 数据库连接工具类
@Autowired
private ConnectionUtils connectionUtils;
/**
* 更新
*
* @param account
*/
public void updateAccount(Account account) {
try {
runner.update(connectionUtils.getThreadConnection(),
"update account set accountNum=?,money=? where id=?",
account.getAccountNum(), account.getMoney(), account.getId());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 根据编号查询账户
*
* @param accountNum
* @return 如果没有结果就返回null,如果结果集超过一个就抛异常,如果有唯一的一个结果就返回
*/
public Account findAccountByNum(String accountNum) {
List<Account> accounts = null;
try {
accounts = runner.query(connectionUtils.getThreadConnection(),
"select * from account where accountNum = ? ",
new BeanListHandler<Account>(Account.class),
accountNum);
} catch (SQLException e) {
throw new RuntimeException(e);
}
if (accounts == null || accounts.size() == 0) {
// 如果没有结果就返回null
return null;
} else if (accounts.size() > 1) {
// 如果结果集超过一个就抛异常
throw new RuntimeException("结果集不唯一,数据有问题");
} else {
// 如果有唯一的一个结果就返回
return accounts.get(0);
}
}
}
Account模块Service层:AccountService.java
package services;
public interface AccountService {
/**
* 转账
*
* @param sourceAccount 转出账户
* @param targetAccount 转入账户
* @param money 转账金额
*/
void transfer(String sourceAccount, String targetAccount, Integer money);
}
Account模块Service层实现类:AccountServiceImpl.java
package services.impl;
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
/**
* 转账
*
* @param sourceAccount 转出账户
* @param targetAccount 转入账户
* @param money 转账金额
*/
public void transfer(String sourceAccount, String targetAccount, Integer money) {
// 查询原始账户
Account source = accountDao.findAccountByNum(sourceAccount);
// 查询目标账户
Account target = accountDao.findAccountByNum(targetAccount);
// 原始账号减钱
source.setMoney(source.getMoney() - money);
// 目标账号加钱
target.setMoney(target.getMoney() + money);
// 更新原始账号
accountDao.updateAccount(source);
// 更新目标账号
accountDao.updateAccount(target);
System.out.println("转账完毕");
}
}
Account模块测试类:AccountTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountTest {
@Autowired
private AccountService accountService;
@Test
public void testTransfer() {
accountService.transfer("622200001", "622200002", 100);
}
}
执行结果
控制台打印结果
引入代理模式解决事务
实现思路介绍
创建一个工具类,目的是用于管理数据库的事务,提供事务的开启,提交,回滚等操作;
创建一个代理处理器类,目的是生成转账实现类的代理对象,对转账的业务方法提供增强,主要是在数据操作之前,和操作之后干点事,
在 Spring 的配置文件中,通过 xml 文件的标签实例化管理事务的工具类和生成代理对象的处理器类。
代码实现
事务管理器:TransactionManager.java
此工具类主要作用是对数据库连接实现事务的开启,提交以及回滚
至于何时开启、提交、回滚事务,根据业务场景需要调用该类的方法即可
package transaction;
@Component
public class TransactionManager {
// 数据库连接工具类
@Autowired
private ConnectionUtils connectionUtils;
/**
* 开启事务
*/
public void beginTransaction() {
try {
System.out.println("开启事务");
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 提交事务
*/
public void commit() {
try {
System.out.println("提交事务");
connectionUtils.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 回滚事务
*/
public void rollback() {
try {
System.out.println("回滚事务");
connectionUtils.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 释放连接
*/
public void release() {
try {
System.out.println("释放连接");
connectionUtils.getThreadConnection().close();
} catch (SQLException e) {
e.printStackTrace();
}
connectionUtils.removeConnection();
}
}
事务代理工具类:TransactionProxyUtils
此类的核心代码是getAccountService方法,该方法返回代理业务类示例
在代理对象的invoke方法内部,实现对原始被代理对象的增强
package utils;
@Component
public class TransactionProxyUtils {
//被代理的业务类接口
@Autowired
private AccountService accountService;
//提供事务管理的工具类
@Autowired
private TransactionManager transactionManager;
/**
* 获取AccountService代理对象
*
* @return
*/
public AccountService getAccountService() {
return (AccountService) Proxy.newProxyInstance(
accountService.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 添加事务的支持
*
* @param proxy 被代理的对象实例本身
* @param method 被代理对象正在执行的方法对象
* @param args 正在访问的方法参数对象
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//
Object rtValue = null;
try {
// 执行操作前开启事务
transactionManager.beginTransaction();
// 执行操作
rtValue = method.invoke(accountService, args);
// 执行操作后提交事务
transactionManager.commit();
// 返回结果
return rtValue;
} catch (Exception e) {
// 捕捉到异常执行回滚操作
transactionManager.rollback();
throw new RuntimeException(e);
} finally {
// 最终释放连接
transactionManager.release();
}
}
});
}
}
核心配置文件:applicationContext.xml
添加事务管理bean
<context:component-scan base-package=“transaction”/>
配置代理Service
<!--配置代理的service-->
<bean id="transactionProxyAccountService" factory-bean="transactionProxyUtils" factory-method="getAccountService"/>
Account模块测试类:AccountTest.java
将原本引入的AccountService实例改为AccountService的事务代理对象
@Qualifier(“transactionProxyAccountService”)
执行结果
首先将数据库中两账户余额都改为1000
update account set money = 1000;
引入AOP(XML)
相关概念
使用Spring的AOP替代代理类。先回顾下AOP的概念
AOP是一种编程设计模式,是一种编程技术,使用AOP后通过修改配置即可实现增加或者去除某些附加功能
学习AOP中的常用术语:
Join point(连接点)
所谓连接点是指那些可以被拦截到的点
在Spring中这些点指的是方法,可以看作正在访问的,或者等待访问的那些需要被增强功能的方法
Spring只支持方法类型的连接点
Pointcut(切入点)
切入点是一个规则,定义了我们要对哪些Joinpoint进行拦截
因为在一个程序中会存在很多的类,每个类又存在很多的方法,Pointcut来标记哪些方法会应用AOP对该方法做功能增强
Advice(通知)
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。也就是对方法做的增强功能。通知分为如下几类:
前置通知:在连接点之前运行的通知类型,它不会阻止流程进行到连接点,只是在到达连接点之前运行该通知内的行为
后置通知:在连接点正常完成后要运行的通知,正常的连接点逻辑执行完,会运行该通知
最终通知:无论连接点执行后的结果如何,正常还是异常,都会执行的通知
异常通知:如果连接点执行因抛出异常而退出,则执行此通知
环绕通知:环绕通知可以在方法调用之前和之后执行自定义行为
Target(目标)
Target指的是代理的目标对象,更通俗的解释就是:AOP对连接点方法做增强,底层是代理模式生成连接点所在类的代理对象,那么连接点所在的类,就是被代理的类称呼为Target
Aspect(切面)
切面本质是一个类,只不过是个功能类,作为整合AOP的切入点和通知。
一般来讲,需要在Spring的配置文件中配置,或者通过注解来配置
Weaving(织入)
织入是一种动作的描述,在程序运行时将增强的功能代码也就是通知,根据通知的类型(前缀后缀等…)放到对应的位置,生成代理对象
Proxy(代理)
一个类被AOP织入增强后,产生的结果就是代理类
代码实现
在执行原始业务类前对方法增强也就是SpringAOP中所谓的前置通知,对原始业务类中的方法执行之后的增强行为就是后置通知
而一旦出现异常,那么所做的动作就是异常通知。本案例使用几种通知,来实现事务的控制。
删除事务代理工具类:TransactionProxyUtils.java
导入aspectjweaver包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
org.aspectj aspectjweaver 1.9.3 配置文件中添加 AOP 的相关配置
<!-- aop相关的节点配置 -->
<aop:config>
<!-- 切入点 表示哪些类的哪些方法在执行的时候会应用Spring配置的通知进行增强 -->
<aop:pointcut expression="execution ( * services.*.*(..))" id="pc"/>
<!-- 配置切面类的节点 作用主要就是整合通知和切入点 -->
<aop:aspect ref="transactionManager">
<aop:before method="beginTransaction" pointcut-ref="pc"/>
<aop:after-returning method="commit" pointcut-ref="pc"/>
<aop:after method="release" pointcut-ref="pc"/>
<aop:after-throwing method="rollback" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
修改测试类代码
XML改注解(AOP)
使用注解介绍
@Aspect
此注解用于表明某个类为切面类,而切面类的作用我们之前也解释过,用于整合切入点和通知
@Pointcut
此注解用于声明一个切入点,表明哪些类的哪些方法需要被增强
@Before 前置通知
在连接点之前运行的通知类型,它不会阻止流程进行到连接点,只是在到达连接点之前运行该通知内的行为
@AfterReturning 后置通知
在连接点正常完成后要运行的通知,正常的连接点逻辑执行完,会运行该通知
@After 最终通知
无论连接点执行后的结果如何,正常还是异常,都会执行的通知
@AfterThrowing 异常通知
如果连接点执行因抛出异常而退出,则执行此通知
代码实现
删除XML中的AOPXML配置并注解代理模式
<!-- 注解 开启代理模式 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
注释事务管理器类:TransactionManager.java
package transaction;
@Component
@Aspect
public class TransactionManager {
// 数据库连接工具类
@Autowired
private ConnectionUtils connectionUtils;
@Pointcut("execution(* services.*.*(..))")
private void transactionPointcut() {
}
/**
* 开启事务
*/
@Before("transactionPointcut()")
public void beginTransaction() {
try {
System.out.println("开启事务");
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 提交事务
*/
@AfterReturning("transactionPointcut()")
public void commit() {
try {
System.out.println("提交事务");
connectionUtils.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 回滚事务
*/
@AfterThrowing("transactionPointcut()")
public void rollback() {
try {
System.out.println("回滚事务");
connectionUtils.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 释放连接
*/
@After("transactionPointcut()")
public void release() {
try {
System.out.println("释放连接");
connectionUtils.getThreadConnection().close();
} catch (SQLException e) {
e.printStackTrace();
}
connectionUtils.removeConnection();
}
}
执行结果
控制台打印结果
mybatis工程搭建
JDBC连接及操作数据库
连接数据库步骤
第一步:注册驱动
第二步:获取连接
第三步:获取statement对象
第四步:执行SQL语句返回结果集
第五步:遍历结果集
第六步:关闭连接释放资源
传统方式连接数据库
public class JDBCDemo {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//1.注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/ssm", "root", "root");
//3.获取Statement对象
PreparedStatement preparedStatement = connection.prepareStatement("select * from tb_user");
//4.执行SQL语句返回结果集
ResultSet resultSet = preparedStatement.executeQuery();
//5.遍历结果集
while (resultSet.next()) {
System.out.println(resultSet.getString("username"));
}
//6.释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
用这种方式连接数据库存在一个重要的问题:
注册驱动时,当前类和MySQL的驱动类有很强的依赖关系。
当我们没有驱动类的时候,连编译都不能通过。
这种调用者与被调用者之间的依赖关系,就叫做程序的耦合,耦合分为高耦合(紧密联系)和低耦合(松散联系)
我们在开发中,理想的状态应该是编译时不依赖,运行时才依赖。
要做到编译时不依赖,就需要使用反射来创建类对象。
即将注册驱动部分的代码稍作修改如下:
编译时不依赖的数据库连接
public class JDBCDemo {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//1.注册驱动
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/ssm", "root", "root");
//3.获取Statement对象
PreparedStatement preparedStatement = connection.prepareStatement("select * from tb_user");
//4.执行SQL语句返回结果集
ResultSet resultSet = preparedStatement.executeQuery();
//5.遍历结果集
while (resultSet.next()) {
System.out.println(resultSet.getString("username"));
}
//6.释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
这样做的好处是,我们的类中不再依赖具体的驱动类,此时就算删除mysql的驱动jar包依然可以通过编译,只不过因为没有驱动类所以不能运行罢了。
不过,此处还有一个问题,就是我们反射类对象的全限定类名称是在java类中写死的,数据库的端口号、用户名密码也是写死的,一旦要修改就等于是要修改源码。
自己小打小闹写的代码改源码什么的还好说,但如果是上线项目,改源码势必要停服务器重新编运行。
这么看来这个问题造成的后果很严重,其实它的解决方法也很简单,使用配置文件配置数据库连接信息就可以啦。
使用配置文件连接数据库
配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.username=root
jdbc.password=root
代码实现
public class JDBCDemoPro {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//读取配置文件db.properties
Properties prop = new Properties();
prop.load(new FileInputStream("db.properties"));
//获取配置文件中的相关参数值
String driver = prop.getProperty("jdbc.driver");
String url = prop.getProperty("jdbc.url");
String user = prop.getProperty("jdbc.username");
String password = prop.getProperty("jdbc.password");
//1.注册驱动
Class.forName(driver);
//2.获取连接
Connection connection = DriverManager.getConnection(url, user, password);
//3.获取Statement对象
PreparedStatement preparedStatement = connection.prepareStatement("select * from tb_user");
//4.执行SQL语句返回结果集
ResultSet resultSet = preparedStatement.executeQuery();
//5.遍历结果集
while (resultSet.next()) {
System.out.println(resultSet.getString("username"));
}
//6.释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
引入mybatis依赖
maven仓库查询网址:MavenRepository
mybatis:MyBatis基础包
logback-classic:日志依赖
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.3.0-alpha5</version>
<scope>test</scope>
</dependency>
刷新maven等待自动下载
libraries中有了所有导入的包表示依赖引入完成
编程式配置方法
MyBatis官方文档中并未详细的介绍如何编程式使用MyBatis,因为编程式配置方法代码有点复杂
但是大家没必要被代码吓退,因为在实际的开发中几乎没有机会去写这段代码,一般都是通过配置文件来拿到配置然后开启会话的
我们之所以讲解编程式配置方法,是因为使用配置文件配置时屏蔽了太多的细节
为了层层递进的介绍MyBatis的基础用法,使大家熟悉MyBatis整体结构,我们需要讲解编程式配置方法
代码实现
删除JDBC连接及操作数据库:JDBCDemo.java
新建编程式配置文件:StartNoXml.java
@SuppressWarnings({"SqlResolve", "SqlNoDataSourceInspection", "Duplicates"})
public class StartNoXml {
public static void main(String[] args) throws SQLException {
// 准备jdbc事务类
JdbcTransactionFactory jdbcTransactionFactory = new JdbcTransactionFactory();
// 配置数据源
PooledDataSource dataSource = new PooledDataSource(
"com.mysql.cj.jdbc.Driver",
"jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false",
"root",
"root");
// 配置环境,向环境中指定环境id、事务和数据源
Environment environment = new Environment.Builder("development")
.transactionFactory(jdbcTransactionFactory)
.dataSource(dataSource).build();
// 新建 MyBatis 配置类
Configuration configuration = new Configuration(environment);
// 得到 SqlSessionFactory 核心类
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
// 开始一个 sql 会话
SqlSession session = sqlSessionFactory.openSession();
// 得到 sql 连接并运行 sql 语句
PreparedStatement preStatement = session
.getConnection()
.prepareStatement("SELECT * FROM user WHERE id = ?");
preStatement.setInt(1, 1);
ResultSet result = preStatement.executeQuery();
// 验证结果
while (result.next()) {
System.out.println("username: " + result.getString("username"));
System.out.println("age: " + result.getString("age"));
}
// 关闭会话
session.close();
}
}
执行结果
控制台打印结果如下图所示,表示我们已经成功连接数据库并查出了需要的值
配置文件配置mybatis
代码实现
在resources文件夹下新建配置文件:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 环境变量 -->
<environments default="development">
<environment id="development">
<!-- 事务管理器 -->
<transactionManager type="JDBC"/>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>
执行结果
控制台打印结果如下图所示,表示我们已经成功连接数据库并查出了需要的值
配置文件配置mybatis
代码实现
在resources文件夹下新建配置文件:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 环境变量 -->
<environments default="development">
<environment id="development">
<!-- 事务管理器 -->
<transactionManager type="JDBC"/>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>
有了上面编程式 API 的使用经验,那么你一定可以轻松的看懂配置项:
configuration 标签对应 Configuration 类
environment 标签对应 Environment 类
transactionManager 标签对应 JdbcTransactionFactory 类
dataSource 标签对应 PooledDataSource 类
启动类:StartWithXml.java
修改类名为StartWithXml
读取配置文件
InputStream configuration = Resources.getResourceAsStream("mybatis-config.xml");
执行结果
控制台打印结果如下图所示,表示我们已经成功连接数据库并查出了需要的值
mybatis实现mapper配置并查询数据 什么是mapper注解方式使用mapper
在MyBatis工程搭建 中我们主要讲解的是 MyBatis 如何连接数据库,具体执行 SQL 语句使用的是 JDBC 方式
但在实际应用中是不会选择 JDBC 来执行 SQL 的,MyBatis 提供了 Mapper 作为 Java 方法和 SQL 语句之间的桥梁,来帮助我们更好地去使用 SQL
Java 接口方法与 SQL 语句以及 mapper 之间的关系如下图所示:
XML方式使用mapper
因为MyBatis是一个持久层框架,所以我们在使用之前需要执行如下SQL语句备好数据源
#删除mybatis_demo数据库
drop database if exists mybatis_demo;
#创建mybatis_demo数据库
create database mybatis_demo;
#使用mybatis_demo数据库
use mybatis_demo;
#创建account表
create table user (
id int auto_increment primary key,
username varchar(20),
age int,
score int
);
#新增数据
insert into user (id, username, age, score) values
(1,'peter', 18, 100), (2,'pedro', 24, 200),
(3,'jerry', 28, 500), (4,'mike', 12, 300),
(5,'tom', 27, 1000);
我们以查询User数据为例感受Mapper引入后和JDBC执行SQL有什么区别
MyBatis 提供了注解和XML两种方式来连接Java方法和SQL语句,首先学习注解方式使用Mapper
select-resultType
代码实现
在UserMapper.xml文件中,我们新增 selectUserById 标签,该 select 标签的作用是:通过id查询用户
在这里插入代码片<select id="selectUserById" resultType="">
SELECT * FROM user WHERE id = #{id}
</select>
上面的内容返回的是所有字段值,我们需要自己创建实体类来存储查出来的值
在entity包下创建User实体类:User.java
package entity;
public class User {
private Integer id;
private String username;
private Integer age;
private Integer score;
// 省略getter&setter方法
// 省略toString方法
}
把UserMapper.xml文件中selectUserById标签的返回类型改为上面创建的实体类
resultType="entity.User"
User模块Mapper层:UserMapper.java
/**
* 通过用户id查询用户信息
*
* @param id
* @return
*/
User selectUserById(Integer id);
User模块测试类:UserTest.java
// 调用通过用户id查询用户信息的方法
User user = mapper.selectUserById(1);
System.out.println(user);
测试结果
select-resultMap
MyBatis自动帮助我们映射数据库数据和Java对象,其实这是MyBatis在幕后帮我们创建了resultMap对象,我们也可手动定义
代码实现
User模块Mapper层配置文件:UserMapper.xml
<resultMap id="userMap" type="entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="age" column="age"/>
<result property="score" column="score"/>
</resultMap>
上标签中有和两个子标签
其中标签是主键,其它字段均使用 result 标签来映射
标签有property和column两个属性
其中 property 是 Java 对象中的属性名称,column 是数据表与之对应的字段名称
把UserMapper.xml文件中selectUserById标签的返回类型改为上面创建的resultMap
resultMap="userMap"
测试结果
十一、 mybatis操作数据
select单条数据查询
要想使用MyBatis首先需要导入MySQL驱动包、MyBatis框架基础包并且添加MyBatis核心配置文件
首先复习下上个章节的内容
代码实现
在entity包下创建User实体类:User.java
package entity;
public class User {
private Integer id;
private String username;
private Integer age;
private Integer score;
// 省略getter&setter方法
// 省略toString方法
}
在dao包下创建User模块Dao层:UserDao.java
package dao;
public interface UserDao {
/**
* 通过用户id查询用户信息
*
* @param id
* @return
*/
User selectUserById(Integer id);
}
在mybatis-config.xml配置文件中添加上对应的mapper配置
<!-- mapper配置 -->
<mappers>
<mapper class="dao.UserDao"/>
</mappers>
在resources文件夹下新建dao包,并在其下新建User模块Dao层配置文件:UserDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.UserDao">
<select id="selectUserById" resultType="entity.User">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>
新建User模块测试类:UserTest.java
@SuppressWarnings({"Duplicates"})
public class UserTest {
public static void main(String[] args) throws IOException, SQLException {
// 读取配置文件
InputStream configuration = Resources.getResourceAsStream("mybatis-config.xml");
// 得到 SqlSessionFactory 核心类
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
// 开始一个 sql 会话
SqlSession session = sqlSessionFactory.openSession();
// 得到 Dao
UserDao dao = session.getMapper(UserDao.class);
// 调用通过用户id查询用户信息的方法
User user = dao.selectUserById(1);
System.out.println(user);
// 关闭会话
session.close();
}
}
执行结果
select多条数据查询
代码实现
但是在实际使用时我们常常需要一次性查询多条数据,例如:SELECT * FROM user
User模块Dao层配置文件:UserDao.xml
<select id="selectUserList" resultType="entity.User">
SELECT * FROM user
</select>
这时要用到List集合装对象来完成多条数据的查询工作,User模块Dao层:UserDao.java
/**
* 查询所有用户信息
*
* @return
*/
List<User> selectUserList();
User模块测试类:UserTest.java
// 调用查询所有用户信息的方法
List<User> userList = dao.selectUserList();
userList.forEach(u -> System.out.println(u));
TIPS: 对于userList.forEach(u -> System.out.println(u));
不理解的需要复习一下for forEach 循环及Lambda表达式使用
执行结果
insert数据
代码实现
User模块Dao层:UserDao.java
/**
* 新增用户信息
*
* @param user
* @return
*/
int insertUser(User user);
User模块Dao层配置文件:UserDao.xml
<insert id="insertUser" parameterType="entity.User">
INSERT INTO user VALUES (#{id},#{username},#{age},#{score})
</insert>
User模块测试类:UserTest.java
// 调用查询所有用户信息的方法
List<User> userListBefore = dao.selectUserList();
userListBefore.forEach(u -> System.out.println(u));
// 创建一个要新增的对象并赋值
User insertUser = new User();
insertUser.setId(6);
insertUser.setUsername("anne");
insertUser.setAge(18);
insertUser.setScore(600);
// 调用新增用户信息的方法
int count = dao.insertUser(insertUser);
// 调用查询所有用户信息的方法
List<User> userListAfter = dao.selectUserList();
userListAfter.forEach(u -> System.out.println(u));
执行结果
update数据
代码实现
User模块Dao层:UserDao.java
/**
* 修改用户信息
*
* @param user
* @return
*/
int updateUserById(User user);
User模块Dao层配置文件:UserDao.xml
<update id="updateUserById" parameterType="entity.User">
UPDATE user SET
age = #{age},
username = #{username},
score = #{score}
WHERE id = #{id}
</update>
User模块测试类:UserTest.java
// 调用通过用户id查询用户信息的方法
User userBefore = dao.selectUserById(6);
System.out.println(userBefore);
// 把成绩改为900
userBefore.setScore(900);
// 调用修改用户信息的方法
dao.updateUserById(userBefore);
// 调用通过用户id查询用户信息的方法
User userAfter = dao.selectUserById(6);
System.out.println(userAfter);
执行结果
控制台打印结果如下图所示
delete数据
代码实现
User模块Dao层:UserDao.java
/**
* 删除用户信息
*
* @param id
* @return
*/
int deleteUserById(Integer id);
User模块Dao层配置文件:UserDao.xml
<delete id="deleteUserById">
DELETE FROM user WHERE id = #{id}
</delete>
User模块测试类:UserTest.java
// 调用查询所有用户信息的方法
List<User> userListBefore = dao.selectUserList();
userListBefore.forEach(u -> System.out.println(u));
// 调用删除用户信息的方法
int count = dao.deleteUserById(6);
// 调用查询所有用户信息的方法
List<User> userListAfter = dao.selectUserList();
userListAfter.forEach(u -> System.out.println(u));
执行结果
控制台打印结果如下图所示
springmvc工程搭建
springmvc请求映射注解
Spring MVC 提供了以下这些请求映射注解: 请求映射注解 说明 类/方法 @RequestMapping 通用的请求处理 类 @GetMapping 处理 HTTP GET 请求 查询方法 @PostMapping 处理 HTTP POST 请求 新增修改方法 @PutMapping 处理 HTTP PUT 请求 新增修改方法 @PatchMapping 处理 HTTP PATCH 请求 新增修改方法 @DeleteMapping 处理 HTTP DELETE 请求 删除方法 除了 @RequestMapping ,其它类型的注解本质上是 @RequestMapping 的简写形式 比如 @GetMapping 其实可以写为@RequestMapping( method = RequestMethod.GET) 建议在类级别上只使用 @RequestMapping ,用于指定基本路径 而在每个方法上根据方法功能不同使用更具体的请求映射注解搭建配置文件springmvc
创建项目
使用IDEA创建Maven工程
IDEA配置Maven
我们新建Maven项目名为“springmvc-demo“,设置好Maven版本、配置文件以及Maven仓库
由于SpringMVC在 MVC设计模式 中是作为控制器负责前后端代码的连接,我们首先需要添加WEB文件夹用来存放前端代码
添加web
右键我们的项目名 -> 选择“Add Framework Support”
编写代码测试
编写Controller层
controller包下新建HelloController类
package controller;
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model){
// Model 封装数据
model.addAttribute("msg","HELLO MY FIRST SPRING MVC PROJECT");
// 返回的字符串就是视图的名字 会被视图解析器处理
return "hello";
}
}
配置Spring容器自动扫描包
将Controller对象放进Spring容器
Spring核心配置文件:applicationContext.xml
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="controller"/>
编写jsp
WEB-INF包下新建jsp包,jsp包下新建hello.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
编写web.xml
配置前端控制器
<!-- 配置前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
配置初始化参数
在服务器启动时 加载spring的核心配置文件applicationContext.xml
配置初始化参数的代码写在前端控制器内
<!-- 配置初始化参数 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
设置启动级别
设置启动级别的代码也写在前端控制器内,数字越小启动越早
1
设置SpringMVC拦截请求
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
用url-pattern中的内容来标识请求拦截:
/ 匹配所有的请求;(不包括.jsp)
/* 匹配所有的请求;(包括.jsp)
为空时所有请求都会被SpringMVC拦截
配置中文乱码过滤器
<!-- 乱码过滤 -->
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
运行web项目
打包
file -> Project Structure 打开项目构建管理框
首先删除默认打好的包
运行web项目
运行TomCat
点击绿色的小三角运行TomCat,出现如下内容表示运行成功
在浏览器输入 http://localhost:8080/hello 可以看到页面打印出了我们设置好的值