SpringBoot(二) 高级以及原理解释

整合RabbitMQ

创建服务提供者

  • 引入依赖
<?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>cn.itcast</groupId>
    <artifactId>rabbitmq_provider</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>
</project>
  • 创建启动类
package com.probuing.sb2.rabbitmq;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author wangxin
 * @date 2020/1/12 15:49
 * @description: TODO
 * GOOD LUCK!
 */
@SpringBootApplication
public class ApplicationRun {
    public static void main(String[] args) {
        SpringApplication.run(ApplicationRun.class, args);
    }
}

  • 创建发送消息的测试类
    ProviderTest.java
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProviderTest {

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Test
    public void sendMsg() {
        amqpTemplate.convertAndSend("", "sb2_message", "这是一条message");

    }
}
  • 配置文件
    主要配置RabbitMQ的ip地址、端口号、虚拟主机、用户名、密码
server:
  port: 8080
spring:
  rabbitmq:
    host: 127.0.0.1
    username: admin
    password: admin
    virtual-host: sb2
    port: 5672
    
  • 队列类
    MQConfig.java
@Component
public class MQConfig {
    @Bean
    public Queue queue(){
        return new Queue("sb2_mq");
    }
}
  • 创建服务消费者 创建module springboot_rabbitmq_consumer
  • 引入依赖
<?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">
    <parent>
        <artifactId>SpringBoot2</artifactId>
        <groupId>com.probuing.sb2</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springboot2_rabbitmq_consumer</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

</project>
  • 启动类
    ApplicationConsumerRun.java
@SpringBootApplication
public class ApplicationConsumerRun {
    public static void main(String[] args) {
        SpringApplication.run(ApplicationConsumerRun.class, args);
    }
}

  • 创建监听类
    MyListener.java
@Component
public class MyListener {
    @RabbitListener(queues = "sb2_mq")
    public void receiveMsg(String msg) {
        System.out.println("listener收到信息" + msg);
    }
}
  • 配置文件
    applicaiton.yml
spring:
  rabbitmq:
    host: 127.0.0.1
    username: admin
    password: admin
    virtual-host: heima118
    port: 5672
server:
  port: 8081
  • 小结

创建服务的提供者

  • 需要一个启动类
  • 需要一个测试类,发送消息
  • 需要AmqpTemplate.convertAndSend发送信息
  • 配置文件需要配置RabbitMQ的信息:IP地址、端口号、用户名、密码、虚拟主机
  • 需要一个初始化队列的类,主要的作用就是将队列写入到Bean里

创建服务的消费者

  • 需要创建启动类
  • 需要监听类,监听消息,主要使用@RabbitListener注解
  • 配置文件需要配置RabbitMQ的信息:IP地址、端口号、用户名、密码、虚拟主机

整合Redis

创建模块,引入依赖

  • 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">
    <parent>
        <artifactId>SpringBoot2</artifactId>
        <groupId>com.probuing.sb2</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springboot2_redis</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>
</project>
  • 创建启动类
    ApplicationRunner.java
@SpringBootApplication
public class ApplicationRun {
    public static void main(String[] args) {
        SpringApplication.run(ApplicationRun.class, args);
    }
}
  • 创建测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ApplicationRun.class)
public class RedisTest {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Test
    public void redisTest() {
        stringRedisTemplate.opsForValue().set("redis_sb", "testRedis");
        String redis_sb = stringRedisTemplate.opsForValue().get("redis_sb");
        System.out.println(redis_sb);
    }
}

注解:@Condition

Condition:Condition是在Spring4.0增加的条件判断功能,通过这个注解可以实现选择性的创建Bean的操作

创建Condition模块

  • 引入依赖
    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">
    <parent>
        <artifactId>SpringBoot2</artifactId>
        <groupId>com.probuing.sb2</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springboot2_condition</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>
</project>
  • 启动类
    ApplicationRunner.java
@SpringBootApplication
public class ApplicationCoRunner {
    public static void main(String[] args) {
        //ConfigurableApplicationContext是Spring的IOC容器,可以通过容器获取bean和环境变量中的
        ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(ApplicationCoRunner.class, args);
        Object redisTemplate = configurableApplicationContext.getBean("redisTemplate");
        System.out.println(redisTemplate);
    }
}

使用Condition注释

创建一个MyCondition类,实现Condition接口,接口中有一个方法,返回boolean
MyCondition.java

/**
 * @author wangxin
 * @date 2020/1/12 17:26
 * @description: TODO
 * GOOD LUCK!
 */
public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        boolean flag = true;
        try {
            Class.forName("redis.clients.jedis.Jedis");
        } catch (ClassNotFoundException e) {
            flag = false;
        }
        return flag;
    }
}

UserConfig类

@Configuration
public class UserConfig {
    @Bean
    @Conditional(MyCondition.class)
    public User user() {
        return new User();
    }
}

动态加载Bean

  • 创建一个注解类
/**
 * @author wangxin
 * @date 2020/1/12 17:38
 * @description: TODO
 * GOOD LUCK!
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(MyCondition.class)
public @interface MyConditional {
    String[] value();
}
  • 修改MyCondition.java
public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        boolean flag = true;
        Map<String, Object> map = annotatedTypeMetadata.getAnnotationAttributes(MyConditional.class.getName());
        String[] strings = (String[]) map.get("value");
        try {
            for (String string : strings) {
                Class.forName(string);

            }
        } catch (ClassNotFoundException e) {
            flag = false;
        }
        return flag;
    }
}

SpringBoot常用条件注解

  • ConditionalOnProperty:判断配置文件中是否有对应的属性和值才初始化Bean
  • ConditionalOnClass:判断环境中是否有对应的字节码文件才初始化Bean
  • ConditionalOnMissingBean:判断环境中是否有对应的Bean才初始化Bean

注解@Enable

SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的,而其底层原理是使用@Import注解导入一些配置类,实现Bean的动态加载

springboot工程是否可以直接获取jar包中定义的bean?

案例背景:创建两个模块,一个是springboot_enable 一个是springboot_enable_other
需求:在springboot_enable里通过@Enable注解加载User这个Bean,而User这个Bean是在springboot_enable_other模块中

创建两个模块

  • springboot_enable
  • springboot_enable_other
  • 在enable模块中加入enable_other依赖
  • pom.xml
 <dependency>
            <groupId>com.probuing.sb2</groupId>
            <artifactId>springboot_enable_other</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

在springboot_enable_other中创建User、UserConfig(配置类)

  • UserConfig.java
@Configuration
public class UserConfig {
    @Bean
    public User user() {
        return new User();
    }
}
  • User.java
public class User {
}

加载UserBean

方式一:扩大扫描包范围:

在Enable启动类里增加ComponentScan

@SpringBootApplication
@ComponentScan("com.probuing.sb2.domain")
public class EnableApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
        //获取user
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

方式二:导入User类

  • EnableApplication.java
@SpringBootApplication
//@ComponentScan("com.probuing.sb2.domain")
@Import(User.class)
public class EnableApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
        //从Ioc容器中获取map集合
        Map<String, User> map = context.getBeansOfType(User.class);
        User user = map.get("com.probuing.sb2.domain.User");
        System.out.println(user);
    }
}

注意
直接导入User类,spring容器导入bean之后取了一个名字,这个名字是bean的全限定名称,所以可以通过类型获取Bean

三 导入配置类

  • EnableApplicaiton.java
@SpringBootApplication
@Import(UserConfig.class)
public class EnableApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

四 通过Enable注解

在spring_enable_other中自定义一个注解

  • EnableUser.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)
public @interface EnableUser {
}

启动类

  • EnableApplication.java
@SpringBootApplication
@EnableUser
public class EnableApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

@Import注解

@Enable*底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4种用法

  • 导入Bean
  • 导入配置类
  • 导入ImportSelector实现类,一般用于加载配置文件中的类
  • 导入ImportBeanDefinitionRegistrar实现类

导入ImportSelector

  • 创建实现类
    • MyImportSelector.java
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.probuing.sb2.domain.User"};
    }
}
  • 修改启动类
    • EnableApplication.java
@SpringBootApplication
@Import(MyImportSelector.class)
public class EnableApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

导入ImportBeanDefinitionRegistrar实现类

  • 创建实现类
    • MyImportBeanDefinitionRegister.java
public class MyImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata,
                                        BeanDefinitionRegistry beanDefinitionRegistry) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
        beanDefinitionRegistry.registerBeanDefinition("user",beanDefinition);
        
    }
}
  • 修改启动类
    ·* EnableApplication.java
@SpringBootApplication
@Import(MyImportBeanDefinitionRegister.class)
public class EnableApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
//        Object user = context.getBean(User.class);
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

自定义Starter

模拟Mybatis的Starter加载过程

  • redis-spring-boot-autoconfigure

  • redis-spring-boot-starter

  • redis-spring-boot-starter引入依赖

<dependencies>

        <dependency>
            <groupId>com.probuing.sb2</groupId>
            <artifactId>redis-spring-boot-autoconfigure</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
  • redis-spring-boot-autoconfigure 引入依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.1.0</version>
        </dependency>
  • 创建RedisAutoConfigure
    autoconfigure模块
    • RedisAutoConfigure.java
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfig {
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfig {
    @Bean
    public Jedis jedis(RedisProperties redisProperties) {
        return new Jedis(redisProperties.getHost(), redisProperties.getPort());
    }
}
}
  • 创建一个新的springboot模块
    • 引入依赖
 <dependencies>

        <dependency>
            <groupId>com.probuing.sb2</groupId>
            <artifactId>redis-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
  • 启动类
@SpringBootApplication
public class MyRedisRun {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(MyRedisRun.class);
        Jedis jedis = (Jedis) context.getBean("jedis");
        jedis.set("probuing", "abc");
        String probuinhg = jedis.get("probuing");
        System.out.println(probuinhg);
    }
}

springboot监控

  • 创建模块 springboot_actuator
  • 引入依赖
    • pom.xml
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>
  • 配置文件
server:
  port: 8090
info:
  name: heima118
  class: springboot
management:
  endpoint:
    health:
      show-details: always
  endpoints:
    web:
      exposure:
        include: "*"

springboot可视化监控

springboot_admin

  • 创建一个模块
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.itcast</groupId>
    <artifactId>springboot_admin</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot_admin</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-boot-admin.version>2.2.1</spring-boot-admin.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>de.codecentric</groupId>
                <artifactId>spring-boot-admin-dependencies</artifactId>
                <version>${spring-boot-admin.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
  • 启动类
@SpringBootApplication
@EnableAdminServer
public class AdminApplication {
    public static void main(String[] args) {
        SpringApplication.run(AdminApplication.class, args);
    }
}

注意
这里要添加@EnableAdminServer,开启一个admin服务,主要是管理所有在admin里注册的服务

  • 配置文件
server.port=8088

加入客户端

  • 创建模块 springboot_myredis
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>2.1.3</version>
</dependency>
  • 客户端的配置文件
#在admin服务中进行注册,url指向的是admin对应的地址
spring:
  boot:
    admin:
      client:
        url: http://localhost:8088

#显示所有的健康信息
management:
  endpoint:
    health:
      show-details: always
      
  #显示所有的信息    
  endpoints:
    web:
      exposure:
        include: "*"
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,646评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,979评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,391评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,356评论 0 215
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,740评论 3 293
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,836评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,022评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,764评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,487评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,728评论 2 252
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,200评论 1 263
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,548评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,217评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,134评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,921评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,919评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,766评论 2 274

推荐阅读更多精彩内容