SpringBoot3-外部化配置与aop实现

POM文件中为何要以继承的方式引入SpringBoot?

继承父工程的优势

  1. 依赖管理:在父工程中定义依赖的版本,子模块直接引用而不必指定版本号
  2. 插件管理:在父工程中配置插件,子模块直接使用
  3. 属性设置:在父工程中定义一些通用属性,如项目编码、java版本等
  4. 统一配置:可以统一多个子模块的构建配置,确保一致性。

继承了父工程,那么引入依赖的时候不需要指定版本号,因为在父工程中,各种依赖的版本号已经预设好了。

SpringBoot核心注解

@SpringBootApplication

被此标注表示该类是SpringBoot项目的入口类。

此注解被以下三个注解标注,说明@SpringBootApplication同时有以下三个注解的功能

复制代码
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

@SpringBootConfiguration

@SpringBootConfiguration被@Configuration标注说明项目的主入口类同时是一个配置类,因此在主入口类中使用@Bean注解方法的话,该方法返回值对象会被纳入Ioc容器管理。

@EnableAutoConfiguration

启用自动配置,SpringBoot默认情况下启用自动配置。

自动配置有什么用?

自动配置只要启动,SpringBoot就会去类路径中查找Class。根据类路径中有某些类来自动管理Bean,不需要程序员手动配置。

比如SpringBoot检测到SqlSessionFactory,或者在application.properties中配置了数据源,SpringBoot会认为项目中含有MyBatis框架,会将MyBatis相关的Bean初始化,然后放到Ioc容器中管理起来。

@ComponentScan

@ComponentScan负责组件扫描。会扫描此包及此包下所有子包或子包的子包等的路径。

外部化配置

外部化配置是指将配置信息存储 在应用程序代码之外的地方。这样配置信息独立于 代码进行管理。方便配置修改。修改后不需要重新编译,也不需要重新部署。

springboot默认先找外部化配置

Application.properites

  • Application.properites配置文件是SpringBoot默认的额配置文件

  • Application.properites不是必须的,SpringBoot提供了默认配置,如果需要修改默认配置,就在Application.properites中进行配置。

  • Application.properites可以放在类路径中,也可以放在项目之外,因此成为外部化配置

SpringBoot在启动时会从以下位置按顺序加载Application.properites:

  1. file:./config/: 首先在SpringBoot当前工作目录下的config文件夹中查找(如果没找到Application.properites,会继续查找Application.yml,2个都没找到,才会进入下一个位置查找,以此类推)
  2. file:./: 这里找不到会继续查找下一个位置
  3. classpath:/config/:
  4. classpath:/

如果在多个位置有相同属性的定义,那么最先检查的位置中的属性值先使用。

如果要指定配置文件位置,可以通过--spring.config.location=进行指定,比如:

cmd 复制代码
java -jar xxxx.jar --spring.config.location=file:///E:\a\b\application.properties

注意:以上的--spring.config.location=file:///E:\a\b\application.properties属于命令行参数,会被传递到main方法的(String[] args)参数上。

读取配置

java 复制代码
// 读取配置文件中myapp.path的值,
// 如果这个key不存在,并且没有指定默认值,那么会报错
    // ${myapp.path:50}指定myapp.path的默认值是50
    @Value("${myapp.path:50}")
    private String appPath;

YAML语法规则

数据结构

  1. 支持多种数据结构,包括:字符串、数字、布尔值、数组、List集合、Map键值对等

  2. yaml使用一个空格来分割属性名和属性值,比如:

yaml 复制代码
name: jack
  1. yaml使用换行+空格表示层级关系,注意不能使用tab 必须是空格,空格数量无要求,建议2个或4个,比如:
yaml 复制代码
myapp: 
  name: mall
  1. 同级元素左侧对其
  2. 大小写敏感
  3. 使用# 进行注释
  4. 在一个映射中,键必须唯一
  5. 普通文本可以使用单引号,也可以使用双引号,也可以什么都不用**(单引号中的\n表示普通文本,双引号中的\n表示换行)**
  6. 保留文本原格式使用 | 比如:
yaml 复制代码
username: |
  aaaa
  bbb
  ccc
  1. 文档切割: --- 这个符号下面的配置认为是一个独立的yaml文件,便于大文件的阅读。

配置文件合并

properties 复制代码
#properties文件合并
# 对于数组来说,使用逗号进行分隔开
spring.config.import=classpath:/application-mysql.properties,classpath:/application-redis.properties

yml文件合并的第一种写法

yaml 复制代码
spring: 
  config: 
    import: [classpath:/application-mysql.yml,classpath:/application-redis.yml]

yml文件合并的第二种写法

yaml 复制代码
spring: 
  config: 
    import: 
      - classpath:/application-mysql.yml
      - classpath:/application-redis.yml

多环境切换

开发环境配置文件:application-dev.properties

测试环境配置文件:application-test.properties

预生产环境配置文件:application-preprod.properties

生产环境配置文件:application-prod.properties

如果启用生产环境配置,可以有以下两种操作方式:

  1. 在application.properties添加配置:spring.profiles.active=prod
  2. 在命令行参数上添加: --spring.profiles.active=prod

将配置绑定到简单Bean

java 复制代码
package com.ali.bindtobean.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

// 纳入Ioc容器
@Component
// 将配置文件一次性绑定到Bean对象上
@ConfigurationProperties(prefix = "myapp")
public class AppConfig {

    // 要实现一次性绑定,配置文件中的属性名 必须和Bean对象的属性名要一致
    // 底层在给对象属性赋值时,调用了setter方法,因此每个属性必须有setter方法
  private String name;
  private Integer age;
  private String password;
  private Boolean gender;

    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;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Boolean getGender() {
        return gender;
    }

    public void setGender(Boolean gender) {
        this.gender = gender;
    }

    @Override
    public String toString(){
        return "AppConfig [name=" + name + ", age=" + age + ", gender=" + gender + "]";
    }
}
properties 复制代码
spring.application.name=bindtobean

myapp.name=jack
myapp.age=12
myapp.password=123
myapp.gender=true

绑定嵌套Bean

在一个Bean的属性中,有一个其他Bean类型。这样就是嵌套Bean。

java 复制代码
package com.ali.bindtobean.bean;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "app.xyz")
public class User {
    private String name;
    private Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User [name=" + name + ", address=" + address.toString() + "]";
    }
}
java 复制代码
package com.ali.bindtobean.bean;

public class Address {
    private String city;
    private String street;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    @Override
    public String toString() {
        return "Address [city=" + city + ", street=" + street + "]";
    }
}
properties 复制代码
app.xyz.name=lucy
app.xyz.address.city=xj
app.xyz.address.street=dayang

其他方式绑定Bean

java 复制代码
// 在主入口程序添加以下注解,启用将配置信息绑定到User这个Bean
@EnableConfigurationProperties({User.class, Address.class})

另一种方式:

java 复制代码
// 在主入口程序添加以下注解,扫面指定包。将配置信息绑定到这个包下的类
@ConfigurationPropertiesScan(basePackages = "com.ali.bindtobean.bean")

复杂的属性结构绑定Bean

绑定数组、集合、Map到Bean

properties 复制代码
app2.abc.names[0]=jack
app2.abc.names[1]=lucy
app2.abc.names[2]=tom

app2.abc.addresses[0].city=bj
app2.abc.addresses[0].street=chaoyang
app2.abc.addresses[1].city=tj
app2.abc.addresses[1].street=nankai

app2.abc.addressList[0].city=bj_list
app2.abc.addressList[0].street=chaoyang_list
app2.abc.addressList[1].city=tj_list
app2.abc.addressList[1].street=nankai_list

# addr1 和addr2 都是key
app2.abc.addressMap.addr1.city=bj_map
app2.abc.addressMap.addr1.street=chaoyang_map
app2.abc.addressMap.addr2.city=tj_map
app2.abc.addressMap.addr2.street=nankai_map

yaml文件配置方式如下:

yaml 复制代码
app2:
    abc:
        names:
          - tom
          - smith
        addresses:
            - city: beijing
              street: chaoyang
            - city: tianjin
              street: nankai
       # addressList 可以写成 address-list
        addressList:
            - city: beijing
              street: chaoyang
            - city: tianjin
              street: nankai
        addressMap:
            addr1:
                city: beijing
                street: chaoyang
            addr2:
                city: tianjin
                street: nankai
java 复制代码
package com.ali.bindtobean.bean;

import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Map;

@ConfigurationProperties(prefix = "app2.abc")
public class AppBean {
    // 数组中元素是简单类型
    private String[] names;
    // 数组中元素是Bean
    private Address[] addresses;
    //List集合。List中元素是Bean
    private  List<Address> addressList;
    //Map集合: String,Bean
    private Map<String,Address> addressMap;

    public void setNames(String[] names) {
        this.names = names;
    }

    public void setAddresses(Address[] addresses) {
        this.addresses = addresses;
    }

    public void setAddressList(List<Address> addressList) {
        this.addressList = addressList;
    }

    public void setAddressMap(Map<String, Address> addressMap) {
        this.addressMap = addressMap;
    }

    @Override
    public String toString() {
        return "";
    }
}

将配置绑定到第三方对象

yaml 复制代码
other:
  abc:
    city: beijing
    street: daxing
java 复制代码
package com.ali.bindtobean.config;

import com.ali.bindtobean.bean.Address;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig2 {

    //  address 是第三方类,使用以下方式完成配置到属性的绑定
    @Bean
    @ConfigurationProperties(prefix = "other.abc")
    public Address address() {
        return new Address();
    }
}

指定配置数据来源

java 复制代码
@Component
@ConfigurationProperties(prefix = "app2.abc")
// 指定配置数据来自/a/b/group-info.properties路径的配置文件
@PropertySource("classpath:/a/b/group-info.properties")
public class AppBean {
...
}

@ImportResource注解

当SpringBoot项目中出现ApplicationContext.xml文件。并且文件中配置了Bean。要把这个Bean注入到容器中。

java 复制代码
package com.ali.bindtobean.bean;


public class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return super.toString();
    }
}

配置文件如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="com.ali.bindtobean.bean.Person">
        <property name="name" value="jack"/>
        <property name="age" value="20"/>
    </bean>
</beans>
java 复制代码
// 在主入口程序添加以下注解让applocationContext.xml文件生效
@ImportResource("classpath:/applocationContext.xml")

Environment

spring提供的一个接口。SpringBoot启动的时候会把环境、系统信息封装到Environment对象中,需要获取这些信息,可使用Environment接口的方法。

Environment对象主要包括

  • 当前激活的配置文件 active-profiles
  • 系统属性,如系统名字 、java版本
  • 环境变量
  • 应用程序启动时传给主方法的命令行参数
java 复制代码
@Autowired
private Environment environment;

public void  doSomething() {
    // 获取当前激活的配置文件
    String[] activeProfiles = environment.getActiveProfiles();
    for (String activeProfile : activeProfiles) {
        System.out.println(activeProfile);
    }
    //  获取配置信息
    String property = environment.getProperty("app.xyz.address.city");
    System.out.println(property);
}

SpringBoot aop实现

添加依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>3.3.5</version>
</dependency>

编写切面类

java 复制代码
package com.ali.springaop.component;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;

import java.util.Arrays;

// 指定切面类
@Aspect
@Component
public class LoggingAspect {
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    // 定义切入点,匹配所有以"service"结尾的包下的所有方法
    @Pointcut("execution(* com.ali.springaop.service..*(..))")
    public void ServiceMethods(){

    }

    // 前置通知,切入点的方法执行前执行此代码
    @Before("ServiceMethods()")
    public void before(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        logger.info("Method [{}] with parameters [{}] is called", methodName, Arrays.toString(args));
    }
}
相关推荐
ThinkPet2 小时前
【AI】大模型知识入门扫盲以及SpringAi快速入门
java·人工智能·ai·大模型·rag·springai·mcp
派大鑫wink2 小时前
【Day39】Spring 核心注解:@Component、@Autowired、@Configuration 等
java·后端·spring
输出输入2 小时前
JAVA能进行鸿蒙系统应用的开发吗
java
a努力。2 小时前
宇树Java面试被问:数据库死锁检测和自动回滚机制
java·数据库·elasticsearch·面试·职场和发展·rpc·jenkins
PwnGuo2 小时前
Android逆向:在 Unidbg 中解决 native 函数内调用 Java 方法的报错
android·java·python
输出输入3 小时前
IJ IDEA 目录结构
java
Kratzdisteln3 小时前
【1902】预先生成完整的树状PPT结构
java·前端·powerpoint
Sylvia-girl3 小时前
Lambda表达式
java·开发语言
Pluchon3 小时前
硅基计划4.0 算法 动态规划入门
java·数据结构·算法·动态规划