目录
工具:Intellij
版本:2.1.10
创建SpringBoot项目
项目创建成功后,目录如下:
相关配置如端口号等可以写在application.properties中,SpringBoot项目默认端口号8080,因为这个端口号容易冲突,所以我们改为9999。
测试Controller
在Controller层可能会用到的注解:@RestController、@RequestMapping、@GetController、@PostController、@PutController、@DeleteController、@RequestBody,@RequestParam、@Autowired。
新建web目录,创建测试Controller类如下
@RestController("/builder")
public class BuilderController {
@GetMapping("/test")
public String test() {
return "OK";
}
}
运行启动类,控制台输入如下内容,说明项目启动成功。
从浏览器中输入http://localhost:9999/builder/test进行访问,返回OK说明接口调用成功。
单元测试
选中上一步创建的BuilderController类,按下右键 -> go to -> Test创建单元测试类,也可在待测试的类内部按下 alt + insert 键,在弹窗中选中Test来创建测试类。在测试类上使用注解@RunWith(SpringRunner.class) 和@SpringBootTest,使得测试类可单独运行。使用@Autowired注解自动注入待测试的类,在测试方法上使用@Test注解,然后在测试方法中调用被测试的方法即可。
@RunWith(SpringRunner.class)
@SpringBootTest
public class BuilderControllerTest {
@Autowired
private BuilderController controller;
@Test
public void test1() {
System.out.println(controller.test());
}
}
点击运行test1方法,运行过程中没有报错,且运行结果正确,说明单元测试通过。如果在测试之前需要提前加载一些资源如配置等,可在测试类中使用@Beafore注解来实现。
@RunWith(SpringRunner.class)
@SpringBootTest
public class BuilderControllerTest {
private String name;
@Autowired
private TestConfig testConfig;
/**
* 使用该注解加载测试需要的资源
*/
@Before
public void init() {
name = testConfig.getServerName();
}
@Autowired
private BuilderController controller;
@Test
public void test1() {
System.out.println(name);
System.out.println(controller.test());
}
}
读取配置
获取默认配置文件中的内容
Springboot默认配置文件名为application.properties,也可将properties改为yml,默认配置文件路径为classpath根目录,也可将其配置在classpath:/config、file:/或者file:/config目录下。
读取配置以及加载配置类可能用到的注解:@Value、@Configuration、@Component、@PropertySources、@PropertySource、@ConfigurationProperties等。
@Component
public class TestConfig {
/**
* 使用该注解获取配置
*/
@Value("${server.name}")
private String serverName;
/**
* 注入环境配置类,可以从配置类中获取属性
*/
@Autowired
private Environment environment;
public String getServerName() {
return serverName;
}
public void print() {
System.out.println(serverName + ", " + environment.getProperty("server.host"));
}
}
修改配置文件
加载单个配置文件
在classpath下创建db/db.properties配置,如果想读取到该配置文件的内容,就需要先加载该配置文件。
@Configuration
@PropertySource("classpath:db/db.properties")
public class DbConfiguration {
@Value("${db.name}")
private String dbName;
@Value("${db.username}")
private String dbUsername;
@Value("${db.password}")
private String dbPassword;
public String print() {
return "DbConfiguration{" +
"dbName='" + dbName + '\'' +
", dbUsername='" + dbUsername + '\'' +
", dbPassword='" + dbPassword + '\'' +
'}';
}
}
一次加载多个配置文件
按照上述方法创建db1.properties和db2.properties文件,在配置类中加载这两个文件并获取属性值。
@Configuration
@PropertySources({
@PropertySource("classpath:db/db1.properties"),
@PropertySource("classpath:db/db2.properties")
})
public class DbConfiguration1 {
@Value("${db1.name}")
private String dbName1;
@Value("${db1.username}")
private String dbUsername1;
@Value("${db1.password}")
private String dbPassword1;
@Value("${db2.name}")
private String dbName2;
@Value("${db2.username}")
private String dbUsername2;
@Value("${db2.password}")
private String dbPassword2;
}
注入集合
在配置文件中添加配置数组
@PropertySource("classpath:db/db.properties")
@Configuration
@ConfigurationProperties("ds")
public class DbConfiguration2 {
/**
* 集合名必须与配置文件中的数组名一致,且必须有get和set方法
*/
private List<DbCollection> db;
/**
* Map名称必须与配置文件中的一致
*/
private Map<String, String> ds1;
private Map<String, String> ds2;
public List<DbCollection> getDb() {
return db;
}
public void setDb(List<DbCollection> db) {
this.db = db;
}
public Map<String, String> getDs1() {
return ds1;
}
public void setDs1(Map<String, String> ds1) {
this.ds1 = ds1;
}
public Map<String, String> getDs2() {
return ds2;
}
public void setDs2(Map<String, String> ds2) {
this.ds2 = ds2;
}
}
public class DbCollection {
private String username;
private String password;
private String host;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getHosts() {
return host;
}
public void setHosts(String host) {
this.host = host;
}
}
不同环境使用不同配置文件
新建配置文件application-dev.properties,在默认配置文件中新增配置spring.profiles.active=dev,即可将配置文件切换为-dev文件。
@Component
public class EnvConfiguration {
/**
* 从-dev配置文件中获取app.name
*/
@Value("${app.name}")
private String address;
public String getAddress() {
return address;
}
}
自动装配
非自动装配体
public interface OssService {
}
public class AliOssService implements OssService {
}
public class HuaweiOssService implements OssService {
}
@Configuration
public class OssConfiguration {
@Bean
public OssService createAliOssService() {
return new AliOssService();
}
@Bean
public OssService createHuaweiOssService() {
return new HuaweiOssService();
}
}
按条件自动装配
使用@Conditional注解
public class AliOssCondition implements Condition {
/**
* 返回true,则装配AliOssService
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String oss = System.getProperty("oss.type");
return null != oss && oss.equals("ali");
}
}
public class HuaweiOssCondition implements Condition {
/**
* 返回true,则装配HuaweiOssService
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String oss = System.getProperty("oss.type");
return null != oss && oss.equals("huawei");
}
}
@SpringBootConfiguration
public class OssConfiguration {
@Bean
@Conditional(AliOssCondition.class)
public OssService createAliOssService() {
return new AliOssService();
}
@Bean
@Conditional(HuaweiOssCondition.class)
public OssService createHuaweiOssService() {
return new HuaweiOssService();
}
}
@Conditional注解可以作用在方法上,也可以作用在类上。作用于方法时,值对方法返回的对象按条件装配体,作用在类上时,对类中所有方法返回的对象都按条件装配。
public class OssCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String oss = System.getProperty("file.server.type");
return null != oss && oss.equals("oss");
}
}
@SpringBootConfiguration
@Conditional(OssCondition.class)
public class OssConfiguration {
@Bean
// @Conditional(AliOssCondition.class)
public OssService createAliOssService() {
return new AliOssService();
}
@Bean
// @Conditional(HuaweiOssCondition.class)
public OssService createHuaweiOssService() {
return new HuaweiOssService();
}
}
@Conditional可接收一个数组,一次可传入多个Condition,当传入的Condition中的matches都返回true时,对类中所有方法返回的对象都进行装配。
@SpringBootConfiguration
@Conditional({AliOssCondition.class, HuaweiOssCondition.class})
public class OssConfiguration {
@Bean
// @Conditional(AliOssCondition.class)
public OssService createAliOssService() {
return new AliOssService();
}
@Bean
// @Conditional(HuaweiOssCondition.class)
public OssService createHuaweiOssService() {
return new HuaweiOssService();
}
}
还可使用org.springframework.boot.autoconfigure.cloud包下的@ConditionalOnProperty、@ConditionalOnClass、@ConditionalOnBean等注解进行装配。
@SpringBootConfiguration
public class OssConfiguration {
/**
* 当已加载的配置文件中存在oss.type且值为ali时装配该对象
*/
@Bean
@ConditionalOnProperty(name = "oss.type", havingValue = "ali")
public OssService createAliOssService() {
return new AliOssService();
}
/**
* 当已加载的配置文件中不存在oss.type时装配
*/
@Bean
@ConditionalOnProperty(name = "oss.type", havingValue = "ali", matchIfMissing = true)
public OssService createHuaweiOssService() {
return new HuaweiOssService();
}
/**
* 当存在HuaweiOssCondition时装配
*/
@Bean
@ConditionalOnClass(HuaweiOssCondition.class)
public OssService createHuaweiOssService1() {
return new HuaweiOssService();
}
/**
* 当存在huaweiCondition的bean时装配
*/
@Bean
@ConditionalOnBean(name = "huaweiCondition")
public OssService createHuaweiOssService2() {
return new HuaweiOssService();
}
/**
* 当存在HuaweiOssCondition类型的bean时装配
*/
@Bean
@ConditionalOnBean(HuaweiOssCondition.class)
public OssService createHuaweiOssService3() {
return new HuaweiOssService();
}
}
装配没有加注解的bean
事件监听
流程:
- 自定义事件,一般是继承ApplicationEvent抽象类;
- 定义事件监听器,有多种实现方式,并且对同一个事件可创建多个监听器;
- 发布事件。
实现方式1:通过ConfigurableApplicationContext对象添加并发布事件。
/**
* 定义一个事件
*/
public class MyEvent extends ApplicationEvent implements Serializable {
private static final long serialVersionUID = 1L;
public MyEvent(Object source) {
super(source);
}
}
/**
* 定义一个监听器
*/
public class MyEventListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent myEvent) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("接收到事件【" + myEvent.getClass().getName() + "】,实现相关业务逻辑。");
}
}
@SpringBootApplication
public class ManagementApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ManagementApplication.class, args);
// 添加监听器
context.addApplicationListener(new MyEventListener());
// 发布事件
context.publishEvent(new MyEvent(new Object()));
}
}
实现方式2:直接将监听器托管给Spring,再使用ConfigurableApplicationContext对象发布事件。
/**
* 定义一个监听器
*/
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent myEvent) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("接收到事件【" + myEvent.getClass().getName() + "】,实现相关业务逻辑。");
}
}
@SpringBootApplication
public class ManagementApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ManagementApplication.class, args);
// 发布事件
context.publishEvent(new MyEvent(new Object()));
}
}
实现方式3:使用@EventListener注解而不实现ApplicationListener接口,然后使用ConfigurableApplicationContext对象发布事件。
/**
* 定义一个监听器
*/
@Component
public class MyEventListener {
@EventListener
public void onApplicationEvent(MyEvent myEvent) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("接收到事件【" + myEvent.getClass().getName() + "】,实现相关业务逻辑。");
}
}
@SpringBootApplication
public class ManagementApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ManagementApplication.class, args);
// 发布事件
context.publishEvent(new MyEvent(new Object()));
}
}
实例:用户注册成功后发送邮件通知。
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public User register(@RequestBody User user) {
return userService.register(user);
}
}
@Service
public class UserService {
@Autowired
private ConfigurableApplicationContext context;
public User register(User user) {
System.out.println("将数据【" + user.toString() + "】添加到数据库。");
context.publishEvent(new UserRegisterEvent<User>(user));
return user;
}
}
public class UserRegisterEvent<User> extends ApplicationEvent {
public UserRegisterEvent(User source) {
super(source);
}
}
@Component
public class UserRegisterListener {
@EventListener
public void sendEmail(UserRegisterEvent<User> event) {
User user = (User) event.getSource();
System.out.println("用户【" + user.getUsername() + "】注册成功,发送邮件【" + user.getEmail() + "】");
}
}
SpringBoot JDBC
引入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
配置
/**
* 加载数据库配置,也可获取到配置文件属性后,在该类中再进行详细配置
*/
@Configuration
@PropertySource("classpath:config/database.properties")
public class DBConfig {
}
使用
@Repository
public class UserDao {
/**
* 直接注入jdbcTemplate,使用该对象进行数据库操作
*/
@Autowired
private JdbcTemplate jdbcTemplate;
public User queryByUsername(String username) {
// 查询示例
RowMapper<User> rowMapper = BeanPropertyRowMapper.newInstance(User.class);
return jdbcTemplate.query("select * from user where username = '" + username + "'", rowMapper).get(0);
}
}
SpringBoot AOP
Aop用途:日志记录、权限处理、性能统计、监控、事务处理、异常处理等。
可能使用的注解:@Aspect、@Pointcut、@Before、@After、@AfterReturning、@AfterThrowing等。
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
定义切面和通知
@Component
@Aspect
public class LogAspect {
/**
* 定义切面,..*.*(..)表示对management包以及子包下的任何类中任何方法都有效
*/
@Pointcut("execution(* com.builder.management..*.*(..))")
public void log() {
}
/**
* 前置通知
*/
@Before("log()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("before class: " + joinPoint.getTarget() + "...method: " + joinPoint.getSignature().getName());
}
/**
* 后置通知
*/
@After("log()")
public void logAfter(JoinPoint joinPoint) {
System.out.println("after class: " + joinPoint.getTarget() + "...method: " + joinPoint.getSignature().getName());
}
@AfterReturning(returning = "ret", pointcut = "log()")
public void afterReturning(Object ret) {
System.out.println("returning: " + ret);
}
@AfterThrowing("log()")
public void logAfterThrowing(JoinPoint joinPoint) {
System.out.println("throwing: " + joinPoint.getSignature().getName());
}
}
使用
/**
* 该类在management包下,所以切面对该类有效
*/
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 在执行该方法前、后以及返回后进行相关的通知
*/
public User queryByUsername(String username) {
RowMapper<User> rowMapper = BeanPropertyRowMapper.newInstance(User.class);
return jdbcTemplate.query("select * from user where username = '" + username + "'", rowMapper).get(0);
}
}
SpringBoot Redis
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
添加配置
@Configuration
@PropertySource("classpath:config/redis.properties")
public class RedisConfiguration {
/**
* 对redisTemplate做一个简单的配置
*/
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
StringRedisSerializer keySerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(keySerializer);
redisTemplate.setHashKeySerializer(keySerializer);
GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setValueSerializer(valueSerializer);
redisTemplate.setHashValueSerializer(valueSerializer);
return redisTemplate;
}
}
使用
@Service
public class RedisService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 做一个基本的String类型数据的查询
*/
public String get() {
return redisTemplate.opsForValue().get("mykey");
}
}
SpringBoot监控和度量
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
添加配置
启动程序,访问检测的url,测试结果如下
SpringBoot整合Mybatis
添加依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
添加配置
# mapper文件所在路径
mybatis.mapper-locations=classpath:mapper/*.xml
# 实体类如User所在路径
mybatis.type-aliases-package=com.builder.management.user
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/crud?useSSL=false&useJDBCCompliantTimezoneShift=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
实现Mapper接口
package com.builder.management.user;
import org.apache.ibatis.annotations.Param;
public interface UserMapper {
User find(@Param("username") String username);
}
<?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.builder.management.user.UserMapper">
<!--结果集映射-->
<resultMap id="userMap" type="com.builder.management.user.User">
<result column="username" property="username" jdbcType="VARCHAR"/>
<result column="email" property="email" jdbcType="VARCHAR"/>
</resultMap>
<select id="find" resultMap="userMap">
select * from user where username = #{username};
</select>
</mapper>
扫描Mapper接口
/**
* @MapperScan 用于扫描mapper接口,如果用该注解,则无需对mapper接口做任何处理
* 若不使用该接口,则需要在每个mapper接口类上添加@Mapper注解
*/
@SpringBootApplication
@MapperScan("com.builder.management.user")
public class ManagementApplication {
public static void main(String[] args) {
SpringApplication.run(ManagementApplication.class, args);
}
}
测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void find() {
System.out.println(userMapper.find("liuqian").toString());
}
}
测试结果如下
温馨提示:如果你使用的是Intellij来编写Java代码的话,添加Free MyBatis Plugin插件,能够自动帮你检测mapper接口与mapper文件的映射关系,如果找不到映射关系,mapper接口或者mapper.xml文件中就会报错,推荐使用。
效果如下
以上Spring Boot基础知识写的比较粗略,希望能对正在看这篇文章的你有所帮助,文章中有什么不足之处,大家可以留言指出来哦。