黑马程序员2022新版SSM框架Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术——万字学习笔记:Spring+SpringMVC篇
Spring
为什么要学Spring?
1.专业角度:
-
简化开发,降低企业级开发的复杂性
-
框架整合,高效整合其他计算,提高企业级应用开发与运行效率
2.学什么?
- 简化开发
- IOC
- AOP
- 事务处理
- 框架整合
- MyBatis
- MyBatis-plus
- Struts
- Struts2
- Hibernate
- .....
Spring Framework系统架构
- Core Container:核心容器
- Aspects:AOP思想实现
- AOP:面向切面
- Data Integration:数据集成
- Data Access:数据访问
- Test:单元测试与集成测试
大纲
第一部分:核心容器
- 核心概念(IoC/DI)
- 容器基本操作
第二部分:整合
- 整合数据层技术MyBatis
第三部分:AOP
- 核心概念
- AOP基础操作
- AOP实用开发
第四部分:事务
- 事务开发
第五部分:家族
- SpringMVC
- SpringBoot
- SPringCloud
核心容器
1.核心概念
- IoC(Inversion of Control)控制反转
- 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转
- Spring技术对IoC思想进行了实现
- 由Spring提供了一个容器,称为IoC容器,来充当IoC思想中的"外部"
- IoC容器负责对象的创建,初始化等一系列,被创建或被管理的对象在IoC容器中统称为Bean
- DI(Dependency Injection)依赖注入
- 在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
- 目标:充分解耦
- 使用IoC容器管理bean(IoC)
- 在IoC容器内将有依赖关系的bean进行关系绑定(DI)
- 最终效果
- 使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系
2.IoC入门案例
-
管理什么?(Service与Dao)
-
如何将被管理的对象告知IoC容器?(配置)【将bean注册到application.xml里面】【通过xml管理IoC容器】
-
被管理的对象交给IoC容器,如何获取到IoC容器?(接口)
一般,通过ApplicationContext接口中的实现类ClassPathXmlApplicationContext()
-
IoC容器得到后,如何从容器中获取bean?(接口方法)
通过ApplicationContext接口中的getBean方法
-
使用Spring导入哪些坐标?(pom.xml,在depencies里面导入坐标)
<!--步骤1:导入Spring坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency>
//步骤2:定义Spring管理的类(接口)
public interface BookService{
void save();
}
//实现接口
public class BookServiceImpl implements BookService{
private BookDao bookDao = new BookDaoImpl();
public void save(){
bookDao.save();
}
}
<!--步骤3:创建Spring配置文件,配置对应作为Spring管理的bean-->
<?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="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl"/>
<!--id不能重复-->
3.DI入门案例
- 基于IoC管理bean
- Service中使用new形式创建的Dao对象是否保留?(否)
- Service中需要的Dao对象如何进入到Service中?【使用依赖注入】(提供方法)
- Service与Dao间的关系如何描述?(配置)【一个业务层可以控制很多数据层】
//步骤1:将业务层中的new的DAO对象删除,使用set方法实现
public class BookServiceImpl implements BookService{
private BookDao;
public void save(){
bookDao.save();
}
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
System.out.println("提供了对应的set方法");
}
}
<!--步骤2:配置bean进行依赖注入,使用property标签注册一个属性-->
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl">
<!--配置dao与service的关系-->
<property name="bookDao" ref="bookDao"/>
</bean>
4.Bean的配置
Bean的别名
在application.xml起别名,或取多个别名,【用空格,或者逗号,或者分号分隔开来】
ref
属性既可以引用id
也可以引用name
<bean id="bookDao" name="dao dao2" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" name="service" class="com.hcx.service.impl.BookServiceImpl">
<!--配置dao与service的关系-->
<property name="bookDao" ref="dao"/>
</bean>
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService= (BookService) context.getBean("service");
bookService.save();
}
//把getBean方法里的名称换为bean的别名也是可以的
注意:获取bean无论是通过id还是name获取,如果无法获取到,将抛出NoSuchBeanDefinitionException
Bean的作用范围【单例或非单例】
在application.xml中配置bean的作用范围
属性:scope
所属:bean标签
功能:定义bean的作用范围:
- singleton:单例(默认)
- prototype:非单例
范例:
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl" scope="prototype"/>
- 为什么
bean
默认的是单例的?
---便于管理复用对象,提高效率
-
适合交给容器进行管理的bean
- 封装实体的域对象(有状态的)
Bean的实例化方法
- Bean本质上就是对象,创建bean使用构造方法完成
方法一:构造方法实现Bean
//首先在application中注册bean
//其次在实现类中实现构造方法实例化实现类
private BookDaoImpl(){
System.out.println("Book is constructor is running ...");
}
//无论是使用private还是public都能访问到构造方法,利用到了反射
//无参构造方法是默认实现
<bean id="bookkDao" class="com.hcx.dao.impl.BookDaoImpl"/>
方法二:使用静态工厂
//新建一个静态工厂
public class OrderDaoFactory{
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
//实例化对象时getBean()方法调用静态工厂实例化对象
<bean id="orderDao" class="com.hcx.factory.OrderDaoFactory" factory-method="getOrderDao"/>
方法三:实例工厂
public class AppForInstanceUser{
public static void main(String[] args){
//创建实例静态工厂对象
UserDaoFactory userDaoFactroy = new UserDaoFactory();
//通过实例工厂对象创建对象
User userDao = userDaoFactory.getUserDao();
userDao.save();
}
}
public class UserDaoFactory{
public UserDao getUserDao(){
retrun new UserDaoImpl();
}
}
<bean id="userFactory" class="com.hcx.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
方式四:FactoryBean
public class UserDaoFactoryBean implements FactoryBean<UserDao>{
public UserDao getObject() throws Expection{
return new UserDaoImpl();
}
public Class<?> getObjectType(){
return UserDao.class;
}
}
<bean id="userDao" class="com.hcx.factory.UserDaoFactoryBean"/>
Bean的生命周期
- 生命周期:从创建到消亡的完整过程
- bean生命周期:bean从创建到销毁的整体过程
- bean生命周期控制:在bean创建后到销毁前做一些事情
public void init(){
System.out.print("Bean初始化操作");
}
public void destory(){
System.out.print("Bean销毁操作");
}
<bean id="bookDao" class="com.hcx.impl.BookDaoImpl" init-method="init" destory-method="destory"/>
//无法调用销毁方式原因,JVM直接退出,未执行关闭ClassPathXmlApplicationContext对象
eg:
// 不用 ApplicationContext接口下ClassPathXmlApplication实现类型下的close()方法 ,还可以使用使用ApplicationContext下的实现类AnnotationConfigApplicationContext()实现下的close()方法
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao =(BookDao) context.getBean("bookDao");
//暴力方法
context.close();
//灵巧方法
context.registerShutdownHook();
//bean生命周期,标准实现方式,实现InitializingBean, DisposableBean两个接口中的方法
eg:
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
// 删除业务层中使用new的方式创建dao对象
// private BookDao bookDao = new BookDaoImpl();
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
System.out.println("提供了对应的set方法");
}
@Override
public void destroy() throws Exception {
System.out.println("service destory");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}
总结
bean生命周期
-
初始化容器
- 创建对象(内存分配)
- 执行构造方法
- 执行属性注入(set操作)
- 执行bean初始化方法
-
使用bean
- 执行业务操作
-
关闭/销毁容器
- 执行bean销毁方法
bean销毁时机
-
容器关闭前触发bean的销毁
-
关闭容器方式
-
手工关闭容器
ConfigurableApplicationContext接口
close()
操作 -
注册关闭钩子,在虚拟机退出前线关闭容器再退出虚拟机
ConfigurableApplicationContext接口
registerShutdownHook()
操作
-
控制bean生命周期的两种方法
-
配置
- init-method
- destory-method
-
接口
-
InitializingBean
-
DisposableBean
-
依赖注入方式
- 思考:向一个类中传递数据的方式有几种?
- 普通方法(set方法)
- 构造方法
- 思考:依赖注入描述了在容器中建立bean与bean之间依赖关系的过程,如果bean运行需要的是数字或字符串呢?
- 引用类型
- 简单类型(基本数据类型与String)
- 依赖注入方式
- setter注入
- 简单类型
- 引用类型
- 构造器注入
- 简单类型
- 引用类型
- setter注入
Setter注入——引用类型
-
在bean中定义引用类型属性并提供可访问的set方法
public class BookServiceImpl implements BookService( private BookDao bookDao; public void setBookDao(BookDao bookDao){ this.bookDao = bookDao; } )
-
配置中使用property标签****value属性注入引用类型对象
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl"> <property name="bookDao" ref="bookDao"/> </bean> <bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
Setter注入——简单类型
- 在bean中定义简单类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao(
private int connectionNum;
private String databaseName;
public void setConnectionNum(int connectionNum){
this.connectionNum=connectionNum;
}
public void setDatabaseName(String databaseName){
this.databaseName=datanaseName;
}
)
- 配置中使用property标签****value属性注入基本类型对象
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl">
<property name="connectionNum" value="100"/>
<property name="databaseName" value="MySql"/>
</bean>
构造器注入——引用类型
- 在bean中定义引用类型属性并提供可访问的构造方法
public class BookServiceImpl implements BookService(
private BookDao bookDoa;
public BookServiceImpl(BookDao bookDao){
this.bookDao = bookDao;
}
)
- 配置中使用constructor-arg标签ref属性注入引用类型对象
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
构造器注入——简单类型
- 在bean中定义引用类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao{
private int connectionNumber;
public BookDaoImpl(int connectionNumber){
this.connectionNumber = connectionNumber;
}
}
- 在配置中使用constructor-arg标签value属性注入简单数据类型
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl">
<constructor-arg name="connectionNumber" value="10"/>
</bean>
参数设置
- 配置中使用constructor-arg标签type属性设置按形参类型注入
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
- 配置中使用constructor-arg标签index属性设置按形参位置注入
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl">
<constructor-arg index="0" value="10"/>
<constructor-arg index="1" value="MYSQL"/>
</bean>
依赖注入方式选择
- 强制依赖使用构造器进行,使用setter注入有概率步进行注入导致null对象出现
- 可选依赖使用setter注入进行,灵活性能强
- Spring框架倡导使用构造器,第三方框架内大多数采用构造器注入的形式进行数据初始化,相对严谨
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选项依赖的注入
- 实际开发过程中还要更卷实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
- 自己开发的模块推荐使用setter注入
依赖自动装配
-
IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称之为自动装配
-
自动装配方式
- 按类型(常用)
- 按名称
- 按构造方法
- 不启用自动装配
-
配置中使用bean标签autowire属性设置自动装配的类型
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl" autowire="byType"/>
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名和配置耦合,不推荐使用
- 自动装配优先级低于setter注入和构造器注入,同时出现时自动装配失效
集合注入
数据类型:
数组
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
List
<property name="list">
<list>
<value>加油</value>
<value>每天</value>
<value>学习</value>
</list>
</property>
Set
<property name="set">
<set>
<value>好好</value>
<value>工作</value>
<value>跑路</value>
</set>
</property>
Map
<property name="map">
<map>
<entry key="country" value="china"></entry>
<entry key="city" value="成都"></entry>
<entry key="province" value="四川"></entry>
</map>
</property>
Properties
<property name="properties">
<props>
<prop key="country">中国</prop>
<prop key="city">南昌</prop>
<prop key="province">江西</prop>
</props>
</property>
案例:数据源对象管理
-
导入druid坐标
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency>
-
配置数据源对象作为spring管理的bean
<!-- 管理DruidDataSource对象-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/message"/>
<property name="username" value="root"/>
<property name="password" value="505489"/>
</bean>
- 导入c3p0坐标和mysql连接坐标
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
- 配置数据源对象作为spring管理的bean
<!-- 管理c3p0对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/message"/>
<property name="user" value="root"/>
<property name="password" value="505489"/>
</bean>
- 加载properties文件
- 首先开启命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
</beans>
2.使用context空间加载properties文件
<context:property-placeholder location="jdbc.properties"/>
3.使用属性占位符${}读取properties文件中的属性
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.Driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
加载properties文件
- 不加载系统属性
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
- 加载多个properties文件
<context:property-placeholder location="jdbc.properties,msg.properties"/>
- 加载所有properties文件
<context:property-placeholder location="*.properties"/>
- 加载properties文件标准格式
<context:property-placeholder location="classpath:*.properties"/>
- 从路径或jar包中搜索properties文件
<context:property-placeholder location="classpath*:*.properties"/>
容器
容器创建(顶级父类BeanFactory接口)
- 方式一:路径加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
- 方式二:文件路径加载配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\applicationContext.xml");
- 方式三:读取web下的资源文件
XmlWebApplicationContext ctx = new XmlWebApplicationContext();
- 加载多个配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");
获取bean
- 方式一:使用bean名称获取
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
- 方式二:使用bean名称获取并指定类型
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
- 方式三:使用bean类型获取
BookDao bookDao = ctx.getBean(BookDao.class);
容器层次结构
总览 BeanFactory
体系,按照接口的抽象层次,大体可以分层四层:
- 第一层:
BeanFactory
; - 第二层:
HierarchicalBeanFactory
,ListableBeanFactory
,AutowireCapableBeanFactory
; - 第三层:
ConfigurableBeanFactory
,此外还有一个关联性较强SingletonBeanRegistry
; - 第四层:
ConfigurableListableBeanFactory
;
BeanFactory初始化
- 类路径加载配置文件
Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean("bookDao",BookDao.class);
bookDao.save();
注意:BeanFactory创建完毕后,所有的bean均为延时加载
延时加载好处:当为了追求传输效率就会需要什么就再去创建什么时,就会体现出延迟加载的好处,有一个缓冲时间。
核心容器总结
容器相关
-
BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延时加载
-
ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
-
ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
-
ApplicationContext接口常用初始化类
- ClassPathXmlApplicationContext
- FileSystemXmlApplicationContext
bean相关
<bean
id="bookDao" bean的Id
name="dao bookDaoImpl daoImpl" bean的别名
class="com.itheima.dao.impl.BookDaoImpl" bean的类型,静态工厂,FactoryBean类
scope="singleton" 控制bean的实例数量
init-method="init" 生命周期初始化方法
destory-method="destory" 生命周期销毁方法
autowire="byType" 自动装配类型
factory-method="getInstance" bean工厂方法,应用于静态工厂或实例工厂
factory-bean="com.itheima.factory.BookDaoFactory" 实例工厂bean
lazy-init="true" 控制bean延时加载
/>
依赖注入
<bean id="bookSerbice" class="com.hcx.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/> 构造器注入引用类型
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="msg" value="WARN"/> 构造器注入简单类型
<constructor-arg type="java.lang.String" index="3" value="WARN"/> 类型匹配与索引匹配
<property name="bookDao" ref="bookDao"/> setter注入引用类型
<property name="userDao" ref="userDao"/>
<property name="msg" value="WARN"/> setter注入简单类型
<property name="names"> list集合
<list>
<value>itcast</value> 集合注入简单类型
<ref bean="dataSource"/> 集合注入引用类型
</list>
</property>
</bean>
注解开发
注解开发定义bean
- 使用@Component定义bean
@Component("bookDao")
public class BookDaoImpl implements BookDao{
}
@Component
public class BookServiceImpl implements BookService{
}
- 核心配置文件中通过组件扫描加载bean
<context:component-scan base-package="com.hcx"/>
- Spring提供@Component注解的三个衍生注解
- @Controller:用于表现层bean定义
- @Service:用于业务层bean定义
- @Repository:用于数据层bean定义
@Repository("bookDao")
public class BookDaoImpl implements BookDao{
}
@Service
public class BookServiceImpl implements BookService{
}
Spring 3.0 纯注解开发
- Spring 3.0 升级了纯注解开发模式,使用了Java类代替配置文件,开启了Spring快速开发赛道
- Java类代替Spring核心配置文件,
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.springframework.org/schema/beans/spring-beans.xsd">
<context:component-scan base-package="com.hcx"/>
</beans>
被
@Configuration
@ComponentScan("com.hcx")
public class SpringConfig{
}
代替
- @Configuration注解用于设定当前类为配置类
- @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
@ComponentScan({"com.hcx.service","com.hcx.dao"})
- 读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象
//加载配置文件初始化容器(xml版)
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//加载配置类初始化容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
总结
-
定义bean
-
@Component
-
@Controller
-
@Service
-
@Repository
-
-
<context:component-scan/>
-
-
纯注解开发
- @Configuration
- @ComponentScan
- AnnotationConfigApplicationContext
注解开发bean管理
- bean的作用范围
- @Scope
- bean的生命周期
- @PostConstruct
- @PreDestroy
注解开发依赖注入
自动装配
引用类型注入
- 使用@Autowired注解开启自动装配模式(按类型)
@Servcie
public class BookServiceImpl implements BookService{
@Autowired
private BookDao bookDao;
public void setBookDao(BookDao bookDao){
this.bookDao=bookDao;
}
public void save(){
System.out.print("book service save ...");
bookDao.save();
}
}
-
注意:自动装配基于反射设计创建对象并暴力反射对应属性初始化数据,因此无需提供setter()方法
-
注意:自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法
-
使用@Qualifier注解开启指定名称装配bean
@Service
public class BookServiceImpl implements BookService{
@Autowired
@Qualifier("bookDao")
private BookDao bookDAO;
}
- 注意:@Qualifier注解无法单独使用,必须配合@Autowired注解使用
简单数据类型注入
在定义的数据类型上放使用@Value("值")
@Value("学习")
private String action;
加载外部properties文件
- 在SpringConfig类下,使用@PropertySource注解加载properties文件
@Configuration
@ComponentScan("com.hcx")
@uPropertySource("classpath:jdbc.properties")
public class SpringConfig{}
- 在给属性注入值时
@Value("${外部文件中定义的属性名}")
private String value;
- 注意:路径仅支持单一文件配置,多文件请使用数组格式配置,不允许使用通配符*
总结回顾
- 自动装配
- @Autowired:自动装配暴力映射
- @Qualifier:指定名称注入
- @value:注入简单类型
- 读取properties文件
- @PropertySource:在SpringConfig里读取外部文件
第三方bean管理
第三方bean配置
- 使用@Bean配置第三方bean
@Configuration
public class SpringConfig{
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}
//不推荐
- 将独立的配置类加入核心配置
- 方式一:导入式
public class JdbcConfig{
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
//相关配置
return ds;
}
}
- 使用@Import注解手动加入配置类到核心配置,此注解只能添加一次,多个数据请用数组格式
@Configuration
@Import(JdbcConfig.class)
public class SpringConfig{
}
第三方bean依赖注入
- 简单类型依赖注入
public class JdbcConfig{
@Value("com.mysql.jdbc.Driver")
private String dirver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private String url;
@Value("root")
private String userName;
@Value("505489")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDatSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUserName(userName);
ds.setPassword(password);
return ds;
}
}
- 引用类型依赖注入
@Bean
public DataSource dataSource(BookService bookService){
Sytstem.out.print(bookService);
DruidDataSource ds = new DruidDataSource();
//属性设置
return ds;
}
- 引用类型注入只需要为bean定义方法设置形参即可,容器会更卷类型自动装配对象
总结
第三方Bean总结
- 第三方bean管理
- @Bean
- 第三方依赖注入
- 引用类型:方法形参
- 简单类型:成员变量
XML配置对比注解配置
功能 | XML配置 | 注解 |
---|---|---|
定义bean | bean标签:id属性 class属性 | @ComponentScan @Component:@Controller @Service @Repository |
设置依赖注入 | setter注入(set方法):引用/简单 构造器注入(构造方法):引用/简单 自动装配 | @Autowired:@Qualifier @Value |
配置第三方bean | bean标签 静态工厂,实例工厂,FactoryBean | @Bean |
作用范围 | scope属性 | @Scope |
生命周期 | 标准接口:init-method destory-method | @PostConstryctor @ProDestory |
Spring整合MyBatis
xml方式实现
- MyBatis程序核心对象分析
//1.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilde();
//2.加载SqlMapConfig.xml配置文件
InpiyStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//3.创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
/*初始化SqlSessionFactroy*/
//4.获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//5.执行SqlSession对象执行查询,获取结果User
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
/*获取实现*/
Account ac = accountDao.findById(2);
System.out.printIn(ac);
/*获取数据层接口*/
//6.释放资源
sqlSession.close();
/*关闭连接*/
- 整合MyBatis
<configuration>
<!--初始化属性数据-->
<properties resource="jdbc.properties"></properties>
<!--初始化类型别名-->
<typeAliases>
<package name="com.itheima.domain"/>
</typeAliases>
<!--初始化dataSource-->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"> <dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</transactionManager>
</environment>
</environments>
<!--初始化映射配置-->
<mappers>
<package name="com.itheima.do"></package>
</mappers>
</configuration>
注解方式实现
- 注册SqlSessionFactoryBean类,并配置数据库
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactryBean ssfb = new SqlSessionFactoryBean();
ssfb.setTypeAliasePackage("com.hcx.pojo");
ssfb.setDataSource(dataSource);
return ssfb;
}
- 配置映射包
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.hcx.dao");
return msc;
}
整合JUnit
测试接口(一般测业务层,很少测数据层)
- 使用Spring整合Junit专业类加载器
@RunWith(SpringJUnit4ClassRunner.class)
@ConetextConfiguration(classes = SpringConfig.class)
public Class BookServiceTest{
@Autowired
private BookService bookService;
@Test
public void testSave(){
bookservice.save();
}
}
AOP
AOP简介
- AOP(Aspect Oriented Programming)面向切面编程,是一种编程范式,指导开发者如何组织程序结构
- OOP(Object Oriented Programming)面向对象编程
- 作用:在步惊动原始设计的基础上进行功能增强,追加功能
- Spring理念:无侵入式
AOP核心概念
- 连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法,抛出异常,设置变量等
- 在SpringAOP中,理解为方法的执行
- 切入点(Pointcut):匹配连接点的式子
- 在SpringAop中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
- 一个具体方法:com.hcx.dao包下的BookDao接口中的无形参返回值的save方法
- 匹配多个方法,所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
- 在SpringAop中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
- 通知(Advice):在切入点处执行的操作,也就是共性功能
- 在Spring中,功能最终以方法的形式呈现
- 通知类:定义通知的类
- 切面(Aspect):描述通知与切入点的对应关系
AOP入门案例(注解版)
- 导入aop相关坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
说明:Spring-context坐标依赖spring-aop坐标
2.定义dao接口与实现类
public interface BookDao{
public void save();
public void update();
}
@Repository
public class BookDaoImpl implements BookDao{
public void save(){
System.out.println(System.currentTimeMills());
System.out.println("book dao save...");
}
public void update(){
System.out.println("book dao update...");
}
}
3.定义通知类,制作通知
public class MyAdvice{
public void before(){
System.out.println(System.currentTimeMills());
}
}
4.定义切入点
public class MyAdvice{
@Pointcut("execution(void com.hcx.dao.BookDao.update())")
private void pt(){}
}
说明:切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑
5.绑定切入点与通知关系,并指定通知添加到原始连接点的具有执行位置
public class MyAdvice{
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println(System.currenTimeMillis());
}
}
6.定义通知类受Spring容器管理,并定义当前类为切面类
@Componet
@Aspect
public class MyAdvice{
@Pointcut("execution(void com.hcx.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println(System.currentTimeMillis());
}
}
7.开启Spring对AOP注解驱动支持
@Configuration
@ComponetScan("com.hcx")
@EnableAspectAutoProxy
public class SpringConfig{
}
AOP工作流程
1.Spring容器启动
2.实现所有切面配置中的切入点
@Component
@Aspect
public class MyAdvice{
@Pointcut("excution(void com.hcx.dao.BookDao.save())")
private void ptx(){}
@Pointcut("excution(void com.hcx.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
3.初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
- 匹配失败,创建对象
- 匹配成功,创建原始对象(目标对象)的代理对象
4.获取bean执行方法
- 获取bean,调用方法并执行,完成
- 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
AOP核心概念
- 目标对象(Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
- 代理(Proxy):目标对象无法直接工作,需要对其进行功能会填,通过原始对象的代理对象实现
- SpringAOP本质:代理模式
AOP切入表达式
- 切入点:要进行增强的方法
- 切入点表达式:要进行增强方法的描述方式
public Interface BookDao{
public void update();
}
public class BookDaoImpl implements BookDao{
public void update(){
System.out.println("book dao update ...");
}
}
描述方式一:执行com.hcx.dao包下的BookDao接口中的无参数update方法
execution(void com.hcx.dao.BookDao.update())
描述方法二:执行com.hcx.dao.impl包下的BookDaoImpl类中的无参数update方法
execution(void com.hcx.iml.BookDaoImpl.update())
-
切入点表达式标准格式:动作关键字(访问修饰符,返回值,包名,类/接口.方法名(参数)异常名)
execution(public User com.hcx.service.UserService.findById(int))
-
动作关键字:描述切入点的行为动作,例如
excution
表示执行到指定切入点 -
访问修饰符:public,private 等,可以省略
-
返回值
-
包名
-
类/接口名
-
方法名
-
参数
-
异常名:方法定义中抛出异常,可以省略
AOP通知类型
-
AOP通知描述了抽取的共性功能,根据共性功能抽取位置不同,最终运行代码时要将其加入到合理的位置
-
AOP通知分为5种类型
- 前置通知
- 后置通知
- 环绕通知(重点)
- 返回通知(了解)
- 抛出执行异常后通知(了解)
-
名称:@Around(重点,常用)
-
类型:方法注释
-
位置:通知方法定义上方
-
作用:设置当前通知方法与切入点之间的绑定关系,当通知方法在原始切入点方法前后运行
-
范例:
@Around("pt()") public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("around before advice ..."); Object ret = pjp.proceed(); System.out.println("arround after advice ..."); return ret; }
-
@Around注意事项
- 环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
- 通知中如果为使用ProceedIngJoinPoint对原始方法进行调用将跳过原始方法的执行
- 对原始方法的调用可以不接收返回值,通知方法设置称void即可,如果接收返回值,必须设定为Object类型
- 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
- 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
@Around("pt()") public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("around before advice ..."); Object ret = pjp.proceed(); System.out.println("around after advice ..."); return ret; }
-
名称:@AfterReturning(了解)
-
类型:方法注解
-
位置:通知方法定义上方
-
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法正常执行完毕后运行
-
范例:
@AfterReturning("pt()") public void after Returning(){ System.out.println("afterReturning advice ..."); }
-
相关属性:value(默认):切入点方法名,格式为类名.方法名()
-
名称:@AfterThrowing(了解)
-
类型:方法注解
-
位置:通知方法定义上方
-
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行
-
范例:
@AfterThorwing("pt()") public void afterThorwing(){ System.out.println("afterThrowing advice ..."); }
-
相关属性:value(默认):欺辱点方法名,格式为:类名.方法名()
-
案例:测量业务层接口万次执行效率
步骤一:
@Around("ProjectAdvice.serbicePt()")
public void runSpeed(ProceedingJoinPoint pjp) throws Throwable{
long start = System.currentTimeMillis();
//获取执行签名信息
Singnature signature= pjp.getSignature();
//通过签名获取执行类型(接口名)
Stirng className = signature.getDeclaringTypeName();
String methodName = signature.getName();
for(int i= ; i< 10000;i++){
pjp.proceed();
}
long start =System.currentTimeMillis();
System.out.println("业务层接口万次执行"+className+"."+methodnName+"时间
:"+(end-start))+"ms"
}
需求:任意业务层接口军可执行显示器执行效率(执行时长)
分析:
- 业务层功能:业务接口执行前后分别记录时间求差值得到执行效率
- 通知类型选择前后均可增强的类型——环绕通知
AOP通知获取数据
AOP通知获取参数数据
-
获取切入点方法的参数
- JoinPoint:适用于前置,后置,返回后,抛出异常通知
- ProceedJointPoint:适用于环绕通知
-
获取切入点方法返回值
- 返回后通知
- 环绕通知
-
获取切入点方法运行异常信息
- 抛出异常后通知
- 环绕通知
-
JointPoint对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数
@Before("pt()")
public void before(JointPoint jp){
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
}
- ProceedJointPoint的子类
public Object around(ProceedingJointPoint pjp) threos Throwable{
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
Object ret = pjp.proceed();
return ret;
}
AOP通知获取返回值数据
- 抛出异常后通知可以获取切入点方法中的异常信息,使用形参可以接收对应的异常对象
@AfterReturning(Value = "pt()",returning = "ret")
public void afterReturning(String ret){
System.out.println("afterRetruning advice ..."+ ret);
}
- 环绕通知中可以手工书写对原始方法的调用,得到结果即为原始方法的返回值
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
Object ret = pjp.proceed();
return ret;
}
AOP通知获取异常数据(了解)
- 抛出异常后通知可以获取切入点帆帆中出现的异常信息,是用形参可以接收对应的异常对象
@AfterThrowing(value = "pt()",throwing ="t")
public void afterThrowing(Throwable t){
System.out.println("afterThrowing adivce ..."+ t);
}
- 抛出异常后通知可以回去切入点方法运行的异常信息,使用形参可以接收运行时抛出的异常对象
@Around("pt()")
public Object around(ProceedingJoinPoint pjp){
Object ret = null;
try{
ret= pjp.proceed();
}catch(Throwable t){
t.printStackTrace();
}
return ret;
}
总结
AOP通知获取数据
- 参数
- 返回值
- 异常
案例:百度网盘密码数据兼容处理
案例:百度网盘分析链接输入密码数据错误兼容性处理
分析:
- 在业务方法执行前对之前对所有的输入输出参数进行格式处理——trim()
- 使用处理后的参数调用原始方法——环绕通知中存在对原始方法的调用
@Around("DataAdvice.servicePt()")
public Object trimString(ProceedingJoinPoint pjp) throws Throwable{
Object[] args = pjp.getArgs();
//对原始参数的每一个参数进行操作
for(int i = 0; i < args.leng ; i++){
//如果是字符串数据
if(args[i].getClass().equals(String.class)){
//取出数据,trim()操作后,更新数据
args[i] = args[i].toString().trim();
}
}
return pjp.proceed(args);
}
AOP总结
核心概念
- 概念:AOP面向切面编程,是一种编程范式
- 作用:在不惊动原始设计的基础上为方法进行功能增强
- 核心概念
- 代理(Proxy):SpringAOP的本质核心就是采用代理模式实现的
- 连接点(JoinPoint):在SpringAop中,理解为任意方法的执行
- 切入点(Pointcut):匹配连接点的式子,也是具有共性功能的方法描述
- 通知(Advice):若干个方法的共性,在切入点处执行,最终体现为一个方法
- 切面(Aspect):描述通知与欺辱点对应关系
- 目标对象(Target):被代理的原始对象称为目标对象
切入点
- 切入点表达式标准格式,动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名 )
excution(* com.hcx.service.*Service.*(..))
- 切入点表示描述统配符:
- 作用:用于快速描述,范围描述
- *:匹配任意符号(常用)
- ..:匹配多个连续任意符号(常用)
- +:匹配子类类型
- 切入点表达式书写技巧
- 按标准规范开发
- 查询操作的返回值建议在使用 * 匹配
- 减少使用..去描述包
- 对接口进行描述,使用表示模块名,例如UserService的匹配描述为Service
- 方法名书写保留动词,例如get,使用表示名称,例如getById匹配描述为getBy
- 参数更卷实际情况灵活调整
环绕通知
- 通知类型
- 前置通知
- 后置通知
- 环绕通知(重点)
- 环绕通知依赖形参ProceedingJoinPoint才能实现对原始方法的调用
- 环绕通知可以隔离原始方法的调用执行
- 环绕通知返回设置为Objec类型
- 环绕通知中可以对原始方法调用过程中出现的异常进行处理
- 返回后通知
- 抛出异常后通知
AOP通知获取数据
- 获取切入点方法的参数
- JoinPoint:适用于前置,后置,返回后,抛出异常后通知,设为方法发第一个形参
- ProceedJointPoint:适用于环绕通知
- 获取切入点方法返回值
- 返回后通知
- 环绕通知
- 获取切入点方法运行异常信息
- 抛出异常后通知
- 环绕通知
Spring事务简介
- 事务作用:在数据层保障一系列的数据库操作同成功同失败
- Spring事务作用:在数据层或业务层保障一系列的数据操作同成功同失败
public interface PlatformTransactionManager{
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionSattus status) throws TransactionException;
}
public class DataSourceTransactionManager{
...
}
案例:银行账户转账
需求:实现任意两个账户间转账操作
需求微缩:A账户减钱,B账户加钱
分析:
- 数据层提供基础操作,指定账户减钱(outMoney),指定账户加钱(inMoney)
- 业务层提供转账操作(transfer),调用减钱和加钱的操作
- 提供2个账号和操作金额执行转账操作
- 基于Spring整合MyBatis环境搭建上述操作
结果分析:
- 程序正常执行时,账号金额A减B加,没有问题
- 程序出现异常,转账失败,单是异常之前操作成功,异常之后操作失败,整体业务失败
添加事务管理操作流程
-
在业务层接口上添加Spring事务管理
public interface AccountService{ @Transactional public void transfer(String out,String in,Double money); }
注意:Spring注解式事务通常添加在业务层接口中而不会添加到业务层实习类中,降低耦合度
注解式业务可以添加到业务方法上表示开启事务,也可以添加到接口上表示当前接口所有方法开启事务
-
设置事务管理
@Bean public PlatformTransactionManager transactionManager(DataSource dataSoure){ DataSouceTransactionManager ptm = new DataSourceTransactionManager(); ptm.setDataSource(dataSource); return ptm; }
注意:事务管理要根据实现技术进行选择
MyBatis框架使用JDBC事务
-
开启注解事务驱动
@Configuration @ComponentScan("com.hcx") @PropertySource("classpath:jdbc.properties") @Import({JdbcConfig.class,MybatisConfig.class}) @EnableTransactionManagement public class SpringConfig{ }
Spring事务角色(通过相同数据源来管理)
- 事务角色
- 事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法
- 事务协调员:加入事务方,在Spring中通常带着数据层方法,也可以是业务层方法
事务相关配置
注意:IOException不属于运行时异常,事务回滚不管理
事务传播行为
- 事务传播行为:事务协调员对事务管理员所携带事务的处理态度
Spring中七种事务传播行为
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
案例:转账业务追加日志
需求:实现任意两个账户间转账操作,并对每次转账操作在数据库进行留痕
需求微缩:A账户减钱,B账户加钱,数据库记录日志
分析:
- 基于转账操作案例添加日志模块,实现数据库中记录日志
- 业务层转账操作(transfer),调用减钱,加钱与记录日志功能
实现效果预期:
无论转账是否成功,均进行转账操作的日志留痕
存在问题:
日志的记录与转账操作隶属于同一事务,同成功同失败
失效效果预取改进:
无论转账操作是否成功,日志必须保留
步骤
- 在业务层接口上添加Spring事务,设置事务传播行为REQUIRES_NEW(需要新事务)
@Service
public class LogServiceImpl implements LogService{
@Autowired
private LogDao logDao;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void log(String out,String in,Double money){
logDao.log("转账操作由"+out+"到"+in+",金额:"+money);
}
}
SpringMVC
- SpringMVC技术与Servlet技术功能相同,均属于web层开发技术,但SpringMVC技术更简洁,能用更少的代码进行开发
SpringMVC概述
- SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
- 功能
- 用于进行表现层功能开发
- 优点
- 使用简单,开发便捷(相比于Servlet)
- 灵活性强
SpringMVC入门案例
-
使用SpringMVC技术需要县导入SpringMVC坐标与Servlet坐标
<!--1.导入坐标springmvc和servlet的--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency>
-
创建SpringMVC控制器类(等同于Servlet功能)
@Controller public class UserController{ @RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'module':'springmvc'}"; } }
-
初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean
@Configuration @ComponentScan("com.iteima.controller") public class SpringMvcConfig{ }
-
初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理请求
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer; //4.定义一个servlet容器启动的配置类,在里面加载spring的配置 public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { //加载springMVC容器配置 @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(SpringMvcConfig.class); return context; } //设置哪些请求归属springMVC处理 @Override protected String[] getServletMappings() { return new String[]{"/"}; } //加载spring容器配置 @Override protected WebApplicationContext createRootApplicationContext() { return null; } }
入门案例用到的注解
-
名称:@Controller
-
类型:类注解
-
位置:SpringMVC控制器定义上方
-
作用:设置SpringMVC的核心控制器bean
-
范例:
@Controller public class UserController{ }
-
名称:@ResquestMapping
-
类型:方法注释
-
位置:SpringMVC控制器方法定义上方
-
作用:设置当前控制器方法请求访问路径
-
范例:
@ResquestMapping("/save") public void save(){ System.out.println("user save ..."); }
-
相关属性
- value(默认):请求访问路径
-
名称:@ResponseBody
-
类型:方法注释
-
位置:SpringMVC控制器方法定义上方
-
作用:设置当前控制器方法响应内容为当前返回值,无需解析
-
范例:
@RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'info':"springmvc"}"; }
-
SpringMVC入门程序开发总结(1+N)
- 一次性工作
- 创建工程,设置服务器,加载工程
- 导入坐标
- 创建web容器启动类,加载SpringMVC配置,并设置SpringMVC请求拦截路径
- SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器bean)
- 多次工作
- 定义处理请求的控制器类
- 定义处理请求的控制器方法,并配置映射路径(@RequestMapping)与返回json数据(@ResponseBody)
- 一次性工作
-
Servlet容器配置类
-
AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化web3.0容器的抽象类
-
AbstractDispatcherServletInitializer提供三个接口方法供用户实现
-
createServletAppilcationContext()方法,创建servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围
protected WebApplicationContext createServletApplicationContext(){ AnnotattionConfigWebApplicationContext context = new AnnotattionConfigWebAppklicationtContext(); context.register(SpringMvcConfig.class); return context; }
-
getServletMappings()方法,设定SpringMVC对应的请求映射路径,设置为/表示拦截所有请求,任意请求都将转入道SpringMVC进行处理
protected String[] getServletMapping(){ return new String[]("/"); }
-
createRootApplicationContext()方法,如果创建Servlet容器时需要加载飞SpringMVC对应的bean,使用当前方法进行,使用方式用createServletAppilcationContext()
protected WebApplicationContext createRootApplicationContext(){ return null; }
入门案例工作流程分析
- 启动服务器初始化过程
- 服务器启动,执行ServletContainersInitConfig类,初始化web容器
- 执行createServletApplicationContext方法,创建了WebApplicationContext对象
- 加载SpringMvcConfig
- 执行@ComponentScan加载对应的bean
- 加载UserController,每过@RequestMapping的名称对应一个具体的方法
- 执行getServletMappings方法,定义所有请求都通过SpringMVC
- 单次请求过程
- 发射请求localhost/save
- web容器发现所有请求都经过SpringMVC,将请求交割SpringMVC处理
- 解析请求路径/save
- 由/save匹配执行对应的方法saev()
- 执行save()
- 检测道由@ResponseBody之际将save()方法的返回值作为响应体返回给请求方
- 启动服务器初始化过程
-
Controller加载控制与业务bean加载控制
- SpringMVC相关bean(表现层bean)
- Spring控制的bean
- 业务bean(Service)
- 功能bean(DatatSource等)
- SpringMVC相关bean加载控制
- SpringMVC加载的bean对应的包均在com.hcx.controller包内
- Spring相关bean加载控制
- 方式一:Spring加载的bean设定扫描范围为com.hcx,排除controller包内的bean
- 方式二:Spring加在的bean设定扫描范围为精准范围,例如service包,dao包等
注解复习
-
名称:@ComponentScan
-
类型:类注解
-
范例:
@Configuration @ConmponentScan(value="com.itheima", excludeFilters = @ComponentScna.Filter( type = FilterType.ANNOTATION, classer = Controller.class ) ) public class SpringConfig{}
-
属性
- excludeFilters: 排除扫描路径中加载的bean,需要指定类别(type)与具体项(classer)
- includeFilters: 加载指定的bean,需要指定类别(type)与具体项(class)
-
bean的加载格式
//4.定义一个servlet容器启动的配置类,在里面加载spring的配置 public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { //加载springMVC容器配置 @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(SpringMvcConfig.class); return context; } //设置哪些请求归属springMVC处理 @Override protected String[] getServletMappings() { return new String[]{"/"}; } //加载spring容器配置 @Override protected WebApplicationContext createRootApplicationContext() { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(SpringConfig.class); return context; } }
-
简化开发
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{ @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringMvcConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[0]; /* return new Class[]{SpringConfig.class}*/ } @Override protected String[] getServletMappings() { return new String[]{"/"}; } }
请求与响应
-
名称:@RequestMapping
-
类型:方法注解 类注解
-
位置:SpringMVC控制器方法定义上方
-
作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前方法请求访问路径前缀
-
范例:
@Controller @RequestMapping("/user") public class UserController{ @RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'module':'user save'}"; } }
-
属性
- value(默认):请求访问路径,或访问路径前缀
中文乱码处理
Post请求中文乱码处理
-
为web容器添加过过滤器并指定字符集,Spring-web包中提供了专用的字符过滤器
//乱码处理 @Override protected Filter[] getServletFilters() { CharacterEncodingFilter filter = new CharacterEncodingFilter(); filter.setEncoding("UTF-8"); return new Filter[]{filter}; }
请求参数
-
参数种类
- 普通参数
- POJO类型参数
- 嵌套POJO类型参数
- 数组类型参数
- 集合类型参数
-
普通参数:url 地址穿参,地址参数名与形参变量名相同,定义形参即可接收参数
@RequestMapping("/commoParam") @ResponseBody public String commonParam(String name,int age){ Stystem.out.println("普通参数传递 name ==> " + name ); System.out.println("普通参数 age ==> " + age); return "{'module':'common param'}"; }
使用到的注解
-
名称:@RequestParam
-
类型:形参注解
-
位置:SpringMVC控制器方法形参定义前面
-
作用:绑定请求参数与处理器方法形参间的关系
-
范例:
@RequestMapping("/commonParamDifferentName") @ResponseBody public String commonParamDifferentName(@RequestParam("name") String userName , int age){ System.out.println("普通参数 userName ==>" + userName); System.out.println("普通参数 userName ==>" + age); return "{'module':'common param different name'}"; }
-
参数:
- required: 是否为必传参数
- defaultValue:参数默认值
POJO参数/嵌套的POJO参数
-
POJO参数/嵌套的POJO参数:请求数名与形参对象属性名相同,定义POJP类型形参即可接收参数
//嵌套pojo参数 @RequestMapping("/saveObj") @ResponseBody public String saveObj(User user){ System.out.println("对象接收数据"+user); return "{'module':'pojo contain pojo param'}"; }
Json数据传参
请求参数
- json数组
- json对象(POJO)
- json数组(POJO)
步骤一:导入JSON的pom坐标
<!--json数据处理-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
</dependency>
步骤二:设置发送json数据(请求body中添加json数据)
步骤三:在SpringMvcConfig.class里面加入@EnableWebMvc,开启自动转换json数据的支持
//3.创建springmvc的配置文件,加载controller对应的bean
@Configuration
//@ComponentScan("com.hcx.controller")
@ComponentScan(value = "com.hcx"/*, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)*/)
@EnableWebMvc
public class SpringMvcConfig {
}
第四步:在Controller(控制层)编写代码,设置json数据
//集合参数:JSON格式
@RequestMapping("/listParamJson")
@ResponseBody
public String listParamJson(@RequestBody List<String> list){
System.out.println("集合参数传递 list ==>" + list);
return "{'module':'listJson param'}";
}
//POJO参数:JSON格式
@RequestMapping("/pojoParamJson")
@ResponseBody
public String pojoParamJson(@RequestBody User user){
System.out.println("pojo(json)参数传递 user ==>"+user);
return "{'module':'pojoJson param'}";
}
//对象集合类型:JSON格式
@RequestMapping("/listUserParamJson")
@ResponseBody
public String listUserParamJson(@RequestBody List<User> list){
System.out.println("List<User>参数传递 ==>" + list);
return "{'module':'List<User>Json param'}";
}
注解复习
-
名称:@EnableWebMvc
-
类型:配置类注解
-
位置:SpringMVC配置定义上方
-
作用:开启SpringMVC多项负责功能
-
范例:
//3.创建springmvc的配置文件,加载controller对应的bean @Configuration //@ComponentScan("com.hcx.controller") @ComponentScan(value = "com.hcx"/*, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)*/) @EnableWebMvc public class SpringMvcConfig { }
-
名称:@RequestBody
-
类型:形参注解
-
位置:SpringMVC控制器方法形参定义前面
-
作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次
-
范例:
@RequestMapping("/listParamForJson") @ResponseBody public String listParamForJson(@RequestBody List<String> likes){ System.out.println("list common(json)参数传递 list ==>"+ likes); return "{'moudle':'list common for json param'}"; }
@RequestBody与@RequestParam区别
- 区别
- @RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】
- @RequestBody用于接收json数据【application/json】
- 应用
- 后期开发中,发送json格式数据为主,@ResquestBody应用较广
- 如果发送飞json格式数据,选用@RequestParam接收请求参数
日期类型参数传递
-
日期类型数据基于系统不同格式也不尽相同
- 2022-08-30
- 2022/08/30
- 08/18/2022
-
接收形参是,根据不同的日期格式设置不同的接收方式
@RequestMapping("/dateParam") @ResponseBody public String dateParam(Date date, @DateTimeFormat(pattern = "yyyy-MM-dd") Date date1, @DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss")Date date2){ System.out.println("参数传递 date ==>" + date); System.out.println("参数传递 date(yyyy-MM-dd) ==>" + date1); System.out.println("参数传递 date(yyyy/MM/dd HH:mm:ss) ==>" + date2); retrun "{'module':'data param'}"; }
日期类型注解
-
名称:@DateTimeFormat
-
类型:形参注解
-
位置:SpringMVC控制器方法形参前面
-
作用:设定日期时间型数据格式
-
范例:
@RequestMapping("/dateParam") @ResponseBody public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date){ System.out.println("参数传递 date ==>" + date); retrun "{'module':'data param'}"; }
-
属性:pattern = "日期时间格式字符串"
类型转化器
-
Converter接口
public interface Converter<S,T>{ @Nullable T convert(S source); }
- 请求参数年龄数据(String+Integer)
- 日期格式转换 (String -> Date)
-
@EnableWebMvc功能之一:根据类型匹配对应类型转换器
响应
-
响应页面
-
响应数据
- 文本数据
- JSON数据
-
响应文本数据
@RequestMapping("/toText") @ResponseBody public String toText(){ return "response text"; }
-
响应json数据(对象转json)
//响应POJO对象数据 @RequestMapping("/toJsonPOJO") @ResponseBody public User toJsonPOJO(){ System.out.println("返回json对象数据"); User user = new User(); user.setName("hcx"); user.setAge(21); user.setAddress(new Address("南昌","江西")); return user; }
-
响应对象集合转json数组
//响应POJO集合对象 @RequestMapping("/toJsonList") @ResponseBody public List<User> toJsonList(){ System.out.println("返回json对象集合"); User user= new User(); user.setName("开源协议"); user.setAge(21); user.setAddress(new Address("南昌","江西")); User userOne= new User(); userOne.setName("阿里巴巴开发手册"); userOne.setAge(15); userOne.setAddress(new Address("杭州","浙江")); List<User> userList = new ArrayList<>(); userList.add(user); userList.add(userOne); return userList; }
注解复习
-
名称:@ResponseBody
-
类型:方法注解
-
位置:SpringMVC控制器方法定义上方
-
作用:设置当前控制器方法响应内容为当前返回值,无需解析
-
范例:
@RequestMapping("/save") @ResponseBody public String save(){ System.out.print("Save..."); return "{'info':'springmvc'}"; }
-
HttpMessageConverter接口
public interface HttpMessageConverter<T> { boolean canRead(Class<?> var1, @Nullable MediaType var2); boolean canWrite(Class<?> var1, @Nullable MediaType var2); List<MediaType> getSupportedMediaTypes(); T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException; void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException; }
REST风格
-
REST(Representational State Transfer),表现形式状态转换
-
优点:
- 隐藏资源的访问行为,无法通过地址得治对资源是何种操作
- 简化书写
REST风格简介
- 安装REST风格访问资源是使用行为动作区分对资源进行何种操作
- http://localhost/users 查询全部用户信息 GET (查询)
- http://loocalhost/users/1 查询指定用户信息 GET(查询)
- http://localhost/users 添加用户信息 POST(新增/保存)
- http://localhost/users 修改用户信息 PUT(修改/更新)
- http://localhost/users/1 删除用户信息 DELETE(删除)
注意事项:
上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users,books,accounts....
RESTful入门案例
-
设定http请求动作(动词)
@RequestMapping(value="/users",method =RequestMethod.POST) @ResponseBody public String save(@RequestBody User user){ System.out.println("user save..." + user); return "{'module':'user save'}"; } @RequestMapping(value="/users",method=RequestMethod.PUT) @ResponseBody publiic String update(@RequestBody User user){ System.out.println("user update ..."+ user); return "{'module':'user update'}"; }
-
设置请求参数(路径变量)
@RequestMapping(value ="/users/{id}",method =RequestMethod.DELETE) @ResponseBody public String delete(@PathVariable Integer id){ System.out.println("user delete ..."+ id); return "{'module':'user deleter'}"; }
注解复习
-
名称:@PathVariable
-
类型:形参注解
-
位置:SpringMVC控制器方法形参定义前面
-
作用:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应
-
范例:
@RequestMapping(value = "/users/{id}",method =RequestMethod.DELETE) @ResponseBody public String save(@PatVariable Integer id){ System.out.println("user delete ..." + id); return "{'module':'user delete'}"; }
-
名称:@RequestMapping
-
类型:方法注解
-
位置:SpringMVC控制器定义上方
-
作用:设置当前控制器方法请求访问路径
-
范例:
@RequestMapping(value = "/users",method =RequestMethod.POST) @ResponseBody public String save(@RequestBody User user){ System.out.println("user save ..." + user); return "{'module':'user save'}"; }
-
属性
- value(默认):请求访问路径
- method:http请求动作,标准动作(POST/GET/PUT/DELETE)
-
@RequestBody @RequestParam @PathVariable
- 区别
- @RequestParam用于接收url地址传参或表单传参
- @RequestBody用于接收JSON数据
- @PathVariable用于接收路径参数,使用{参数名称}描述路径参数
- 应用
- 后期开发中,发送请求参数超过一个时,以json格式为主,@RequestBody应用较广
- 如果发送非json格式数据,选用@RequestParam接收请求参数
- 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收路径变量,常用于传递id值
RESTful快速开发
@RequestMapping(value="/books",method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody Book book){
System.out.println("book save ..."+ book);
return "{'module':'book save'}";
}
@RequestMapping(value="/books",method = RequestMethod.PUT)
@ResponseBody
public String save(@RequestBody Book book){
System.out.println("book update ..."+ boook);
return "{'module':'book update'}";
}
合并注解事项简化开发
-
名称:@RestController
-
类型:类注解
-
位置:基于SpringMVC的RESTful开发控制器类定义上方
-
作用:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能
-
范例:
@RestControoler public class BookController{ }
-
名称:@GetMapping @PostMapping @PutMapping @DeleteMapping
-
类型:方法注解
-
位置:基于SpringMVC的RESTful开发控制器方法定义上方
-
作用:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应Get请求
-
范例:
@GetMapping("/{id}") public String getById(@PathVariable Integer id){ System.out.pringln("book getById ..." + id); return "{'module':'book getById'}"; }
-
属性
- value(默认):请求访问路径
案例:基于RESTful页面数据交互
-
制作SpringMVC控制器,并通过PostMan测试接口功能
@RestController @RequestMapping("/books") public Class BookController{ @PostMapping public String save(@RequestBody Book book){ System.out.println("book save ==>" + book); return "{'moudule':'book save success'}"; } @GetMapping public List<Book> getAll(){ List<Book> bookList = new ArrayList<>(); Book book = new Book(); book.setType("计算机"); book.setName("SpringMVC入门教程"); book.setDescription("小试牛刀"); bookList.add(book); Book book1 = new Book(); book1.setType("计算机"); book1.setName("SpringMVC入门教程"); book1.setDescription("加油"); bookList.add(book1); //模拟数据 return bookList; } }
-
设置对静态支援的访问通行
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { //当访问/pages/???不要走mvc,走/pages目录下的内容访问 registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); //放行js registry.addResourceHandler("/js/**").addResourceLocations("/js/"); //放行css registry.addResourceHandler("/css/**").addResourceLocations("/css/"); //放行插件 registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } }
-
前端页面通过异步提交访问后台控制器
//添加 saveBook(){ axios.post("/books",this.formData).then((res)=>{ }); }, //主页列表查询 getAll(){ axios.get("/books").then((res)=>{ this.dataList = res.data; }); },
-
总结
- 先做后台功能,开发接口并调通接口
- 再做页面异步调用,确认功能可以正常访问
- 最后再完成页面数据的显示
- 补充:放行静态资源访问
SSM整合
概览
- SSM整合
- 表现层数据封装
- 异常处理器
- 项目异常处理方案
- 案例:SMM整合标准开发
SSM整合流程
-
创建工程,并导入坐标
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>springmvc_08_ssm</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!-- spring整合mybatis需要三个坐标: mybatis mybatis-spring mysql-connect-java --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- 该项目中选择使用druid数据源--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.11</version> </dependency> <!-- 测试用,不解释--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- 做springmvc开发时,web容器要用到的--> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <!-- 实现json文件与各数据类型之间的互相转换--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.1</version> <configuration> <port>8080</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
-
SSM整合
-
Spring
-
SpringConfig
@Configuration @ComponentScan("com.hcx") @PropertySource("classpath:jdbc.properties") @Import({JdbcConfig.class,MyBatisConfig.class}) public class SpringConfig{ }
-
-
MyBatis
-
MybatisConfig
import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.context.annotation.Bean; import javax.sql.DataSource; public class MyBatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){ SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setTypeAliasesPackage("com.hcx.domain"); return factoryBean; } @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer mapperScannerConfigurer=new MapperScannerConfigurer(); mapperScannerConfigurer.setBasePackage("com.hcx.dao"); return mapperScannerConfigurer; } }
-
JdbcConfig
public class JdbcConfig{ @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String userName; @Value("${jdbc.password}") private String password; @Bean public DataSource dataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } //开启事务管理 @Bean public PlatformTransactionManager transactionManager(DataSource dataSource){ DataSourceTransactionManager ds = new DataSourceTransactionManager(); ds.setDataSource(dataSource); return ds; } }
-
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssm_db jdbc.username=root jdbc.password=******
-
-
SpringMVC
-
ServletConfig
package com.hcx.config; import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; import javax.servlet.Filter; public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; } protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMvcConfig.class}; } protected String[] getServletMappings() { return new String[]{"/"}; } //乱码处理——post表单提交时处理中文乱码 @Override protected Filter[] getServletFilters() { CharacterEncodingFilter filter = new CharacterEncodingFilter(); filter.setEncoding("UTF-8"); return new Filter[]{filter}; } }
-
SpringMvcConfig
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @Configuration @ComponentScan("com.hcx.controller") @EnableWebMvc public class SpringMvcConfig { }
-
-
-
功能模块
-
表与实体类
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Book { private Integer id; private String type; private String name; private String description; }
-
dao(接口+自动代理)
import com.hcx.domain.Book; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; import java.util.List; public interface BookDao { /*写法一:@Insert("insert into tbl_book values(null,#{type},#{name},#{description})")*/ @Insert("insert into tbl_book (type,name,description) values(#{type},#{name},#{description})") public void save(Book book); @Update("update tbl_book set type= #{type},name = #{name},description = #{description} where id = #{id}") public void update(Book book); @Delete("delete from tbl_book where id = #{id}") public void delete(Integer id); @Select("select * from tbl_book where id = #{id}") public Book getById(Integer id); @Select("select * from tbl_book") public List<Book> getAll(); }
-
service(接口+实现类)
- 接口
import com.hcx.domain.Book; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Transactional public interface BookService { /** * 保存 * @param book * @return */ public Boolean save(Book book); /** * 修改 * @param book * @return */ public Boolean update(Book book); /** * 根据id删除 * @param id * @return */ public Boolean delete(Integer id); /** * 根据id查询 * @param id * @return */ public Book getById(Integer id); /** * 查询全部 * @return */ public List<Book> getAll(); }
实现类
import com.hcx.dao.BookDao; import com.hcx.domain.Book; import com.hcx.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; public Boolean save(Book book) { bookDao.save(book); return true; } public Boolean update(Book book) { bookDao.update(book); return true; } public Boolean delete(Integer id) { bookDao.delete(id); return true; } public Book getById(Integer id) { return bookDao.getById(id); } public List<Book> getAll() { return bookDao.getAll(); } }
-
业务层接口测试(整合JUnit)
import com.hcx.config.SpringConfig; import com.hcx.domain.Book; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.List; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class BookServiceTest { @Autowired private BookService bookService; @Test public void testGetById(){ Book book = bookService.getById(1); System.out.println(book); } @Test public void testGetAll(){ List<Book> list=bookService.getAll(); System.out.println(list); } }
-
controller
import com.hcx.domain.Book; import com.hcx.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/books") public class BookController { @Autowired private BookService bookService; @PostMapping public Result save(@RequestBody Book book) { boolean flag = bookService.save(book); return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag); } @PutMapping public Result update(@RequestBody Book book) { boolean flag = bookService.update(book); return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERR, flag); } @DeleteMapping("/{id}") public Result delete(@PathVariable Integer id) { boolean flag = bookService.delete(id); return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERR, flag); } @GetMapping("/{id}") public Result getById(@PathVariable Integer id) { Book book=bookService.getById(id); Integer code = book != null ? Code.GET_OK:Code.GET_ERR; String msg = book != null ? "数据查询成功" : "数据查询失败请重试"; return new Result(code,book,msg); } @GetMapping public Result getAll() { List<Book> bookList = bookService.getAll(); Integer code = bookList != null ? Code.GET_ALL_OK:Code.GET_ALL_ERR; String msg = bookList != null ? "全部数据查询成功":"数据查询失败请重试"; return new Result(code,bookList,msg); } }
数据传输协议所要用到的bean
Result.java
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; @Data @AllArgsConstructor @NoArgsConstructor @ToString public class Result { private Integer code; private Object data; private String msg; public Result(Integer code, Object data) { this.code = code; this.data = data; } }
Code.java
public class Code { public static final Integer SAVE_OK = 20011; public static final Integer DELETE_OK = 20021; public static final Integer UPDATE_OK = 20031; public static final Integer GET_OK = 20041; public static final Integer GET_ALL_OK = 20051; public static final Integer SAVE_ERR = 20010; public static final Integer DELETE_ERR = 20020; public static final Integer UPDATE_ERR = 20030; public static final Integer GET_ERR = 20040; public static final Integer GET_ALL_ERR = 20050; }
- 表现层接口测试(Apifox)
-
SSM整合-表现层与前端数据传输协议定义
数据返回格式:
{
"code":Integer,
"data":Obj,
"msg":String
}
完成方式:
在Controller里面添加Result类和Code类,并让Controller里面的方法都返回Result类型的结果
SSM整合-异常处理
异常处理器
- 程序开发过程中不可避免的会出现异常
- 出现异常现象的常见位在与常见诱因如下:
- 框架内部抛出异常:因使用不规范导致
- 数据层抛出的异常:因为外部服务器故障导致(例如:服务器访问超时)
- 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
- 表现层抛出的异常:因数据收集,校验规则导致(例如:不匹配的数据类型间导致异常)
- 工具类抛出异常:因工具类书写不严谨不狗健壮导致(例如:必要释放的连接长使劲啊未释放等)
-
各个层级均出现异常,异常处理代码书写在哪一层
——所有的异常均抛出到表现层处理
-
表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不强,如何解决——AOP思想
-
定义异常处理器
- 集中的,统一的处理项目中出现的异常
@ResControllerAdvice public class ProjectExceptionAdvice{ @ExceptionHandler(Exception.class) public Result doExpetion(Exception ex){ System.out.println("异常被捕获"); return new Result(001,null,"异常被捕获"); } }
注解使用:
-
名称:@RestControllerAdvice
-
类型:类注解
-
位置:Rest风格开发的控制器增强类定义上方
-
作用:为Rest风格开发的控制器类做增强
-
范例:
@ResControllerAdvice public class ProjectExceptionAdvice{ }
-
说明:
- 此注解自带@ResponseBody注解与@Component注解,具备对应的功能
-
名称:@ExceptionHandler
-
类型:方法注解
-
位置:专用于异常处理的控制器上方
-
作用:设定指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行
-
范例:
@RestControllerAdvice public class ProjectExceptionAdvice{ @ExceptionHandler(Exception.class) public Result doException(Exception exception){ return new Result(001,null,"异常已被捕获") } }
-
说明:
- 此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常
SSM整合-项目异常处理方案
-
项目异常分类
- 业务异常(BusinessException)
- 规范的用户行为产生的异常
- 不规范的用户行为操作产生的异常
- 系统异常(SystemException)
- 项目运行过程中可预计且无法避免的异常
- 其他异常(Exception)
- 编程人员为预期到的异常
- 业务异常(BusinessException)
-
项目异常处理方案
- 业务异常(BusinessException)
- 发送对应消息传递给用户,提醒规范操作
- 系统异常(SystemException)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给运维人员,提醒维护
- 记录日志
- 其他异常(Exception)
- 发送固定消息给用户,安抚用户
- 发送特定消息给编程人员,提醒维护(纳入预期范围内)
- 记录日志
- 业务异常(BusinessException)
项目异常处理
-
自定义项目系统级异常
public class SystemException extends RuntimeException{ private Integer code; public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public SystemException(Integer code) { this.code = code; } public SystemException(Integer code,String message ) { super(message); this.code = code; } public SystemException(Integer code,String message, Throwable cause ) { super(message, cause); this.code = code; } public SystemException(Integer code,Throwable cause ) { super(cause); this.code = code; } public SystemException(Integer code,String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); this.code = code; } }
-
自定义项目业务级异常
public class BusinessException extends RuntimeException{ private Integer code; public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public BusinessException(Integer code) { this.code = code; } public BusinessException(Integer code, String message ) { super(message); this.code = code; } public BusinessException(Integer code, String message, Throwable cause ) { super(message, cause); this.code = code; } public BusinessException(Integer code, Throwable cause ) { super(cause); this.code = code; } public BusinessException(Integer code, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); this.code = code; } }
-
自定义异常编码(持续补充)
public class Code{ public static final Integer SAVE_OK = 20011; public static final Integer DELETE_OK = 20021; public static final Integer UPDATE_OK = 20031; public static final Integer GET_OK = 20041; public static final Integer GET_ALL_OK = 20051; public static final Integer SAVE_ERR = 20010; public static final Integer DELETE_ERR = 20020; public static final Integer UPDATE_ERR = 20030; public static final Integer GET_ERR = 20040; public static final Integer GET_ALL_ERR = 20050; public static final Integer SYSTEM_ERR = 20060; public static final Integer SYSTEM_TIMEOUT_ERR = 20070; public static final Integer BUSINESS_ERR = 20080; public static final Integer SYSTEM_UNKNOW_ERR = 20090; }
-
触发定义异常
@Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; public Boolean save(Book book) { bookDao.save(book); return true; } public Boolean update(Book book) { bookDao.update(book); return true; } public Boolean delete(Integer id) { bookDao.delete(id); return true; } public Book getById(Integer id) { //模拟业务异常 if(id == 1){ throw new BusinessException(Code.BUSINESS_ERR,"访问连接过多异常"); } //将可能出现的异常进行包装,转换称自定义异常 try { int i = 1/0; }catch (Exception exception){ throw new SystemException(Code.SYSTEM_TIMEOUT_ERR,"服务器访问超时,请重试",exception); } return bookDao.getById(id); } public List<Book> getAll() { return bookDao.getAll(); } }
-
拦截并处理异常
@RestControllerAdvice public class ProjectExceptionAdvice { @ExceptionHandler(Exception.class) public Result doException(Exception exception) { //记录日志(错误堆栈) //发送消息给运维 //发送邮件给开发人员,exception对象发送给开发人员 System.out.println("其他异常被捕获"); return new Result(Code.SYSTEM_UNKNOW_ERR, null, "系统繁忙请稍后再试"); } @ExceptionHandler(SystemException.class) public Result doSystemException(SystemException exception) { //记录日志(错误堆栈) //发送消息给运维 //发送邮件给开发人员,exception对象发送给开发人员 System.out.println("系统异常被捕获"); return new Result(exception.getCode(), null, exception.getMessage()); } @ExceptionHandler(BusinessException.class) public Result doSystemException(BusinessException exception) { System.out.println("业务异常被捕获"); return new Result(exception.getCode(), null, exception.getMessage()); } }
-
异常处理效果对比
总结:
- 异常处理器
- 自定义异常
- 异常编码
- 自定义消息
SSM整合标准开发
...
SpringMVC拦截器
拦截器概念
- 拦截器(Interceptor)是一种动态拦截方法调用的机制,再SpringMVC中动态拦截控制器方法的执行
- 作用:
- 在指定的方法调用前后执行预先设定的代码
- 阻止原始方法的执行
拦截器和过滤器的区别
- 归属不同:Filter属于Servlet计算,Interceptor属于SpringMvc技术
- 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强
拦截器入门案例
-
制作拦截器功能类
-
配置拦截器的执行位置
-
声明拦截器的bean,并实现HandlerInterCeptor接口(注意:扫描加载bean)
@Component public class ProjectInterCeption implements HandlerInterceptor{ public boolean preHandle(..) throws Exception{ System.out.println("preHandle..."); return true; } public void postHandle(..) throws Exception{ System.out.println("postHandle..."); } public void afterCompletion(..) throws Exception{ Syetm.out.println("afterCompletion..."); } }
-
定义配置类,集成WebMvcConfigurationSupport,实现addInterceptor方法(注意:扫描加载配置)
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport{ @Override public void addInterceptors(InterceptorRegistry registry){ ... } }
-
添加拦截器并设置拦截访问路径,路径可以通过可变参数设置多个
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport{ @Autowired private ProjectInterceptor projectInterceptor; @Override public void addInterCeptors(InterceptorRegisetry registry){ regisyry.addInterceptor(proejectInterceptor).addPathPatternas("/books"); } }
-
使用标准接口WebMvcConfigurer简化开发(注意:侵入式较强)
@Configuration @ComponentScan("com.hcx.controller") @EnableWebMvc public class SpringMvcConfig implements WebMvcConfigurer{ @Autowired private ProjectInterCeptor projectInterceptor; public void addInterceptors(InterceptorRegistry registry){ registry.addInterceptor(projectInterceptor).addPatternas("/books","/books"); } }
拦截器执行流程
- preHandle
- return true
- contrller
- postHandle
- afterCompletion
- return false
- 结束
- return true
拦截器参数
-
前置处理
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle..."); return true; }
-
具体参数:
-
handler:被调用的处理器对象,本质上是一个方法对象,对反射计数中的Method对象进行了再包装,可用于获取拦截方法信息
-
request:请求对象
-
response:响应对象
-
modelAndView:获取页面跳转相关数据
-
ex:拿到原始程序执行过程中出现的异常,表现层出现的异常
-
-
返回值
- 返回值未false,被拦截的处理器将不执行
多个拦截器执行顺序
- 当配置多个拦截器是,形成拦截器链
- 拦截器链的运行属性呢参照拦截器添加顺序未准
- 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
- 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作
总结:
-
拦截器链配置方式
-
拦截器链的运行顺序
- preHandle:与配置顺序相同,必定运行【配置顺序就是运行顺序】
- postHandle:与配置顺序相反,可能不运行【配置顺序就是运行顺序反过来】
- afterComletion: 与配置顺序相反,可能不运行【配置顺序就是运行顺序反过来】
解释:顺序情况下的话,走到哪里出现错误返回false,则后面的postHandle拦截器都不运行,afterComletion会运行
Mave进阶
内容概览
- 分模块开发的意义
- 依赖管理
- 聚合与继承
- 属性管理
- 多环境配置与应用
- 私服
学习目标
- 理解分模块开发的意义
- 能使用聚合工程快速构建项目
- 能够使用继承简单化项目配置
- 能够根据需求配置生产、开发、测试、环境,并在各个环境间切换运行
分模块开发与设计
分模块开发的意义
- 将原始模块按照功能拆分成若干个子模块,方便魔窟间的相互调用,接口共享
分模块开发(模块拆分步骤)
步骤
-
创建Maven模块
-
书写模块代码
注意事项:分模块开发需要先针对模块功能进行设计,在进行编码。不会先将工程开发完毕,然后进行拆分
-
通过maven指令安装模块到本地仓库(insatall指令)
注意事项:团队内部开发需要发布模块功能到团队内部可共享的仓库中(私服)
依赖管理
-
依赖指当前项目运行所需的jar,一个项目可以设置多个依赖
-
格式:
<!--设置当前项目所依赖的所有jar,一个项目可以设置多个依赖--> <dependencies> <!--设置具体的依赖--> <dependency> <!--依赖所属群组id--> <groupId>org.springframework</groupId> <!--依赖所属项目id--> <artifactId>spring-webmvc</artifactId> <!--依赖版本号--> <version>5.2.10.RELEASE</version> </dependency> </dependencies>
依赖传递
- 依赖具有传递性
- 直接依赖:在当前项目中通过依赖配置简历的依赖关系
- 间接依赖:被资源的资源如果依赖其他资源,当前项目间解依赖其他资源
- 依赖传递冲突问题
- 路径优先:当依赖中出现相同的资源时,层级越深,优先级越低,层级越浅,优先级越高
- 声明优先:当资源在相同层级被依赖时,配置顺序越靠前的覆盖配置顺序靠后的
- 特殊优先:当同级配置了相同资源的不同版本,后配置的覆盖先配置的
- 可在idea中看mave结构选项内显示哪个优先级哪个大,可以打开看结构关系深度图
可选依赖
-
可选依赖指对外隐藏当前所依赖的资源——不透明
-
可选依赖是隐藏带你给钱工程所依赖的资源,隐藏后对应资源不具有依赖传递性
<optional>true</optional>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> <!--可选依赖是隐藏带你给钱工程所依赖的资源,隐藏后对应资源不具有依赖传递性--> <optional>true</optional> </dependency>
排除依赖
-
排除依赖指主动断开依赖的资源,被排除的资源无需治党版本——不需要
-
在依赖的模块内排除不需要的依赖
<exclusions></exclusions>
<!--依赖domain运行-->
<dependency>
<groupId>com.hcx</groupId>
<artifactId>Maven_03_pojo</artifactId>
<version>1.0-SNAPSHOT</version>
<!--排除依赖是隐藏当前资源对应的依赖关系-->
<exclusions>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusions>
</dependency>
- 排除依赖资源仅治党GA即可,无需指定V
继承和聚合
聚合
- 聚合:将多个模块组织成一个整体,同时进行项目构建的过程称为聚合
- 聚合工程:通常是一个不具有业务功能的"空"工程(有且仅有一个pom文件)
- 作用:使用聚合工程可以将多个工程编组,通过对聚合工程进行构建,事项对所有包含的模块进行同步构建
- 当工程中某个模块发送更新(变更)时,必须保障工程中已更新模块关联的模块同步更新,此时可以使用聚合工程来解决批量模块同步构建的问题
聚合工程开发
步骤:
-
创建Maven模块,设置打包类型为pom
<groupId>com.hcx</groupId> <artifactId>maven_01_parent</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging>
注意事项:每个maven工程都有对应的打包方式,默认为jar,web工程打包方式为war
-
设置当前聚合工程中所包含的子模块名称
<modules> <module>../Maven_03_pojo</module> <module>../maven_04_dao</module> <module>../springmvc_08_ssm</module> </modules>
注意事项:聚合工程中所包含的模块在进行构建时会更卷模块间的依赖关系设置构建顺序,与聚合工程中模块的配置书写位置无关,参与聚合的工程无法向上干燥是否参与聚合,只能向下配置哪些模块参与本工程的聚合
继承
- 概念:继承描述的是连个工程间的关系,与java中的继承类似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承
- 作用:
- 简化配置
- 减少版本冲突
继承关系开发工程
-
创建Mave模块,设置打包类型为pom
<packageing>pom</packageing>
注意事项:建议父工程打包方式设为pom
-
在父工程的pom文件纵配置依赖关系(子工程将沿用父工程中的依赖关系)
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RRLEASE</version> </dependency> ...... </dependencies>
-
配置子工程中可选的依赖关系
<dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> ....... </dependencies> </dependencyManagement>
-
在子工程中配置当前使用父工程中可选依赖的坐标
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> </dependencies>
注意事项:子工程中使用父工程中的可选依赖是,仅需要提供群组id和项目id,版本由父工程统一管理,避免版本冲突子工程中还可以定义父工程中没有定义的依赖关系
聚合和继承的区别
- 作用
- 聚合用于快速构建项目
- 继承用于快速配置
- 相同点
- 聚合与继承的pom.xml文件打包方式均为pom,可以将两种关系制作到同一个pom文件中
- 聚合与继承均属于设计型模块,并无实际的模块内容
- 不同点:
- 聚合是当前模块中配置关系,聚合可以感知到参与聚合的模块有哪些
- 继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己
属性
属性
解决版本号统一问题
属性的配置与使用
-
定义属性
<!--定义属性--> <properties> <spring.version>5.2.10.RELEASE</spring.version> </properties>
注意事项:properties标签内的标签可自行定义【尽量做到间名知意】
-
引用属性
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency>
配置文件加载属性
资源文件引用属性
-
定义属性
<!--定义自定义属性--> <properties> <spring.version>5.2.10.RELEASE</spring.version> <junit.version>4.12</junit.version> <jdbc.url>jdbc:mysql://127.0.0.1:3306/ssm_db</jdbc.url> </properties>
-
配置文件中引用属性
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=${jdbc.url} jdbc.username=root jdbc.password=******
-
开启资源文件目录加载属性的过滤器
<build> <resources> <resource> <directory>${project.basedir}/src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build>
-
配置maven打war包,忽略web.xml检查
<!--配置插件加载proterties文件--> <build> <resources> <resource> <directory>${project.basedir}/src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build>
其他属性
-
属性列表
- 自定义属性(常用)
- 内置属性
- Setting属性
- Java系统属性
- 环境变量属性
-
表格详细展示(了解)
属性分类 引用格式 示例 自定义属性 $ $ 内置属性 $ ${basedir} $ Setting属性 $ $ Java系统属性 $ $ 环境变量属性 $ $
版本
版本管理
- 工程版本
- SNAPSHOT(快照版本)
- 项目开发过程中临时输出的版本,称为快照版本
- 快照版本会随着开发的进展不断更新
- RELEASE(发布版本)
- 项目开发到静茹阶段里程碑后向团队外部发布较为稳定的版本,这种版本所对应的构件文件是稳定的,即便进行功能的后续开发,也不会改变当前发布版本内容,这种版本称为发布版本
- SNAPSHOT(快照版本)
- 发布版本
- alpha版
- beta版
- 纯数字版
多环境配置与应用
多环境开发
- mave提供了配置多种环境的设定,帮助开发者使用过程种快速切换环境
-
定义多环境
<!--配置多环境开发--> <profiles> <!--开发环境--> <profile> <id>env_dep</id> <properties> <jdbc.url>jdbc:mysql://127.0.0.1:3306/ssm_db</jdbc.url> </properties> <!--设定是否是默认环境,设置默认启动--> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <!--生产环境--> <profile> <id>pro_dep</id> <properties> <jdbc.url>jdbc:mysql://127.1.1.1:3306/ssm_db</jdbc.url> </properties> </profile> <!--测试环境--> <profile> <id>test_dep</id> <properties> <jdbc.url>jdbc:mysql://127.2.2.2:3306/ssm_db</jdbc.url> </properties> </profile> </profiles>
-
使用多环境(构建过程)
mvn 指令 -P 环境定义id
范例:
mvn install -P pro_env
跳过测试
目的:为了跳过一些没有开发完成的完成测试内容
-
跳过测试
mvn 指令 -D skipTests
-
范例:
mvn install -D skipTests
注意事项:执行的项目构建指令必须包含测试声明周期,否则无效果。例如执行compile生命周期,不经过test生命周期
-
细粒度控制跳过测试
<!--跳过为完成测试的插件--> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.12.4</version> <configuration> <skipTests>true</skipTests> <!--包含指定的测试用例--> <includes> <include>**/User*Test.java</include> </includes> <!--排除参与测试的内容--> <excludes> <exclude>**/BookServiceTest.java</exclude> </excludes> </configuration> </plugin> </plugins>
私服
-
私服是一台独立的服务器,用于解决团队内容不的资源共享与资源同步问题
-
Nexus
- Sonatype公司的一款maven私服产品
- 下载地址:https://help.sonatype.com/repomanager3/download
Nexus安装与启动
-
启动服务器(命令行启动)
nexus.exe /run nexus
-
访问服务器(默认端口:8081)
http://localhost:8081
-
修改基础配置信息
- 安装路径下etc目录中nexus-default.properties文件保存有nexus基础配置信息,例如访问端口
-
修改服务器运行配置信息
- 安装路径下bin目录中nexus.vmoptions文件保存有nexus服务器对应的配置信息,例如默认占用内存空间
私服资源操作流程分析
- 我们的坐标是放在私服的仓库中,下载资源是使用仓库组
私服仓库分类
仓库类别 | 英文名称 | 功能 | 关联操作 |
---|---|---|---|
宿主仓库 | hosted | 保存自主研发+第三方资源 | 上传 |
代理仓库 | proxy | 代理连接中央仓库 | 下载 |
仓库组 | group | 为长裤编组简化下载操作 | 下载 |
资源的上传和下载
本地仓库访问
-
配置位置(setting.xml文件中)
<servers> <!--配置访问私服权限--> <server> <id>hcx-release</id> <username>admin</username> <password>admin</password> </server> <server> <id>hcx-snapshot</id> <username>admin</username> <password>admin</password> </server> </servers>
-
配置位置(setting.xml文件中)
<mirrors> <!--私服的访问路径--> <mirror> <id>maven-public</id> <mirrorOf>*</mirrorOf> <url>http://localhost:8081/repository/maven-public/</url> </mirror> <!--阿里的私服镜像--> <mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors>
-
发布命令
mvn deploy
-
私服配置位置(nexus服务器页面设置)
SpringBoot
目录
- SpringBoot简介
- 基础配置
- 整合第三方技术
学习目标
- 掌握基于SpringBoot框架的程序开发步骤
- 熟练使用SpringBoot配置信息修改服务器配置
- 基于SpringBoot的完成SSM整合项目开发
SpringBoot简介
入门案例
-
SpringBoot是由Pivotal团队提供的全新框架,器设计目的是用来简化Spring应用的初始搭建以及开发过程
-
原生开发SpringMVC程序过程
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- 做springmvc开发时,web容器要用到的--> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> </dependencies>
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; } protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMvcConfig.class}; } protected String[] getServletMappings() { return new String[]{"/"}; } //乱码处理——post表单提交时处理中文乱码 @Override protected Filter[] getServletFilters() { CharacterEncodingFilter filter = new CharacterEncodingFilter(); filter.setEncoding("UTF-8"); return new Filter[]{filter}; } }
@Configuration @ComponentScan({"com.hcx.controller","com.hcx.config"}) @EnableWebMvc public class SpringMvcConfig{ }
@RestController @RequestMapping("/books") public class BookController { @Autowired private BookService bookService; @PostMapping public Result save(@RequestBody Book book) { boolean flag = bookService.save(book); return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag); } }
-
SpringBoot则仅需创建完工程后进行编写Contrller就行了
-
具体过程
-
创建新模块,选择Spring初始化,并配置模块相关基础信息
-
选择当前模块需要使用的技术集
- 开发控制器
- 运行自动生成的Application类
-
-
最简SpringBoot程序所包含的基础文件
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.hcx</groupId> <artifactId>springbootdemo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springbootdemo</name> <description>springbootdemo</description> <properties> <java.version>8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.9.1</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
-
Applicationl类
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
-
Spring程序与SpringBoot程序对比
类/配置文件 Spring SpringBoot pom文件中的坐标 手动添加 勾选添加 web3.0配置类 手动添加 无需手动 Spring/SpringMVC配置类 手动制作 无需手动 控制器 手动制作 手动制作 注意事项:基于idea开发SpringBoot程序需要确保联网且能够加载到程序框架结构
-
SpringBoot项目也可以在SpringBoot官方网站上创建项目并下载到本地
-
前后断分离合作开发
-
SpringBoot项目快速启动
-
对SpringBoot项目打包(执行Maven构建指令package)
-
执行启动指令
java -jar springboot.jar
注意事项
jar支持命令行启动需要依赖maven插件支持,请确认打包时是都就有SpringBoot对应的maven插件
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
SpringBoot概述
-
SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程
-
Spring程序缺点
- 配置繁琐
- 依赖设置繁琐
-
SpringBoot程序优点
- 自动化配置
- 起步依赖(简化依赖配置)
- 辅助功能(内置服务器,....)
SpringBoot起步依赖
-
starter
-
SpringBoot中常见项目名称,定义了当前项目使用的所有项目坐标,可以减少手动依赖配置的目的
-
起步依赖的东西
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
好处:无需写版本,无需再去配置更多的依赖
-
-
parent
-
所有SpringBoot项目要继承的项目,定义了若干个坐标版本号(依赖管理,而非依赖),以大道减少依赖冲突的目的
-
spring-boot-starter-parent(2.5.0)与spring-boot-start-parent(2.4.6)共计57处坐标版本不同
-
-
实际开发
- 使用任意坐标是,仅书写GAV中的GA,V由SpringBoot提供
- 如果发送坐标错误,再指定version(要小心版本冲突)
-
辅助功能
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
-
启动方式
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
-
SpringBoot在创建项目是,采用jar的打包方式
-
SpringBoot的引导类是项目的入口,运行main方法就可以启动项目
-
使用maven依赖管理变更起步依赖项
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!--排除依赖--> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> </dependencies>
-
Jetty比Tomcat更轻量级,可扩展性更强(相较于Tomcat),谷歌应用引擎(GAE)已经全面切换为Jetty了
-
换技术换起步依赖就行了
配置文件格式
-
修改服务器端口
-
SpringBoot提供了多种属性配置方式
-
application.properties
sever.port=80
-
application.yml(主要)
server: port:81
-
appliction.yaml
server: port:82
-
-
自动提示功能消失解决方案
-
SpringBoot配置文件加载顺序(了解)
- application.properties > application.yml >application.yaml
注意事项:
SpringBoot核心配置文件名为application
SpringBoot内置属性过多,且所有属性集中在一起修改,在使用是,通过提示键+关键字修改属性
-
基础配置
yaml
yaml概览
- YAML(YAML Ain't Markup Language), 一种数据序列化格式
- 优点:
- 容易阅读
- 容易与脚本语言交互
- 以数据为核心,重数据轻格式
- YAML文件拓展名
- .yml(主流)
- .yaml
- yaml语法规则
- 大小写敏感
- 属性多级层次关系使用多行描述,每行结尾使用冒号结束
- 使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
- 属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔
- #表示注释
- 核心规则:数据前面要加空格与冒号隔开
yaml数组数据
-
数组数据再数据书写位置的下方使用减号作为数据开始符号,每行书写一个数据,减号与数据键空格分隔
enterprise: name: hcx age: 21 tel: 4006154000 subject: -Java -前端 -大数据
yaml数据读取方式
1.使用@Value读取单个数据,属性名引用方式:${一级属性名.二级属性名}【直接读取】
server:
port: 8080
logging:
level:
root: info
lesson: SpringBoot
enterprise:
name: hcx
age: 21
tel: 4006184000
subject:
- Java
- 前端
- 大数据
@RestController
@RequestMapping("/books")
public class BookController {
@Value("${lesson}")
private String lesson;
@Value("${server.port}")
private Integer port;
@Value("${enterprise.subject[0]}")
private String[] subject_00;
}
2.封装全部数据到Environment对象【封装后读取】
server:
port: 8080
logging:
level:
root: info
lesson: SpringBoot
enterprise:
name: hcx
age: 21
tel: 4006184000
subject:
- Java
- 前端
- 大数据
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private Environment environment;
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
System.out.println(environment.getProperty("lesson"));
System.out.println(environment.getProperty("server.port"));
System.out.println(environment.getProperty("enterprise.age"));
System.out.println(environment.getProperty("enterprise.subject[1]"));
return "hello,spring boot";
}
}
3.自定义对象封装指定数据【封装到类里面后读取】
server:
port: 8080
logging:
level:
root: info
lesson: SpringBoot
enterprise:
name: hcx
age: 21
tel: 4006184000
subject:
- Java
- 前端
- 大数据
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private Environment environment;
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
System.out.println(enterprise);
return "hello,spring boot";
}
}
@Component
@ConfigurationProperties(prefix = "enterprise")
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Enterprise {
private String name;
private Integer age;
private String tel;
private String[] subject;
}
注意事项:
自定义对象封装数据警告解决方案
pom.xml里面加上
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
多环境启动
yaml格式
# 设置启用的环境
spring:
profiles:
active: dev
---
# 生产
spring:
profiles: pro
server:
port: 80
---
# 开发
spring:
profiles: dev
server:
port: 8080
---
# 测试
spring:
profiles: test
server:
port: 8081
# 在springboot2.5中这是一种过时格式
spring:
profiles:
active: pro
---
server:
port: 80
spring:
config:
activate:
on-profile: pro
# 推荐格式
properties文件多环境启动
-
主动配置文件application.properties
spring.profiles.active=pro
-
环境分类配置文件application-pro.properties
server.port=80
-
环境分类配置文件application-dev.properties
server.port=81
-
环境分类配置文件application-test.properties
server.port=82
多环境启动命令格式
-
带参数启动SpringBoot
- 修改运行环境
java -jar springboot.jar --spring.profiles.active=test
-
修改端口号
java -jar springboot.jar --server.port=88
-
修改端口号并且修改环境
java -jar springboot.jar --server.port=88 --spring.profiles.active=test
-
参数加载优先级
- 详见核心功能 (spring.io)【优先级由上到下,由低到高】
- 默认属性(由设置 指定)。
SpringApplication.setDefaultProperties
@PropertySource
类的注释。请注意,在刷新应用程序上下文之前,不会将此类属性源添加到 中。配置某些属性(如 和 在刷新开始之前读取的属性)为时已晚。@Configuration``Environment``logging.*``spring.main.*
- 配置数据(如文件)。
application.properties
- 仅在 中具有属性的 。
RandomValuePropertySource``random.*
- 操作系统环境变量。
- Java 系统属性 ()。
System.getProperties()
- 来自 的 JNDI 属性。
java:comp/env
ServletContext
初始化参数。ServletConfig
初始化参数。- 属性来自(嵌入在环境变量或系统属性中的内联 JSON)。
SPRING_APPLICATION_JSON
- 命令行参数。
properties
测试的属性。在@SpringBootTest
和测试注释中可用,用于测试应用程序的特定切片。@TestPropertySource
测试上的注释。- 当 devtools 处于活动状态时,目录中的 Devtools 全局设置属性。
$HOME/.config/spring-boot
- 默认属性(由设置 指定)。
- 详见核心功能 (spring.io)【优先级由上到下,由低到高】
-
注意:
- 打包时Maven生命周期先执行clean再去执行package
- 记得修改项目编码字符集为utf-8
- 配置文件较多时记得整理,不要让多余的配置文件干扰到你的验证效果
整合第三方技术
整合JUnit
Spring整合JUnit(复习)
@RunWith(SpringJUnit4ClassRunner.class) //设置运行器
@ContextConfiguration(classes = SpringConfig.class) //加载配置环境
public class UserServiceTest{
@Autowired
private BookService bookService; //注入测试对象
@Test //功能测试
public void testSave(){
bookService.save()
}
}
SpringBoot整合JUnit
@SpringBootTest
class SpringbootApplicationTests{
@Autowired
private BookService bookService;
@Test
public void testSave(){
bookService.save();
}
}
新注解学习
-
名称:@SpringBootTest
-
类型:测试类注解
-
位置:测试类定义上方
-
作用:设置JUnit加载的SpringBoot启动类
-
范例:
@SpringBootTest(classes = SpringbootJunitApplication.class) class SpringbootJunitApplicationTests{}
-
相关属性
- classes: 设置SpringBoot启动类
注意事项
- 如果测试类再SpringBoot启动类的包或子包中,可以省略启动类的设置,也就是省略classes的设定
整合SSM
基于SpringBoo实现SSM整合
- SpringBoot整合Spring(不存在)
- SpringBoot整合SpringMvc(不存在)
- SpringBoot整合MyBatis(主要)
Spring整合MyBatis(复习)
- Spring整合MyBatis(复习)
- SpringConfig
- 导入JdbcConfig
- 导入MyBatisConfig
- JDBCConfig
- 定义数据源(加载properties配置项:driver、url、username、password)
- MyBatisConfig
- 定义SqlSessionFactoryBean
- 定义映射配置
- SpringConfig
SpringBoot整合MyBatis
-
创建模块,选择Spring初始化,并配置模块相关基础信息
-
选择当前模块需要使用的技术集(MyBatis、MySQL)
-
设置数据源参数
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db username: root password: ******
注意事项:
SpringBoot版本低于2.4.3(不含),Mysql驱动版本大于8.0时,需要再url连接串中配置时区
jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
或再MySQL数据库端配置时区解决此问题
-
定义数据层接口与映射配置
@Mapper public interface BookDao { @Select("select * from tbl_book where id = #{id}") public Book getById(Integer id); }
-
测试类中注入dao接口,测试功能
@SpringBootTest class SpringbootMybatisApplicationTests { @Autowired private BookService bookService; @Test void contextLoads() { Book book = bookService.getById(1); System.out.println(book); } }
基于SpringBoot的SSM整合案例
-
pom.xml
配置起步依赖,必要的资源坐标
-
application.yml
设置数据源、端口等
-
配置类
全部删除
-
dao
设置@Mapper
-
测试类
-
页面
放置在resources目录下的static目录中