【深入浅出Spring原理及实战】「夯实基础系列」360全方位渗透和探究Spring配置开发实战详解

360全方位渗透和探究Spring配置开发实战详解

  • Spring对于配置的转折点
  • Xml配置 vs Java配置
    • Xml配置模式的优点
    • Xml配置模式的缺点
    • Java配置模式的优点
    • Java配置模式的缺点
  • Java编程配置流程
    • 配置代码案例
  • 组件注入
    • Bean注解
      • 配置自动扫描包路径和规则
        • @Filter常用的拦截类型
        • FilterType.ASSIGNABLE_TYPE的过滤实现
        • FilterType.ANNOTATION的过滤实现
        • FilterType.ANNOTATION的过滤实现
        • FilterType.CUSTOM的过滤实现

Spring对于配置的转折点

我们都知道,Spring4在配置方式上有一个重要的变革点。在Spring4.x之前,通常使用XML配置来完成应用的基本配置,而在业务逻辑中则推荐使用注解方式。但是自从Spring4.x开始,官方推荐使用基于Java的编程配置来代替XML配置,这是一个重要的转变。

Xml配置 vs Java配置

Xml配置模式的优点

XML配置的优点在于对于老一辈程序员来说非常熟悉和简单,易于扩展,不需要重新编译就能修改应用配置参数。

Xml配置模式的缺点

XML配置的缺点是读取和解析配置文件需要耗费时间,当配置文件过多时会显得臃肿,管理起来不方便。

Java配置模式的优点

基于Java的编程配置的优点是结构更清晰,可读性更高,同时也节省了解析XML的时间。

Java配置模式的缺点

基于Java的编程配置的缺点是修改应用配置参数需要重新编译。然而,在实际的生产环境中,应用配置完成后很少会随意修改,因此这并不是一个大问题。

Java编程配置流程

【深入浅出Spring原理及实战】「夯实基础系列」360全方位渗透和探究Spring配置开发实战详解_第1张图片

  1. 配置类(通常命名为AppConfig),并在类名上添加@Configuration注解,这样告诉Spring这是一个配置类,类似于XML文件。

  2. 如果有外部配置文件,可以在类名上添加@PropertySource注解,并指定properties文件的路径。这样可以加载外部配置文件中的属性值。

  3. 在需要获取应用配置属性值的地方,可以在对应的变量上添加@Value注解,通过${}表达式来获取配置文件中的参数。

  4. 如果需要进行依赖注入,可以在相应的方法上添加@Bean注解,并返回对应的Bean对象。也可以使用FactoryBean来创建对应的Bean。

配置代码案例

在这个例子中,我们声明了一个配置类DataSourceConfiguration 。它使用@Configuration注解标识为配置类,@PropertySource注解引入了一个名为application.properties的外部文件,@ComponentScan配置了自动扫描的包路径。

/**
 * Spring 配置类
 * 配置数据源,事务管理,bean,自动扫描包
 */
@Configuration	// 声明该类为配置类
@PropertySource({"classpath:application.properties"})	// 引入外部文件
@ComponentScan("com.example")	// 配置自动扫描包的路径
public class DataSourceConfiguration {
    
 private String DB_USERNAME;
    
    @Value("${database.password}")
    private String DB_PASSWORD;
    
    @Value("${database.driver}")
    private String DB_DRIVER;
    
    @Value("${database.jdbcUrl}")
    private String DB_JDBC_URL;
    
    @Bean // 数据源
    public DataSource dataSource() {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(DB_USERNAME);
        dataSource.setPassword(DB_PASSWORD);
        dataSource.setDriverClass(DB_DRIVER);
        dataSource.setJdbcUrl(DB_JDBC_URL);
        return dataSource;
    }
}

在配置类中,我们使用@Value注解来注入外部属性文件中的值。具体来说,我们注入了数据库用户名、密码、驱动和JDBC URL四个属性。

我们使用@Bean注解来定义了一个dataSource()方法,该方法返回一个数据源对象ComboPooledDataSource。在方法中,我们根据注入的属性值设置了数据源的相关属性,最终将数据源返回。

组件注入

【深入浅出Spring原理及实战】「夯实基础系列」360全方位渗透和探究Spring配置开发实战详解_第2张图片

  • Bean注解:Bean注解类似于XML文件中的标签,用于将一个方法或类标记为Spring容器中的一个组件。被Bean注解修饰的方法名对应标签中的id,也可以通过Bean注解的value属性设置id的值。在Spring Boot的底层代码中广泛使用。

  • 默认单实例:在Spring中,默认情况下,被注解为Bean的对象是单例的。即容器启动后会创建对象,并将其保存在容器中,以后每次使用时从容器中获取。

  • 懒加载:如果希望在需要使用时才创建对象,并将其保存在容器中以供下次使用,可以使用Lazy注解修饰对象。当使用到该对象时,容器会创建并保存,以后每次使用时都从容器中获取。

  • 多实例:如果希望每次使用时都创建一个新对象而不是使用先前创建的对象,则可以使用Scope注解。将其参数值设置为prototype,即@Scope(“prototype”)。

  • 条件注入:如果希望根据条件选择需要注入的Bean,可以使用注解Conditional进行条件判断。Spring Boot的底层代码中广泛使用该注解。

Bean注解

在这个例子中,我们使用@Bean注解来配置一个名为sampleBeanName的Bean。我们在方法名SampleBean ()上使用了@Bean注解,并将其value值设置为sampleBeanName,表示该Bean的id为sampleBeanName

@Bean(value="sampleBeanName")
public SampleBean sampleBean() {
    return new SampleBean ();
}

在方法内部,我们返回一个SampleBean 对象。这样,在容器启动时,会创建一个名为sampleBeanName的Bean,并将其保存在容器中,供其他组件使用。

通过这个例子,我们可以学习到如何使用@Bean注解来配置Bean,并设置Bean的id。这种方式相当于在XML文件中使用标签来配置Bean。

  1. 注解Bean的value值表示bean的id
  2. 注解Bean的value值未设置,则方法名表示bean的id

配置自动扫描包路径和规则

@Filter常用的拦截类型
  • FilterType.ANNOTATION:按照注解进行过滤
  • FilterType.ASSIGNABLE_TYPE:按照指定类型进行过滤,包括子类和实现类
  • FilterType.CUSTOM:使用自定义规则进行过滤
FilterType.ASSIGNABLE_TYPE的过滤实现

当使用FilterType.ASSIGNABLE_TYPE时,你可以根据给定的类型及其子类和实现类来进行过滤。下面是一个使用FilterType.ASSIGNABLE_TYPE的过滤实现的案例:

interface Animal {
    void makeSound();
}

class Dog implements Animal {
    public void makeSound() {
        System.out.println("Dog barks...");
    }
}

class Cat implements Animal {
    public void makeSound() {
        System.out.println("Cat meows...");
    }
}

现在,我们定义一个Filter来只扫描Animal及其子类:

import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.filter.AbstractClassTestingTypeFilter;
import org.springframework.core.type.filter.TypeFilter;

public class AssignableTypeFilter implements TypeFilter {
    private final Class<?> targetType;

    public AssignableTypeFilter(Class<?> targetType) {
        this.targetType = targetType;
    }

    @Override
    public boolean match(ClassMetadata metadata) {
        try {
            Class<?> clazz = Class.forName(metadata.getClassName());
            return targetType.isAssignableFrom(clazz);
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}

然后,在@ComponentScan注解中使用FilterType.ASSIGNABLE_TYPE过滤器:

@ComponentScan(basePackages = "com.example",
        includeFilters = {
            @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = Animal.class)
        })
public class MyApplication {
    // ...
}

在上述示例中,我们定义了一个AssignableTypeFilter类,实现了TypeFilter接口,并在match()方法中通过isAssignableFrom()方法判断待扫描的类是否是给定类型或其子类或实现类。

在@ComponentScan注解中使用@ComponentScan.Filter指定type为FilterType.ASSIGNABLE_TYPE,并将Animal类作为参数传入,这样在扫描时会过滤出Animal及其子类。

FilterType.ANNOTATION的过滤实现
@ComponentScan(value = "com.example",
    excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, 
        classes = {Controller.class, Service.class, Repository.class}),
    includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,
        classes = {CustomAnnotation.class}),
    useDefaultFilters = false)

在这段代码中,我们使用@ComponentScan注解来配置自动扫描的包路径和规则。

  1. 我们使用@ComponentScan注解的value属性来设置自动扫描的包路径,这里设置为"com.example"。

  2. 我们通过设置@ComponentScan注解的excludeFilters属性来排除某些类型的组件。使用@ComponentScan.Filter注解来指定需要排除的类型,其中type=ANNOTATION表示按照注解进行排除。具体的注解类如Controller、Service、Repository。

    • 同时,我们通过设置@ComponentScan注解的includeFilters属性来包含某些类型的组件。同样使用@ComponentScan.Filter注解来指定需要包含的类型,也是按照注解进行筛选。这里的例子中使用了自定义注解类CustomAnnotation。
  3. 我们通过设置@ComponentScan注解的useDefaultFilters=false来关闭Spring默认扫描全部的功能,使includeFilters生效。

FilterType.ANNOTATION的过滤实现
FilterType.CUSTOM的过滤实现

上面案例已经介绍了对应的type=ANNOTATION的案例,当使用FilterType.CUSTOM来进行过滤时,你需要自定义一个类来实现TypeFilter接口,然后根据自己的规则进行过滤。

以下是一个示例:

import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.filter.AbstractClassTestingTypeFilter;
import org.springframework.core.type.filter.TypeFilter;

public class CustomFilter implements TypeFilter {
    
    @Override
    public boolean match(ClassMetadata metadata) {
        // 在这里实现自定义的过滤逻辑
        // 返回 true 表示匹配成功,类将被扫描
        // 返回 false 表示匹配失败,类将被排除
        
        // 例如,只扫描带有"Service"字符串的类
        String className = metadata.getClassName();
        return className.contains("Service");
    }
}

然后,在@ComponentScan注解中使用自定义的过滤器:

@ComponentScan(basePackages = "com.example",
        excludeFilters = {
            @ComponentScan.Filter(type = FilterType.CUSTOM, classes = CustomFilter.class)
        })
public class MyApplication {
    // ...
}

在上述示例中,我们定义了一个CustomFilter类,实现了TypeFilter接口,并在match()方法中实现了自定义的过滤逻辑。在这个示例中,我们只会扫描带有"Service"字符串的类,排除其他类。在@ComponentScan注解中使用@ComponentScan.Filter指定type为FilterType.CUSTOM,并将CustomFilter类作为参数传入,这样在扫描时会应用我们定义的自定义过滤器来进行过滤。

你可能感兴趣的:(#,spring,python,java)