SpringBoot原理梳理

1.实现加载第三方的Bean

像controller、service,dao三层体系下编写的类,这些类都是我们在项目当中自己定义的类(自定义类)。当我们要声明这些bean,也非常简单,我们只需要在类上加上@Component以及它的这三个衍生注解(@Controller、@Service、@Repository),就可以来声明这个bean对象了。但是在我们项目开发当中,还有一种情况就是这个类它不是我们自己编写的,而是我们引入的第三方依赖当中提供的。

在pom.xml文件中,引入dom4j:

<!--Dom4j-->
<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.3</version>
</dependency>

dom4j就是第三方组织提供的。 dom4j中的SAXReader类就是第三方编写的。
当我们需要使用到SAXReader对象时,直接进行依赖注入是不是就可以了呢?

按照我们之前的做法,需要在SAXReader类上添加一个注解@Component(将当前类交给IOC容器管理)
在这里插入图片描述
结论:第三方提供的类是只读的。无法在第三方类上添加@Component注解或衍生注解。

那么我们应该怎样使用并定义第三方的bean呢?

  • 如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component 及衍生注解声明bean的,就需要用到 @Bean 注解。

1.解决方案1:在启动类上添加@Bean标识的方法

  //声明第三方bean
    @Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
    public SAXReader saxReader(){
        return new SAXReader();
    }

xml文件

<?xml version="1.0" encoding="UTF-8"?>
<emp>
    <name>Tom</name>
    <age>18</age>
</emp>

测试类:

//第三方bean的管理
    @Test
    public void testThirdBean() throws Exception {
        Document document = saxReader.read(this.getClass().getClassLoader().getResource("1.xml"));
        Element rootElement = document.getRootElement();
        String name = rootElement.element("name").getText();
        String age = rootElement.element("age").getText();

        System.out.println(name + " : " + age);
    }

运行结果
在这里插入图片描述
说明:以上在启动类中声明第三方Bean的作法,不建议使用(项目中要保证启动类的纯粹性)

注意事项 :

  • 通过@Bean注解的name或value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名。

  • 如果第三方bean需要依赖其它bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配。

关于Bean只需要保持一个原则:

  • 如果是在项目当中我们自己定义的类,想将这些类交给IOC容器管理,我们直接使用@Component以及它的衍生注解来声明就可以。
  • 如果这个类它不是我们自己定义的,而是引入的第三方依赖当中提供的类,而且我们还想将这个类交给IOC容器管理。此时我们就需要在配置类中定义一个方法,在方法上加上一个@Bean注解,通过这种方式来声明第三方的bean对象。

2.@ComponentScan 组件扫描

当前工程中如果引用了另外一个工程中的坐标依赖,如果包体不在要运行的类所在包以及子类里面,是无法被正常IOC的。但是可以在@SpringBoot的主类中使用注解进行加载此包体。

@SpringBootApplication
@ComponentScan({"com.weijisheng","com.example"}) //指定要扫描的包
public class SpringbootWebConfig2Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class, args);
    }
}

结论:SpringBoot中并没有采用以上这种方案。

3.@Import 导入(使用@Import导入的类会被Spring加载到IOC容器中)

@Import导入

  • 导入形式主要有以下几种:
    1. 导入普通类
    2. 导入配置类
    3. 导入ImportSelector接口实现类

下面是import的类的源码
在这里插入图片描述
1). 使用@Import导入普通类:

@Import(TokenParser.class) //导入的类会被Spring加载到IOC容器中
@SpringBootApplication
public class SpringbootWebConfig2Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class, args);
    }
}

2). 使用@Import导入配置类:

  • 配置类
@Configuration
public class HeaderConfig {
    @Bean
    public HeaderParser headerParser(){
        return new HeaderParser();
    }

    @Bean
    public HeaderGenerator headerGenerator(){
        return new HeaderGenerator();
    }
}
@Import(HeaderConfig.class) //导入配置类
@SpringBootApplication
public class SpringbootWebConfig2Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class, args);
    }
}

3). 使用@Import导入ImportSelector接口实现类:

  • ImportSelector接口实现类
public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //返回值字符串数组(数组中封装了全限定名称的类)
        return new String[]{"com.example.HeaderConfig"};
    }
}

我们使用@Import注解通过这三种方式都可以导入第三方依赖中所提供的bean或者是配置类。

思考:如果基于以上方式完成自动配置,当要引入一个第三方依赖时,是不是还要知道第三方依赖中有哪些配置类和哪些Bean对象?

是的。 (对程序员来讲,很不友好,而且比较繁琐)

思考:当我们要使用第三方依赖,依赖中到底有哪些bean和配置类,谁最清楚?

第三方依赖自身最清楚。

结论:我们不用自己指定要导入哪些bean对象和配置类了,让第三方依赖它自己来指定。

怎么让第三方依赖自己指定bean对象和配置类?

  • 比较常见的方案就是第三方依赖给我们提供一个注解,这个注解一般都以@EnableXxxx开头的注解,注解中封装的就是@Import注解

4). 使用第三方依赖提供的 @EnableXxxxx注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)//指定要导入哪些bean对象或配置类
public @interface EnableHeaderConfig {
}
在使用时只需在启动类上加上@EnableXxxxx注解即可

@EnableHeaderConfig  //使用第三方依赖提供的Enable开头的注解
@SpringBootApplication
public class SpringbootWebConfig2Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class, args);
    }
}

以上四种方式都可以完成导入操作,但是第4种方式会更方便更优雅,而这种方式也是SpringBoot当中所采用的方式。

2.SpringBoot自动配置原理

在这里插入图片描述

@ComponetScan 注解用于扫描某一个包下的所有的类,将类带有注解:@componet,@controller,@service@repository.默认的情况下,会自动的扫描,该注解的类所在的包以及子包。
@SpringBootConfiguration 作用就是修饰类标识该类就是一个配置类。
@EnableAutoConfiguration 启用自动配置


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1.main方法跑起来 (tomcat跑起来)
2.初始化容器 找到@springbootapplication注解
3.找到开启自动配置的注解@enableautoconfiguration
4.注解导入了importSelector (作用就是从外部文件中获取对应的【配置类】的全路径,放到spring容器 就会根据条件自动的进行配置)
5.内部获取到自动配置的注解对应的属性值 作为 外部配置文件的key
6.从meta-info/spring.factories中获取 刚刚上边获取到的key 找配置文件中匹配这个key对应的所有的值
值就是所有需要用到的【自动配置类】
7.交给spring容器,自动的根据条件来执行自动配置 spring容器中就自动的有了某一些Bean


3.@EnableAutoConfiguration(重要–>自动配置原理)

在这里插入图片描述

4.@Conditional

我们在跟踪SpringBoot自动配置的源码的时候,在自动配置类声明bean的时候,除了在方法上加了一个@Bean注解以外,还会经常用到一个注解,就是以Conditional开头的这一类的注解。以Conditional开头的这些注解都是条件装配的注解。下面我们就来介绍下条件装配注解。

@Conditional注解:

  • 作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring的IOC容器中。
  • 位置:方法、类
  • @Conditional本身是一个父注解,派生出大量的子注解:
    • @ConditionalOnClass:判断环境中有对应字节码文件,才注册bean到IOC容器。
    • @ConditionalOnMissingBean:判断环境中没有对应的bean(类型或名称),才注册bean到IOC容器。
    • @ConditionalOnProperty:判断配置文件中有对应属性和值,才注册bean到IOC容器。

5.SpringBoot的yml配置

yaml文件等价于properties文件,在使用过程中都是一样的效果。但是yml文件书写的方式和properties文件不一样。更加简洁,那么我们可以根据需要选择性的使用properties和yml文件。如果同时存在两个文件,那么优先级properties要高于yml。

语法特点如下:

  • 大小写敏感
  • 数据值前必须有空格,作为分隔符
  • 缩进的空格数目不重要,只需要对齐即可
  • # 表示注释

书写格式如下要求如下:key和key之间需要换行以及空格两次。 简单key value之间需要冒号加空格。

key1:
  key2:
    key3: value
key4: value4    
    

比如:

server:
  port: 8081

注意:yml语法中,相同缩进代表同一个级别

# 基本格式 key: value
name: zhangsan
# 数组   - 用于区分
city:
  - beijing
  - tianjin
  - shanghai
  - chongqing
#集合中的元素是对象形式
students:
  - name: zhangsan
    age: 18
    score: 100
  - name: lisi
    age: 28
    score: 88
  - name: wangwu
    age: 38
    score: 90
#map集合形式(了解)
maps: {"name":"zhangsan", "age": "15"}
#参数引用
person:
  name: ${name} # 该值可以获取到上边的name定义的值	

1读取配置文件中的值

获取配置文件中的值我们一般有几种方式:

  • @value注解的方式 只能获取简单值
  • Environment的方式
  • @ConfigurationProperties

yml中配置

# 基本格式 key: value
name: zhangsan
# 数组   - 用于区分
city:
  - beijing
  - tianjin
  - shanghai
  - chongqing
#集合中的元素是对象形式
students:
  - name: zhangsan
    age: 18
    score: 100
  - name: lisi
    age: 28
    score: 88
  - name: wangwu
    age: 38
    score: 90
#map集合形式
maps: {"name":"zhangsan", "age": "15"}
#参数引用
person:
  name: ${name} # 该值可以获取到上边的name定义的值
  age: 12

java代码

@RestController
public class Test2Controller {
    @Value("${name}")
    private String name;

    @Value("${city[0]}")
    private String city0;

    @Value("${students[0].name}")
    private String studentname;

    @Value("${person.name}")
    private String personName;


    @Value("${maps.name}")//value注解只能获简单的值对象
    private String name1;

    @Autowired
    private Student student;

    @RequestMapping("/show")
    public String showHello() {
        System.out.println(name);
        System.out.println(city0);
        System.out.println(studentname);
        System.out.println(personName);
        
        System.out.println(">>>>"+student.getAge());

        return "hello world";
    }
}

pojo

@Component
@ConfigurationProperties(prefix = "person")
public class Student {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

}

6. profile

在开发的过程中,需要配置不同的环境,所以即使我们在application.yml中配置了相关的配置项,当时在测试是,需要修改数据源等端口路径的配置,测试完成之后,又上生产环境,这时配置又需要修改,修改起来很麻烦。

  • yml配置方式
    在这里插入图片描述
    application.yml:
#通过active指定选用配置环境
spring:
  profiles:
    active: pro

application-dev.yml:

#开发环境
server:
  port: 8081

application-test.yml:

#测试环境
server:
  port: 8082

applicatioin-pro.yml

#生产环境
server:
  port: 8083

还有一种是分隔符的方式(了解)

spring:
  profiles:
    active: dev

---
#开发环境
server:
  port: 8081
spring:
  profiles: dev

---
#测试环境
server:
  port: 8082
spring:
  profiles: test

---
#生产环境
server:
  port: 8083
spring:
  profiles: pro

激活profile的方式(了解)

  • 配置文件的方式(上边已经说过)
  • 运行是指定参数 java -jar xxx.jar --spring.profiles.active=test
  • 上面的spring.profiles.active=test就是在测试环境下使用
  • jvm虚拟机参数配置 -Dspring.profiles.active=dev

7.springboot整合mybatis

1.导入依赖

    <!--驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
            <!--mybatis的 起步依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>

2.配置yml文件

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost/springboot_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 1998
#配置mapper的映射文件的位置
mybatis:
  mapper-locations: classpath:mappers/*Mapper.xml

3.Mapper扫描

@SpringBootApplication
@MapperScan(basePackages = "com.itheima.dao")
//MapperScan 用于扫描指定包下的所有的接口,将接口产生代理对象交给spriing容器
public class MybatisApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisApplication.class,args);
    }
}

8.SpringBoot整合Junit

1.添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

2.在service层创建一个UserService

@Service
public class UserService {

    public String getUser() {
        System.out.println("获取用户的信息");
        return "zhangsan";
    }
}

3.在test/java/下创建测试类,类的包名和启动类的报名一致即可

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootApplicationTests {

    @Autowired
    private UserService userService;

   
    @Test
    public void getUser() {
        String userinfo = userService.getUser();
        System.out.println(userinfo);
    }
}

4.解释

@RunWith(SpringRunner.class) 使用springrunner运行器
@SpringBootTest 启用springboot测试

使用的方式和之前的spring的使用方式差不多。

9.SpringBoot整合redis

1.添加起步依赖
2.准备好redis服务器 并启动
3.在Service中的方法中使用
3.1 注入redisTemplate
3.2 在方法中进行调用
4.配置yml 配置redis的服务器的地址
1.添加起步依赖

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

2.配置redis链接信息

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost/springboot_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 
  redis:
    host: localhost
    port: 6379
#配置mapper的映射文件的位置
mybatis:
  mapper-locations: classpath:mappers/*Mapper.xm

3.service中调用

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public List<User> findAllUser() {

        //1.获取redis中的数据
        List<User> list = (List<User>) redisTemplate.boundValueOps("key_all").get();
        //2.判断 是否有,如果有则返回,如果没有则从mysql中获取设置到redis中再返回
        if (list != null && list.size() > 0) {
            return list;
        }
        List<User> allUser = userMapper.findAllUser();

        //3 从mysql中获取设置到redis中再返回
        redisTemplate.boundValueOps("key_all").set(allUser);

        return allUser;
    }
}

10.redis的序列化机制

在这里插入图片描述

如上图所示,出现了乱码,这个是由于redis的默认的序列化机制导致的。这里需要注意下:并不是错误,由于序列化机制,导致我们数据无法正常显示。如果有代码的方式获取则是可以获取到数据的。
1,默认的情况下redisTemplate操作key vlaue的时候 必须要求 key一定实现序列化 value 也需要实现序列化
2,默认的情况下redisTemplate使用JDK自带的序列化机制:JdkSerializationRedisSerializer
3,JDK自带的序列化机制中要求需要key 和value 都需要实现Serializable接口
4. RedisTemplate支持默认以下几种序列化机制:机制都实现了RedisSerializer接口
+ OxmSerializer
+ GenericJackson2JsonRedisSerializer
+ GenericToStringSerializer
+ StringRedisSerializer
+ JdkSerializationRedisSerializer
+ Jackson2JsonRedisSerializer
1.我们可以进行自定义序列化机制:例如:我们定义key 为字符串序列化机制,value:为JDK自带的方式则,应当处理如下:

@Bean
public RedisTemplate<Object, Object> redisTemplate(
        RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
    RedisTemplate<Object, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(redisConnectionFactory);
    //设置key的值为字符串序列化方式 那么在使用过程中key 一定只能是字符串
    template.setKeySerializer(new StringRedisSerializer());
    //设置value的序列化机制为JDK自带的方式
    template.setValueSerializer(new JdkSerializationRedisSerializer());
    return template;
}

在工作中,根据我们业务的需要进行设置和选择,如果没有合适的还可以自己定义。只要实现RedisSerializer接口即可。

11.SpringBoot自定义starer(略)

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值