1、Servlet项目(JSP)
1.创建项目
- 先创建一个普通项目,再添加web Application框架支持
- 添加一个Tomcat配置,设置本地的Tomcat Server, 在Deployment添加当前项目。
注意点:Application context设置为 “/”, “Update” action配置为 Update resources, 端口最好改为8081 - 添加项目依赖模块 Modules, 在WEB-INF创建 classes,lib文件夹, 并且在paths, Dependencies引入这两个文件夹。
- 完成基本项目的配置
2.运行项目
- 点击新创建的Tomcat配置(因此并不需要手动打开tomcat)
- 浏览器访问: http://localhost:8081 即可正常访问。
2、SpringFramework框架
Spring发展到今天已经形成了一种开发的生态圈,Spring提供了若干个项目,每个项目用于完成特定的功能
Spring Framework是Spring生态圈中最基础的项目,是其他项目的根基:
注意点:maven的配置是:使用内置maven + 本地maven配置(文件 + 仓库),否则很容易出现问题
1.创建Spring项目
- 通过Maven选择Create from archetype创建webapp新项目, Artifact Coordinates添加GroupId,然后选择上方maven的配置。(创建完成后,等待org.apache包的下载,,出现src,pom.xml 文件即为正常安装)
- 在pol.xml添加相应的Spring-framework坐标依赖(如下面xml配置文件示例),点击Maven选项下的刷新。
- 创建java原码文件夹,resources资源包,并且创建java下的基本包(com.xx.xxx, 手动),然后在resources创建Spring config的xml配置文件(一般通过内置maven创建的项目,同级目录一定会有webapp文件)
2.运行Spring项目
- 在pol.xml添加基于SpringMVC的tomcat插件。
- 新建项目配置,添加Maven配置,输入tomcat7:run 的运行命令,点击运行按钮(出现Starting ProtocolHandler [“http-bio-8082”] 即为正常访问)
- 浏览器访问:http://localhost:8081
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EDmdmBeg-1662813226512)(C:/Users/Icy-yun/AppData/Roaming/Typora/typora-user-images/image-20220829203231449.png)]
1.xml配置文件
(1.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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>SSMStudy</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>SSMStudy Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<!-- 项目需要添加的坐标属性 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<spring.version>5.0.5.RELEASE</spring.version>
</properties>
<!-- 项目需要添加的坐标依赖 -->
<dependencies>
<!--spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- 项目需要添加的插件 -->
<build>
<plugins>
<!-- Tomcat插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.IoC控制反转
源由:
因此代码书写的耦合度偏高,所以在程序中不要主动使用new产生对象,转换为由外部提供对象
Ioc(Inversion of Control) 控制反转:
(1.对象的创建控制权 由程序转移到外部,这种思想称为控制反转。
(2.Spring技术对Ioc思想进行了实现,就是提供了一个容器,称为Ioc容器,用来充当Ioc思想中的外部。
(3.IoC容器负责对象的创建,初始化等一系列工作,被创建或者被管理的对象在IoC容器中称为Bean
1.Ioc 容器的使用
Ioc入门案例
1.管理什么? ( Service与Dao )
2.如何将被管理的对象告知IoC容器?(配置)
3.被管理的对象交给Ioc容器,如何获取到IoC容器?(接口)
4.IoC容器得到后,如何从容器中获取bean ?(接口方法)
5.使用Spring导入哪些坐标? ( pom.xml )
Ioc使用流程:
1.在java文件里面定义Dao层和Service层, 分别定义接口,然后实现对应的接口
2.然后在resource中创建applicationContext.xml,使用Ioc容器,创建对象,然后生成Bean
3.在bean中向Service层传递Dao层传递对象 。(service是Dao层实现的目标,Dao层是业务层,Service是数据层)
4.创建App.java文件,获取Ioc容器 ClassPathXmlApplicationContext, 然后通过ctx.getBean方法获取Dao层,Service层对象 ,执行对应的方法。
容器相关的接口与类:
1.BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
(bean中也可以设置 lazy-init="true" 实现延迟加载)
2.ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
3.ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
4.ApplicationContext接口常用初始化类
classPathXmlApplicationContext、FileSystemXmlApplicationContext
-- java/com.itheima/dao层
(1. BookDao.java
package com.itheima.dao;
public interface BookDao {
public void save();
}
(2. impl/BookDaoImpl.java
package com.itheima.dao.impl;
import com.itheima.dao.BookDao;
public class BookDaoImpl implements BookDao{
public void save(){
System.out.println("book dao save...");
}
}
-- java/com.itheima/Service层
(1. BookDao.java
package com.itheima.service;
public interface BookService {
public void save();
}
(2. impl/BookServiceImpl.java
package com.itheima.service.impl;
import com.itheima.dao.BookDao;
import com.itheima.dao.impl.BookDaoImpl;
import com.itheima.service.BookService;
public class BookServiceImpl implements BookService{
// 5.删除业务层中使用new的方式创建的dao对象
// private BookDao bookDao = new BookDaoImpl();
private BookDao bookDao ;
public void save(){
System.out.println("book service save ...");
bookDao.save();
}
// 6.提供对应的set方法
public void setBookDao(BookDao bookDao){
this.bookDao = bookDao;
}
}
-- java/com.iteima/App2.java
package com.itheima;
import com.itheima.dao.BookDao;
import com.itheima.service.BookService;
import com.itheima.service.impl.BookServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App2 {
public static void main(String[] args) {
// 3.获取Ioc容器
// 所有容器类的顶层接口是BeanFactory
// (1.加载类路径下的配置文件(常用)
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// (2.从文件系统下加载配置文件(绝对路径)
ApplicationContext ctx = new FileSystemXmlApplicationContext("C:\\xxxx\xxxx\\applicationContext.xml");
// (3.加载多个文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");
// 4.获取bean
// (1.强制类型转换的格式
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
// (2.添加类名的格式
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
// (3.按照类型获取(简洁)
BookDao bookDao = ctx.getBean(BookDao.class);
bookDao.save();
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
--resources/applicationContext.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 2.配置bean-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!-- 7.配置server 与dao的关系-->
<property name="bookDao" ref="bookDao" />
</bean>
</beans>
2.Bean的实例化方式
bean的配置:
<bean id="bookDao" name="bookDao1" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>
name是别名,可以定义多个,可以使用 “,” “;” “ ”,但建议使用id去定义
scope 控制bean是否单例,默认是单例, 其中单例可以有效提高对象的复用率,提升spring的性能。否则非单例模式会对Ioc容器造成非常大的压力。
其中适合交给容器进行管理的bean: 表现层,业务层,数据层,工具对象; 不适合交给容器进行管理的bean: 封装实例的域对象
1.实例化bean的三种方式——构造方法
public class BookDaoImglimplements BookDao{
public BookDaoImpl(){
System.out.println("book constructor is running ,...")
}
}
注意: 无参构造方法如果不存在,将抛出异常BeanCreationException
2.实例化bean的三种方式——静态工厂
--java/com.itheima/factory层 (Dao层的同上)
1.UserDaoFactory静态工厂
package com.itheima.factory;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
--resources/applicationContext.xml文件
<!-- 使用静态工厂实例化bean-->
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
-- java/com.iteima/App2.java
// 通过静态工厂创建对象
OrderDao orderDao = OrderDaoFactory.getOrderDao();
orderDao.save();
3.实例化bean的三种方式——实例工厂
--java/com.itheima/factory层 (Dao层的同上)
1.OrderDaoFactory实例工厂
package com.itheima.factory;
import com.itheima.dao.OrderDao;
import com.itheima.dao.impl.OrderDaoImpl;
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
--resources/applicationContext.xml文件
<!-- 使用实例工厂实例化bean-->
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory" />
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
-- java/com.iteima/App2.java
// 通过实例工厂创建对象
UserDaoFactory userDaoFactory = new UserDaoFactory();
UserDao userDao = userDaoFactory.getUserDao();
userDao.save();
4.实例化bean的三种方式2——FactoryBean (方法三的变型)
--java/com.itheima/factory层 (Dao层的同上)
1.OrderDaoFactoryBean 实例工厂
package com.itheima.factory;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.FactoryBean;
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
public UserDao getObject() throws Exception{
return new UserDaoImpl();
}
public Class<?> getObjectType(){
return UserDao.class;
}
// 决定创建的对象是否为单例
public boolean isSingleton(){
return false;
}
}
--resources/applicationContext.xml文件
<!-- 使用FactoryBean实例化bean-->
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean" />
3.Bean的生命周期
(1) 生命周期:从创建到消亡的完整过程
(2) bean生命周期: bean从创建到销毁的整体过程
(3) bean生命周期控制:在bean创建后到销毁前做一些事情
·
初始化容器
1.创建对象(内存分配)
2.执行构造方法
3.执行属性注入( set操作)
4.执行bean初始化方法
·
使用bean
1.执行业务操作
关闭/销毁容器
1.执行bean销毁方法
--resources/applicationContext.xml文件
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
-- java/com.itheima/dao层 (配置方法)
(1. impl/BookDaoImpl.java
package com.itheima.dao.impl;
import com.itheima.dao.BookDao;
public class BookDaoImpl implements BookDao{
public void save(){
System.out.println("book dao save...");
}
// 表示bean的初始化操作 (created)
public void init(){
System.out.println("init...");
}
// 表示bean的销毁前的操作(beforeUnmount)
public void destory(){
System.out.println("destory...");
}
}
-- java/com.itheima/service层 (接口方法)
(1. impl/BookServiceImpl.java
package com.itheima.service.impl;
import com.itheima.dao.BookDao;
import com.itheima.dao.impl.BookDaoImpl;
import com.itheima.service.BookService;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
// 5.删除业务层中使用new的方式创建的dao对象
// private BookDao bookDao = new BookDaoImpl();
private BookDao bookDao ;
public void save(){
System.out.println("book service save ...");
bookDao.save();
}
// 6.提供对应的set方法
public void setBookDao(BookDao bookDao){
this.bookDao = bookDao;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("book service init...");
}
@Override
public void destroy() throws Exception {
System.out.println("book service destroy...");
}
}
-- java/com.iteima/App2.java
// 把 ApplicationContext替换成 ClassPathXmlApplicationContext 或其它上层对象都可以,但就是它不行
// ClassPathXmlApplicationContext 生成的对象,可以采用
// 1.obj.close() 方法强制关闭;
// 2.obj.registerShutdownHook() 方法等待IoC容器关闭再关闭JVM虚拟机
3.DI依赖注入
DI(Dependency Injection) 依赖注入:
1.在容器中建立bean和bean之间的依赖关系的过程,称为依赖注入
依赖注入方式选择
1.强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
2.可选依赖使用setter注入进行,灵活性强
3. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
4.如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
5.实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
6.自己开发的模块推荐使用setter注入
1.DI的注入方式
1.setter注入
1.方法一: setter注入,引用类型
在 bean中定义引用类型并提供可访问的set方法, 然后在配置中使用property标签ref属性注入引用类型对象
-- java/com.itheima/Service层
(1. impl/BookServiceImpl.java
package com.itheima.service.impl;
import com.itheima.dao.BookDao;
import com.itheima.dao.impl.BookDaoImpl;
import com.itheima.service.BookService;
public class BookServiceImpl implements BookService{
// 5.删除业务层中使用new的方式创建的dao对象
// private BookDao bookDao = new BookDaoImpl();
private BookDao bookDao ;
public void save(){
System.out.println("book service save ...");
bookDao.save();
}
// 6.提供对应的set方法
public void setBookDao(BookDao bookDao){
this.bookDao = bookDao;
}
}
--resources/applicationContext.xml文件
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!-- 7.配置server 与dao的关系-->
<property name="bookDao" ref="bookDao" />
<property name="userDao" ref="userDao" />
</bean>
2.方法二: setter注入,基本类型
在 bean中定义引用类型并提供可访问的set方法, 然后在配置中使用property标签rvalue属性注简单基本类型
-- java/com.itheima/Dao层
(1. impl/BookDaoImpl.java
package com.itheima.dao.impl;
import com.itheima.dao.BookDao;
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 = databaseName;
}
public void save(){
System.out.println("book dao save..."+databaseName+","+connectionNum);
}
}
--resources/applicationContext.xml文件
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" >
<property name="databaseName" value="mysql" />
<property name="connectionNum" value="10" />
</bean>
3.对简单类型的注入,进行解耦(引用类型是不会变的)
(1.多种互斥的形参
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" >
<property type="java.lang.String" value="mysql" />
<property type="int" value="10" />
</bean>
(2.有重复类型的形参
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" >
<property index="0" value="mysql" />
<property index="1" value="10" />
</bean>
2.构造器注入
基本操作,同setter注入。
把setXXX方法改成构造器
public BookServiceImpl(BookDao bookDao) {
this.bookDao = bookDao;
}
然后将applicationContext.xml文件的property修改成constructor-arg
<constructor-arg name="bookDao" ref="bookDao" />
3.集合注入
-- java/com.itheima/Dao层
(1. impl/BookDaoImpl.java
package com.itheima.dao.impl;
import com.itheima.dao.BookDao;
import java.util.*;
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("book dao save..." );
System.out.println("遍历数组:"+ Arrays.toString(array) );
System.out.println("遍历List:"+ list);
System.out.println("遍历Set"+ set);
System.out.println("遍历Map"+ map);
System.out.println("遍历Properties"+ properties);
}
}
--resources/applicationContext.xml文件
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" >
<property name="array">
<array>
<value>100</value>
<value>200</value>
</array>
</property>
<property name="list">
<array>
<value>it100</value>
<value>it200</value>
</array>
</property>
<property name="set">
<array>
<value>set100</value>
<value>set200</value>
</array>
</property>
<property name="map">
<map>
<entry key="country" value="china" />
<entry key="province" value="jiangxi" />
</map>
</property>
<property name="properties">
<props>
<prop key="country" >china</prop>
<prop key="province" >jiangxi</prop>
</props>
</property>
</bean>
2.依赖自动装配
IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配·自动装配方式: 按类型(常用)、按名称、按构造方法、不启用自动装配
依赖自动装配特征:
1.自动装配用于引用类型依赖注入,不能对简单类型进行操作
2.使用按类型装配时( byType )必须保障容器中相同类型的bean唯一,推荐使用
3.使用按名称装配时( byName )必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
4.自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
Dao层,Service层需要使用set方法注入
// (1.按类型装配
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType" />
// (2.按名称装配
注入的id需要和目标对象的相同(并非形参)
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byName" />
4.数据源管理
1.c3p0连接池对象
--pol.xml文件
<!-- c3p0驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.16</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
--resources/applicationContext.xml文件
<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/hello"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
-- java/com.iteima/App2.java
package com.itheima;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
public class App2 {
public static void main(String[] args) {
// 3.获取Ioc容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
}
}
2.druid连接池对象(x)
--pol.xml文件
<!--druid驱动 (无效)-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.6</version>
</dependency>
--resources/applicationContext.xml文件
<!-- (无效 )-->
<!-- <bean id="dataSource0" class="com.alibaba.druid.pool.DruidDataSource">-->
<!-- <property name="driverClassName" value="com.mysql.jdbc.Driver"/>-->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/hello"/>-->
<!-- <property name="username" value="root"/>-->
<!-- <property name="password" value="123456"/>-->
<!-- </bean>-->
3.properties资源文件
1.创建resources/jdbc.properties文件
--resources/jdbc.properties文件(新建立,普通文件修改后缀名)
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/study_java_hello?characterEncoding=utf-8
jdbc.user=root
jdbc.password=123456
2.开启context命名空间
--resources/applicationContext.xml文件的开头替换为以下内容
<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
">
3.使用context空间加载properties
(1.普通格式
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
(2.限制系统环境变量的加载
属性system-properties-mode去限制是否加载系统环境变量
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
(3.多文件加载
*.properties,逗号分隔,classpath*:*.poperties
<context:property-placeholder location="*.properties" system-properties-mode="NEVER"/>
4.使用属性占位符${}读取properties文件中的属性
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
5.@注解开发
1.注解的简单使用
-- java/com.itheima/Dao层
(1. impl/BookDaoImpl.java
//在定义类的上方,添加注解,定义bean
@Component("bookDao")
public class BookDaoImpl implements BookDao{
...
}
// Spring建议使用以下方式定义
@Service('xx') 服务层, 在实现类上填写注解
@Repository('xx') 仓库
@Controller('xx') 控制器
// 如果没有给注解上设置名称,就需要采用类型访问
BookDao bookDao = ctx.getBean(BookDao.class);
--resources/applicationContext.xml文件的开头替换为以下内容
<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
">
<!-- 核心配置文件中通过组件扫描加载bean-->
<context:component-scan base-package="com.itheima.dao.impl"/>
2.注解定义Bean
@ComponentScan 扫描类
@Configuration 声明配置文件
@Component (@Controller / @Service / @Repository)
@Scope 单例/非单例模式控制
@PostConstruct、@PreDestroy 业务层生命周期
-- java/com.itheima/Config配置(新建立)
(1.SpringConfig.java
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
// 扫描单个文件
//@ComponentScan("com.itheima")
// 扫描多个文件
@ComponentScan({"com.itheima"})
public class SpringConfig {
}
// bean的作用范围和生命周期
-- java/com.itheima/Dao层
(1. impl/BookDaoImpl.java
package com.itheima.dao.impl;
import com.itheima.dao.BookDao;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.*;
@Component("bookDao")
// 单例模式 singleton , 非单例模式 prototype
@Scope("prototype")
public class BookDaoImpl implements BookDao{
// 初始化
@PostConstruct
public void init(){
System.out.println("init...");
}
// 卸载时
@PreDestroy
public void destory(){
System.out.println("destory...");
}
public void save(){
System.out.println("book dao save..." );
}
}
-- java/com.iteima/App2.java
// 加载配置文件初始化容器(取消使用,替换为下面的方式)
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 加载配置类被初始化容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
3.注解设置依赖注入
@Autowired 自动装配
@Value 变量赋值
@ComponentScan 扫描类
@PropertySource 加载资源
-- java/com.itheima/Service层
(1. impl/BookServiceImpl.java
package com.itheima.service.impl;
import com.itheima.dao.BookDao;
import com.itheima.dao.impl.BookDaoImpl;
import com.itheima.service.BookService;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("bookService")
public class BookServiceImpl implements BookService{
// 自动装配
// 连setter方法都不需要,采用反射设计创建对象并暴力反射对应属性为私有属性初始化数据
// 以前Ioc提供入口去替代new,现在直接连入口都省略
@Autowired
// 指定名称装配bean(如果有多个同类型的才使用)
@Qualifier("bookDao")
// 5.删除业务层中使用new的方式创建的dao对象
// private BookDao bookDao = new BookDaoImpl();
private BookDao bookDao ;
// 简单类型的注入(请勿交叉注解)
// @Value("itheima123")
// 使用properties文件的变量
@Value("${jdbc.driver}")
private String name;
public void save(){
System.out.println("book service save ..."+name);
bookDao.save();
}
}
-- java/com.itheima/Config配置
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
//@ComponentScan("com.itheima")
@ComponentScan({"com.itheima"})
@ComponentScan(value="com.itheima",excludeFilters=@ComponentScan.Filter(
type = FilterType.ANNOTATION, classes = Controller.class
))
//@PropertySource("jdbc.properties") // 注意,不允许使用*
//@PropertySource({"jdbc.properties"})
@PropertySource({"classpath:jdbc.properties"}) // 建议写法
public class SpringConfig {
}
4.注解管理数据源
@Bean 定义Bean
@Import 导入mybatis相关配置类
@PropertySource 加载资源
-- java/com.itheima/Config配置
(1.JdbcConfig.java
package com.itheima.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
// 这种方法不推荐
//@Configuration + @ComponentScan({"com.itheima.config"})
public class JdbcConfig {
// 第三方bean依赖注入:简单类型注入
// 1.定义一个方法获取要管理的对象
// 2.添加@Bean, 表示当前方法的返回值是一个bean
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.user}")
private String user;
@Value("${jdbc.password}")
private String password;
// 第三方bean依赖注入:引用类型注入
// 只要定义了引用类型的形参,就会自动装配,查找对应的类型。
// 如: public DataSource dataSource(BooService bookService)
@Bean
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(user);
ds.setPassword(password);
return ds;
}
}
(2.SpringConfig.java
// 导入式方案: 将独立的配置类加入核心配置
//@Import(JdbcConfig.class)
@Import({JdbcConfig.class})
public class SpringConfig {
}
-- java/com.iteima/App2.java
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource);
6.AOP面向切面编程
AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构
(1.OOP(object oriented Programming)面向对象编程
(2.作用︰在不惊动原始设计的基础上为其进行功能增强
(3.Spring理念:无入侵式
(4.切面描述的是通知的共性功能和对应切入点的关系
AOP核心概念:
(1.连接点 ( JoinPoint ):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
在SpringAOP中,理解为方法的执行
(2.切入点( Pointcut ):匹配连接点的式子; 在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
一个具体方法: com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法
匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
(3.通知(Advice ):在切入点处执行的操作,也就是共性功能; 在SpringAOP中,功能最终以方法的形式呈现
(4.通知类∶定义通知的类
(5.切面(Aspect ):描述通知与切入点的对应关系
AOP工作流程(本质是代理模式)
(1. Spring容器启动
(2.读取所有切面配置中的切入点
(3.初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
(1.匹配失败,创建对象
(2·匹配成功,创建原始对象(目标对象)的代理对象
(4.获取bean执行方法
(1.获取bean,调用方法并执行,完成操作
(2.获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
(3.一般采用bookDao.getClass() 输出代理对象
(5.目标和代理对象
(1.目标对象(Target ):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
(2.代理( Proxy )︰目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AqEqHFwp-1662813226515)(C:/Users/Icy-yun/AppData/Roaming/Typora/typora-user-images/image-20220901101845515.png)]
1.AOP的使用(过滤作用)
-- java/com.itheima/aop
(1. MyAdvice.java
package com.itheima.aop;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect // 告诉spring这个类是用于aop
public class MyAdvice {
// 定义通知
// 描述方式一:执行指定包下BookDao接口中的update方法
@Pointcut("execution(void com.itheima.dao2.BookDao.update())")
// 描述方式二:执行指定包下BookDaoImpl接口中的update方法
@Pointcut("execution(void com.itheima.dao2.impl.BookDaoImpl.update())")
private void pt(){}
// 把切入点和通知绑定好
// 前置通知
@Before("pt()")
// 后置通知
@After("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
-- java/com.itheima/Dao层
(1. impl/BookDao.java
public void save(){
System.out.println(System.currentTimeMillis());
}
public void update(){
System.out.println("book dao update..." );
}
(2. BookDao.java
public void save();
public void update();
-- java/com.itheima/Config配置
(1.SpringConfig.java
// 告诉Spring有注解开发aop
@EnableAspectJAutoProxy
--pom.xml文件添加为以下内容
<!-- aop aspectj -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
2.AOP切入点表达式
1.可以使用通配符描述切入点,快速描述
1. *︰单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现(默认有1个参数)
匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
execution (public * com.itheima.*.UserService.find*(*))
匹配com.itheima包下的任意包中的UserService类或接口中所有tion结尾的带有一个参数的方法
execution (public * com.itheima.*.UserService.*tion(*))
2. ..︰多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写(可以为0个参数)
匹配com包下的任意包中的UserService类或接口中所有名称为findByld的方法
execution (public User com..UserService.findById (..) )
3. +:专用于匹配子类类型
execution(* *..*Service+.*( ..))
2.书写技巧
1.所有代码按照标准规范开发,否则以下技巧全部失效
2.描述切入点通常描述接口,而不描述实现类
3.访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述)
4.返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述
5.包名书写尽量不使用..匹配,效率过低,常用*做单个包描述匹配,或精准匹配
6.接口名/类名书写名称与模块相关的采用*匹配,例如UserService书写成*Service,绑定业务层接口名
7.方法名书写以动词进行精准匹配,名词采用*匹配,例如getByld书写成getBy*,selectAll书写成selectAll
8.参数规则较为复杂,根据业务方法灵活调整
9.通常不使用异常作为匹配规则
3.AOP通知类型
1.Before类型:前置通知类型
@Before("pt()")
public void before(){
System.out.println("before...");
}
2.After类型:后置通知类型
@After("pt()")
public void after(){
System.out.println("after...");
}
3.Around类型:环绕通知类型
@Around("pt()")
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around before...");
// ProceedingJoinPoint表示对原始操作的调用,可以获取返回值
pjp.proceed();
System.out.println("around after...");
}
4.AfterReturning类型:正常运行通知类型
// 正常结束运行的时候,才会运行
@AfterReturning("pt()")
public void afterReturning(){
System.out.println("afterReturning...");
}
5.AfterThrowing类型:异常运行通知类型
// 异常结束运行的时候,才会运行
@AfterThrowing("pt()")
public void afterThrowing(){
System.out.println("afterThrowing...");
}
6.注意:如果有返回值,需要设置为Object类型,适用于所有的通知类型
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around before...");
// ProceedingJoinPoint表示对原始操作的调用,可以获取返回值
Integer ret = (Integer)pjp.proceed();
// pjp的getSignature方法生成的对象可以获取当前执行的类和方法
Signature signature = pjp.getSignature();
System.out.println(signature.getDeclaringType());
System.out.println(signature.getDeclaringTypeName ());
System.out.println(signature.getName());
System.out.println("around after...");
return ret;
}
4.AOP通知获取数据
AOP通知获取数据
1.获取切入点方法的参数
JoinPoint :适用于前置、后置、返回后、抛出异常后通知
ProceedJointPoint :适用于环绕通知
2.获取切入点方法返回值
返回后通知
环绕通知
3.获取切入点方法运行异常信息
抛出异常后通知
环绕通知
AOP获取数据
1.JoinPoint方式
@Before("pt()")
public void before(JoinPoint jp){
Object[] args = jp.getArgs();
String s = Arrays.toString(args);
System.out.println("before...");
}
@AfterReturning(value="pt()",returning="ret")
public void afterReturning(JoinPoint jp,Object ret){
System.out.println("afterReturning..."+ret);
}
@AfterThrowing(value="pt()",throwing="t")
public void afterThrowing(Throwable t){
System.out.println("afterThrowing..."+t);
}
2.ProceedJointPoint方式
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around before...");
// getArgs()获取传递的参数,然后再传递进去
Object[] args = jp.getArgs();
String s = Arrays.toString(args);
args[0] = 123;
Integer ret = (Integer)pjp.proceed(args);
// 获取异常数据(把throws Throwable去除)
try{
Integer ret = (Integer)pjp.proceed();
}catch(Throwable t){
t.printStackTrace();
}
// ProceedingJoinPoint表示对原始操作的调用,可以获取返回值
Integer ret = (Integer)pjp.proceed();
// pjp的getSignature方法生成的对象可以获取当前执行的类和方法
Signature signature = pjp.getSignature();
System.out.println(signature.getDeclaringType());
System.out.println(signature.getDeclaringTypeName ());
System.out.println(signature.getName());
System.out.println("around after...");
return ret;
}
7.Spring事务管理
事务作用︰在数据层保障一系列的数据库操作同成功同失败
Spring事务作用︰在数据层或业务层保障一系列的数据库操作同成功同失败
public interface PlatformTransactionManager{
void commit(Transactionstatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
public class DataSourceTransactionManager {
...
}
1.Spring事务的使用
银行的转帐实用案例
-- java/com.itheima/Config配置
(1.JdbcConfig.java
// 设置事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
-- java/com.itheima/Dao层
(1. AccountDao.java
@Update("update account set money=money + #{money} where name=#{name}")
void inMoney(@Param("name") String name,@Param("money") Double money);
@Update("update account set money=money - #{money} where name=#{name}")
void outMoney(@Param("name") String name,@Param("money") Double money);
-- java/com.itheima/Service层
(1. AccountService.java
// 设置为事务
@Transactional
void transfer(String out,String in,Double money);
(2. impl/AccountServiceImpl.java
public void transfer(String out,String in,Double money){
accountDao.outMoney(out,money);
// 主动执行异常代码
int a = 1/0;
accountDao.inMoney(in,money);
}
-- java/com.itheima/Config配置
(1.SpringConfig.java
// 开启注解式事务驱动
// 有注解开发事务
@EnableTransactionManagement
2.Spring事务角色
事务角色
(1.事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法
(2.事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法
(3.事务中使用的dataSource和myBatis中使用的dataSource是相同的,否则将不能正常管理。
3.Spring事务属性
1.不执行回滚的情况
(1.报出Error
而IOEXception不属于这种异常
(2.运行时异常
如NullPointException,ThreadExcption才属于这个异常
2.给指定异常添加回滚
-- java/com.itheima/Service层
(1. AccountService.java
// 设置为事务
@Transactional(rollbackFor={IOEXception.class})
void transfer(String out,String in,Double money);
3.事务传播行为
事务传播行为:事务协调员对事务管理员所携带事务的处理态度
// 设置事务的传播形为
// 设置为事务,隔离其它事务,将会执行try...finally的内容
@Transactional(propagation = Propagation.REQUIRES_NEW)
void log(String out,String in,Double money);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hBF2SIUz-1662813226517)(C:/Users/Icy-yun/AppData/Roaming/Typora/typora-user-images/image-20220901194051126.png)]
转账业务追加日志事例
无论转帐操作是否成功,均进行转帐操作的日志留痕。
而Spring的事务,异常的情况下,默认是try…finally…不会向下执行。所以需要设置事务传播形为
-- java/com.itheima/Dao层
(1. AccountLogDao.java
package com.itheima.dao;
import com.itheima.domain.Account;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface AccountLogDao {
@Insert("insert into account_log(info,create_time) values(#{info},current_time())")
void log(String info);
}
-- java/com.itheima/Service层
(1. AccountLogService.java
package com.itheima.service;
import com.itheima.domain.Account;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
public interface AccountLogService {
// 设置事务的传播形为
// 设置为事务,隔离其它事务,将会执行try...finally的内容
@Transactional(propagation = Propagation.REQUIRES_NEW)
void log(String out,String in,Double money);
}
(2. impl/AccountLogServiceImpl.java
package com.itheima.service.impl;
import com.itheima.dao.AccountDao;
import com.itheima.dao.AccountLogDao;
import com.itheima.domain.Account;
import com.itheima.service.AccountLogService;
import com.itheima.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AccountLogServiceImpl implements AccountLogService {
// 自动装配
// 连setter方法都不需要,采用反射设计创建对象并暴力反射对应属性为私有属性初始化数据
// 以前Ioc提供入口去替代new,现在直接连入口都省略
@Autowired
private AccountLogDao accountLogDao;
public void log(String out,String in,Double money){
System.out.println("转帐操作由"+out+"到"+in+",金额"+money);
accountLogDao.log("转帐操作由"+out+"到"+in+",金额"+money);
}
}
3. impl/AccountServiceImpl.java
// 可以裝配其它服务层
@Autowired
private AccountLogService accountLogService;
public void transfer(String out,String in,Double money){
try{
accountDao.outMoney(out,money);
// 主动执行异常代码
// int a = 1/0;
accountDao.inMoney(in,money);
}finally {
accountLogService.log(out,in,money);
}
}
3、SpringMVC框架
SpringMVC技术与servlet技术功能等同,均属于web层开发技术。
- 是一种表现层,业务层,数据层的开发模式。
- 是一种基于Java实现MVC模型的轻量级Web框架
- 使用简单,开发便捷,灵活性强
1.xml配置文件*
注意点:
- 在idea中项目的maven和tomcat都正常配置。
- spring的版本5.2.0.5.2.10.RELEASE,maven的编译版本是1.8
- scope 里面的 provided 一定要写
- java同级目录下的webapp子文件,WEB-INF删除xml文件)
1.pol.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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>SSMStudy</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>SSMStudy Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<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>
<spring.version>5.2.10.RELEASE</spring.version>
</properties>
<dependencies>
<!--spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- c3p0 数据源驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.16</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!-- druic驱动 -->
<!-- <dependency>-->
<!-- <groupId>com.alibaba</groupId>-->
<!-- <artifactId>druid</artifactId>-->
<!-- <version>1.1.16</version>-->
<!-- </dependency>-->
<!-- mybatis 依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring + mybatis整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- junit 测试包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring + junit整合包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- aop aspectj 面向切面编程-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!--servlet web核心包-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--spring-webmvc 核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring-webmvc 支持json 核心包-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<!-- 项目需要添加的插件 -->
<build>
<plugins>
<!-- Tomcat插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8082</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.项目结构
├─main
│ ├─java java源文件
│ │ └─com
│ │ └─itheima 项目下的包
│ │ ├─cofig 项目配置文件
│ │ │ JdbcConfig.java jdbc数据源配置
│ │ │ MyBatisConfig.java mybatis连接配置
│ │ │ ServletInitConfig.java servlet初始化容器
│ │ │ SpringConfig.java spring配置文件
│ │ │ SpringMvcConfig.java SpringMVC配置文件
│ │ │ SpringMvcSupport.java SpringMVC过滤器
│ │ ├─controller 表现层(Controller + routes)
│ │ │ BookController.java
│ │ ├─dao 数据层(Models)、Mapper结构也可以
│ │ │ BookDao.java
│ │ ├─domain 模型、请求体,响应体实体类 entity结构也可以
│ │ │ Book.java
│ │ │ Code.java
│ │ │ Response.java 自定义响应类
│ │ │ User.java
│ │ ├─exception 异常类
│ │ │ BusinessException.java 自定义业务异常类
│ │ │ ExceptionAdvice.java 所有异常捕获类
│ │ │ SystemException.java 自定义系统异常类
│ │ ├─interceptor 拦截器
│ │ │ ProjectInterceptor.java 项目拦截器
│ │ │ ProjectInterceptor2.java
│ │ └─service 业务层
│ │ │ BookService.java 业务接口
│ │ └─impl
│ │ BookServiceImpl.java 实现类
│ ├─resources
│ │ jdbc.properties jdbc配置
│ └─webapp web页面资源
│ │ index.jsp
│ ├─pages
│ │ 123.jpg
│ │ hello.html
│ │ hello.txt
│ ├─static
│ │ hello.html
│ └─WEB-INF
│ web.txt
└─test 测试业务层
└─java
└─com
└─itheima
└─service
BookServiceTest.java
2.SpringMVC的使用
启动服务器初始化过程
1.服务器启动,执行ServletContainersInitconfig类,初始化web容器
2.执行createServletApplicationContext方法,创建了webApplicationContext对象
3.加载SpringMvcConfig
4.执行@ComponentScan加载对应的bean
5.加载UserController,每个@RequestMapping的名称对应一个具体的方法
6.执行getServletMappings方法,定义所有的请求都通过SpringMvc
单次请求过程
1.发送请求localhost/save
2. web容器发现所有请求都经过SpringMVc,将请求交给SpringMVC处理
3.解析请求路径/save
4.由/save匹配执行对应的方法save()
5.执行save()
6.检测到有@ResponseBody直接将save()方法的返回值作为响应求体返回给请求方
1.基本使用
在完成xml的配置后,配置以下文件
-- java/com.itheima/Controller层
// 加载控制器
(1.UserController.java
package com.itheima.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
// 设置当前操作的访问路径
@RequestMapping("/save")
// 设置当前操作的返回值类型
@ResponseBody
public String save(){
System.out.println("user save...");
return "{'module':'springmvc'}";
}
}
-- java/com.itheima/config层
// 服务器启动,执行当前类,初始化web容器
(1.ServletInitConfig.java
package com.itheima.config;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
public class ServletInitConfig extends AbstractDispatcherServletInitializer {
// 加载springMVC容器配置
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
// 设置哪些请求归springMVC处理
// 执行这个方法,定义所有请求都通过SpringMVC
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
// 加载spring容器配置
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
(2.SpringMvcConfig.java
// 执行扫描,加载对应的bean
@ComponentScan("com.itheima.controller")
-- Tomcat运行
新建项目配置,添加Maven配置,输入tomcat7:run 的运行命令,点击运行按钮(出现Starting ProtocolHandler ["http-bio-8082"] 即为正常访问)
2.优化ServletInitConfig配置
-- java/com.itheima/config层
// 服务器启动,执行当前类,初始化web容器
(1.ServletInitConfig.java
package com.itheima.cofig;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
public class ServletInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
//public class ServletInitConfig extends AbstractDispatcherServletInitializer {
加载springMVC容器配置
// @Override
// protected WebApplicationContext createServletApplicationContext() {
// AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// ctx.register(SpringMvcConfig.class);
// return ctx;
// }
//
设置哪些请求归springMVC处理
// @Override
// protected String[] getServletMappings() {
// return new String[]{"/"};
// }
//
加载spring容器配置
// @Override
// protected WebApplicationContext createRootApplicationContext() {
// return null;
// }
//}
3.SpringMVC的交互
1.请求映射路径
1.对整个控制器添加注解,就是请求路径的前缀 /user + 方法路径
//@Controller
//@ResponseBody
// 等同于@RestController
@RequestMapping("/user")
public class UserController {
...
}
2.对控制器的方法添加注解,就是具体的请求路径 /方法路径
// 设置当前操作的访问路径
@RequestMapping("/save")
// 设置当前操作的返回值类型
@ResponseBody
public String save(){
System.out.println("user save...");
return "{'module':'user'}";
}
3.设置请求方式
@RequestMapping(value="/save",method=RequestMethod.POST)
4.RestController的简写格式
@PostMapping
@DeleteMapping("/{id}")
@PutMapping
@GetMapping("/{id}")
2.Form表单参数
SpringBoot对是否为Json数据有非常严格的要求,只有定义实体类,才可以接收json数据。
而post请求的x-www-form-urlencoded和form-data同为param参数处理
1.解决中文乱码的问题(SpringMVC框架专属)
ServletInitConfig配置过滤器,解决乱码
为web容器添加过滤器并指定字符集,Spring-web包中提供了专用的字符过滤器
// 请求过滤器
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter cef = new CharacterEncodingFilter();
cef.setEncoding("UTF-8");
return new Filter[]{cef};
}
2.传递json类型的请求参数
(1.pol.xml添加坐标依赖
<!--json 请求参数支持-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
(2.java/com.itheima/config层
// 服务器启动,执行当前类,初始化
(1.SpringMvcConfig.java
@EnableWebMvc // 支持json数据的格式转化
(3. @RequestBody支持json格式的数据, 其它所有的方式的形参都需要添加@RequestBody
public String save(@RequestBody List<String> likes){
System.out.println("user save..."+ likes);
return "{'module':'user'}";
}
3.get请求和post请求的参数,都是在请求方法上定义形参
// 1.HttpServletRequest 请求对象
// form表单提交的格式不是json格式,所以可以通过HttpServletRequest请求对象获取数据
public String save(HttpServletRequest request){
String token = request.getHeader("token"); // 请求头数据
String username = request.getParameter("username") // 请求体或者query参数
...
}
// 2.@RequestParam可以绑定参数,获取query参数 (也可以省略)
// form-data,application/x-www-form-urlencoded 传递的参数同属于这种类型
public String save(@RequestParam("username") String name,@RequestParam("user_id") Integer id){
...
}
// 2.1 数组类型的形参 获取query参数
// 则需要传递 多个同likes键名的键值对参数
public String save(@RequestParam String[] likes){
...
}
// 2.2 集合类型的形参,获取query参数
// 则需要传递 多个同likes键名的键值对参数 ["a","b"]
public String save(@RequestParam List<String> likes){
...
}
// 2.3 @DateTimeForma 格式化传递日期类型的请求参数,获取query参数
// Date日期类型的参数
// date 默认格式 2022/09/05
// @DateTimeFormat设置日期格式 2022-09-05
// @DateTimeFormat设置日期时间格式 2022-09-05 08:20:39
// http://localhost:8083/user/save?date=2022/09/05&date1=2022-09-05&date2=2022-09-05 08:20:39
public String save(Date date, @DateTimeFormat(pattern = "yyyy-MM-dd") Date date1,
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date2){
...
}
// 3.@PathVariable可以绑定参数, 获取路径参数
@RequestMapping(value="/save/{name}/{id}",method=RequestMethod.POST)
public String save(@PathVariable String name,@PathVariable Integer id){
...
}
// 4.@RequestBody 用于接收请求包体的数据,获取json数据
// form-data,application/x-www-form-urlencoded 都只适合@RequestParam类型参数
// application/json才适合这种上方法,一般情况下,都是为提供json转换成实体类服务的。
// (4.1 集合中保存多个对象 (json方法专属,普用于复杂类型结构)
// 需要传递 多个同likes键名的键值对参数 [{"name":"a"},{"name":"b"}]
public String save(@RequestBody List<User> users){
...
}
// (4.2 实体类作为形参, pojo参数 (json方法专属,普用于复杂类型结构)
// 如果实体类的成员变量又是一个实体类,则需要传递 xxx.xx 类型的形参 {"name":{"type":"a"}}
public String save(@RequestBody User user){
...
}
// 创建User.java实体类用于接收json转换的对象
// 实体类定义成员变量,getter,setter,toString方法
package com.itheima.domain;
public class User {
private String name;
private int age;
@Override
public String toString() {..}
public String getName() {...}
...
}
3.Response响应
1.基本使用
1.响应jsp页面
// 把 @ResponseBody去除,就默认是页面
public String save(){
// 填写jsp文件的路径, webapp是根路径
return "/index.jsp";
}
2.返回字符串
// 添加 @ResponseBody 注解就可以
public String save(){
return "hello world";
}
3.响应json数据
// (1.响应一个实体类(会转换为json数据)
public User save(){
User user = new User();
user.setName("jack");
user.setAge(10);
return user;
}
// (2.响应一个POJO集合对象(会转换为json数据)
public List<User> save(){
User user1 = new User();
user1.setName("jack");
user1.setAge(10);
User user2 = new User();
user2.setName("mike");
user2.setAge(12);
List<User> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
return userList;
}
// (3.响应一个map结构(也会转换为json数据)
public Response login(){
Map<String ,Object> map = new HashMap<>();
map.put("token","123");
return new Response("登录成功",map,Code.RESONSE_OK);
}
2.封装响应数据
// 1. controller 中 响应一个自定义Response实体类
// (1.使用构造函数(建议)
@PostMapping
public Response save(@RequestBody Book book) {
System.out.println("已经响应了请求");
boolean flag = bookService.save(book);
List<String> s = new ArrayList<>();
s.add("12123");
return new Response("操作成功",s, Code.SAVE_OK);
}
// (2.使用set方法
public Response save(){
Response res = new Response();
try{
// (1.data响应字符串
// 结果: data:"查找成功"
String str = new String("查找成功");
res.setData((Object) str);
// (2.data响应实体类
// 结果: data:{"data":"jack","age":10}
User userObj = new User();
userObj.setName("jack");
userObj.setAge(10);
res.setData((Object) userObj);
// (3.data响应POJO集合对象
// 结果: data:[ {"data":"jack","age":10}]
User userObj = new User();
userObj.setName("jack");
userObj.setAge(10);
List<User> list = new ArrayList<>();
list.add(userObj);
res.setData((Object) list);
res.setMsg("数据获取成功");
res.setCode(200);
}catch (Exception e){
System.out.println("错误信息是"+e);
res.setData(null);
res.setMsg("出现异常!数据获取失败!");
res.setCode(403);
}
return res;
}
// 2.创建一个Response响应类
package com.itheima.domain;
import java.util.List;
public class Response {
private String msg;
private Object data;
private int code;
public Response(String msg, Object data, int code) {
this.msg = msg;
this.data = data;
this.code = code;
}
public void setMsg(String msg) {
this.msg = msg;
}
public void setData(Object data) {
this.data = data;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public Object getData() {
return data;
}
public int getCode() {
return code;
}
@Override
public String toString() {
return "Response{" +
"msg='" + msg + '\'' +
", data=" + data +
", code=" + code +
'}';
}
}
// 3.创建Code 状态码静态类
package com.itheima.domain;
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_UNKNOW_ERR = 59999;
public static final Integer BUSINESS_ERR = 60002;
}
4.MVC的功能模块
1.请求过滤器
1.优先匹配本地文件路径
不仅仅是页面,而且可以是图片,css,js文件.
注意点:资源文件是放在webapp的根目录(webapp就是根目录),与有无web.xml文件无关, 注意需要被SpringWebConfig.java文件给扫描。
-- java/com.itheima/config层
(1.SpringMvcSupport.java
package com.itheima.cofig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
@Configuration
@EnableWebMvc
public class SpringMvcSupport implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
}
}
2.动态拦截器
1.基本概念
1. 基本概念
- ·拦截器( Interceptor )是一种动态拦截方法调用的机制,在springWVC中动态拦截控制器方法的执行
- 作用︰(1.在指定的方法调用前后执行预先设定的代码。(2.阻止原始方法的执行
2. 拦截器与过滤器区别
- 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
- 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVc的访问进行增强
3.多拦截器执行顺序
- 当配置多个拦截器时,形成拦截器链
- 拦截器链的运行顺序参照拦截器添加顺序为准
2.使用实例
--java/com.itheima/interceptor层
(1.PojectInterceptor.java拦截器
package com.itheima.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 {
String contentType = request.getHeader("Content-Type");
String name = request.getParameter("name");
HandlerMethod hm = (HandlerMethod)handler;
// hm.getMethod()可以操作原始对象执行的方法。request 可以拿到请求的参数
System.out.println("这是请求前拦截。。。"+name );
// false 表示终止原始操作。tue表示继续执行原始操作
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("这是请求后拦截。。。");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("这是请求结束后拦截。。。");
}
}
// 向过滤器中添加拦截器配置
-- java/com.itheima/config层
(1.SpringMvcSupport.java
package com.itheima.cofig;
import com.itheima.interceptor.ProjectInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
@Configuration
@EnableWebMvc
public class SpringMvcSupport implements WebMvcConfigurer {
@Autowired
private ProjectInterceptor projectInterceptor;
// 过滤器配置
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
}
// 拦截器配置
// 注入拦截器,对指定请求路由拦截
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
}
}
3.异常处理器
1.基本的异常处理
- 逻辑异常,比如出现1/0,数据库或者表不存在,会抛出Exception异常
1.出现异常现象的常见位置与常见诱因如下:
1.框架内部抛出的异常:因使用不合规导致
2.数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
3.业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
4.表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
5.工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)
数据层 => dao层, 业务层 => service层 , 表现层 => Controller
Model Controller V(controller + Routes)
2.创建异常处理器 (基本的所有异常)
package com.itheima.domain;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class ExceptionAdvice {
@ExceptionHandler(Exception.class)
public Response doException(Exception e){
System.out.println("出现的异常是"+e);
return new Response("操作出现异常!",null, Code.SAVE_ERR);
}
}
2.封装分类异常处理(规范化)
包含上面的response响应的封装
1.项目异常处理方案
(3.1项目异常分类
(1.业务异常(BusinessException)
规范的用户行为产生的异常
不规范的用户行为操作产生的异常
(2.系统异常(systemException)
项目运行过程中可预计且无法避免的异常
(3.其他异常(Exception)
编程人员未预期到的异常
(3.2项目异常处理方案
(1.业务异常(BusinessException)
发送对应消息传递给用户,提醒规范
(2.操作系统异常(systemException)
发送固定消息传递给用户,安抚用户发送特定消息给运维人员,提醒维护
记录日志
(3.其他异常(Exception)
发送固定消息传递给用户,安抚用户
发送特定消息给编程人员,提醒维护(纳入预期范围内)
记录日志
--- java.itheima/exception层
(1.ExceptionAdvice.java异常文件
package com.itheima.exception;
import com.itheima.domain.Code;
import com.itheima.domain.Response;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class ExceptionAdvice {
@ExceptionHandler(BusinessException.class)
public Response doBusinessException(BusinessException e){
System.out.println("出现的业务异常是"+e);
return new Response(e.getMessage(),null,e.getCode());
}
@ExceptionHandler(SystemException.class)
public Response doSystemException(SystemException e){
// 记录日志
// 发送消息给运维
// 发送邮件给开发人员
System.out.println("出现的系统异常是"+e);
return new Response(e.getMessage(),null,e.getCode());
}
@ExceptionHandler(Exception.class)
public Response doException(Exception e){
// 记录日志
// 发送消息给运维
// 发送邮件给开发人员
System.out.println("出现的未知异常是"+e);
return new Response("系统繁忙!请稍后再试",null, Code.SYSTEM_UNKNOW_ERR);
}
}
(2.BusinessException.java自定义业务异常
package com.itheima.exception;
public class BusinessException extends RuntimeException{
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public BusinessException(String message, Integer code) {
super(message);
this.code = code;
}
public BusinessException(String message, Throwable cause, Integer code) {
super(message, cause);
this.code = code;
}
}
(3.SystemException.java自定义系统异常
package com.itheima.exception;
public class SystemException extends RuntimeException{
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public SystemException(String message, Integer code) {
super(message);
this.code = code;
}
public SystemException(String message, Throwable cause, Integer code) {
super(message, cause);
this.code = code;
}
}
--- java.itheima/service层
(1.impl/BookService.java
@Override
public Book getById(Integer id) {
if(id == 1){
throw new BusinessException("请按照规范传递参数!",Code.BUSINESS_ERR);
}
try{
Integer a = 1/0;
}catch (Exception e){
throw new SystemException("服务器访问异常!请重试!",e, Code.SYSTEM_TIMEOUT_ERR);
}
return bookDao.getById(id);
}
5.Spring整合模块
1.整合JUnit
-- test/com.itheima/Service层
(1.AccountServiceTest.java (测试用例)
package com.itheima.service;
import com.itheima.config.SpringConfig;
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;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
public void testFindById(){
System.out.println(accountService.findById(1));
}
@Test
public void testFindAll(){
System.out.println(accountService.findAll());
}
}
--pom.xml文件添加为以下内容
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring + junit整合包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
2.整合MyBatis
- 在controller中自动装配service服务类时,一定要保证扫描com.xxx.service层
- SpringConfig和SpringMvcConfig的加载配置需要向SpringMVC初始化容器中注入。
-- java/com.itheima/Config配置
(1.MyBatisConfig.java (新建)
package com.itheima.config;
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 ssfb = new SqlSessionFactoryBean();
ssfb.setTypeAliasesPackage("com.itheima.domain");
ssfb.setDataSource(dataSource);
return ssfb;
}
// 扫描类型业务设备的包
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
}
-- java/com.itheima/Dao层 (注意先清空同级的其它java文件)
(1. AccountDao.java
package com.itheima.dao;
import com.itheima.domain.Account;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface AccountDao {
@Insert("insert into account(name,money) values(#{name},#{money})")
void save(Account account);
// int save(Account account); 表示返回影响的行数
@Delete("delete from account where id = #{id}")
void delete(Integer id);
@Update("update account set name=#{name},money=#{money} where id=#{id}")
void update(Account account);
@Select("select * from account where id = #{id}")
Account findById(Integer id);
@Select("select * from account")
List<Account> findAll();
// 特殊字符处理
@Select("select * from <![CDATA[ dbo.ST_STBPRP_B ]]> ")
}
-- java/com.itheima/domain层
(1. Account.java
package com.itheima.domain;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public Double getMoney() {
return money;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" + "id=" + id + ",name='" +name+ "',money=" + money + "}";
}
}
-- java/com.itheima/Service层
(1. AccountService.java
package com.itheima.service;
import com.itheima.domain.Account;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface AccountService {
void save(Account account);
void delete(Integer id);
void update(Account account);
Account findById(Integer id);
List<Account> findAll();
}
(2. impl/AccountServiceImpl.java
package com.itheima.service.impl;
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AccountServiceImpl implements AccountService {
// 自动装配
// 连setter方法都不需要,采用反射设计创建对象并暴力反射对应属性为私有属性初始化数据
// 以前Ioc提供入口去替代new,现在直接连入口都省略
@Autowired
private AccountDao accountDao;
public void save(Account account){
accountDao.save(account);
}
public void update(Account account){
accountDao.update(account);
}
public void delete(Integer id){
accountDao.delete(id);
}
public Account findById(Integer id){
return accountDao.findById(id);
}
public List<Account> findAll( ){
return accountDao.findAll();
}
}
--pom.xml文件添加为以下内容
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- spring jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring + mybatis整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
3.webapp静态资源
添加动态拦截器,然后匹配到指定路径时,首先选择本地文件路径
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
}
// 文件目录
webapp/static/books.html
webapp/pages/books.html
// 浏览器访问路径(直接访问上述的文件)
http://localhost:8083/static/books.html
http://localhost:8083/pages/books.html
4.config配置预览
一、MyBatis相关配置
1.JdbcConfig.java连接数据库的配置文件,里面定义DataSource数据源,和Spring事务PlatformTransactionManager
2.MyBatisConfig.java 创建扫描实体类映射方法SqlSessionFactoryBeant和扫描数据层的方法MapperScannerConfigurer
3.jdbc.properties 用于保存连接数据库的全局变量参数。
二、SpringMVC相关配置
1.ServletInitConfig.java 用于指定Spring配置类和Servlet配置类。 同时也于添加servlet过滤器getServletFilters。
2.SpringMvcConfig.java 配置类用于扫描controller,domain,exception,config,interceptor等web框架资源的
3.SpingMvcSupport.java 用于添加资源过滤器addResourceHandlers和注册拦截器addInterceptors
三、Spring相关配置
1.SpringConfig.java 用于扫描jdbc.properties资源, 引入JdbcConfig和MyBatisConfig类,扫描其它包体下的文件 com.xxxx,同时也可以注册事务。
5.常用注解预览
1.Spring相关注解
@ComponentScan 扫描类
@Import 导入mybatis相关配置类
@PropertySource 加载资源
@Configuration 声明配置文件
@Component (@Controller / @Service / @Repository) 声明类的所属模块
@Scope 单例/非单例模式控制
@PostConstruct、@PreDestroy 业务层生命周期
@Autowired 自动装配
@Value 变量赋值
@Bean 定义Bean
2. SpringMvc相关注解
@RestController 声明控制器类(@Controller + @RequestBody)
@RequestMapping 匹配路径映射 (@PostMapping+@GetMapping+@PutMapping+@DeleteMapping)
@xxx 接口注解(@Select/@Insert/@Update/@Delete)
@CrossOrigin cors跨域,在SpringMvcSupport声明注解@CrossOrigin("*")
4、SpringBoot框架(boot)
SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程
内置了起步依赖,用于快速配置依赖。
Starter: SpringBoot中项目名称定义了当前项目使用的所有项目坐标,以达到减少依赖配置的目的
辅助功能:tomcat插件内置tomcat服务器
引导类 :SpringBoot的引导类是项目的入口, 并且会默认扫描当前目录下的文件,运行main方法就可以启动项目.
测试类:测试类会默认加载引导类,扫描引导类下的文件,
1.安装流程
- 通过Spring Initializr创建custom项目(http://start.aliyun.com),然后选择java 8版本,设置项目的包名Group(小写),清除默认的子包名。(如果需要数据库支持,则在创建项目时勾选 SQL = > (MyBatis Framework + MySQL Driver))
- 然后选择Web栏,再选择SpringWeb,最后填写项目名称。如果出现src目录,pol.xml文件即为安装正常
2.创建项目后
- 在pol.xml中添加myBatisPlus相关依赖(如下方配置),刷新maven模块,如果文件复制,配置idea的编码
- 查看ideal是否安装Lombok插件,以自动创建setter,getter,toString----(首次使用idea搭建springboot)
- 指定maven的配置(使用内置maven + 本地maven配置(文件 + 仓库)),刷新maven模块
- 添加项目热布署,以便于在开发时可以自动更新。(如下方配置),刷新maven模块
- 使用代码生成器快速生成代码对应的项目架构。-----(可忽略)
3.运行项目
- 运行com.itheima包下Application类,就会默认开启一个tomcat服务器, 8080端口
- 浏览器访问(如果创建下方的控制器): http://localhost:8080/books/1
1.创建新项目
1.项目文件配置
1.pol.xml坐标依赖
注意:
- MyBatisPlus的版本(com.baomidou),3.4.2有问题,所以需要修改成3.2.0
- springboot项目不要开启@EnableWebMvc注解,否则中文乱码(json转换不需要这个注解)
<!-- 添加新的依赖-->
<!-- druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<!-- 或 c3p0 数据源驱动-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!--json 参数转换支持-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<!-- 不要选择版本,让springBoot自己选择-->
<!--<version>2.9.6</version>-->
</dependency>
<!-- lombok简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!--添加热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>true</scope>
<!-- 最后的scope可以写也可以不写 -->
</dependency>
<!-- 代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
</dependency>
<!-- velocity模板引擎 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
2.application.yml项目配置
# spring项目配置
spring:
# mysql数据源
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot-test?characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
# 热布署
devtools:
restart:
enabled: true
additional-paths: src/main
exclude: static/**
main:
# 清除多余的输出
banner-mode: off
# server服务项目配置
server:
# 项目运行端口
# 解决占用方法:netstat -ano | findstr 8081 、 taskkill /F /PID 1234
port: 8081
# mybatis-plus项目配置
mybatis-plus:
# mapper.xml的配置文件 , 注意,将mapper生成的xml文件移动到resources文件下
mapper-locations: classpath*:mapper/*.xml
global-config:
# 隐藏多余输出
banner: false
# 数据库表的映射配置
db-config:
id-type: auto
logic-delete-field: is_delete
logic-not-delete-value: 0
logic-delete-value: 1
# table-prefix: tb1_
configuration:
# 输出正在执行的sql语句
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# log项目配置
logging:
level:
# 根日志输出警告 info 输出,warn 警告
root: info
3.xx.properties和xx.yml的区别
1.application.properties(默认)
// 配置服务启动的端口
server.port=8082
2.application.yml(新建)/yaml(新建)
server:
port: 8082
// 注意:封号后有空格
// 优先级: poperties > yml > yaml, 常用yml格式的数据
2.Idea安装插件
1.安装Lombok插件
- 在idea需要安装lombok插件,在编译的时候,才能自动生成getter,setter。
- 在插件市场,搜索LomBok,install安装到idea.
- 然后添加到当前项目:settings->build Execution->compiler->Annotation processors -> 勾选 enable annotation processing, 然后重启就ok.
2.项目热部署
注意:热布署对配置文件,全局变量文件,page静态资源文件无效。只对SpringMVC的修改有效果。
1.注意xml的配置文件,然后在plugins中添加自动执行参数fork
<plugin>
<!--热部署配置-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--fork:如果没有该项配置,整个devtools不会起作用-->
<fork>true</fork>
</configuration>
</plugin>
3.配置ideal运行环境
(1)勾选自动编译 File->Settings->Compiler->Build Project automatically
(2)注意运行时允许自动操作 ctrl + shift + alt + /,选择Registry,勾上 Compiler autoMake allow when app running
3.yml多环境配置
1.yml的配置格式
---application.yml
#启动的环境
spring:
profiles:
active: dev
# 使用 “---”分隔环境配置
---
#开发环境
spring:
profiles: dev
server:
port: 8081
---
#生产环境
spring:
profiles: pro
server:
port: 8082
---
#测试环境
spring:
profiles: test
server:
port: 8083
---
# 新的配置方式
spring:
config:
activate:
on-profile: new
---
2.properties的格式
---application.properties
spring.profiles.active=dev
---application-dev.properties
server.port = 80
2.SpringBoot整合
1.整合JUnit
-- test/java/com.itheima
(1.ApplicationTests.java
package com.itheima;
import com.itheima.service.BookService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
// 默认加载同名包下的入口文件
@SpringBootTest
// 加载指定包下的入口文件
// @SpringBootTest(classes=Application.class)
public class ApplicationTests {
@Autowired
private BookService bookService;
@Test
public void save(){
bookService.save();
}
}
2.整合MyBatis
在创建SpringBoot项目时,勾选 Web的 同时,也勾选SQL = > (MyBatis Framework + MySQL Driver)
---application.yml文件
// mysql数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/study_java_hello?characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
// sql server数据源
spring:
datasource:
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
url: jdbc:sqlserver://117.40.228.164:1433;DatabaseName=QS_J323?characterEncoding=utf-8&serverTimezone=UTC
username: sa
password: Sky02810@Zt
-- com.itheima.dao层
(1.BookDao.java
需要在类名前添加 @Mapper注解映射 (原来是Repository)
--- pol.xml(修改数据源,可以正常使用druid)
// 数据源
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
// mysql(一般在创建项目的时候,已经默认添加了依赖)
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.16</version>
</dependency>
// sqlserver(一般在创建项目的时候,已经默认添加了依赖)
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>6.1.0.jre8</version>
</dependency>
3.static静态资源
页面资源放在 resources/static文件夹内,相当于公有文件夹,比如
注意:第一个路径必须是文件夹,而不能是文件
// 文件目录
resources/static/pages/books.html
// 浏览器访问路径(直接访问上述的文件)
http://localhost:8083/pages/books.html
// 模板页面上使用静态资源
<script th:src="@{/js/jquery-3.1.1.min.js}"></script>
4.templates模板页面
SpringBoot有resources/templates目录,里面存放的是html页面文件,而SpringMvc里面没有特定的目录,并且也必须在webapps里面存放jsp页面文件。
模板引擎是一种可以把程序员提供的数据和模板通过模板引擎转换成固定格式来动态生成HTML的技术。
其主要优点在于:可以提高代码的可读性和维护性,可大大提高一些简单页面的开发效率,减少字符串拼接。
实现流程为:浏览器请求抛给控制器,控制器处理好数据后,就跳转板引擎页面。 (注意在跳转的同时,会将数据组装好,也交给模板引擎处理。)模板引擎会根据数据和模板引擎的规则,动态生成 HTML 页面,最后返回给浏览器显示。
现阶段常用Spring Boot模板引擎有 FreeMarker 、Thymeleaf。而Spring Boot默认使用的是Thymeleaf。因此可以来实现一个基于Thymeleaf 商品浏览项目实例。
1.pom.xml引入依赖
<!-- Thymeleaf模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.application.yml文件配置
spring:
thymeleaf:
#关闭模板缓存(默认为true)
cache: false
#模板编码
encoding: utf-8
#在构建URL时添加到视图名称前的前缀(默认值:classpath:/templates/)
prefix: classpath:/templates/
#在构建URL时添加到视图名称后的后缀(默认值:.html)
suffix: .html
mode: HTML5
check-template-location: true
content-type: text/html
3.在controller中指向templates的文件(可以使用html文件)
@RequestMapping("/goods")
// 方法一:ModelAndView
public ModelAndView login(ModelAndView mav){
mav.addObject("name","小明");
mav.setViewName("index");
return mav;
}
5、MyBatisPlus框架(MP)
MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率
1.MyBatisPlus注解格式
1.注解格式
@Mapper
public interface User_msgMapper extends BaseMapper<User_msg> {
// // 一、注解格式
// // 查询数据
// // 查询单条语句
// @Select(" select * from user_msg where username = #{username} ")
// List<User_msg> queryUsername(String username);
//
// // 更新数据
// // 设置多个参数,需要使用arg0进行传参,返回值表示影响的行数
// @Update(" update user_msg set username = #{arg0} where id = #{arg1}")
// boolean updateUsername(String username,Integer id);
// // 通过@Param("xx")绑定形参
// @Update(" update user_msg set username = #{username} where id = #{id}")
// boolean updateUsername1(@Param("username") String username, @Param("id") Integer id);
//
// // @Insert, @Delete略
// 二、xml标签格式
// 注意点:select需要指明resultType返回类型,update, insert,delete不需要指定返回类型
// 查询数据
List<User_msg> queryUsername2(@Param("username") String username);
// 更新数据
// 设置多个参数,需要使用arg0进行传参,返回值表示影响的行数
boolean updateUsername2(String username,Integer id);
// 通过@Param("xx")绑定形参
boolean updateUsername3(@Param("username") String username,@Param("id") Integer id);
}
2.xml标签格式
注意:将生成的mapper.xml文件移动到resources/mapper文件夹下。
// id是mapper方法名称,resultType是返回值类型, namespace是绑定mapper文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.webserve.mapper.User_msgMapper">
<select id="queryUsername2" resultType="com.webserve.entity.User_msg">
select * from user_msg where username = #{username}
</select>
<update id="updateUsername2" >
update user_msg set username = #{arg0} where id = #{arg1}
</update>
<update id="updateUsername3" >
update user_msg set username = #{username} where id = #{id}
</update>
</mapper>
2.MyBatisPlus连接数据库
1.新注解预览
1.@Mapper注解,
如果注解的xxxDao类如果继承BaseMapper<xxx>, 就可以使用上层对象的方法
// 不需要填写任何方法,也可以使用上层对象的方法,比如 基本的crud,
// 调用insert, selectList, deleteById, updateById等方法
2.服务层和实现服务类
xxService服务接口类继承 IService<xx>就可以使用上层对象的方法
xxServiceImpl服务接口类继承 ServiceImpl<xxDao,xxx>就可以使用上层对象的方法
// 不需要填写任何方法,也可以使用上层对象的方法,比如 基本的crud,
// 调用insert, getList, removeById, updateById等方法
3.@Data简化实体类注解
// @Data注解等同于 @Setter,@Getter,@ToString,@EqualAndHashCode
// (除了@NoArgsConstructor,@AllArgsConstructor)
@Data
--- java/com.itheima/dao(需要创建User实体类)
(1.UserDao.java
@Mapper
public interface UserDao extends BaseMapper<User>{
}
--- java/com.itheima/service
(1.impl/UserServiceImpl.java
@Service
public class UserServiceImpl extends ServiceImpl<UserDao,User> implements UserService{
}
(2.UserServiceImpl.java
public class UserService extends IService<User>{
}
--- java/com.itheima/domain
(1.User.java
import lomlok.*;
@Data
public class User{
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
}
Mapper层操作和Service操作的区别是: Mapper多是含操作的方法(更多使用id进行crud),而Service封装了操作,使得业务层更有利于代码的可读性,维护性(只关注业务而不关注实现的过程),它们都可以使用Wraper进行过滤查询。
2.Mapper层操作数据
@Autowired
User_msgMapper user_msgMapper;
// mapper层直接操作数据库表
// 1.insert添加数据
@Test
void testInsert(){
User_msg user = new User_msg();
user.setUsername("黑马程序员");
user.setPassword("1234");
user_msgMapper.insert(user);
}
// 2.select查询数据
@Test
void testSelect(){
// 查询所有数据
List<User_msg> userList = user_msgMapper.selectList(null);
System.out.println(userList);
// 查询多个数据
List<Integer> list = new ArrayList<>();
list.add(14);
list.add(15);
List<User_msg> userList2 = user_msgMapper.selectBatchIds(list);
System.out.println(userList2);
// 查询多个数据2
LambdaQueryWrapper<User_msg> lqw = new LambdaQueryWrapper<User_msg>();
lqw.gt(User_msg::getId,22);
List<User_msg> userList3 = user_msgMapper.selectList(lqw);
System.out.println(userList3);
}
// 3.delete 删除数据
@Test
void testDelete(){
// 删除单个数据
user_msgMapper.deleteById(17);
// 删除多个数据
List<Integer> list = new ArrayList<>();
list.add(14);
list.add(15);
user_msgMapper.deleteBatchIds(list);
}
// 4.update更新数据
@Test
void testUpdate(){
User_msg user = new User_msg();
user.setId(18);
user.setUsername("白马程序员");
user.setPassword("1234");
user_msgMapper.updateById(user);
}
3.Service层操作数据
@Autowired
IUser_msgService iUser_msgService;
// service层间接操作数据库表
// 1.save添加数据
@Test
void testSave(){
User_msg user = new User_msg();
user.setUsername("黑马程序员2");
user.setPassword("1234");
iUser_msgService.save(user);
}
// 2.list查询数据
@Test
void testList(){
// 查询所有数据
List<User_msg> userList = iUser_msgService.list();
System.out.println(userList);
// 查询多个数据
LambdaQueryWrapper<User_msg> lqw = new LambdaQueryWrapper<User_msg>();
lqw.eq(User_msg::getId,22);
lqw.or().eq(User_msg::getId,23);
List<User_msg> userList2 = iUser_msgService.list(lqw);
System.out.println(userList2);
}
// 3.remove 删除数据
@Test
void testRemove(){
// 删除单个数据
iUser_msgService.removeById(24);
// 删除多个数据
LambdaQueryWrapper<User_msg> lqw = new LambdaQueryWrapper<User_msg>();
lqw.eq(User_msg::getId,23);
lqw.or().eq(User_msg::getId,22);
iUser_msgService.remove(lqw);
}
// 4.Update更新数据
@Test
void testUpdate2(){
LambdaQueryWrapper<User_msg> lqw = new LambdaQueryWrapper<User_msg>();
lqw.eq(User_msg::getId,24);
User_msg user = new User_msg();
user.setId(24);
user.setUsername("黑马程序员24");
user.setPassword("1234");
iUser_msgService.update(user,lqw);
}
// 5.saveOrUpdate保存/更新数据
@Test
void testSaveOrUpdate(){
// 如果存在相同主键的数据,则更新,否则插入
User_msg user = new User_msg();
user.setId(24);
user.setUsername("黑马程序员3");
user.setPassword("1234");
iUser_msgService.saveOrUpdate(user);
}
3.表映射的高级属性
1.分页拦截器
--- java/com.itheima.config
(1.MyBatisConfig.java类
@Configuration
public class MyBatisConfig{
@Bean
public MybatisPlusInterceptor myInterceptor(){
// 1.定义mp拦截器
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
// 2.添加具体的拦截器
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mpInterceptor;
}
}
--- ApplicationTests.java文件
@Test
void testGetByPage(){
IPage page = new Page(1,5);
userDao.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());
}
2.id生成策略
public class User{
@TableId(type=IdType.AUTO) // 使用数据库的自增id
@TableId(type=IdType.INPUT) // 自定义id(插入数据的时候需要添加)
@TableId(type=IdType.ASSIGN_ID) // 雪花算法的生成策略
@TableId(type=IdType.ASSIGN_UUID) // 以uuid算法的作为id生成策略
private Long id;
private String password;
}
3.字段,表名映射
@TableName("tb_user") // 将当前User实体类连接到tb_user表(默认是小写类名作为表名)
public class User{
private Long id;
@TableField(value="pwd")
private String password; // 将password连接到pwd的数据库表字段
@TableField(value="pwd",select=false)
private String password; // 将password连接到pwd的数据库表字段, 使用select不显示password字段
@TableField(exist=false)
private Integer online; // online字段不存在于数据库表中
}
4.逻辑删除数据
删除操作业务问题:业务数据从数据库中丢弃。
逻辑删除: 为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中
开发的重点:
- 事务操作
- 操作数据的时间(create_time)
- 清除垃圾数据(is_delete)
- 并发操作(version)
public class User{
private Long id;
private String password;
// 逻辑删除字段,标记当前记录是否被删除
@TableLogic(value="0",delval="1")
private Integer is_delete;
}
// delete删除的sql语句实际上为update更新is_delete字段
// select的结果标记为删除的数据则不显示,因此需要获取删除逻辑删除的数据,则需要自定义sql语句查询
5.更新的乐观锁
适合中小型的开发应用处理并发性
public class User{
private Long id;
@Version
private Integer version; // 乐观锁字段,数据库表中都默认设置为1
}
--- java/com.itheima.config
(1.MyBatisConfig.java类
@Configuration
public class MyBatisConfig{
@Bean
public MybatisPlusInterceptor myInterceptor(){
// 1.定义mp拦截器
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
// 2.添加分页的拦截器
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
// 3.添加乐观锁的拦截器
mpInterceptor.addInnerInterceptor(new OptimisticLockerInterceptor());
return mpInterceptor;
}
}
--- ApplicationTests.java文件
@Test
void testUpdate(){
// 1.可以先通过要修改的数据id将当前数据查询出来
User user = userDao.selectById(3L);
// 2.将要修改的属性逐一设置进去, 否则需要手动设置 user.setVersion(1);
user.setName("Jock444")
userDao.updateById(user)
// 实现原理
// 1.先通过要修改的数据id将当前数据查询出来
User user = userDao.selectById(3L); //version=3
User user2 = userDao.selectBy1d(3L); //version=3
user2.setName( "Jock88t");
userDao.updateById(user2); // 执行后,version=>4
user.setName("Jock888"");
userDao.updateById(user); //原来verion=3?条件还成立吗?
}
6.代码生成器
模板:MyBatisPlus提供
数据库相关配置:读取数据库获取信息
开发者自定义配置:手工配置
注意:Mapper数据层的@Mapper注解需要手动添加
--- pol.xml文件
<!-- 代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
</dependency>
<!-- velocity模板引擎 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
--- java/com.itheima/
(1.GeneratorConstructor.java类
package com.webserve;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GeneratorConstructor{
public static void main(String[] args) {
AutoGenerator autoGenerator = new AutoGenerator();
// 设置数据源
DataSourceConfig dataSource = new DataSourceConfig();
dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/springboot-test?characterEncoding=utf-8&serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("123456");
autoGenerator.setDataSource(dataSource);
// 设置全局配置(注意在 com.xx.GeneratorConstructor运行)
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir")+"/../WebServeSpringbootApi/src/main/java");
globalConfig.setOpen(false);
globalConfig.setAuthor("small_cat");
globalConfig.setFileOverride(true);
globalConfig.setMapperName("%sMapper"); // 接口名称
globalConfig.setIdType(IdType.AUTO);
autoGenerator.setGlobalConfig(globalConfig);
// 设置包名相关配置
PackageConfig packageInfo = new PackageConfig();
packageInfo.setParent("com.webserve");
packageInfo.setEntity("entity"); // 或者domain
packageInfo.setMapper("mapper"); // 或者dao
autoGenerator.setPackageInfo(packageInfo);
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
// StrategyConfig.setInclude("tb1_user"); // 仅仅包含
// StrategyConfig.setTablePrefix("tb1_");
// StrategyConfig.setVersionFieldName("version"); // 乐观锁字段
strategyConfig.setRestControllerStyle(true);
strategyConfig.setLogicDeleteFieldName("is_delete");
strategyConfig.setEntityLombokModel(true);
autoGenerator.setStrategy(strategyConfig);
// 执行生成操作
autoGenerator.execute();
}
}
7.定时启动模块
然后在Application.java里面添加 @EnableScheduling注解
package com.ecut.bus.controller;
import com.ecut.bus.mapper.*;
import com.ecut.bus.service.*;
import com.ecut.bus.vo.THPrecVo;
import com.ecut.bus.vo.TWLHydVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
@CrossOrigin
@RestController
@Component
@RequestMapping("/Data")
public class DataController {
@Autowired
OutFlowService outFlowService;
@Autowired
InFlowService inFlowService;
@Autowired
CapService capService;
@Autowired
ITWLHydService2 itwlHydService;
@Autowired
ITHPrecService2 ithPrecService;
@Autowired
GateRoomMapper gateRoomMapper;
@Autowired
XinquanStationMapper xinquanStationMapper;
@Autowired
PlantMapper plantMapper;
@Autowired
CapacityMapper capacityMapper;
@Autowired
InMapper inMapper;
@Autowired
OutMapper outMapper;
@Autowired
RainMapper rainMapper;
@Async
@Scheduled(fixedRate = 300000)
public Integer updateGateNum() throws ParseException {
List<String> userId = new ArrayList<String>();
int rows = 0;
List<List<TWLHydVo>> rainMinute;
List<List<TWLHydVo>> rainHour = null;
userId.add("0721141130");
DateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date dayend1;
dayend1 = new Date();
Date today = new Date();//获取今天的日期
Calendar c = Calendar.getInstance();
c.setTime(today);
c.add(Calendar.DAY_OF_MONTH, -1);
Date yesterday = c.getTime();//这是昨天
c.add(Calendar.DAY_OF_MONTH, -2);
Date Beforeyesterday = c.getTime();
String str_dayend1 = format1.format(dayend1);
String str_dayend2 = format1.format(yesterday);
String str_dayend3 = format1.format(Beforeyesterday);
int hour = dayend1.getHours();
if (hour >= 8) {
rainMinute = itwlHydService.listbyMinute(str_dayend1, userId);
rainHour = itwlHydService.listbyHour2(str_dayend2, userId);
} else {
rainMinute = itwlHydService.listbyMinute(str_dayend2, userId);
rainHour = itwlHydService.listbyHour2(str_dayend3, userId);
}
if (rainMinute.get(0).get(0).getRz() == null) {
rainMinute.get(0).get(0).setRz(rainHour.get(23).get(0).getRz());
}
for (int i = 1; i < rainMinute.size(); i++) {
if (rainMinute.get(i).get(0).getRz() == null) {
rainMinute.get(i).get(0).setRz(rainMinute.get(i - 1).get(0).getRz());
}
}
int id = 0;
for (int i = 0; i < rainMinute.size(); i++) {
id++;
gateRoomMapper.updateNum(rainMinute.get(i).get(0).getRz(), id);
rows++;
}
return rows;
}
}
4.DQL编程控制(wrapper)
类似于ORM模型
1.查询格式(wrapper)
--- ApplicationTests.java文件
@Test
void testGetByPage(){
// 方法一:按条件查询
QueryWrapper qw = new QueryWrapper();
qw.lt("age",18);
List<User> userList = userDao.selectList(qw);
// 方法二:lambda格式条件查询
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.lambda().lt(User::getAge,18);
List<User> userList = userDao.selectList(qw);
// 方法三:lambda格式条件查询2(常用)
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge,18)
List<User> userList = userDao.selectList(lqw);
}
2.查询投影(where)
--- ApplicationTests.java文件
@Test
void testGetByPage(){
UserQuery uq = new UserQuery();
uq.setAge(20);
uq.setAge2(30);
// 方式一
QueryWrapper qw = new QueryWrapper();
qw.select("id","name","age")
qw.select("count(*) as count")
qw.groupBy("tel")
// 方式二
LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<User>();
qw.select(User::getId,User::getName,User::getAge)
// 范围查询:大于gt,小于lt,等于eq , 大于等于le, 小于等于ge
LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<User>();
qw.eq(User.getUsername,'admin').eq(User.getPassword,"admin");
qw.between(User::getAge,10,20);
// 或查询匹配
lqw.gt(User::getAge,30).or().lt(User::getAge,10);
// 模糊匹配
qs.like(User::getName,"admin") // %admin%
qs.likeLeft(User::getName,"admin") // 左侧是% %admin
}
3.限制字段空值(if)
--- ApplicationTests.java文件
@Test
void testGetByPage(){
UserQuery uq = new UserQuery();
uq.setAge(20);
uq.setAge2(30);
LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<User>();
// 先判定第一个参数是否为true,如果为true,则连接当前条件
qw.gt(null != uq.getAge2(), User::getAge, 10);
}
5.SSM的功能模块
1.Hash密码加密
--- pol.xml坐标依赖
<!--shiro 密码加密模块,-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.9.1</version>
</dependency>
--- controller中使用
// 加密密码
String encrypt_pwd = new Md5Hash(pwd, "salt", 2).toString();
// 解密信息:再次输入密码加密,如果生成加密信息的相同,则原密码也相同
String encrypt_pwd2 = new Md5Hash(pwd, "salt", 2).toString();
if(encrypt_pwd2.equals(encrypt_pwd)){ ... }
2.JWT认证功能
--- pol.xml坐标依赖
<!-- jsonwebtoken 模块支持 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
--- common层
package com.webserve.util;
import io.jsonwebtoken.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.UUID;
public class JwtUtil {
private static long time=1000*60*60*24*15;
private static String signature="abc123456";
// 创建token(静态方法,可以通过类名调用 )
public static String createToken(Integer nickname){
JwtBuilder jwtBuilder= Jwts.builder();
String jwtToken=jwtBuilder
.setHeaderParam("typ","JWT")
.setHeaderParam("alg","HS256")
.claim("user_id",nickname)
.setSubject("wisdom_reservoir")
.setExpiration(new Date(System.currentTimeMillis()+time))
.setId(UUID.randomUUID().toString())
.signWith(SignatureAlgorithm.HS256,signature)
.compact();
return jwtToken;
}
// 解析token数据(静态方法,可以通过类名调用 )
// String token = request.getHeader("token");
public static Integer parseToken(String token) throws Exception {
Jws<Claims> claimsJws = null;
try {
claimsJws=Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
} catch (Exception e) {
return -1;
}
Claims claims=claimsJws.getBody();
Integer user_id= (Integer) claims.get("user_id");
return user_id;
}
}
--- interceptor层
package com.webserve.interceptor;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.webserve.domain.Code;
import com.webserve.entity.User_msg;
import com.webserve.exception.BusinessException;
import com.webserve.service.IUser_msgService;
import com.webserve.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
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;
import java.util.List;
@Component
public class ProjectInterceptor implements HandlerInterceptor {
@Autowired
private IUser_msgService iUser_msgService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// String name = request.getParameter("name");
// HandlerMethod hm = (HandlerMethod)handler;
// hm.getMethod()可以操作原始对象执行的方法。request 可以拿到请求的参数
//
// // false 表示终止原始操作。tue表示继续执行原始操作
// // 如果认证成功,则放行,反之禁止通行。
// System.out.println("这是请求前拦截。。。" );
String token = request.getHeader("token");
if(token == null){
if(true)
throw new BusinessException("访问失败!请输入认证token!", Code.BUSINESS_AUTH_ERR);
return false;
}
try{
Integer user_id = JwtUtil.parseToken(token);
// 解析token是否有效
if(user_id == -1){
throw new Exception("认证失败!请输入有效令牌!");
}else{
// 判断token是否存在于数据库中
LambdaQueryWrapper<User_msg> lqw = new LambdaQueryWrapper<>();
lqw.eq(User_msg::getToken,token);
lqw.eq(User_msg::getId,user_id);
List<User_msg> userlist = iUser_msgService.list(lqw);
if(userlist == null || userlist.size() == 0){
throw new Exception("认证失败!当前帐号没有登录");
}else{
return true;
}
}
}catch (Exception e){
String msg = e.toString().replace("java.lang.Exception: ","");
System.out.println("解析token失败!"+msg);
if(true)
throw new BusinessException(msg, Code.BUSINESS_AUTH_ERR);
return false;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// System.out.println("这是请求后拦截。。。");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// System.out.println("这是请求结束后拦截。。。");
}
}
--- config层
package com.webserve.config;
import com.webserve.interceptor.ProjectInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration // 声明配置文件
// springboot项目不要开启@EnableWebMvc注解,否则中文乱码
//@EnableWebMvc
public class SpringMvcSupport implements WebMvcConfigurer {
@Autowired
private ProjectInterceptor projectInterceptor;
// // 过滤器配置
// @Override
// public void addResourceHandlers(ResourceHandlerRegistry registry) {
// registry.addResourceHandler("/static/**").addResourceLocations("/static/");
// registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
// }
// 拦截器配置
// 注入拦截器,对指定请求路由拦截
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns(
"/api/admin/logout", "/api/admin/unregister",
"/api/home/*","/api/admin/*"
);
}
}
--- controller层
// 创建token
String token = JwtUtil.createToken(user.getNickname())
// 解析token
Integer user_id = JwtUtil.parseToken(token)
3.Session会话登录
--- common层
package com.ecut.sys.common;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public class WebUtils {
/**
* 得到request
* @return
*/
// 只要是HttpServletRequest对象,就可以获取请求对象,设置session
public static HttpServletRequest getRequest(){
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
// 设置session的值
request.getSession().setAttribute("user","abcd")
// 获取session的值
request.getSession().getAttribute("user")
return request;
}
}
--- controller层
// 设置session的值
request.getSession().setAttribute("user",user_obj)
// 获取session的值
request.getSession().getAttribute("user")
// 移除当前这个会话session
request.getSession().removeAttribute("user");
--- templates模板输出
// Thymeleaf通过内置对象直接获取
th:text="${session.userinfo}"
6.SSM的模板语法
${…} 变量表达式,Variable Expressions
@{…} 链接表达式,Link URL Expressions, 注意,如果找不到资源,就会报错
~{…} 代码块表达式,Fragment Expressions
*{…} 选择变量表达式,Selection Variable Expressions
#{…} 消息表达式,Message Expressions
1.th:-数据插值
1.th:text 变量插值
<span th:text="${name}"> </span>
<p th:text="2020+1"></p>
<p th:text="1 > 2 ? '是' : '否'"></p>
2.th:x属性插值
<input th:value="${username}">百度一下你就知道</a>
<a th:href="@{https://www.baidu.com/}">百度一下你就知道</a>
<script th:src="@{/js/jquery-3.1.1.min.js}"></script>
2.1 th:attr属性插值
<a th:attr="href=@{https://www.baidu.com/}">百度一下你就知道</a>
2.th:each循环遍历
<div th:each="item:${goodsList}">
<span th:text="${item.name}"></span>
<span th:text="${item.price}"></span>
</div>
3.th:if条件判断
// th:if 条件判断,类似的有th:switch,th:case,优先级仅次于th:each, 其中#strings是变量表达式的内置方法
<p th:text="${thIf}" th:if="${not #strings.isEmpty(thIf)}"></p>
4.th:replace替换引入
th:replace:将代码块片段 “整个” 替换使用了th:replace的HTML标签中,常用
th:insert:将代码块片段 “整个” 插入到使用了th:insert的HTML标签中,
th:include:将代码块片段 “包含的内容” 插入到使用了th:include的HTML标签中,替换原有的内容
注意:
- 建议使用x-前缀封装代码块, 然后使用 引入
- 这个模板语法,会先构建html结构,再解析html结构里面的变量。
1. public/header.html
<body>
<header id="header" th:fragment="public-header">
<p>this is header </header>
</header>
</body>
2.content.html
<body>
<!-- 替换当前标签,并向header传递activeUrl实参 -->
<x-header th:replace="~{public/header::public-header(activeUrl='employees')}"></x-header>
<div> this is body</div>
</body>
3.public/component.html
<x-component th:fragment="public-component">
<!-- Bootstrap 的 JS 和 CSS 文件 -->
<link rel="stylesheet" th:href="@{/lib/ca-bootstrap-4.6.1/css/ca-bootstrap.css}" />
<script th:src="@{/lib/ca-bootstrap-4.6.1/js/ca-bootstrap.js}" ></script>
</x-component>
4.index.html
<head>
...
<x-component th:replace="~{public/component::public-component}" ></x-component>
</head>
5.redirect:重定向
@PostMapping("/auth/doRegister")
public ModelAndView doRegister( HttpServletRequest request){
// 重定向到/login登录页
return new ModelAndView("redirect:/login");
// 携带参数重定向到 /login登录页, 结果是 /login?successMsg=注册成功!请登录
return new ModelAndView("redirect:/login","successMsg","注册成功!请登录");
}
6.使用内置方法
1.基本语法
一、strings:字符串格式化方法,常用的Java方法它都有。比如:equals,equalsIgnoreCase,length,trim,toUpperCase,toLowerCase,indexOf,substring,replace,startsWith,endsWith,contains,containsIgnoreCase等
二、numbers:数值格式化方法,常用的方法有:formatDecimal等
三、bools:布尔方法,常用的方法有:isTrue,isFalse等
四、arrays:数组方法,常用的方法有:toArray,length,isEmpty,contains,containsAll等
五、lists,sets:集合方法,常用的方法有:toList,size,isEmpty,contains,containsAll,sort等
六、maps:对象方法,常用的方法有:size,isEmpty,containsKey,containsValue等
七、dates:日期方法,常用的方法有:format,year,month,hour,createNow等
7.使用内置对象
1.基本语法
一、ctx :上下文对象。
二、vars :上下文变量。
三、locale:上下文的语言环境。
四、request:(仅在web上下文)的 HttpServletRequest 对象。
五、response:(仅在web上下文)的 HttpServletResponse 对象。
六、session:(仅在web上下文)的 HttpSession 对象。
七、servletContext:(仅在web上下文)的 ServletContext 对象
2.使用实例
// java 代码将用户名放在session中
session.setAttribute("userinfo",username);
// Thymeleaf通过内置对象直接获取
th:text="${session.userinfo}"
6、maven包管理工具
1.分模块开发
将原始模块按照功能拆分成若干个子模块,方便模块间的相互调用,接口共享
1.如果需要其它项目下的包,则可以在pol.xml添加坐标依赖,将对应的模块通过maven的install打包到本地仓库, 对当前项目的compile编译
<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven_03_projo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
2.依赖的管理
如果一个的依赖有共同部分,则删除其中的公共部分依赖,依赖可以进行传递。
1.依赖的传递性
直接依赖:在当前项目中通过依赖配置建立的依赖关系
间接依赖:被资源的资源如果依赖其他资源,当前项目间接依赖其他资源
2.依赖传递冲突问题
路径优先:当依赖中出现相同的资源时,层级越深,优先级越低,层级越浅,优先级越高
声明优先:当资源在相同层级被依赖时,配置顺序靠前的覆盖配置顺序靠后的
特殊优先:当同级配置了相同资源的不同版本,后配置的覆盖先配置的
在Maven工具栏有依赖层级展示功能。
3.可选依赖和排除依赖
可选依赖:隐藏当前工程所依赖的资源,隐藏后对应资源将不具有依赖的传递性。 即在对应的依赖中添加
<optional>true</optional>
排除依赖:隐藏当前资源的对应关系。主动断开依赖的资源 即在引用的对应依赖上,添加
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
....
</exclusion>
3.Maven的性质
1.聚合
聚合:将多个模块组织成一个整体,同时进行项目构建的过程称为聚合
聚合工程:通常是一个不具有业务功能的“空”工程(有且仅有一个pom文件)
作用:使用聚合工程可以将多个工程编组,通过对聚合工程进行构建,实现对所包含的模块进行同步构建
当工程中某个模块发生更新(变更)时,必须保障工程中与已更新模块关联的模块同步更新,此时可以使用聚合工程来解决批量模块同步构建的问题.
注意:聚合工程中所包含的模块在进行构建时会根据模块间的依赖关系设置构建顺序,与聚合工程中模块的配置书写位置无关参与聚合的工程无法向上感知是否参与聚合,只能向下配置哪些模块参与本工程的聚合
在当前工程的pol.xml全局添加以下代码
<packagin>pom</packaging>
<modules>
<module>../maven_ssm</module>
<module>../maven_pojo</module>
<module>../maven_dao</module>
</module>
2.继承(基于聚合功能)
概念︰继承描述的是两个工程间的关系,与java中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。
作用:简化配置,减少版本冲突
(1.继承所有依赖
在使用的子模块的pol.xml全局添加以下代码, 配置当前工程继承的parent工程
<parent>
<groupId>com.itheima</groupId>
<artifactId>maven_01_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../maven_01_pol.xml</relativePath>
</parent>
(2.可选择继承依赖
父模块的pol.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
子模块的pol.xml(如果有需要继承的模块)
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</denpendency>
(3.聚合与继承的区别
作用
聚合用于快速构建项目
继承用于快速配置
相同点:
聚合与继承的pom.xml文件打包方式均为pom,可以将两种关系制作到同一个pom文件中
聚合与继承均属于设计型模块,并无实际的模块内容
不同点:
聚合是在当前模块中配置关系,聚合可以感知到参与聚合的模块有哪些
继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己
3.属性
(1.设置当前pol变量
在pol.xml定义属性
<properties>
<spring.version>5.2.10.RELEASE</spring.version>
</properties>
然后可以在各spring版本中使用
<version>${spring.version}</version>
(2.设置resources中的变量
打包需要在目标模块的webapp/WEB-INF添加web.xml文件(可以为空文件)
在需要引入的文件中添加
<build>
<resources>
<resource>
<directory>../maven_02_ssm/src/main/resources</directory>
<filtering>ture</filtering>
</resource>
</resources>
</build>
目标文件(建议使用空的web.xml)
<!-- 项目需要添加的插件 -->
<build>
<plugins>
<!-- Tomcat插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8084</port>
<path>/</path>
</configuration>
</plugin>
<!-- war打包 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
(3.其它属性的作为变量
系统属性可以通过 mvn help:system查看
自定义属性 ${自定义属性名} ${spring.version}
内置属性 ${内置属性名} ${basedir} ${version}
Setting属性 ${setting.属性名} ${settings.localRepository}
Java系统属性 ${系统属性分类.系统属性名} ${user.home}
环境变量属性 ${env.环境变量属性名} ${env.JAVA_HOME}
4.版本管理
工程版本:
SNAPSHOT(快照版本)
项目开发过程中临时输出的版本,称为快照版本
快照版本会随着开发的进展不断更新
RELEASE(发布版本)
项目开发到进入阶段里程碑后,向团队外部发布较为稳定的版本,这种版本所对应的构件文件是稳定的即便进行功能的后续开发,也不会改变当前发布版本内容,这种版本称为发布版本
发布版本
alpha版、beta版、纯数字版
4.Maven高级配置
1.多环境开发
maven提供配置多种环境的设定,帮助开发者使用过程中快速切换环境
在maven工具栏找到指令运行按钮,输入命令 mvn install-P env_test
<!-- 配置多环境 -->
<profiles>
<!-- 开发环境-->
<profile>
<id>env_dep</id>
<properties>
<profile.active>dep</profile.active>
<jdbc.url>jdbc:mysql://127.1.xxxxx</jdbc.url>
</properties>
</profile>
<!-- 生产环境-->
<profile>
<id>env_pro</id>
<properties>
<profile.active>pro</profile.active>
<jdbc.url>jdbc:mysql://127.1.xxxxx</jdbc.url>
</properties>
<!-- 设置为默认启动环境 -->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!-- 测试环境-->
<profile>
<id>env_test</id>
<properties>
<profile.active>test</profile.active>
<jdbc.url>jdbc:mysql://127.1.xxxxx</jdbc.url>
</properties>
</profile>
</profiles>
2.跳过打包测试
在 maven工具栏中,可以点击全部跳过测试。
下面是指定模块跳过测试
<!-- 项目需要添加的插件 -->
<build>
<plugins>
<!-- 默认的test测试插件-->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<!-- 设置所有都跳过-->
<skipTests>true</skipTests>
<!-- 跳过部分模块的测试-->
<skipTests>false</skipTests>
<excludes>
<exclude>**/BookServiceTest.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
3.让resources的文件中识别profiles多环境变量(yml)
---pom.xml文件
<!-- 项目需要添加的插件 -->
<build>
<plugins>
<!-- maven插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<encoding>UTF-8</encoding>
<useDefaultDelemiters>true</useDefaultDelemiters>
</configuration>
</plugin>
</plugins>
</build>
---application.yml文件
spring:
profiles:
active:${profile.active}
5.私服仓库的开发
私服是一台独立的服务器,用于解决团队内部的资源共享与资源同步问题
Nexus ,Sonatype公司的一款maven私服产品
下载地址: https://help.sonatype.com/repomanager3/download
1.私服的简介
先用默认的帐号,密码登录
启动服务器(命令行启动)
nexus.exe / run nexuS
访问服务器(默认端口:8081)
http://localhost:8081
修改基础配置信息
安装路径下etc目录中nexus-default.properties文件保存有nexus基础配置信息,例如默认访问端口
修改服务器运行配置信息
安装路径下bin目录中nexus.vmoptions文件保存有nexus服务器启动对应的配置信息,例如默认占用内存空间
公
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o7ID0Xpk-1662813226520)(C:/Users/Icy-yun/AppData/Roaming/Typora/typora-user-images/image-20220904112256851.png)]
仓库类别 英文名称 功能 联操作
宿主仓库 hosted 保存自主研发+第三方资源 上传
代理仓库 proxy 代理连接中央仓库 下载
仓库组 group 为仓库编组简化下载操作 下载
2.私服资源的上传和下载
1.在maven的setting.xml中配置以下内容
servers标签里面添加以下内容
<!--配置访问私服的权限-->
<server>
<id>itheima-release</id> 在localhost:88081里面自定义的仓库
<username>admin</username>
<password>admin</password>
</server>
<server>
<id>itheima-snapshot</id> 在localhost:88081里面自定义的仓库
<username>admin</username>
<password>admin</password>
</server>
mirrors标签里面添加以下内容
<!-- 私服的访问路径 -->
<mirror>
<id>maven-public</id>
<mirrorOf>*</mirrorOf>
<url>http://localhost:8081/repository/maven-public</url>
</mirror>
2.在项目的pol.xml里面配置
<!-- 配置当前工程保存在私服中的具体位置 -->
<distributionManagement>
<repository>
<id>itheima-release</id>
<url>http://localhost:8081/repository/itheima-release</url>
</repository>
<repository>
<id>itheima-snapshot</id>
<url>http://localhost:8081/repository/itheima-snapshot</url>
</repository>
</distributionManagement>
3.然后点击maven工具栏的deploy, 就会根据项目的 版本后缀,决定放在哪个仓库里面。
或者 mvn deploy
6.yaml文件格式
- yaml简介
- YAML (YAML Ain 't Markup Language) ,一种数据序列化格式
- 优点:容易阅读,容易与脚本语言交互,以数据为核心,重数据轻格式
- YAML文件扩展名 .yml(主流) .yaml
- yaml语法规则
- 大小写敏感
- 属性层级关系使用多行描述,每行结尾使用冒号结束
- 使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)属性值前面添加空格(属性名
- 与属性值之间使用冒号+空格作为分隔)
- #表示注释
- yaml数组数据
- 数组数据在数据书写位置的下方使用减号作为数据开始符号,每行书写一个数据,减号与数据间空格分隔
lession: springBoot
enterprise:
name:itcast
age:16
tel:400618400
subject:
- Java
- 前端
- 实体数据
- yaml数据的读取
- 采用. 连接对象和其下面的子属性, [] 获取数组数据
@Value("${lession}")
@Value("${server.port}")
@Value("${enterprise.subject[0]}")
- 项目环境自动装配
@Autowired
private Environment environment;
System.out.println(environment.getProperty("lession"))
System.out.println(environment.getProperty("server.port"))
System.out.println(environment.getProperty("enterprise.subject[1]"))
- 实体类绑定环境变量(常用)
--- java/com.itheima/domain
(1.Enterprise.java类
package com.itheima.domain;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
@ConfigurationProperties(prefix = "enterprise")
public class Enterprise {
private String name;
private Integer age;
private String tel;
private String[] subject;
@Override
public String toString() {
return "Enterprise{" +
"name='" + name + '\'' +
", age=" + age +
", tel='" + tel + '\'' +
", subject=" + Arrays.toString(subject) +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String[] getSubject() {
return subject;
}
public void setSubject(String[] subject) {
this.subject = subject;
}
}
-- 控制器访问
@Autowired
private Enterprise enterprise;
System.out.println(enterprise.lession)
--- pom.xml配置文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
7.Jar包的使用
1.打包成jar包
在maven工具点击package, 就会把打包成jar包放在 项目下的 target/xxx.jar目录下。
注意点:
- 排除中文的影响 ,在idea中配置utf-8的encoding
- 排除上一次的影响,先点击clean
2.运行jar包
在任意含有JVM环境下执行以下命令:
只要不关闭控制台,就会保持开启SpringBoot服务,即可以正常访问接口。
命令: java -jar xxx.jar
3.调用多环境配置
带参数启动jar包,并且可以后面追加配置
优先级顺序,可以查看官网文档(向下方向是 优先级从低指向高):Spring-boot 核心功能
注意:pol.xml 的优先级高于SpringBoot的多环境配置。
(1.命令行参数
命令:java -jar xxx.jar --spring.profiles.active=test
命令:java -jar xxx.jar --server.port=8082
(2.配置文件参数
SpringBoot中4级配置文件
1级: file : config/ application.yml【最高】
2级: file : application.yml
3级: classpath: config/ application.yml
4级: classpath: application.yml【最低】
注意: file表示运行的打包后的jar文件的同级目录, classpath 表示com.itheima包下的子 文件
4.替换默认服务器
使用jetty替换tomcat服务器
排除tomcat依赖,添加新的依赖
<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>
5.jar运行插件支持
jar支持命令行启动需要依赖maven插件支持,请确认打包时是否具有SpringBoot对应的maven插件(spring-boot-maven-plugin插件)
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.itheima.Application</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
9.生成War包
1.在pol.xml添加配置,然后添加坐标依赖,插件支持
<!-- 打包成war包-->
<packaging>war</packaging>
<!-- war包的支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<build>
<plugins>
<!-- 提供war包的支持-->
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
2.再spring-boot启动类同级目录,添加ServletInitializer类
public class WebserveSpringbootapiApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(WebserveSpringbootapiApplication.class, args);
}
// 提供打包成war包的支持
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(WebserveSpringbootapiApplication.class);
}
}
3.执行maven命令打包
在maven工具点击package, 就会把打包成jar、war包放在 项目下的 target目录下。
(或者使用maven工具打包,注意idea的encoding编码和clean清空测试)
mvn clean package
8.Tomcat布署项目
1.使用默认8080端口(+ROOT根路径)
- 如果把生成的字节码文件放在ROOT根路径,则会使用默认的端口, 同时是唯一不添加前缀端口的项目。
- 浏览器访问: http://localhost:8080
- 浏览器访问: http://localhost:8080/test/index.html(webapp下的静态文件)
2.使用默认8080端口(+文件指定)
- 将war包JavaWebTest.war复制粘贴到apache-tomcat-8.5\webapps\ROOT\目录下
(如果放在ROOT目录下,注意文件夹名称不要与路径path冲突,因为成功运行会在同级生成路径前缀同名的字节码文件夹) - 配置apache-tomcat-8.5\conf\server.xml文件, 向Host标签里面添加war包路径,以及外加的前缀路径 “/QS_AP”
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- 水库后端 -->
<Context path="/QS_AP" docBase="C:\Apps\ComputerWorkStation\JavaEclipse\apache-tomcat-9.0.54\webapps\ROOT\reservoir_web-1.0-SNAPSHOT.war" reloadable="true"></Context>
</Host>
- 浏览器访问: http://localhost:8080/QS_AP
- 注意点:如果配置的文件找不到,tomcat会启动异常
3.使用war包内置端口(+自动扫描)
- 开启server.xml的端口,添加以下内容
<!-- 用户模块系统 -->
<Connector port="8081" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- 山口岩水库系统-->
<Connector port="8889" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
- 将war包重命名为My_USER_APP.war, 将其复制到tomcat/webapps目录下启动服务器即可访问,tomcat中运行war包
执行到这一步,如果控制台没有出现ERROR就打包OK了, - 浏览器访问: http://localhost:8081/My_USER_APP(目前来说,还是8080,8081,8089端口都能访问)
4.使用war包内置端口(+文件指定)
- 在配置完server.xml端口后,可以去替换前缀,并且可以自由指定文件路径
解释前缀问题:只有指定前缀,tomcat才会在webapp下生成可访问的字节码文件。 - 配置apache-tomcat-8.5\conf\server.xml文件, 向Host标签里面添加war包路径,以及前缀路径 “/”
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- 用户api测试 -->
<Context path="/My_USER_APP" docBase="C:\Apps\ComputerWorkStation\JavaEclipse\apache-tomcat-9.0.54\webapps\MY_USER_APP_WAR\My_USER_APP.war" reloadable="true"></Context>
</Host>
- 浏览器访问: http://localhost:8081/My_USER_APP(目前来说,还是8080,8081,8089端口都能访问)
4.可能出现的异常
(1.tomcat日志编码
- 如果tomcat日志中文异常,可以在config\loggin.properties配置编码
java.util.logging.ConsoleHandler.encoding = UTF-8