Spring5
1、简介
1.2、优点
- Spring 是一个开源的免费框架
- Spring 是一个轻量级的、非入侵式的框架
- 控制反转(IOC),面向切面编程
- 支持事务处理,对框架整合的支持
2、IOC
1、IOC 概念
- 控制反转,把创建对象创建与对象调用过程,交给 Spring 进行管理
- 目的降低耦合度、
2、IOC 底层的原理
- xml 解析、工程模式、反射
3、IOC 创建对象的方式
-
使用无参构造创建对象,默认!
-
假设我们要使用有参构造创建对象
-
下标赋值
<!-- 第一种下标赋值--> <bean id="user" class="com.kuang.pojo.User"> <!-- 无参构造--> <!-- <property name="name" value="longtime no seee"/>--> <constructor-arg index="0" value="你好"/>
-
<!-- 第二种不建议使用--> <bean id="user" class="com.kuang.pojo.User"> <constructor-arg type="java.lang.String" value="你好"/> </bean>
-
<bean id="user" class="com.kuang.pojo.User"> <constructor-arg name="name" value="秦疆"/> </bean>
-
4、Spring 的配置
4.1、别名
<!-- 如果添加了别名,我们也可以使用别名来获取这个类-->
<alias name="user" alias="userNew"/>
4.2、 Bean 的配置
<bean id="user" class="com.kuang.pojo.User" name="userNew,res"> 或者直接在 name 内放别名
<constructor-arg name="name" value="秦疆"/>
</bean>
4.3、 import
import 一般用于团队开发使用,他可以将多个配置文件导入合并为一个
<import resource="bean1.xml"/>
5.依赖注入
5.1 构造器注入
也就是直接使用 有参无参构造
5.2 Set 方式注入
- 依赖 bean 对象的创建依赖于容器
- 注入 bean 对象的属性,由容器来注入
- 复杂类型
package com.kuang.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
- 真实测试对象
package com.kuang.pojo;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbys() {
return hobbys;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
}
- beans.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">
<bean id="user" class="com.kuang.pojo.User" name="userNew">
<constructor-arg name="name" value="秦疆"/>
</bean>
<bean id="student" class="com.kuang.pojo.Student">
<!-- 第一种普通值注入直接使用 value 就可以了-->
<property name="name" value="秦疆"/>
<!-- 第二种 ref 注入-->
<property name="address" ref="address"/>
<!-- 数组-->
<property name="books">
<array>
<value>红楼梦</value>
<value>水浒传</value>
<value>三国演义</value>
<value>西游记</value>
</array>
</property>
<!-- list-->
<property name="hobbys">
<list>
<value>听歌</value>
<value>敲代码</value>
<value>看电影</value>
</list>
</property>
<!-- map-->
<property name="card">
<map>
<entry key="身份证" value="432234234234"/>
<entry key="银行卡" value="43234223454"/>
</map>
</property>
<!-- set-->
<property name="games">
<set>
<value>LoL</value>
<value>coc</value>
<value>BOB</value>
</set>
</property>
<!-- null-->
<property name="wife">
<null/>
</property>
<!-- properties-->
<property name="info">
<props>
<prop key="学号">2001030402</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
<bean id="address" class="com.kuang.pojo.Address">
</bean>
</beans>
- 测试类
public class Address {
private String address;
public String getAddress() {
return address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
public void setAddress(String address) {
this.address = address;
}
}
5.3 其他方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- p 命名空间注入,可以直接注入属性:property-->
<bean id="user" class="com.kuang.pojo.User" p:name="爱秋">
</bean>
<!-- c 命名空间注入,通过构造器注入:construct-args-->
<bean id="user2" class="com.kuang.pojo.User" c:name="爱人"></bean>
</beans>
5.4 bean 的作用域
Scope | Description |
---|---|
singleton | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. |
prototype | Scopes a single bean definition to any number of object instances. |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext . |
session | Scopes a single bean definition to the lifecycle of an HTTP Session . Only valid in the context of a web-aware Spring ApplicationContext . |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket . Only valid in the context of a web-aware Spring ApplicationContext . |
-
单例模式 (Spring)
<bean id="user" class="com.kuang.pojo.User" p:name="爱秋" scope="singleton"> </bean>
-
原型模式:每次从容器中 get 的时候都会产生一个新对象
-
其余的 request、session、application 这些都只能在 web 开发中使用
6、bean 自动装配
- 自动装配是 Spring 满足 bean 依赖的一种方式
- Spring 会在上下文中自动寻找并自动给 bean 装配属性
在 spring 中有三种装配的方式
- 在xml 中配置
- 在java 中显示配置
- 隐匿的自动装配 bean
xml 中配置
<!-- byname 会自动在容器的上下文中查找和对象set 后面值对象的 bean id
byType 会自动在容器上下文中找自己对象属性相同的 bean
-->
<bean id="people" class="com.kuang.pojo.People" autowire="byName">
<property name="name" value="greed"></property>
<!-- <property name="cat" ref="cat"/>-->
<!-- <property name="dog" ref="dog"/>-->
</bean>
小结
- byname 的时候,需要保证所有的 bean 的 id 唯一,并且这个 bean 需要和自动注入的属性的 set 方法完全一致
- byType 的时候,需要保证所有的 bean 的 class 唯一,并且这个 bean 需要和自动注入的属性的类型一致
使用注解实现自动
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/context/spring-aop.xsd
">
<context:annotation-config/>
</beans>
@Atuowired 直接在属性上使用即可,也可以在 set 方式上使用
使用 Atuwired 我们可以不用编写 set 方法,前提是你这个自动装配的属性在 IOC 容器中存在且符合名称 byname
科普:
@Nullable 字段标记了这个注解,说明这个字段可以为 true
public @interface Autowired {
boolean required() default true;
}
如果 @Autowired 自动装配比较复杂,自动装配无法通过一个[@Autowired]完成的时候,我们可以通过 @Qualifier(value=xxx)去配置,指定一个唯一的bean 对象注入
@Resource(name = "dog")
private Dog dog;
@Autowired 与 @Resource 区别
- 都是用来自动装配的,都可以放在属性字段上
- @ Autowired 通过 byType
- @ Resource 默认通过 byName 的方式实现,如果没有查找到就通过 byType
- @ Component :组件放在类上说明这个类被 Spring 管理了,就是 bean
7、使用注解开发
在spring 4 后使用 注解开发,必须要保证 aop 包的导入
使用注解还需要context 约束,增加注解的支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/context/spring-aop.xsd
">
<context:annotation-config/>
</beans>
-
bean
-
属性如何注入
-
@Value("有趣") //放在属性上或者放在 set方法上实现注解,如果是复杂类型推荐使用写配置文件 public void setName(String name) { this.name = name; }
-
-
衍生的注解
@component 有几个衍生注解,我们 web 开发中使用 mvc 三层架构
- dao @Repository
- service @Service
- controller @Controller
- 这四个注解的功能都是一样的,都是将代表某个类被装配到 Spring 容器中
-
自动装配
-
作用域
@Component @Scope("prototype") public class People { // 如果定义了 required = false 说明这个对象可以为 null ,否则不可以为空 @Autowired(required = false) private Cat cat; @Resource(name = "dog") private Dog dog; }
-
小结
- xml 用来管理 bean
- 注解来完成属性的注入
- 我们在开发的时候只需要注意一个问题,想让注解生效就必须开启对注解的支持
8、使用 Java 的方式配置 Spring
我们现在不要 spring 配置了,全权交给 java 来管理,在 spring4 后推荐使用这种方式来做
@Bean 可以用于通过方法获取数据库连接池的对象 Connection
@Bean 只写在方法上,返回的是一个对象,但一般不获取已经在容器中存在的对象
配置类
//这个也会被 Spring 托管,注册到容器中,因为它本身也是一个 Component
// @Config代表这是一个配置类就和我们之前看到的 beans.xml一样的
@Configuration
@ComponentScan("com.kuang.pojo")
@Import(MyConfig2.class)
public class MyConfig {
// 注册一个bean 就相当于我们之前写的一个 bean 标签
// 这个方法的名字就是这个 bean 的 id
// 这个bean 的类型就是这个方法的返回值
@Bean
public User getUser(){
return new User();
}
}
配置类二
@Configuration
public class MyConfig2 {
}
测试类
public class Mytest {
public static void main(String[] args) {
// 如果完全使用配置类的方式来做我们只能使用AnnotationConfig 上下文来获取容器,通过配置类的对象来加载
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User getUser = (User) context.getBean("getUser");
System.out.println(getUser.getName());
}
}
9、代理模式
代码模式的分类
9.1、静态代理
角色分析:
- 抽象角色:一般使用接口或者抽象类
- 真实角色:被代码的角色
- 代理角色:代理真实角色,代理真实角色后我们一般会做一些附属操作
- 客户:访问代理对象的人
代码步骤
-
接口
package com.kuang.client.demo01; public interface Rent { public void rent(); }
-
真实角色
public class Landlord implements Rent { public void rent() { System.out.println("房东要出租房子"); } }
-
代理角色
public class Proxy implements Rent{ private Landlord landlord; public Proxy() { } public Proxy(Landlord landlord) { this.landlord = landlord; } public void rent() { landlord.rent(); seeHouse(); contract(); charge(); } public static void seeHouse(){ System.out.println("中介带你看房"); } public static void contract(){ System.out.println("中介来签和同"); } public static void charge(){ System.out.println("中介收你房租"); } }
-
客户端访问角色
public class Client { public static void main(String[] args) { Landlord landlord = new Landlord(); Proxy proxy = new Proxy(landlord); proxy.rent(); } }
代理模式的好处
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共可以交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点
- 一个真实角色就会产生一个代理角色,代理量会翻倍~开发效率会变低
9.2、动态代理
-
动态代理和静态代理角色一样
-
动态代理的代理类是动态生成的,不是我们直接写好的
-
动态代理分为:基于接口的动态代理,基于类的动态代理
- 基于接口—JDK 动态代理
- 基于类:cglib
- Java 字节码实现: javasist
需要了解两个类:Proxy 代理,InvocationHandler 调用处理程序
//等会我们会用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
// 生成代理类
public Object getProxy(){
return Proxy.newProxyInstance
(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//处理代理实例并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
return result;
}
}
使用
public class Client {
public static void main(String[] args) {
// 真实角色
Landlord landlord = new Landlord();
// 代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
// 通过调用程序处理角色来处理我们要调用的对象
pih.setTarget(landlord);
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共可以交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
- 一个动态代理代理的是一个接口,一般对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了了同一个接口即可
10、 AOP
10.1、 什么是 AOP
AOP为(Aspect Oriented Programming)的缩写,是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。复用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高程序的可我不用性,同时提高了开发的效率。
10.2 使用Spring 实现AOP
[重点] 使用AOP ,需要导入一个依赖包!
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
方式一:使用Spring 的API 接口 [主要SpringAPI接口定义]
方式二:自定义来实现 AOP [主要是切面定义]
方式三:使用注解实现
12、整合Mybatis
步骤:
- 导入相关 jar 包
- junit
- mybatis
- mysql 数据库
- spring 相关的
- aop 织入
- mybatis-spring[new]
- 编写配置文件
- 测试
pom.xml 导入包与静态资源过滤
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
mybatis-config.xml配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 引入外部文件-->
<properties resource="db.properties"/>
<settings>
<!-- 显示的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
<setting name="logImpl" value="LOG4J"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.kuang.dao"/>
</mappers>
</configuration>
dp.properties配置
driver = com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
Mybatis-Spring
- 编写数据源配置
- SqlSessionFactory
- SqlSessionTemplate
- 需要下给接口加实现类
- 将自己写的实现类注册到sql 中
- 测试使用
13、声明式事务
1、 回顾事务
- 要么都成功,要么都失败
- 事务在项目开发中十分地重要,涉及数据一致的问题
- 确保完整性与一致性
ACID 原则
- 原子性
- 一致性
- 隔离性
- 多个事务操作一个数据库防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生什么问题,结果都不会被影响,被永久地写入到存储器中
2、spring 中的事务管理
- 声明性事务:AOP
- 编程性事务:需要在代码中进行事务的管理
<!-- 配置声明性事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>
<!-- 结合AOP 实现事务的织入-->
<!-- 配置事务的通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 给哪些方法配置事务-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.kuang.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
思考:
为什么需要事务?
- 如果不配置事务,可能存在提交不一致的情况
- 如果不在Spring 中配置声明性事务,我们就需要在代码中手动配置事务
- 事务在项目的开发中十分的重要,涉及到数据的一致性和完整性
Mybatis
环境
- JDK 1.8
- Mysql 5.7.19
- maven 3.8.3
- IDEA
回顾:
- JDBC
- Mysql
- Java 基础
- Maven
- Junit
1、简介
1.1、什么是Mybatis
- MyBatis 是一款优秀的持久层框架,
- 它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了[google code](https://baike.baidu.com/item/google code/2346604),并且改名为MyBatis 。
- 2013年11月迁移到Github。
如何获得Mybatis ?
-
maven 仓库
-
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency>
-
Github:https://github.com/mybatis/mybatis-3
-
GIthub 中文注释:https://github.com/tuguangquan/mybatis
-
中文文档:https://mybatis.org/mybatis-3/zh/index.html
1.2、持久化
数据持久化,持久化就是将程序的数据在持久状态与瞬时状态转换的过程
- 数据库(jdbc)、IO 文件持久化
1.3、持久层
Dao层,service 层,Controller 层
- 完成持久化工作的代码块
- 层的界限十分明显
1.4 为什么需要Mybatis
-
帮助程序猿将数据存入到数据库中
-
方便
-
传统的JDBC 太复杂了,需要简化,框架
-
技术没有高低之分
Spring SpringMVC SpringBoot
2、第一个 Mybatis 程序
思路:搭建环境->导入Mybatis—>编写代码—>测试!
2.1、搭建数据库
新建项目
-
新建一个普通的maven项目
-
导入Maven 依赖
<dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> </dependencies>
2.2、创建一个模块
-
编写 mybatis的核心配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useSSL=true&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration>
-
编写 mybatis 的工具类
public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { // 使用 Mybatis 第一步 String resource = "mybatis-config.xml"; InputStream inputStream = null; try { inputStream = Resources.getResourceAsStream(resource); } catch (IOException e) { e.printStackTrace(); } sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } // 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。 // SqlSession 提供了在数据库执行 org.apache.ibatis.jdbc.SQL 命令所需的所有方法。 // 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句 public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
2.3、编写代码
-
实体类
-
// 实体类 public class User { private String id; private String name; private String pwd; public User() { } public User(String id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } @Override public String toString() { return "User{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
-
-
Dao 接口
-
public interface UserDao { List<User> getUserList(); }
-
-
接口实现类由原来的 impl 转变为 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.kuang.dao.UserDao"> <select id="getUserList" resultType="com.kuang.pojo.User"> select * from mybatis.user; </select> </mapper>
-
2.4、测试
注意点
BindingException
MapperRegistry是什么?
核心配置文件中注册 mappers
-
junit 测试
public class UserDaoTest { @Test public void test(){ // 第一步获得 sqlSession SqlSession sqlSession = MybatisUtils.getSqlSession(); // 方式一:getMapper UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> userList = mapper.getUserList(); for (User user : userList) { System.out.println(user); } // 关闭 sqlSession sqlSession.close(); } }
-
会遇到的问题
- 配置文件没有注册
- 绑定接口错误
- 方法名不对
- 返回类型不对
- Maven 导出资源问题
3、CRUD
1. namespace
namespace 中的包名要和Dao/mapper 接口的包名一致!
2、select
选择查询语句:
- id: 就是对应的 namespace 中的方法名
- resultType:sql 语句执行的返回值!
- parameterType:参数类型
总之就几步
-
编写接口
// 查询全部用户 List<User> getUserList();
-
编写mybatis-config.xml
<select id="getUserList" resultType="com.kuang.pojo.User"> select * from mybatis.user; </select>
-
测试
// 增删改需要提交事务 @Test public void addUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.addUser(new User("4", "张三", "greed")); sqlSession.commit(); sqlSession.close(); }
注意点需要提交事务
3、分析错误
- 标签不要错误
- resource 绑定 mapper ,需要使用路径
- 程序配置文件必须符合规范
4、万能Map
假设我们实体类中,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map
Mapper
User addUser2(Map<String,Object> map);
Mapper.xml
<insert id="addUser2" parameterType="Map">
insert into mybatis.user (id, name, pwd) values(#{userid},#{userName},#{passWord});
</insert>
test
@Test
public void addUser2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> HashMap = new HashMap<String, Object>();
HashMap.put("userid","6");
HashMap.put("userName","上午");
HashMap.put("passWord","happy");
mapper.addUser2(HashMap);
sqlSession.commit();
sqlSession.close();
}
Map 传递参数,直接在sql 中取出 key 即可
对象传递参数,直接在 sql 中取对象的属性即可
多个参数用 Map,或者注解。
5、思考题
模糊查询怎么写?
-
Java 代码执行的时候,传递通配符 % %
List<User> userLike = mapper.getUserLike("%余%");
-
sql 拼接中使用通配符
select * from mybatis.user where name like "%"#{value}"%"
4、配置解析
4.1、核心配置文件
- mybatis-config.xml
- MyBatis 的配置文件包含了会沉淀影响 MyBatis 行为的调用与属性信息
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
4.2、环境配置
MyBatis 可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器
Mybatis 默认的事务管理器就是 JDBC 连接池 POOLED
4.3、属性(properties)
我们可以通过 properties 属性来实现引用配置文件
编写配置文件
resources->db.properties
driver = com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
引入外部文件,需要注意的是 properties 必须放丰configuration 最上面
<!-- 引入外部文件-->
<properties resource="db.properties"/>
- 可以直接引入外部文件
- 可以在其中增加一些属性配置
- 默认是先读取外部的配置文件
4.4 类型别名(type)
- 类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
给实体类起别名
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
也可以指定一个包名,MyBatis 会在包名中搜索需要的 java bean
在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author
的别名为 author
;若有注解,则别名为其注解值。
在实体类比较少的时候,使用第一种方式,在实体类比较多的时候使用第二种方式
第一种可以 diy 别名,第二种则不行,如果需要则可以在实体上增加注解
@Alias("user")
public class User{}
4.5、设置
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
4.6、其它配置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- mybatis-generator-core
- mybatis-plus
- 通用 mapper
4.7 映射器
MapperRegistry:注册绑定我们的Mapper 文件
方式1、
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
</mappers>
方式2、使用class类
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper 文件必须要在同一个包下
方式3、使用扫描包进行绑定
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper 文件必须要在同一个包下
4.8 生命周期与作用域
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
结果映射
当数据库中的字段名与bean 内的名字不一样的时候可以这样配置
<resultMap id="UserMapper" type="user">
<result column="pwd" property="password"/>
</resultMap>
resultMap
元素是 MyBatis 中最重要最强大的元素。- ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
- 这就是
ResultMap
的优秀之处——你完全可以不用显式地配置它们 - 如果这个世界总是这么简单就好了。
6、日志
6.1、日志工厂
如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的工具!
曾经:sout、debug
现在:日志工厂
- SLF4J
- LOG4J [掌握]
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING [掌握]
- NO_LOGGING
在Mybatis 中具体使用哪一个日志由我们的设定决定
在mybatis 核心配置文件中进行配置
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
-
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
-
我们也可以控制每一条日志的输出格式;
-
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
-
最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
- 先导入 Log4j 的包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- log4j.properties
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=【%c】-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=【%p】【%d{yy-MM-dd}】【%c】%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
- 配置 log4j 为日志的实现
<settings>
<!-- 标准的日志工厂-->
<setting name="logImpl" value="LOG4J"/>
</settings>
简单使用
-
在要使用 log4j 的类中导入包 import org.apache.log4j.Logger;
-
日志对象,参数为当前类的 class
static Logger logger = Logger.getLogger(UserMapperTest.class);
-
如何解决idea 打不开 .log 文件 https://blog.csdn.net/Doraemon_Nobita/article/details/115953238
logger.info("进入log4j日志"); logger.debug("进入log4j日志"); logger.error("进入log4j日志");
7、分页
思考:为什么要分页
- 减少数据的处理量
7.1、使用limit分页
select * from user limit startIndex,pagesize;
接口
List<User> getUserByLimit(Map<String,Integer> map);
xml
<select id="getUserByLimit" parameterType="map" resultType="user">
select * from mybatis.user limit #{startIndex},#{pageSize};
</select>
测试
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userByLimit = mapper.getUserByLimit(new HashMap<String, Integer>() {{
put("startIndex", 1);
put("pageSize", 2);
}});
for (User user : userByLimit) {
System.out.println(user);
}
sqlSession.close();
7.2、RowBounds分页
即逻辑分页,逻辑分页利用游标分页,好处是所有数据库都统一,坏处就是效率低。
不在使用sql 来实现分页
-
接口
-
List<User> getUserByRowBounds();
-
-
mapper.xml
<select id="getUserByRowBounds" resultType="user"> select * from mybatis.user; </select>
-
测试
@Test public void getUserByRowBounds(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); // 通过 RowBounds实现 RowBounds rowBounds = new RowBounds(1, 2); // 通过 java 代码来实现分页 List<User> userList = sqlSession.selectList("com.kuang.dao.UserMapper.getUserByRowBounds",null,rowBounds); for (User user : userList) { System.out.println(user); } sqlSession.close(); }
8、使用注解开发
8.1、示例
注解只能用 class 方式注册
- 注解在接口上实现
@Select("select * from user")
-
需要在核心配置文件中编写接口
<mappers> <mapper class="com.kuang.dao.UserMapper"/> </mappers>
本质:反射机制实现
底层:动态代理
8.3、CRUD
我们可以在工具类创建的时候实现自动提交事务
return sqlSessionFactory.openSession(true);
方法存在多个参数,所有的参数前面必须加上 @Param(“id”)注解
注意我们必须将接口注册绑定到我们的核心配置文件内
关于@Param()注解
- 基本类型的参数或者String 类型需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话可以忽略,但建议都加上
#{}与${}区别
#{}可以防止sql 注入
9、Lombok
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
先在idea 里安装插件
然后再去 Maven 内进行导包
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
注解 | 作用 |
---|---|
@Data | 无参构造、有参构造、get、set、toString、hashcode、equals |
@AllArgsConstructor | 有参构造 |
@NoArgsConstructor | 无参构造 |
@EqualsAndHashCode | equals方法与 hashcode |
@ToString | toString 方法 |
10、多对一处理
10.1、测试环境搭建
- 导入 lombok
- 新建实体类
- 建立Mapper接口
- 建立Mapper.xml
- 在核心配置文件中进行绑定
- 测试查询
按照查询嵌套处理
<select id="getStudent" resultMap="StudentTeacher">
select * from mybatis.student;
</select>
<!-- 复杂的属性需要用 association 对象 或者 collection 集合-->
<resultMap id="StudentTeacher" type="Student">
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id=#{id};
</select>
按照结果嵌套处理
按结果嵌套处理
<!-- 按结果嵌套查询-->
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname,t.name tname,t.id tid from student s,teacher t where s.tid = t.id and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!-- javaType 指定属性的类型
集合中泛型信息我们使用 ofType 获取
-->
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
按照查询嵌套处理
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from teacher where id = #{tid}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from student where tid = #{tid}
</select>
小结
- 关联 -association [多对一]
- 集合 collection [一对多]
- javaType & ofType
- JavaType 用来指定实体类中属性的类型
- ofType 用来指定映射到 List或者集合中的元素类型,即泛型中的约束类型
面试必问
- Mysql 引擎
- InnoDB 底层原理
- 索引
- 索引优化
12、动态SQL
什么是动态 SQL:动态SQL 就是指根据不同的sql 生成不同的动态语句
创建一个基础工程
-
导包
-
编写配置文件
-
编写实体类
@Data public class Blog { private int id; private String title; private String author; private Date createTime; private int views; }
-
编写实体类对应 Mapper 接口和 Mapper.xml 文件
if
<select id="queryBlogIF" parameterType="map" resultType="blog">
select * from mybatis.blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
choose(when,otherwise)
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<choose>
<when test="title!=null">
title = #{title}
</when>
<when test="author!=null">
author=#{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
trim(where,set)
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title!=null">
title = #{title},
</if>
<if test="author!=null">
author = #{author}
</if>
</set>
where id = #{id}
</update>
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<choose>
<when test="title!=null">
title = #{title}
</when>
<when test="author!=null">
author=#{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
Foreach
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT * FROM POST P WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
final ArrayList<String> ids = new ArrayList();
ids.add("1");
ids.add("2");
List<Blog> ids1 = mapper.queryBlogForeach(new HashMap() {{
put("ids", ids);
}});
SQL 片段
来实现 sql 代码的复用
- 使用 sql 标签抽取公共的部分
- 在需要使用的地方使用 include 标签引用即可
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select id="queryBlogIF" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<include refid="if-title-author"></include>
</where>
</select>
注意事项
- 最好基于单表定义 SQL 片段!
- 不要存在 where 标签
13、缓存
1、什么是缓存 [ Cache ]?
-
存在内存中的临时数据。
-
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率, 解决了高并发系统的性能问题。
2、为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
3、什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据。
13.2、mybatis 缓存
- mybatis 包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存,缓存可以极大提升查询效率。
- mybatis 默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启
- 二级缓存需要手动开启和配置,它是基于 namespace 级别的缓存
- 为了提高扩展性,mybatis 定义了缓存接口 cache 我们可以通过实现 cache 接口来实现自定义二级缓存
缓存失效的情况
-
查询不同的东西
-
中间进行增删改
-
查询不同的 Mapper.xml
-
手动清理缓存
-
sqlSession.clearCache();
-
二级缓存
步骤:
-
开启全局缓存
<setting name="cacheEnabled" value="true"/>
-
在单个mapper.xml 中进行配置
<cache/> <select id="queryUsersById" resultType="user" useCache="false"> //也可以在标签上显示地选择是否使用缓存 select * from user where id = #{id}; </select>
使用缓存的 pojo 需要序列化即实现 Serializable 接口
当会话结束后他的结果会放在二级缓存中
正常的查询顺序是
二级缓存->一级缓存->数据库
保存的顺序是
一级缓存->二级缓存
Spring MVC
SpringMVC: springMVC的流程
spring:IOC,AOP
原理
@Component 组件
@Service service
@Controller controller
@Repository dao
数据处理
处理提交
-
提交的域名称和处理方法的参数名一致
@RequestMapping("/user") public class UserController { // localhost:8080/user/t1?name=xxx @GetMapping("t1") public String test1(@RequestParam("name") String name, Model model){ System.out.println("接收到前端"+name); // 1. 接收前端参数 model.addAttribute("msg",name); // 2. 将返回的结果传递给前端 return "test"; }
RequestParam 一定要写表明这个参数是前端的数据
-
前端发送的是一个对象
要求发的属性与对象的属性完全一样
http://localhost:8080/springmvc_04_controller_war_exploded/user/t2?name=wql&age=23&id=34
@GetMapping("t2") public String test2(User user){ System.out.println(user); return "test"; }
解决乱码问题
前端发送数据用 post 请求后端接收转发后字符成乱码
直接用 Spring 的过滤器来进行过滤 或者自己写个类来完成
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
别人写的类
public class GenericEncodingFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
// 转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
}
}
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
//是否编码的标记
private boolean hasEncode;
//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}
// 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
//取一个值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}
//取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}
返回json 格式字符串
需要导包 jackson
@RequestMapping("/j1")
@ResponseBody//它就不会走视图解析器,会直接返回一个字符串
public String json1() throws JsonProcessingException {
User user = new User(1,"郭经波01",18);
ObjectMapper mapper = new ObjectMapper();
String s = mapper.writeValueAsString(user);
return s;
}
乱码统一解决
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
restcontroller和controller区别是什么?
- 用Controller配合视图解析器才能返回到指定页面。在对应的方法上加上ResponseBody注解才能返回JSON,XML或自定义mediaType的内容到页面
- 不可以只用RestController注解Controller,因为这样会让Controller中的内容不能返回jsp页面,而且会直接返回Return里的内
- RestController相当于Controller和ResponseBod两者合并起来的作用。
工具类
public class JsonUtils {
public static String getJson(Object object){
return getJson(object,"yyy-MM-dd HH:mm:ss");
}
/**
* @param object 要转换成 json 格式的对象
* @param dateFormat Date 对象想转换的格式如果写为null 默认为 年-月-日 时:分:秒
* @return java.lang.String
* @author guojingbo
* @date 2021-10-15 18:22
*/
public static String getJson(Object object, String dateFormat) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
mapper.setDateFormat(simpleDateFormat);
try {
return mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}
fastJson 使用
public static void main(String[] args) {
//创建一个对象
User user1 = new User("秦疆1号", 3, "男");
User user2 = new User("秦疆2号", 3, "男");
User user3 = new User("秦疆3号", 3, "男");
User user4 = new User("秦疆4号", 3, "男");
List<User> list = new ArrayList<User>();
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
System.out.println("*******Java对象 转 JSON字符串*******");
String str1 = JSON.toJSONString(list);
System.out.println("JSON.toJSONString(list)==>"+str1);
String str2 = JSON.toJSONString(user1);
System.out.println("JSON.toJSONString(user1)==>"+str2);
System.out.println("\n****** JSON字符串 转 Java对象*******");
User jp_user1=JSON.parseObject(str2,User.class);
System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);
System.out.println("\n****** Java对象 转 JSON对象 ******");
JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name"));
System.out.println("\n****** JSON对象 转 Java对象 ******");
User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user);
}
拦截器
拦截器是 spring MVC 框架自己的,只有使用SpringMVC框架的工程师才可以使用
拦截器只会拦截访问的 controller 方法,像 jsp/html/css/image/js 是不会进行拦截的
springboot
springboot 是什么
SpringBoot是一个框架
一种全新的编程规范
简化了Spring众多框架中所需的大量且繁琐的配置文件
所以 SpringBoot是一个服务于框架的框架,服务范围是简化配置文件。
特点是:让文件配置变的相当简单、让应用部署变的简单
配件如何编写 yml
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "dog")
public class Dog {
private String name;
private int age;
private int weight;
}
name: qinjiang
student: {name: dfsa,age: 3} 对象写法
pets: [cat,dog,pig]
只需要加这样的几句就可以直接注入
松散绑定:是指 yaml中写last-name 和 lastName是一样的-后面的默认大写
结论:
-
配置yml和配置 properties 都可以获取值,推荐 yml
-
如果在某个业务中,只需要获取配置文件中的某个值,可以使用@value
-
如果专门写了一个 JavaBean 来和配置文件进行映射就直接使用@configurationProperties
如何使用对类的属性进行合法性验证
https://www.jianshu.com/p/5a99cacb3b63
需要导入包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "dog")
@Validated//开启数据校验
public class Dog {
@Email()
private String name;
private int age;
private int weight;
}
自动装配原理
@SpringBootApplication 说明这个是一个应用程序
@Configuration 说明这是一个配置
@Component 是一个组件
@EnableAutoConfiguration 自动配置
@AutoConfigurationPackage 自动配置包
@Import({Registrar.class}) 自动配置包注册
@Import({AutoConfigurationImportSelector.class}) 导入了选择器
SpringBoot启动的时候通过@EnableAutoConfiguration注解找到META-INF/spring.factories文件中的所有自动配置类,并对其加载,这些自动配置类都是以AutoConfiguration结尾来命名的。它实际上就是一个JavaConfig形式的IOC容器配置类,通过以Properties结尾命名的类中取得在全局配置文件中配置的属性
这些配置文件有的没有生效,需要导入对应的start 才有作用,核心注解@ConditionalOnClass:如果这里面的条件都满足才会生效
spring 所有的自动配置都在启动的时候扫描并加载:spring.factories
里面,但不一定生效,要判断是否生效,只要导入对应的 start ,与启动器,然后就会生效
- springboot 在启动的时候,从类路径下/MENT_INF/spring.factories获取指定的值
- 将这此自动配置的类导入容器,自动配置就会生效,帮我们进行配置
- 以前我们需要自动配置的东西,现在 spring 帮我们做了
- 解决方案和自动配置的东西都在 spring-boot-autoconfigure-2.5.5.jar 包下
- 它会把需要导入的组件,以类名的方式返回,这些组件就会被添加到容器
- 容器中也会存在非常多的 xxxAutoConfiguration 文件,就是这些类导入了这个场景所有的组件,并且自动配置 @Configuration
xxxAutoConfiguration 文件中有大量的 xxxproperties 文件这些类默认与 Application 绑定它里面的属性都是APPlication 中可以配置的属性
精髓
- SpringBoot 启动会加载大量的自动配置类
- 看看我们需要的功能有没有在 SpringBoot 默认写好的自动配置类当中
- 我们再来看这个自动配置了哪些组件
- 容器自动配置类添加组件的时候,会从 properties 类中获取以一些属性,我们只需要在配置文件中指定这些属性的值即可
xxxAutoConfiguration 自动配置类 给容器添加组件
xxxProperties 封装配置文件中的相关的属性
SpringBoot Web 开发
要解决的问题
- 导入静态资源
- 在resource 下面的 pulic 或者 resouce 及static 下面的的文件都是静态文件
- resouce
- static
- public
- 在resource 下面的 pulic 或者 resouce 及static 下面的的文件都是静态文件
- 首页
- jsp,模版引擎Thymeleaf
- 装配扩展 SpringMVC
- 增删改查
- 拦截器
- 国际化!
静态资源
在Webproperties 中有一个这样数组表明我们可以在哪里存放静态资源
之前的 ResouceProperties 已经被弃用了 depreceated
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
模版引擎
如果需要使用 thymeleaf 只需要导入 对应的依赖,我们将我们的 html 放在我们的 templates 目录下
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
如何搭一个网站
-
前端搞定,页面长什么样子:数据
-
设计数据库
-
前后端独立部署
-
数据接口对接
-
前后端联调测试
-
有一套自己熟悉的后台模版 xadmin
-
前端界面至少自己能写一个界面
springsecurity
在web开发中,安全第一位,过滤器拦截器。
做网站:安全应该在设计之初考虑。
springsecurity 做的是身份认证与权限控制的框架
swagger
一个写接口的工具
swagger 的配置类
@Configuration
@EnableSwagger2
public class swagerConfig {
// 配置它的besn 实例
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
// .enable() 配置是否启用 swagger
// .groupName("商场列表") 给api 分组
.select()
//配置要扫描接口的方式
// basepage:指定要扫描的包
// any 全部扫描
// none 不扫描
// withClassAnnotation扫描类上的注解
// withMethodAnnotation 扫描方法上的注解
.apis(RequestHandlerSelectors.basePackage("com.kuang.swaggerdemo.controller"))
// path 过滤什么路径
.paths(PathSelectors.ant("/kuang/**"))
.build();
}
// 配置swager 信息 = apiInfo
private ApiInfo apiInfo() {
Contact contact = new Contact("郭经波", "https://baidu.com", "3312416405@qq.com");
return new ApiInfo(
"郭经波的变强日记",
"Api Documentation",
"1.0",
"urn:tos",
contact, "Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
如何写多个分组
直接写多个 Dockter 配置类就可以了
如何异步发送请求
@EnableAsync//开启异步注解的功能
@EnableScheduling//开始定时任务的注解
@SpringBootApplication
public class SwaggerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SwaggerDemoApplication.class, args);
}
}
@Async
public void hello(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("数据正在处理....");
}
如何发送邮件
spring:
mail:
username: 3312416405@qq.com
password: vtiqondouhafdbhb
host: smtp.qq.com
default-encoding: utf-8
#开启加密验证
properties:
mail:
smtp:
ssl:
enable: true
@Autowired
JavaMailSenderImpl mailSender;
简单邮件
SimpleMailMessage mimeMessage = new SimpleMailMessage();
mimeMessage.setSubject("感谢信");
mimeMessage.setText("你笑起来真好看");
mimeMessage.setTo("878558119@qq.com");
mimeMessage.setFrom("3312416405@qq.com");
mailSender.send(mimeMessage);
复杂邮件
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,html);
helper.setSubject(subject);
helper.setText(context,html);
helper.addAttachment("fdsa",new File("fdsa"));
helper.addTo("3312416405@qq.com");
helper.setFrom("3312416405@qq.com");
mailSender.send(mimeMessage);
定时任务
@EnableAsync//开启异步注解的功能
@EnableScheduling//开始定时任务的注解
@SpringBootApplication
public class SwaggerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SwaggerDemoApplication.class, args);
}
}
@Service
public class SchedualService {
// 在一个特定的时间执行这个方法
// cron 表达式
// 秒 分 时 日 月 周几
@Scheduled(cron = "50 59 20 * * ?")
public void hello(){
System.out.println("hello 你被执行了");
}
}
RPC
远程过程调用,是一种进程通信的方式,是一种技术思想,而不是规范,它允许一个程序调用另一个地址空间的过程或者函数,而不需要程序员显式的编码这个细节。即程序呐无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。
网络传输都需要序列化
vueBlog
jwt = java web toke