SSM框架学习
前言:花了一周学习了SSM(Spring+SpringMVC+Mybatis)以及Maven高级部分加MybatisPlus并且做了这个笔记,自己看黑马视频做的总结,不得不说学习还得多做笔记,做完笔记后还得在回头回忆一遍,遇到没记住的部分可以回来翻阅,很方便。仅上传作纪念,之前看JavaWeb也有记笔记,到时候有时间再放上来,仅供参考,有做的不好的地方还请多多指教!
一:Spring Framework系统框架
二:Spring核心概念
1.IoC、Bean、DI
1.1:IoC&Bean
- IoC(Inversion of Control)控制反转
- 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权转移到外部,此思想称为控制反转。
- Spring提供了一个容器,称为IoC容器,用来充当IoC思想中的"外部"
- IoC容器负责对象的创建、初始化等一系列工作,被创建的对象在IoC容器中被称为Bean。
1.2:DI
- DI(Dependency Injection)依赖注入
- 在容器中建立Bean与Bean之间的依赖关系的整个过程,称为依赖注入。
1.3:最终效果
- 充分解耦
- 使用IoC容器管理Bean
- 在IoC容器内将有依赖关系的Bean进行关系绑定(ID)
- 使用对象时不仅可以直接从IoC容器中获取,并且获取到的Bean已经绑定了所有的依赖关系。
2.快速入门
2.1:导入相关坐标
<!--Spring框架-->
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.21.RELEASE</version> </dependency>
2.2:定义Spring管理的类(接口)
public interface BookDao { public void save(); } public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } } public interface BookService { public void save(); } public class BookServiceImpl implements BookService { //5.删除业务层中使用new的方式创建的dao对象 private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } //6.提供对应的set方法(容器在执行) public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } }
2.3:创建Spring配置文件,配置对应类作为Spring管理的Bean
applicationContext.xml
<!--1.导入spring坐标:spring-context--> <!--2.配置bean--> <!--id属性表示bean的名字 class属性表示给bean定义类型 name属性表示给bean取别名,可以当做id使用--> <bean id="bookDao" name="dao bookDaoImpl" class="com.my.dao.impl.BookDaoImpl"/> <bean id="bookService" class="com.my.service.impl.BookServiceImpl"> <!--7.配置server与Dao的关系--> <!--name属性表示配置哪一个具体的属性,属性名称 ref属性表示参照哪一个bean--> <property name="bookDao" ref="bookDao"/> </bean>
2.4:初始化IoC容器(Spring核心容器/Spring容器),通过容器获取Bean
public static void main(String[] args) { //3.加载配置文件得到上下文对象,也就是容器对象 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //4.获取资源 // BookDao bookDao = (BookDao) ctx.getBean("bookDao"); // bookDao.save(); BookService bookService = (BookService) ctx.getBean("bookService"); bookService.save(); }
3:bean的作用范围
- scope属性用于改变bean的作用范围,默认情况下scope属性为singleton,此时为单例模式,即创建出来的bean都是同一个,当scope属性为prototype时候,即为非单例模式。
- 为什么bean默认情况下为单例模式?可以用一个bean完成多个功能,提高复用性。
- 适合交给容器进行管理的bean:
- 表现层对象(servlet)
- 业务层对象(service)
- 数据层对象(Dao)
- 工具类对象
- 不适合交给容器进行管理的bean
- 封装实体的域对象
4:实例化bean的四种方式
4.1:构造方法(常用)
- 需要提供默认构造方法(无参构造方法)
- 如果无参构造方法不存在,将抛出异常BeanCreationException
4.2:静态工厂(了解)
- 静态工厂
public class OrderDaoFactory{ public static OrderDao getOrderDao(){ return new OrderDaoImpl(); } }
- 配置
<bean id="orderDao" class="com.my.factory.OrderDaoFactory" factory-method="getOrderDao" />
4.3:实例工厂(了解)
4.4:FactoryBean(实用)
BookDaoFactoryBean.java
public class BookDaoFactoryBean implements FactoryBean<BookDao> { @Override public BookDao getObject() throws Exception { return new BookDaoImpl(); } @Override public Class<?> getObjectType() { return BookDao.class; } }
配置信息
<!--FactoryBean方式--> <bean id="bookDaoFac" class="com.my.factory.BookDaoFactoryBean"/>
4.5:Bean生命周期
- 初始化容器
- 创建对象(内存分配)
- 执行构造方法
- 执行属性注入(set操作)
- 执行bean初始化方法
- 使用Bean
- 执行业务操作
- 关闭/销毁容器
- 执行bean销毁方法
- bean生命周期控制
- 配置
- init-method
- destroy-method
- 接口(了解)
- InitializingBean
- DisposableBean
- 关闭容器
- ConfigurableApplicationContext
- 手工关闭容器
ConfigurableApplicationContext接口close()操作 - 注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机
ConfigurableApplicationContext接口registerShutdownHook()操作
5:向类传递数据
5.1:依赖注入方式
- setter注入
- 简单类型
- 引用类型
- 构造器注入
- 简单类型
- 引用类型
5.2:setter注入
- 引用类型
- 在bean中定义引用数据类型属性并提供可访问的set方法
private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; }
- 配置中使用property标签ref属性注入引用类型对象
<bean id="userDao" class="com.my.dao.impl.UserDaoImpl"/> <bean id="bookService" class="com.my.service.impl.BookServiceImpl"> <!--name属性表示配置哪一个具体的属性 ref属性表示参照哪一个bean--> <!--注入引用数据类型--> <property name="userDao" ref="userDao"/> </bean>
- 简单类型
- 在bean中定义引用类型属性并提供可访问的set方法
BookDaoImpl
public class BookDaoImpl implements BookDao { private int conNum; private String dbName; public void setConNum(int conNum) { this.conNum = conNum; } public void setDbName(String dbName) { this.dbName = dbName; } public void save() { System.out.println("book dao save ..."+conNum+" "+dbName); } }
- 配置中使用property标签value属性注入简单类型数据
<bean id="bookDao" class="com.my.dao.impl.BookDaoImpl"> <!--注入简单数据类型--> <property name="conNum" value="10"/> <property name="dbName" value="mysql"/> </bean>
5.3:构造器注入
- 引用类型
- 在bean中定义引用数据类型属性并提供可访问的构造方法
private BookDao bookDao; private UserDao userDao; public BookServiceImpl(BookDao bookDao, UserDao userDao) { this.bookDao = bookDao; this.userDao = userDao; }
- 配置中使用constructor-arg标签ref属性注入引用类型对象
<bean id="bookService" class="com.my.service.impl.BookServiceImpl"> <!--注入引用数据类型--> <constructor-arg name="bookDao" ref="bookDao"/> <constructor-arg name="userDao" ref="userDao"/> </bean>
- 简单类型
- 在bean中定义简单数据类型属性并提供可访问的构造方法
private int conNum; private String dbName; public BookDaoImpl(int conNum, String dbName) { this.conNum = conNum; this.dbName = dbName; }
- 配置中使用constructor-arg标签value属性注入简单类型对象
<bean id="bookDao" class="com.my.dao.impl.BookDaoImpl"> <constructor-arg name="conNum" value="10"/> <constructor-arg name="dbName" value="mysql"/> </bean>
- 配置中使用constructor-arg标签type属性设置按照形参类型注入
- 配置中使用constructor-arg标签index属性设置按照形参位置注入
5.4:依赖注入方式选择
5.5:自动类型的装配
- 使用bean标签autowire属性设置自动装配的类型(byType、byName(耦合度高,不推荐))
<bean id="bookDao" class="com.my.dao.impl.BookDaoImpl"/> <bean id="bookService" class="com.my.service.impl.BookServiceImpl" autowire="byType"/> <bean id="bookService" class="com.my.service.impl.BookServiceImpl" autowire="byName"/>
- 依赖自动装配特征
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y8PTeIUR-1653122017072)(SSM笔记.assets/image-20220515195109170.png)]
5.6:注入集合
public class BookDaoImpl implements BookDao { private int[] array; private List<String> list; private Set<String> set; private Map<String, String> map; private Properties properties; public void setArray(int[] array) { this.array = array; } public void setList(List<String> list) { this.list = list; } public void setSet(Set<String> set) { this.set = set; } public void setMap(Map<String, String> map) { this.map = map; } public void setProperties(Properties properties) { this.properties = properties; } public void save() { System.out.println(Arrays.toString(array)); System.out.println(list); System.out.println(set); System.out.println(map); System.out.println(properties); } }
``
<bean id="bookDao" class="com.my.dao.impl.BookDaoImpl"> <property name="array"> <array> <value>100</value> <value>200</value> <value>300</value> </array> </property> <property name="list"> <list> <value>country</value> <value>aaa</value> <value>bbb</value> </list> </property> <property name="set"> <set> <value>100</value> <value>aaa</value> <!--自动去重--> <value>aaa</value> <value>bbb</value> </set> </property> <property name="map"> <map> <entry key="country" value="china"/> <entry key="province" value="guangdong"/> <entry key="city" value="shenzhen"/> </map> </property> <property name="properties"> <props> <prop key="country">china</prop> <prop key="province">guangdong</prop> <prop key="city">guangzhou</prop> </props> </property> </bean>
6:数据源对象管理
6.1:基本配置
6.1.1导入Druid坐标
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency>
6.1.2配置数据源对象作为spring管理的bean
<bean class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/spring_db"/> <property name="username" value="root"/> <property name="password" value="440983"/> </bean>
6.2:加载properties文件
6.2.1.开启context名称空间
<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" <!--更改1--> xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context <!--更改2--> http://www.springframework.org/schema/context/spring-context.xsd"> <!--更改3-->
6.2.2.使用context命名空间,加载指定properties文件
<context:property-placeholder location="jdbc.properties"/>
6.2.3.使用${}读取加载的属性值
<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>
6.2.4.其他设置
6.2.5.加载配置文件
6.2.6.容器相关
6.2.7获取bean
6.3:注解开发bean
6.3.1使用@Componet定义bean
@Component("bookDao") public class BookDaoImpl implements BookDao { } @Component public class BookServiceImpl implements BookService { }
6.3.2核心配置文件中通过组件扫描加载bean
<!--配置核心文件扫描bean--> <context:component-scan base-package="com.my"/>
6.3.3 Spring提供@Component注解的三个衍生注解(与Component功能一样)
@Controller //用于表现层bean定义 @Service //用于业务层定义bean @Repository //用于数据层bean定义
7:纯注解开发:
7.1:基本配置
7.1.1:Spring3.0开启了纯注解开发模式,使用Java类替代配置文件,开启了Spring快速开发赛道
7.1.2:Java类代替Spring核心配置文件
com.my.config.SpringConfig.java
@Configuration //用于设定当前类为配置类 // @ComponentScan("com.my") @ComponentScan({"com.my.service","com.my.dao"}) //用于设定扫描路径,只能添加一次,多个数据用数组格式 public class SpringConfig { }
7.1.3:读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); //获取资源 按bean名称获取 BookDao bookDao = (BookDao) ctx.getBean("bookDao"); System.out.println(bookDao); // 按bean类型获取 BookService bookService = ctx.getBean(BookService.class); System.out.println(bookService); }
7.1.4:小结
7.2:作用范围及生命周期
7.3:依赖注入
7.3.1:注入引用类型
7.3.2:简单数据类型
@Value("${}") private String name;
7.3.3:小结
7.4:第三方Bean的注入
7.5:XML配置VS注解配置
8:Spring整合Mybatis
8.1: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 ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); return ds; }
8.2:mybatisConfig
public class mybatisConfig { //替换xml基本配置信息 @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setTypeAliasesPackage("com.my.pojo"); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } //替换 /*<mappers> <package name="com.my.dao"/> </mappers>*/ @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer msc = new MapperScannerConfigurer(); msc.setBasePackage("com.my.dao"); return msc; } }
8.3:SpringConfig
@Configuration @ComponentScan("com.my") @PropertySource("classpath:jdbc.properties") @Import({jdbcConfig.class,mybatisConfig.class}) public class SpringConfig { }
9:Spring整合Junit
9.1:导入坐标
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.20.RELEASE</version> </dependency>
9.2:创建junit测试类
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void testFindById(){ Account ac = accountService.findById(1); System.out.println(ac); } @Test public void testFindAll(){ for (Account account : accountService.findAll()) { System.out.println(account); } } }
10:AOP(面向切面编程)
10.1:基本概念
- AOP(Aspect Oriented Programming)面向切面编程,一种编程规范,指导开发者如何组织程序结构
- OOP:面向对象编程
- 作用:在不惊动原始设计的基础上为其进行功能增强
- Spring概念:无侵入式/无入侵式
- 连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
- 在SpringAOP中,理解为方法的执行
- 切入点(Pointcut):匹配连接点的式子
- 在SpringAOP中,一个切入点可以描述一个具体方法,也可以匹配多个方法
- 一个具体方法:com.my.dao包下的BookDao接口中的无形参无返回值的save()方法
- 匹配多个方法:所有的save方法,所有以get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法。
- 通知(Advice):在切入点处执行的操作,也就是共性功能
- 在SpringAOP中,功能最终以方法的形式呈现
- 通知类:定义通知的类
- 切面(Aspect):描述通知与切入点的对应关系
10.2:入门案例
10.2.1:导入aop相关坐标
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
10.2.2:实现dao接口与实现类
@Repository public class BookDaoImpl implements BookDao { public void save() { System.out.println(System.currentTimeMillis()); System.out.println("book dao save ..."); } @Override public void update() { System.out.println("book dao update..."); } }
10.2.3:定义通知类,制作通知
10.2.4:定义切入点
10.2.5:绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置
10.2.6:定义通知类受Spring容器管理,并定义当前类为切面类
@Component //6.1 定义通知类受Spring容器管理 @Aspect //6.2 定义当前类为切面类 public class MyAdvice { //4.定义切入点 @Pointcut("execution(void com.my.dao.BookDao.update())") public void pt(){} //3.定义通知类 @Before("pt()") //5.绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置 public void before(){ System.out.println(System.currentTimeMillis()); } }
10.2.7:开启Spring对Aop注解驱动支持
@Configuration @ComponentScan("com.my") @EnableAspectJAutoProxy //7.开启Spring对Aop注解驱动支持 public class SpringConfig { }
10.2.8:其他代码
public class App { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); BookDao bookDao = ctx.getBean(BookDao.class); bookDao.update(); } }
10.3:AOP执行流程
10.4:AOP切入点表达式
10.5:AOP通知
10.5.1:前置通知 @Before
10.5.2:后置通知 @After
10.5.3:环绕通知(重点)
●参数ProceedingJoinPoint参数必须放在第一位。
10.5.4:返回后通知(了解) @AfterReturning 原始切入点方法正常执行完毕后运行
10.5.5:抛出异常通知(了解) @AfterThrowing 抛出异常后执行,不抛异常不执行
10.6:执行效率案例
@Component @Aspect public class ProjectAdvice { @Pointcut("execution(* com.my.service.*Service.*(..))") private void servicePt(){} @Around("ProjectAdvice.servicePt()") public void runSpeed(ProceedingJoinPoint pjp) throws Throwable { //获取执行签名信息 Signature signature = pjp.getSignature(); //通过签名获取执行类型(接口名) String className = signature.getDeclaringTypeName(); //通过签名获取执行操作名称(方法名) String methodName = signature.getName(); long start = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { Object ret = pjp.proceed(); } long end = System.currentTimeMillis(); System.out.println("执行"+className+"."+methodName+"万次耗时"+(end-start)+"ms"); } }
10.7:AOP通知获取数据
10.7.1:获取参数
10.7.2:获取返回值
11:Spring事务
11.1:开启事务步骤
11.2:事务角色
11.3:事务配置&传播行为
11.3.1:事务配置
11.3.2:事务传播
11.3.3:案例
在jdbcConfig中添加事务管理器
//设置事务管理器 @Bean public PlatformTransactionManager transactionManager(DataSource dataSource){ DataSourceTransactionManager ptm = new DataSourceTransactionManager(); ptm.setDataSource(dataSource); return ptm; }
在SpringConfig中开启事务支持
@Configuration @ComponentScan("com.my") @PropertySource("classpath:jdbc.properties") @Import({jdbcConfig.class,mybatisConfig.class}) @EnableAspectJAutoProxy //开启Spring对Aop注解驱动的支持 @EnableTransactionManagement //开启事务支持 public class SpringConfig { }
Dao层操作
//数据层操作 public interface AccountDao { @Update("update tbl_account set money = money + #{money} where name = #{name}") void inMoney(@Param("name")String name,@Param("money")Double money); @Update("update tbl_account set money = money - #{money} where name = #{name}") void outMoney(@Param("name") String name,@Param("money") Double money); }
public interface LogDao { @Insert("insert into tbl_log(info,createDate) values(#{info},now())") void log(String info); }
Service层
public interface AccountService { @Transactional public void transfer(String out,String in,Double money); }
public interface LogService { //propagation设置事务属性:传播行为设置为当前操作需要新事务 @Transactional(propagation = Propagation.REQUIRES_NEW) void log(String out,String in,Double money); }
@Service public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Autowired private LogService logService; @Override public void transfer(String out, String in, Double money) { try{ accountDao.outMoney(out,money); int a = 1/0; accountDao.inMoney(in,money); }finally { logService.log(out,in,money); } } }
@Service public class LogServiceImpl implements LogService { @Autowired private LogDao logDao; @Override public void log(String out, String in, Double money) { logDao.log(out+"向"+in+"转账"+money+"钱"); } }
Test
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTest { @Autowired AccountService accountService; @Test public void transferTest(){ accountService.transfer("Tom","Jerry",100D); } }
三:SpringMVC
1:概述
- SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
- 优点
- 使用简单,开发便捷(相比于Servlet)
- 灵活性强
2:快速入门
2.1:导入坐标
<dependencies> <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> </dependencies>
2.2:初始化SpringMVC环境
@Configuration @ComponentScan("com.my") //设定SpringMVC加载对应bean public class SpringMVCConfig { }
2.3:创建SpringMVC控制器类(等同于Servlet功能)
@Controller public class UserController { @RequestMapping("/save") //设置当前控制器方法请求访问路径 @ResponseBody //设置当前控制器方法响应内容为当前返回值,无需解析 public String save(){ System.out.println("user save..."); return "{'info':'springmvc'}"; } }
2.4:初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC请求拦截的路径
//AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类 public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { //初始化容器并注册返回容器到Tomcat中,以便可以访问bean @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMVCConfig.class); return ctx; } //设定SpringMVC对应的请求映射路径,设置为/表示拦截所有请求,任意请求都将转入到SpringMVC进行处理 @Override protected String[] getServletMappings() { return new String[]{"/"}; } //如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式同createServletApplicationContext() @Override protected WebApplicationContext createRootApplicationContext() { return null; } }
3:Controller加载控制与业务Bean加载控制
4:URL传递参数
- 正常情况下名称对名称
- 普通参数 save(String name,int age)
- 封装对象 save(User user)
- 数组 save(String[] array)
参数设置 array 10、array 20…
- 名称对不上用@RequestParam
- 集合 save(@RequestParam List list)
参数设置 list 10、list 20…
5:JSON数据传递参数
5.1:添加JSON数据转换坐标
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.6.1</version> </dependency>
5.2:在body中设置JSON数据并发送
5.3:开启自动转换JSON数据的支持
@Configuration @ComponentScan("com.my") @EnableWebMvc //开启自动转换JSON数据的支持,开启SpringMVC多项辅助功能 public class SpringMVCConfig { }
5.4:设置接收JSON数据
@Controller public class UserController { @ResponseBody //将请求中请求体所包含的数据传递给请求参数,此注解一个方法只能使用一次 @RequestMapping("/listParamForJson") public String listParamForJson(@RequestBody List<String> likes){ System.out.println("list common(json)参数传递==>" + likes); return "{'info':'springmvc'}"; }
6:日期类型参数传递
- dataParam(Date date, //2020/2/12
@DateTimeFormat(pattern=“yyyy-MM-dd”) Date date1, //2020-5-17
@DateTimeFormat(pattern=“yyyy/MM/dd HH:mm:ss”) Date date2 //2020/02/20 20:20:20
)
7:响应
//响应页面/跳转页面 @RequestMapping("/toJumpPage") public String toJumpPage(){ System.out.println("跳转页面"); return "page.jsp"; } //响应纯文本数据 @RequestMapping("/toText") @ResponseBody public String toText(){ System.out.println("返回纯文本数据"); return "response text"; } //响应POJO对象 @RequestMapping("/toJsonPOJO") @ResponseBody public User toJsonPOJO(){ System.out.println("返回json对象数据"); User user = new User(); user.setName("Tom"); user.setAge(20); return user; } //响应POJO集合对象 @RequestMapping("/toJsonList") @ResponseBody public List<User> toJsonList(){ System.out.println("返回JSON集合数据"); User user1 = new User(); user1.setName("Tom"); user1.setAge(15); User user2 = new User(); user2.setName("Jerry"); user2.setAge(18); User user3 = new User(); user3.setName("Jack"); user3.setAge(20); List<User> users = new ArrayList<>(); users.add(user1); users.add(user2); users.add(user3); return users; }
8:REST
8.1:概述
8.2:REST风格案例
// REST/POST @RequestMapping(value = "/users", method = RequestMethod.POST) @ResponseBody public String save() { System.out.println("user save..."); return "{'module':'user save'}"; } @RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE) @ResponseBody public String delete(@PathVariable Integer id) { System.out.println("user delete..."+id); return "{'module':'user delete'}"; } @RequestMapping(value = "/users",method = RequestMethod.PUT) @ResponseBody public String update(@RequestBody User user){ System.out.println("user update..."+user); return "{'module':'user update'}"; } //查询 @RequestMapping(value = "/users/{id}",method = RequestMethod.GET) @ResponseBody public String getById(@PathVariable Integer id){ System.out.println("user getById..."+id); return "{'module':'user getById'}"; }
8.3:简化
/** * 简化REST */ @RestController("/book") public class BookController { // REST/POST @PostMapping public String save() { System.out.println("book save..."); return "{'module':'book save'}"; } @DeleteMapping("/{id}") public String delete(@PathVariable Integer id) { System.out.println("book delete..."+id); return "{'module':'book delete'}"; } @PutMapping public String update(@RequestBody Book book){ System.out.println("book update..."+book); return "{'module':'book update'}"; } //查询 @GetMapping("/{id}") public String getById(@PathVariable Integer id){ System.out.println("book getById..."+id); return "{'module':'book getById'}"; } @GetMapping public String getAll(){ System.out.println("book getAll..."); return "{'module':'book getAll'}"; } }
8.4:设置对静态资源的放行
public class SpringMvcSupport extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { //当访问/pages/???时候,走pages目录下的内容,防止被SpringMVC拦截 registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } }
9:表现层返回结果规范化处理
9.1:在controller定义Result类
public class Result { private Object data; //返回数据 private Integer code; //返回结果代码 private String msg; //消息
9.2:定义返回代码
com.my.controller.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 SAVE_ERR = 20010; public static final Integer DELETE_ERR = 20020; public static final Integer UPDATE_ERR = 20030; public static final Integer GET_ERR = 20040; }
9.3:修改表现层返回类型
@GetMapping public Result getAll() { List<Book> bookList = bookService.getAll(); Integer code = (bookList == null ? Code.GET_ERR:Code.GET_OK); String msg = (bookList == null? "数据查询失败!":""); return new Result(code,bookList,msg); }
10:异常处理
10.1:集中处理
com.my.controller.ProjectExceptionAdvice.java
@RestControllerAdvice //将类声明为异常处理类 public class ProjectExceptionAdvice { @ExceptionHandler(Exception.class) //定义处理哪一种异常 public Result doException(Exception ex){ return new Result(11111,null,"捕捉到异常!"); } }
10.2:异常分类
- 业务异常(BusinessException)
- 规范的用户行为产生的异常
- 不规范的用户行为操作产生的异常
- 系统异常(SystemException)
- 项目运行过程中可预计且无法避免的异常
- 其他异常(Exception)
- 编程人员未预期到的异常
10.3:异常分类处理
10.3.1:定义Result返回体
com.my.controller.Result.java
public class Result { private Object data; //返回数据 private Integer code; //返回结果代码 private String msg; //消息
10.3.2:定义异常编码
package com.my.controller; 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 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 SYSTEM_ERR = 50001; public static final Integer SYSTEM_TIMEOUT_ERR = 50002; public static final Integer SYSTEM_UNKNOWN_ERR = 59999; public static final Integer BUSINESS_ERR = 60002; }
10.3.3:定义异常类
package com.my.exception; 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, String message) { super(message); this.code = code; } public BusinessException(Integer code, String message, Throwable cause) { super(message, cause); this.code = code; } }
package com.my.exception; 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, String message) { super(message); this.code = code; } public SystemException(Integer code, String message, Throwable cause) { super(message, cause); this.code = code; } }
10.3.4:定义异常处理类
package com.my.controller; import com.my.exception.BusinessException; import com.my.exception.SystemException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; /** * 处理异常 */ @RestControllerAdvice //将类声明为异常处理类 public class ProjectExceptionAdvice { @ExceptionHandler(SystemException.class) //定义处理哪一种异常 public Result doSystemException(SystemException ex){ //记录日志 //发送消息给运维 //发送邮件给开发人员,ex对象发送给开发人员 return new Result(ex.getCode(),null,ex.getMessage()); } @ExceptionHandler(BusinessException.class) //定义处理哪一种异常 public Result doBusinessException(BusinessException ex){ return new Result(ex.getCode(),null,ex.getMessage()); } @ExceptionHandler(Exception.class) //定义处理哪一种异常 public Result doException(Exception ex){ //记录日志 //发送消息给运维 //发送邮件给开发人员,ex对象发送给开发人员 return new Result(Code.SYSTEM_UNKNOWN_ERR,null,"系统繁忙,请稍后再试!"); } }
10.3.5:捕捉异常
com.my.controller.BookController.java
@GetMapping(value = ("/getById")) public Result getById(@RequestParam("id") Integer id) { if(id == 0){ throw new BusinessException(Code.BUSINESS_ERR,"请规范您的操作!"); } //将可能出现的异常进行包装,转换成自定义异常 try{ int i = 1/0; }catch (Exception e){ throw new SystemException(Code.SYSTEM_TIMEOUT_ERR,"服务器访问超时,请稍后再试!",e); } Book book = bookService.getById(id); Integer code = (book == null? Code.GET_ERR:Code.GET_OK); String msg = (book == null? "数据查询失败!":""); return new Result(code,book,msg); }
11:拦截器
11.1:声明拦截器的bean,并实现HandlerInterceptor接口(注意:扫描加载bean)
package com.my.controller.interceptor; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class ProjectInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle..."); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle..."); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion..."); } }
11.2:定义配置类,继承WebMvcConfigurationSupport,实现addInterceptor方法(注意:扫描加载配置)
package com.my.config;
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { //设置过滤静态访问资源 @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } //自动装配拦截器 @Autowired private ProjectInterceptor projectInterceptor; //添加拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*"); //设置具体拦截路径,可设置多个 } }
11.3:添加扫描路径
@Configuration @ComponentScan({"com.my.controller","com.my.config"}) @EnableWebMvc public class SpringMvcConfig { }
11.4:简化(注意:侵入式较强)
com.my.config.SpringMvcConfig.java
@Configuration @ComponentScan({"com.my.controller"}) @EnableWebMvc public class SpringMvcConfig implements WebMvcConfigurer { //自动装配拦截器 @Autowired private ProjectInterceptor projectInterceptor; //添加拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*"); //设置具体拦截路径 } }
11.5:执行流程
11.6:拦截器参数
11.7:拦截器链
四:Maven高级
1:分模块开发
1.1:创建Maven模块
1.2:书写模块代码
1.3:通过Maven指令安装模块到本地仓库(install指令)
2:可选依赖于排除依赖
2.1:可选依赖
true
指对外隐藏当前所依赖的资源----不透明
2.2:排除依赖
指主动断开依赖的资源,被排除的资源无需指定版本-------不需要
3:多环境配置与应用
3.1:多环境
3.2:跳过测试
4:yml文件读取
五:SpringBoot&MybatisPlus
1:快速入门
1.1:导入坐标
<?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.7.0</version> </parent> <groupId>com.my</groupId> <artifactId>MybatisPlus-demo1</artifactId> <version>0.0.1-SNAPSHOT</version> <name>MybatisPlus-demo1</name> <description>MybatisPlus-demo1</description> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
1.2:创建配置文件(注意空格)
application.yml
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: 440983
1.3:创建Book及BookDao
@Data //添加Lombok注解 public class Book { private int id; private String type; private String name; private String description; } @Mapper public interface BookDao extends BaseMapper<Book> { }
- 说明:@Data注解为当前实体类在编译期设置对应的get/set方法,toString方法、hashCode方法、equals方法等
- 要注意数据库中表的名称要为book
1.4:测试
@SpringBootTest class ApplicationTests { @Autowired private BookDao bookDao; @Test void getByIdTest() { Book book = bookDao.selectById(1); System.out.println(book); } @Test void updateTest(){ Book book = new Book(); book.setId(1); book.setDescription("操作系统从入门到入坟"); int i = bookDao.updateById(book); System.out.println(i); } <!-- bookDao.deleteBatchIds(List); <!--多数据按id删除--> bookDao.selectBatchIds(List); <!--多数据按id查询--> --> }
2:分页查询
2.1:设置分页拦截器作为Spring管理的bean
@Configuration public class MpConfig { @Bean public MybatisPlusInterceptor pageInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } }
2.2:执行分页查询
public void pageTest(){ IPage<Book> page = new Page<Book>(1, 3); bookDao.selectPage(page,null); System.out.println("当前页码:"+page.getCurrent()); System.out.println("每页数据总量:"+page.getSize()); System.out.println("总页数:"+page.getPages()); System.out.println("数据总量:"+page.getTotal()); System.out.println("当前页数据:"+page.getRecords()); }
3:条件查询
//条件查询 @Test public void getAllTest(){ //方式一:按条件查询 QueryWrapper<Book> qw = new QueryWrapper<Book>(); qw.lt("id",6); //小于 for (Book book : bookDao.selectList(qw)) { System.out.println(book); } //方式二:lambda格式 QueryWrapper<Book> qw = new QueryWrapper<Book>(); qw.lambda().lt(Book::getId,6); for (Book book : bookDao.selectList(qw)) { System.out.println(book); } //方式三(推荐) LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>(); lqw.lt(Book::getId,6); for (Book book : bookDao.selectList(lqw)) { System.out.println(book); } //多条件查询 LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>(); //4到10之间 // lqw.lt(Book::getId,10).gt(Book::getId,4); //小于4或者大于10 lqw.lt(Book::getId,4).or().gt(Book::getId,10); for (Book book : bookDao.selectList(lqw)) { System.out.println(book); } /*----------------------------------------NULL判定---------------------------------*/ LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>(); BookQuery bookQuery = new BookQuery(); bookQuery.setId(10); bookQuery.setId2(5); //lt 小于,le 小于等于 //gt 大于,ge 大于等于 //链式编程 /* lqw.lt(null != bookQuery.getId(),Book::getId,bookQuery.getId()) .gt(null != bookQuery.getId2(),Book::getId,bookQuery.getId2()); */ lqw.lt(null != bookQuery.getId(),Book::getId,bookQuery.getId()); lqw.gt(null != bookQuery.getId2(),Book::getId,bookQuery.getId2()); for (Book book : bookDao.selectList(lqw)) { System.out.println(book); } /*----------------------------查询投影---------------------------------*/ //查询结果包含模型中部分属性 LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>(); //查询id、书名、描述 lqw.select(Book::getId,Book::getName,Book::getDescription); List<Book> bookList = bookDao.selectList(lqw); bookList.forEach(System.out::println); //查询结果包含模型中未定义的属性 QueryWrapper<Book> qw = new QueryWrapper<>(); /* qw.select("id","name","description"); for (Book book : bookDao.selectList(qw)) { System.out.println(book); } */ qw.select("count(*) as nums,type"); qw.groupBy("type"); List<Map<String, Object>> maps = bookDao.selectMaps(qw); System.out.println(maps); /*-------------------------其他查询---------------------------------*/ lqw.between(Book::getId,10,20); //查询id在10到20之间,包含10和20 lqw.like("计算机"); //like %计算机% lqw.likeLeft("计算机"); //like %计算机 lqw.likeRigt("计算机"); //like 计算机% }
4:映射匹配兼容性
@Data //添加Lombok注解 @TableName("tbl_book") //设置当前类与数据库表之间的对应关系 public class Book { private Integer id; @TableField(value = "type",select = false) //value设置数据库表段名称,select:设置是否参与查询 private String Type; private String name; private String description; @TableField(exist = false) //设置属性在数据库表段中是否存在,默认为true,此属性无法与value合并使用 private Integer isON; }
- 全局配置
mybatis-plus: global-config: db-config: id-type: auto <!--设置id生成策略--> table-prefix: tbl_ <!--设置表之间对应关系,在所有pojo类名前加上"tbl_"-->
5:逻辑查询
5.1:数据库添加字段用于表示是否删除(注意尽量不要用关键字作为字段名)
5.2:用yml文件全局配置
mybatis-plus: global-config: banner: false db-config: logic-delete-field: is_delete #设置表示逻辑删除的字段 logic-delete-value: 1 #设置表示删除的值 logic-not-delete-value: 0 #设置表示未删除的值
5.3:pojo类添加相应属性
// @TableLogic(value = "0",delval = "1") private Integer is_delete; //表示逻辑删除
5.4:执行删除操作
@Test void deleteTest(){ bookDao.deleteById(3); }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ku5rodfz-1653122017080)(SSM笔记.assets/image-20220521115510938.png)]
- 注意这时执行的是UPDATE操作
Book::getId,Book::getName,Book::getDescription);
List bookList = bookDao.selectList(lqw);
bookList.forEach(System.out::println);
//查询结果包含模型中未定义的属性
QueryWrapper qw = new QueryWrapper<>();
/* qw.select(“id”,“name”,“description”);
for (Book book : bookDao.selectList(qw)) {
System.out.println(book);
} /
qw.select("count() as nums,type");
qw.groupBy(“type”);
List<Map<String, Object>> maps = bookDao.selectMaps(qw);
System.out.println(maps);
/-------------------------其他查询---------------------------------/
lqw.between(Book::getId,10,20); //查询id在10到20之间,包含10和20
lqw.like(“计算机”); //like %计算机%
lqw.likeLeft(“计算机”); //like %计算机
lqw.likeRigt(“计算机”); //like 计算机%
}
###### 4:映射匹配兼容性 ```java @Data //添加Lombok注解 @TableName("tbl_book") //设置当前类与数据库表之间的对应关系 public class Book { private Integer id; @TableField(value = "type",select = false) //value设置数据库表段名称,select:设置是否参与查询 private String Type; private String name; private String description; @TableField(exist = false) //设置属性在数据库表段中是否存在,默认为true,此属性无法与value合并使用 private Integer isON; }
- 全局配置
mybatis-plus: global-config: db-config: id-type: auto <!--设置id生成策略--> table-prefix: tbl_ <!--设置表之间对应关系,在所有pojo类名前加上"tbl_"-->
5:逻辑查询
5.1:数据库添加字段用于表示是否删除(注意尽量不要用关键字作为字段名)
5.2:用yml文件全局配置
mybatis-plus: global-config: banner: false db-config: logic-delete-field: is_delete #设置表示逻辑删除的字段 logic-delete-value: 1 #设置表示删除的值 logic-not-delete-value: 0 #设置表示未删除的值
5.3:pojo类添加相应属性
// @TableLogic(value = "0",delval = "1") private Integer is_delete; //表示逻辑删除
5.4:执行删除操作
@Test void deleteTest(){ bookDao.deleteById(3); }
- 注意这时执行的是UPDATE操作