Spring实战5.0

前言.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客户端。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值