129.【Spring 注解_IOC】(二)

简介: 129.【Spring 注解_IOC】
(2).注解扫描注入方式

MyConfig.java 带有@Configuration的注解一开始就会被默认加入我们的组件中去。

package com.jsxs.config;
import com.jsxs.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
 * @Author Jsxs
 * @Date 2023/8/12 9:05
 * @PackageName:com.jsxs.config
 * @ClassName: MyConfig
 * @Description: TODO
 * @Version 1.0
 */
@ComponentScan(value = "com.jsxs")
@Configuration
public class MyConfig {
    @Bean
    public Person person(){
        return new Person("李三",21);
    }
}

(3).指定扫描或不扫描的包 (过滤)
  1. 使用 @ComponentScan 进行操作扫描包 - (排除xxx)
package com.jsxs.config;
import com.jsxs.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
/**
 * @Author Jsxs
 * @Date 2023/8/12 9:05
 * @PackageName:com.jsxs.config
 * @ClassName: MyConfig
 * @Description: TODO
 * @Version 1.0
 */
// 指定扫描com.jsxs这个包下面的全部文件,但是排除掉注解为 Controller 和 Service 的组件
@ComponentScan(value = "com.jsxs",
        excludeFilters = {  // 这里是排除掉xxx
                @ComponentScan.Filter(
                        type = FilterType.ANNOTATION,
                        classes = {
                                Controller.class,
                                Service.class
                        }
                )
        })
@Configuration
public class MyConfig {
    @Bean
    public Person person() {
        return new Person("李三", 21);
    }
}

结果我们发现: 我们IOC容器中的组件中少我们指定的Controller 和 Service 这两个组件标注的组件

  1. 使用 @ComponentScans 进行扫描多个 @ComponentScan (不排除xxx)
package com.jsxs.config;
import com.jsxs.bean.Person;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
/**
 * @Author Jsxs
 * @Date 2023/8/12 9:05
 * @PackageName:com.jsxs.config
 * @ClassName: MyConfig
 * @Description: TODO
 * @Version 1.0
 */
// 这里可以通过 @ComponentScans 配置多个 @@ComponentScan
    @ComponentScans(value = {
// 指定扫描com.jsxs这个包下面的全部文件,但是排除掉注解为 Controller 和 Service 的组件
            @ComponentScan(value = "com.jsxs",
                    includeFilters = {  // 这里是只有包含这些组件的才被打印出来
                            @ComponentScan.Filter(
                                    type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}
                            )}, useDefaultFilters = false)  // 这里就相当于把所有的非includeFilters全部过滤掉,不显示(默认为true)
    })
@Configuration
public class MyConfig {
    @Bean
    public Person person() {
        return new Person("李三", 21);
    }
}

我们发现我们的结果 只有我们通过配置

3. 自定义TypeFilter指定过滤规则 @Filter

(1).自定义我们的扫描过滤器

1. 过滤的文件是这样写的

package com.jsxs.config;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
/**
 * @Author Jsxs
 * @Date 2023/8/12 19:57
 * @PackageName:com.jsxs.config
 * @ClassName: MyTypeFilter
 * @Description: TODO  自定义过滤的话,我们需要实现这个 TypeFilter 接口
 * @Version 1.0
 */
public class MyTypeFilter implements TypeFilter { //⭐
    /**
     *
     * @param metadataReader  : 读取当前正在扫描的类
     * @param metadataReaderFactory : 可以获取到其他任何类的信息
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
         // 1.获取当前类注解的信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        // 2.获取当前正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        // 3.获取当前类资源(类路径)
        Resource resource = metadataReader.getResource();
         // 4.获取当前类的类名
        String className = classMetadata.getClassName();
        System.out.println("---> "+className);
        // 5.假如 类名中存在 er 的话,我们就将这个容器注入到组件中去
        if (className.contains("er")){
            return true;
        }
        return false;
    }
}

2. 配置文件是这样写的

package com.jsxs.config;
import com.jsxs.Mapper.BookMapper;
import com.jsxs.bean.Person;
import com.jsxs.service.BookService;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
/**
 * @Author Jsxs
 * @Date 2023/8/12 9:05
 * @PackageName:com.jsxs.config
 * @ClassName: MyConfig
 * @Description: TODO
 * @Version 1.0
 */
/**
 *  TODO: 1. ANNOTATION 按照注解 ; 2.  ASSIGNABLE_TYPE 按照给定的类型 3.CUSTOM 自定义规则
 */
// 1. 这里是一个数组,可以包含多个@ComponentScan
@ComponentScans(value = {
        //  2. 指定扫描com.jsxs这个包下面的全部文件,但是排除掉注解为 Controller 和 Service 的组件
        @ComponentScan(value = "com.jsxs",
                // 3. 这里是一个数组,可以包含多个 Filter
                includeFilters = {  // 4. 这里是只有包含这些组件的才被打印出来
                        // 4.1 第一个Filter ⭐
                        @ComponentScan.Filter( // 5. 类型是注解 ; 要过滤的类文件
                                type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}
                        ),
                        // 4.2 第二个Filter ⭐
                        @ComponentScan.Filter(
                                type = FilterType.ASSIGNABLE_TYPE,classes = {BookMapper.class}
                        ),
                        // 4.3 第三个Filter ⭐
                        @ComponentScan.Filter(  // 5.指向我们自定义的过滤类信息
                                type = FilterType.CUSTOM,classes = {MyTypeFilter.class}
                        )
                }, useDefaultFilters = false)  // 这里就相当于把所有的非includeFilters全部过滤掉,不显示
})
@Configuration
public class MyConfig {
    @Bean
    public Person person() {
        return new Person("李三", 21);
    }
}

4.设置组件的作用域 @Scope

(1).组件默认是单实列

组件默认在IOC容器中是单实列的,也就是说我们在IOC容器中使用的组件都是同一个组件。

1. 进行编写我们的配置文件

package com.jsxs.config;
import com.jsxs.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @Author Jsxs
 * @Date 2023/8/12 20:20
 * @PackageName:com.jsxs.config
 * @ClassName: MyConfig2
 * @Description: TODO
 * @Version 1.0
 */
@Configuration
public class MyConfig2 {
    @Bean("person2")
    public Person person(){
        return new Person("张三2",22);
    }
}

2.测试类

package com.jsxs.Test;
import com.jsxs.bean.Person;
import com.jsxs.config.MyConfig2;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
 * @Author Jsxs
 * @Date 2023/8/12 20:22
 * @PackageName:com.jsxs.Test
 * @ClassName: Main_Ann2
 * @Description: TODO
 * @Version 1.0
 */
public class Main_Ann2 {
    public static void main(String[] args) {
        // 1.将这个配置文件中的组件放入我们的IOC容器中
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig2.class);
        // 2.通过我们的组件名获取我们指定的组件信息
        Person person = (Person)applicationContext.getBean("person2");
        Person person2 = (Person)applicationContext.getBean("person2");
        // 3.打印出我们的组件信息
        System.out.println(person);
        // 4.遍历我们所有的组件
        for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
        // 5.组件默认是单实列的 即 在IOC容器中,我们一直使用的都是同一个组件
        System.out.println("判断是否是一个单实列的,结果是:"+(person2==person));
    }
}

(2).修改为多实列 (注解)
package com.jsxs.config;
import com.jsxs.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
 * @Author Jsxs
 * @Date 2023/8/12 20:20
 * @PackageName:com.jsxs.config
 * @ClassName: MyConfig2
 * @Description: TODO
 * @Version 1.0
 */
@Configuration
public class MyConfig2 {
    /**  1. singleton 但是列的
     *   2.prototype 多实列   (默认值)
     *   3. 同一次请求创建一个实列
     *   4. 同一个session创建一个实列
     */
    @Scope(value = "prototype")  // ⭐ 我们这在里修改为多实列的
    @Bean("person2")
    public Person person(){
        return new Person("张三2",22);
    }
}
package com.jsxs.Test;
import com.jsxs.bean.Person;
import com.jsxs.config.MyConfig2;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
 * @Author Jsxs
 * @Date 2023/8/12 20:22
 * @PackageName:com.jsxs.Test
 * @ClassName: Main_Ann2
 * @Description: TODO
 * @Version 1.0
 */
public class Main_Ann2 {
    public static void main(String[] args) {
        // 1.将这个配置文件中的组件放入我们的IOC容器中
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig2.class);
        // 2.通过我们的组件名获取我们指定的组件信息
        Person person = (Person)applicationContext.getBean("person2");
        Person person2 = (Person)applicationContext.getBean("person2");
        // 3.打印出我们的组件信息
        System.out.println(person);
        // 4.遍历我们所有的组件
        for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
        // 5.组件默认是单实列的 即 在IOC容器中,我们一直使用的都是同一个组件  ⭐
        System.out.println("判断是否是一个单实列的,结果是:"+(person2==person));
    }
}

(3).使用我们的XML文件进行实现多实列
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--     包自动扫描: 凡是带有 @Controller @Service @Repository @Component     -->
    <context:component-scan base-package="com.jsxs"/>
    <!--  通过Bean的方式进行我们的组件注入的操作  -->
    <bean id="person" class="com.jsxs.bean.Person" scope="prototype">
        <property name="name" value="李明"/>
        <property name="age" value="19"/>
    </bean>
</beans>

小结: 我们使用多实列的情况下,我们的组件并不会在IOC创建完成的时候进创建我们的实列,而是当我们使用到组件的时候,才会帮助我们创建我们的实列。 (懒汉式)

单实列: 我们的组件会在我们的容器创建完成之后就会帮助我们创建我们的实列。 (饿汉式)

5.@Layz-bean懒加载

(1).懒加载

单实例bean,默认在容器创建启动的时候就会创建对象。但是在懒加载模式下,容器启动的时候不会创建对象,而是在第一次使用(获取)Bean的时候才会创建并初始化。

单实列 + 懒加载 ≠ 多实例

1. 设置配置类的组件为 单实列+懒加载

package com.jsxs.config;
import com.jsxs.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
/**
 * @Author Jsxs
 * @Date 2023/8/12 20:20
 * @PackageName:com.jsxs.config
 * @ClassName: MyConfig2
 * @Description: TODO
 * @Version 1.0
 */
@Configuration
public class MyConfig2 {
    /**  1. singleton 但是列的
     *   2.prototype 多实列   (默认值)
     *   3. 同一次请求创建一个实列
     *   4. 同一个session创建一个实列
     */
    @Scope(value = "prototype")  //⭐
    @Bean("person2")
    @Lazy  //⭐
    public Person person(){
        return new Person("张三2",22);
    }
}

2.进行测试

package com.jsxs.Test;
import com.jsxs.bean.Person;
import com.jsxs.config.MyConfig2;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
 * @Author Jsxs
 * @Date 2023/8/12 20:22
 * @PackageName:com.jsxs.Test
 * @ClassName: Main_Ann2
 * @Description: TODO
 * @Version 1.0
 */
public class Main_Ann2 {
    public static void main(String[] args) {
        // 1.将这个配置文件中的组件放入我们的IOC容器中
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig2.class);
        // 2.通过我们的组件名获取我们指定的组件信息
        Person person = (Person)applicationContext.getBean("person2");
        Person person2 = (Person)applicationContext.getBean("person2");
        // 3.打印出我们的组件信息
        System.out.println(person);
        // 4.遍历我们所有的组件
        for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
        // 5.组件默认是单实列的 即 在IOC容器中,我们一直使用的都是同一个组件
        System.out.println("判断是否是一个单实列的,结果是:"+(person2==person));
    }
}

因为懒加载的意思就是当我们使用的时候才回去创建实列,又因为是单实列的所以我们只会创建一次

6.@Conditional 按照条件注册bean

(1).按照一定的条件进行注册bean

按照一定的条件进行判断,满足条件时才给容器中注册bean。

这里我们要实现 condition 的接口,因为我们的@Conditional({参数}),这里的参数就是我们需要继承 condition 的接口。

WindowConditional.java

package com.jsxs.conditional;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
 * @Author Jsxs
 * @Date 2023/8/13 19:18
 * @PackageName:com.jsxs.conditional
 * @ClassName: WindowConditional
 * @Description: TODO
 * @Version 1.0
 */
public class WindowConditional implements Condition {  //⭐
    /**
     *
     * @param context : 可以获取到上下文
     * @param metadata : 注解
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        if (environment.getProperty("os.name").contains("Windows")){
            return true;
        }
        return false;
    }
}

LinuxConditional.java

package com.jsxs.conditional;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
 * @Author Jsxs
 * @Date 2023/8/13 19:18
 * @PackageName:com.jsxs.conditional
 * @ClassName: LinuxConditional
 * @Description: TODO  我们需要继承Condition这个接口
 * @Version 1.0
 */
public class LinuxConditional implements Condition {  //⭐
    /**
     *
     * @param context : 判断条件能使用的上下文(环境)
     * @param metadata : 注释信息
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 1. 能获取到IOC使用的beanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 2. 能获取到类加载器
        ClassLoader classLoader = context.getClassLoader();
        // 3. 获取我们的开发环境
        Environment environment = context.getEnvironment();
        // 4.获取到bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();
        if (environment.getProperty("os.name").contains("Linux")){
            return true;
        }
        return false;
    }
}

MyConfig2.java

package com.jsxs.config;
import com.jsxs.bean.Person;
import com.jsxs.conditional.LinuxConditional;
import com.jsxs.conditional.WindowConditional;
import org.springframework.context.annotation.*;
/**
 * @Author Jsxs
 * @Date 2023/8/12 20:20
 * @PackageName:com.jsxs.config
 * @ClassName: MyConfig2
 * @Description: TODO
 * @Version 1.0
 */
@Configuration
public class MyConfig2 {
    /*
        @ conditional({condition列表}) 按照条件进行注入我们的组件
        提出需求:  假如我们的系统是window系统的话,那么我们就注册 person01 这个组件,如果是linux系统的话 我们就注册 person02 这个组件。
     */
    @Conditional({WindowConditional.class})  //⭐
    @Bean("person01")
    public Person person01(){
        return new Person("张三01",33);
    }
    @Conditional(LinuxConditional.class)  // ⭐
    @Bean("person02")
    public Person person02(){
        return new Person("张三02",33);
    }
}

相关文章
|
2月前
|
XML Java 数据格式
SpringBoot入门(8) - 开发中还有哪些常用注解
SpringBoot入门(8) - 开发中还有哪些常用注解
56 0
|
8天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
132 73
|
5天前
|
XML Java 数据格式
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
94 69
|
3天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
35 21
|
9天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
8天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
8天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
8天前
|
Java Spring
【Spring配置】idea编码格式导致注解汉字无法保存
问题一:对于同一个项目,我们在使用idea的过程中,使用汉字注解完后,再打开该项目,汉字变成乱码问题二:本来a项目中,汉字注解调试好了,没有乱码了,但是创建出来的新的项目,写的注解又成乱码了。
|
29天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
51 2
|
2月前
|
前端开发 Java Spring
Spring MVC核心:深入理解@RequestMapping注解
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的核心,它将HTTP请求映射到控制器的处理方法上。本文将深入探讨`@RequestMapping`注解的各个方面,包括其注解的使用方法、如何与Spring MVC的其他组件协同工作,以及在实际开发中的应用案例。
47 4