前言.IOC与DI
IOC-Inversion of Control,即"控制反转"。
对象的创建不再由程序本身去创建,而是交给IOC容器去管理对象。
控制:IOC容器控制了对象外部资源的获取-也就说对象的控制权由IOC容器掌控。
反转:传统应用程序对象的构建由程序控制,而反转则是IOC容器来构建对象,也就是说获得依赖对象的方式
反转了。
DI—Dependency Injection,即"依赖注入"。
由IOC容器动态的将某个对象的依赖注入其对象中。
简单来说,我们构建A对象的时,内部需要依赖B对象。那么IOC容器在构建A对象时,会把B对象注入其中。
1、Spring
Spring提供了一个容器(container),可以称呼为Spring的应用上下文(Spring application context)
,它会创建和管理应用组件,组件可以称为bean,会在Spring上下文中装配在一起。将Bean装配在一起的行为
是通过一种基于依赖注入(DI)的模式实现的。
1.通过XML方式描述Bean。
<bean id="a" class="com.ldd.entity.A" />
<bean id="b" class="com.ldd.entity.B" >
<constructor-arg ref="a"></constructor-arg>
</bean>
XML描述了两个Bean,也就是A和B类。并通过构造器参数将A装配到了B中。
2.通过Java配置
@Configuration
public class BeanConfiguration {
@Bean
public A a(){
return new A();
}
@Bean
public B b(){
return new B(a());
}
}
@Configuration注解会告知Spring这是一个配置类,会为Spring应用上下文提供bean。
2、@SpringBootApplication
@SpringBootApplication是一个组合注解,它组合了3个其它的注解。
1.@SpringBootConfiguration:这个注解实际上就是@Configuration注解的特殊形式,将类声明为配置类。
2.@EnableAutoConfiguration:启动SpringBoot的自动配置。
3.@ComponentScan:启用组件扫描。这样我们能够通过像@Component、@Service、@Controller这样的注解
声明其它类,Spring会自动发现它们并将它们注册为Spring应用上下文中的组件。
3.@SpringBootTest
@RunWith(SpringRunner.class)
@SpringBootTest
class SpringshizhanfiveApplicationTests {
@Test
void contextLoads() {
Logger logger = LoggerFactory.getLogger(this.getClass());
logger.info("TEST");
}
}
@SpringBootTest会告诉Junit在启动测试类的时候添加上SpringBoot的功能,我们可以将这个测试类视同为在
main()方法中调用SpringApplication.run()。
@RunWith(SpringRunner.class)是Junit包提供的注解,它会提供一个测试运行器(runner)来指导JUnit如何
运行测试。SpringRunner.class,这是一个Spring提供的测试运行器,它会创建测试运行所需的Spring应用上
下文.SpringRunner是SpringJunit4ClassRunner的别名,是在Spring4.3中引入的,以便于移除对特定JUnit
版本的关联(比如,JUnit4)。
4.MockMvc测试
准备代码
package com.ldd.springshizhanfive.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author 小李同学
* @pageage com.ldd.springshizhanfive.controller
* @date 2021/2/6 17:26
* @week 星期六
*/
@Controller
public class HomeController {
@GetMapping("/")
public String index(){
return "home";
}
}
home.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Welcome to...</h1>
<img th:src="@{/images/jy.jpg}"></img>
</body>
</html>
package com.ldd.springshizhanfive.controller;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.core.StringContains.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* @author 小李同学
* @pageage com.ldd.springshizhanfive.controller
* @date 2021/2/6 17:38
* @week 星期六
*/
@RunWith(SpringRunner.class)
@WebMvcTest(HomeController.class)
public class HomeControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testHomePage()throws Exception{
mockMvc.perform(get("/")) //发起对"/"的GET
.andExpect(status().isOk()) //期待得到HTTP 200
.andExpect(view().name("home")) //期望得到home 试图
.andExpect(content().string(containsString("Welcome to..."))); //期待包含"Welcome to..."
}
}
HomeControllerTest 没有添加@SpringBootTest注解,而是添加了@WebMvcTest注解。这是SpringBoot所
提供的一个特殊注解,它会让这个测试在SpringMVC应用的上下文中执行。在本例中,它会将HomeController
注册到SpringMVC中,这样的话,我们就可以向它发送请求了。
5.表单提交
<form>没有声明action属性是,当表单提交时浏览器会收集表单中的所有数据,将其发送至服务器端,发送路径
与渲染表单的GET请求路径相同。
比如:请求/design 路径渲染了一个HTML页面。点击提交时 <form>没有指定action,那么表单发送请求的
路劲为/design
6.@Valid表单校验
@Valid注解会告诉SpringMVC要对提交的form对象进行校验,而校验时机是在它绑定完表单数据之后。如果
存在校验错误,那么这些错误细节将会捕获到一个Errors对象,调用hasErrors()方法判断是否有校验错误。
thymeleaf提供了便携访问Errors,这就是借助fields及其th:errors属性。
<body>
<form method="POST" th:object="${ingredient}">
<span>名称:</span>
<input id="name" name="name" type="text" value="" />
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
<input type="submit" value="点击提交"/>
</form>
</body>
@PostMapping
public String executeForm(@Valid @ModelAttribute("ingredient") Ingredient ingredient, Errors errors){
if(errors.hasErrors()){
return "design";
}
return "designOk";
}
@Data
@RequiredArgsConstructor
public class Ingredient {
@CreditCardNumber(message = "请输入合法的信用卡号码")
private final String name;
}
7.视图控制器
有一种控制器,只将请求转发到视图而不做其他事情的控制器。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/test_home").setViewName("home");
}
}
WebConfig实现了WebMvcConfigurer接口。在本案例中,我们覆盖了addViewControllers方法,调用
registry的addViewController()方法,将"/test_home"传递进去,视图控制器将会针对该路径执行
GET请求,这个方法返回ViewControllerRegistration对象,立马基于该对象调用setViewName()方法
,用来指明当请求"/test_home"的时候转发到"home"视图上。
8.构造方法注入
@Slf4j
@Component
public class Basketball {
public void outMsg(){
log.info("喜欢打篮球🏀");
}
}
@Component
public class Hobby {
private Basketball basketball;
@Autowired
public Hobby(Basketball basketball){
this.basketball = basketball;
}
public void specificHobby(){
basketball.outMsg();
}
}
当Spring创建Hobby的时候,它会通过@Autowired标注的构造器将Basketball注入进来。这个构造器将Basketball赋值给一个实例变量。
9.Spring的JdbcTemplate
Spring对JDBC的支持要归功于JdbcTemplate类。JdbcTemplate提供了一种特殊的方法,用来进行数据库
SQL操作.Spring对数据库的操作在jdbc上面做了深层次的封装,使用spring的注入功能,可以把DataSource注册到JdbcTemplate之中。
1.pom引入需要jar
<!-- Spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- -->
数据库连接池此处选择c3p0,数据库使用mysql。
2.在resources下创建db.properties(我们通常将数据库的配置信息单独放到一个文件中,这样也为了方便后期维护)
jdbc.user=root
jdbc.password=123456
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/databaseName
3.配置dataSource和jdbcTemplate
<!-- 加载外部db.properties-->
<context:property-placeholder location="classpath:db.properties"/>
<!-- c3p0连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
</bean>
<!--JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
4.Service使用JdbcTemplate
@Service
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public List<User> selectAllList() {
RowMapper<User> rowMapper = new RowMapper<User>() {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setId(resultSet.getInt("id"));
user.setName(resultSet.getString("name"));
user.setAge(resultSet.getInt("age"));
return user;
}
};
return jdbcTemplate.query("select id,name,age from t_user", rowMapper);
}
}
现在已经配置完成,可以直接使用JdbcTemplate进行数据库操作。
10.预加载数据
SpringBoot在应用启动的时候,默认会读取根路径下名为shcema.sql的文件,那么在应用启动的时候将会
基于这个数据库执行这个文件中的SQL。需要将文件放置"src/main/resources"文件夹下。
SpringBoot还会在应用启动的时候执行根路径下名为data.sql的文件,为数据库加载配料数据。也要放置在
"src/main/resources/data.sql"。
注意:
有三种配置ALWAYS,EMBEDDED,NEVER,默认是EMBEDDED,也就是只有嵌入式数据库才会执行。所以我们修改为
always
spring.datasource.initialization-mode=always
-------------------------------------------------------------------------------
还可以指定脚本文件的位置名称
spring.datasource.schema=classpath:schema-test.sql
spring.datasource.data=classpath:data-test.sql
可以初始化多个
spring.datasource.schema[0]=classpath:schema-test.sql
spring.datasource.schema[1]=classpath:schema-test.sql
spring.datasource.data[0]=classpath:data-test.sql
spring.datasource.data[1]=classpath:data-test.sql
11.使用Spring Data JPA持久化数据
SpringData是一个非常大的伞形项目,由多个子项目组成,其中大多数子项目都关注对不同的数据库类型进行
数据持久化。比较流行的几个SpringData项目包括:
Spring Data JPA:基于关系型数据库进行JPA持久化。
Spring Data MongoDB:持久化到Mongo文档数据库。
Spring Data Neo4j:持久化到Neo4j图数据库。
Spring Data Redis:持久化到Redis key-value存储。
Spring Data Cassandra:持久化到Cassandra数据库。
Spring Data为所有项目提供了一项最有趣且最有用的特性,就是基于repository规范接口自动生成
repository的功能。
1.SpringBoot应用可以通过JPA starter来添加Spring Data JPA。这个starter依赖不仅会引入Spring Data
JPA,还会传递性地将Hibernate作为JPA实现引入进来。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
如果你想要使用不同的JPA实现,那么至少需要将Hibernate依赖排除出去并将你所选择的JPA库包含进来。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<exclusion>
<artifactId>hibernate-entitymanager</artifactId>
<groupId>org.hibernate</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入其他JPA实现-->
<dependency>
<groupId></groupId>
<artifactId></artifactId>
</dependency>
2.applicatin.yml配置数据库连接
spring:
datasource:
url: jdbc:mysql://localhost:3306/ratelblog
driver-class-name: com.mysql.jdbc.Driver
username: root
password: admin
3.创建User声明为JPA实体,它必须添加@Entity注解。它的id属性需要使用@Id注解,以便于将其指定为
数据库中唯一表示该实体的属性,@Table指定持久化关联的表。
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
@Table(name = "t_user")
public class User {
@Id
private Integer id;
private String name;
private Integer age;
...
}
4.创建Service继承CrudRepository接口。
import org.springframework.data.repository.CrudRepository;
public interface UserService extends CrudRepository<User,Integer> {
}
CrudRepository定义了很多用于CRUD(创建、读取、更新、删除)操作的方法。
注意,它是参数化的,第一个参数是repository要持久化的实体类型,第二个参数是实体ID属性的类型。
注意:我们不需要编写Service实现类,当应用启动的时候,SpringDataJPA会在运行期间自动生成实现类。
我们只需要将service注入到控制器即可。
5.UserController测试使用
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/findAll")
public String findAll(){
Iterable<User> users = userService.findAll();
users.forEach(item ->{
System.out.println(item.toString());
});
return users.toString();
}
}
User{id=1, name='张三', age=null}
User{id=2, name='李四', age=19}
User{id=3, name='王五', age=20}
User{id=4, name='司总', age=null}
12.Spring Security
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
要保护我们的应用,只需要添加这项依赖就可以了。当应用启动的时候,自动配置功能会探测到Spring
Security出现在了类路径中,因此它会初始化一些安全配置。
如果加入此依赖后,启动应用并尝试访问主页或者任意页面,应用将会弹出一个HTTP basic认证对话框并提示
你进行认证。要想通过这个认证,你需要一个用户名和密码。用户名为user,而密码则是随机生成的,它会被
写入应用的日志文件中。日志条目大致如下所示:
Using default security password: xxxxxx-xxxx-xxx-xxxx...
通过将security starter添加到项目的构建文件中,我们得到了如下的安全特性:
1.所有的HTTP请求路径都需要认证。
2.认证过程是通过HTTP basic认证对话框实现的。
3.系统只有一个用户,用户名为user。
略
...
13.配置嵌入式服务器(HTTPS)
额外知识:
将server.port设置为0,服务器并不会真的在端口0上启动。相反,它会任选一个可用的端口。
server:
port: 0
对底层容器常见的一项设置就是让它处理HTTPS请求。为了实现这一点,我们首先要使用JDK的keytool命令行
工具生成keystore:
keytool -keystore mykeys.jks -genkey -alias tomcat -keyalg RSA
在这个过程中,会询问我们一些关于名称和组织机构相关的问题,大多数问题都无关紧要。但是,它提示输入密
码的时候要记住你所选择的密码。在application.yml中配置如下属性启动HTTPS:
server:
port: 8443
ssl:
key-store: file:///C:\2021-08-05-keystore\mykeys.jks
key-store-password: admin123
key-password: admin123
在这里,我们将server.port设置为8443,这是在开发阶段HTTPS服务器的常用选择。key-store属性设置
为我们创建的mykeys.jks文件的路径。在这里,它使用了file://URL,因此会在文件系统中加载,但是,如果
你需要将它打包到一个应用JAR文件中,就需要使用"classpath"来引用它。key-store-password和key-
password属性都设置成创建key-store时所设置的密码。
14.配置日志
默认情况下,SpringBoot通过Logback配置日志,日志会以INFO级别写入到控制台中。在SpringBoot中要配置
日志如下:
logging:
level:
root: info //设置日志的级别(大于等于该级别的日志会输出,小于该级别的日志不会输出)
file:
path: C:\2021-08-05-keystore\logInfo #日志输出文件位置,会在该位置下生成一个spring.log文
件,默认情况下,日志文件一旦达到10MB,就会轮换。
测试输出日志:
Iterable<User> users = userService.findAll();
users.forEach(item -> {
logger.info(item.toString());
logger.warning("FK....");
logger.config("config...不显示");
logger.fine("fine...不显示");
});
15.profile属性
profile有两种展示方式,一种就是创建另外一个YAML或属性文件,文件名称遵守如下的约定:
application-{profile名}.yml或application-{profile名}.properties。
application.yml
application-dev.yml
application-prod.yml
在application.yml中,我们这样激活profile:
spring:
profiles:
active: dev
这样写会选中使用applicatin-dev.yml中的配置(配置使用是覆盖合并到application.yml)。注意spring.
profiles.active属性名是复数形式的profile。这意味着我们可以设置多个激活的profile。
spring:
profiles:
active:
- dev
- prod
如果以JAR文件的形式运行应用,那么我们还可以以命令行参数的形式设置激活profile:
java -jar xxxxx.jar --spring.profiles.active=dev
profile另一种方式仅使用于YAML配置。它会将待定profile的属性和非profile的属性都放到
application.yml中,他们之前使用3个中划线进行分割,并且使用spring.profiles属性来命名profile。
spring:
datasource:
url: jdbc:mysql://localhost:3306/ratelblog
driver-class-name: com.mysql.jdbc.Driver
username: root
password: admin
profiles:
active: prod
server:
ssl:
key-store: file:///C:\2021-08-05-keystore\mykeys.jks
key-store-password: admin123
key-password: admin123
logging:
level:
root: info
file:
path: C:\2021-08-05-keystore\logInfo
---
server:
port: 7070
spring:
profiles: dev
---
server:
port: 6060
spring:
profiles: prod
16.@Profile注解
@Profile注解将某些bean设置为仅适用于给定的profile。
案例1:
@Bean
@Profile("dev")
public Car getCar(){
return new Car();
}
只有在profile环境是dev的情况下,才创建Car bean。注意:profile也可以写多个。
@Bean
@Profile({"dev","prod"})
public Car getCar(){
return new Car();
}
只有在profile环境是dev和prod的情况下,才创建Car bean。注意:也可以取反写。
@Bean
@Profile("!dev")
public Car getCar(){
return new Car();
}
当profile不是dev环境的情况下,才创建Car bean。
@Bean
@Profile({"!dev","!prod"})
public Car getCar(){
return new Car();
}
当profile不是dev并且也不是prod时,才创建Car bean。
我们还可以在带有@Configuration注解类上使用@Profile。
@Profile("dev")
@Configuration
public class ProfileConfig {
@Bean
public User getUser(){
return new User();
}
}
在这里,ProfileConfig中定义的所有bean只有在dev的环境下才会创建。
17.REST服务
1.@GetMapping HTTP GET请求 读取资源数据
2.@PostMapping HTTP POST请求 创建资源
3.@PutMapping HTTP PUT请求 更新资源(执行大规模的替换操作)
4.@PatchMapping HTTP PATCH请求 更新资源(局部)
5.@DeleteMapping HTTP DELETE请求 删除资源
6.@RequestMapping 通用的请求处理,HTTP方法可以通过method声明
-----------------------------------------------------------------
@RestController注解有两个目的。
1.能够让类被组件扫描功能发现。
2.控制器中的所有处理器方法的返回值都要直接写入响应体中,而不是将值放到模型中并传递给一个视图以便于
进行渲染。
18.RestTemplate消费REST端点
RestTemplate:Spring核心框架提供的简单、同步REST客户端。