一、IOC容器注解驱动(学习笔记)

Spring注解驱动开发

一、IOC容器

根据雷丰阳老师这门教程中的脑图来安排这个专栏的,这里给出这张脑图,如下图所示。

img

根据上面这张脑图,我把整个专栏分成了三个大的部分,分别是:容器、扩展原理以及Web。

一、IOC容器

  • AnnotationConfigApplicationContext
  • 组件添加
  • 组件赋值
  • 组件注入
  • AOP
  • 声明式事务

既然开始学习spring注解驱动力,先来配个spring xml文件吧

  1. 打开Idea—>settings–>Editor–>File and Code Templates

    image-20230309184653887

image-20230309185116195

image-20230309185147246

  1. 附上Spring 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">

</beans>

1. 组件注册—@Configuration&Bean容器中

这里主要是使用org.springframework:spring-context:4.3.12RELEASE进行分析

  1. 首先第一步导入Maven依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.12.RELEASE</version>
        </dependency>
  1. Bean是Spring中最重要的部分之一,我们定义一个Pojo让它成为Bean
package com.xjz.pojo;

public class Person {

    private String name;
    private Integer age;

    public Person() {
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = 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;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  1. 先用配置bean的方式来试一下

beans.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.xjz.pojo.Person">
        <property name="age" value="20"></property>
        <property name="name" value="zhangsan"></property>
    </bean>

</beans>

MyTest.java 测试一下

public class MyTest {

    public static void main(String[] args) {
        //配置bean方式
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        Person bean = (Person) applicationContext.getBean("Person");
        System.out.println(bean);
        
    }
}

image-20230309185147247

  1. 接下来用注解bean的方式来测试一下

注解bean的配置类

//配置类==配置文件
@Configuration  //告诉Spring这是一个配置类
public class MyConfig {

    //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
    @Bean("person")
    public Person person01(){
        return new Person("list",20);
    }

}

测试一下

public class MyTest {

    public static void main(String[] args) {
        //配置bean方式
//        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//        Person bean = (Person) applicationContext.getBean("person");
//        System.out.println(bean);

        //注解bean方式
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);

        String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
        for (String name : namesForType) {
            System.out.println(name);
        }
    }
}

结果如下:

image-20230309192413071

2. 组件注册—@Configuration&Bean容器中

  1. 包扫描、只要标注了@Controller、@Service、@Repository、@Component、任意一个就可以组件就可以自动扫描加进容器中

image-20230309213835817

  1. excludeFilters = Filter[],指定扫描的时候按照什么规则排除那些组件
//配置类==配置文件
@Configuration  //告诉Spring这是一个配置类
@ComponentScans(
        value = {
                @ComponentScan(value = "com.xjz",includeFilters = {
                        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
                },useDefaultFilters = false)
        }
)
//ComponentScan  value:指定要扫描的包

image-20230309214559003

  1. includeFilters = Filter[],指定扫描的时候只需要包含哪些组件

    **注意:**当使用仅包含的注解是,必须先禁用默认扫描所有规则use-default-filters=“false”

    配置文件beans.xml

    <context:component-scan base-package="com.xjz" use-default-filters="false"></context:component-scan>
    

    注解@ComponentScan({…},userDefaultFilters = false)

    image-20230309215046608

3. 自定义TypeFilter指定@ComponentScan注解的过滤规则

  • FilterType.ANNOTATION:按照注解进行包含或者排除
  • FilterType.ASSIGNABLE_TYPE:按照给定的类型进行包含或者排除
  • FilterType.ASPECTJ:按照ASPECTJ表达式进行包含或者排除
  • FilterType.REGEX:按照正则表达式进行包含或者排除
  • FilterType.CUSTOM:按照自定义规则进行包含或者排除

4. 组件注册-@Scope-设置组件作用域

@Scope() 单例调用

image-20230313192630882

@Scope(“prototype”)多例调用

image-20230313192841720

为什么单实例两个bean为true,多实例两个bean不一样(false)

image-20230313193030971

我们来看一下 MyConfig2.java 代码

@Configuration
public class MyConfig2 {

    //默认是单例的

    /**
     * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
     * @see ConfigurableBeanFactory#SCOPE_SINGLETON
     * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
     * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
     * @return\
     * @Scope:调整作用域
     * prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
     *                     每次获取的时候才会去调用方法创建对象;
     * singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。
     *                              以后每次获取就是直接从容器(map.get())中拿;
     * request:同一次请求创建一个实例
     * session:同一个session创建一个实例
     */
    @Scope("prototype")
    @Bean("person")
    public Person person(){
        System.out.println("给容器中添加Person....");
        return new Person("张三",21);
    }
}

测试代码

public class IOCTest {

    @Test
    public void test02(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig2.class);
//        // 我们现在来看一下IOC容器中有哪些bean,即容器中所有bean定义的名字
//        String[] definitionNames = applicationContext.getBeanDefinitionNames();
//        for (String name : definitionNames) {
//            System.out.println(name);
//        }
//        //默认是单实例的
        Object bean = applicationContext.getBean("person");
        Object bean2 = applicationContext.getBean("person");
        System.out.println(bean==bean2);
    }

4.2 懒加载 @Lazy

懒加载;
单实例bean:默认在容器启动的时候创建对象;(默认是单实例)
懒加载:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化;

单实例加载如下所示:

@Configuration
public class MyConfig2 {
    //@Scope("prototype")
    @Bean("person")
    public Person person(){
        System.out.println("给容器中添加Person....");
        return new Person("张三",21);
    }
}

image-20230313203408072

懒加载

@Configuration
public class MyConfig2 {
    //@Scope("prototype")
    @Lazy
    @Bean("person")
    public Person person(){
        System.out.println("给容器中添加Person....");
        return new Person("张三",21);
    }
}

image-20230313203702659

5. 组件注册-Conditional-按照条件注册bean

@Conditional注解可以按照一定的条件进行判断,满足条件向容器中注册bean,不满足条件就不向容器中注册bean。

image-20230313211354960

  • ConditionContext:判断条件能使用的上下文(环境)
  • AnnotatedTypeMetadata:注释信息
public interface Condition {

	/**
	 * Determine if the condition matches.
	 * @param context the condition context
	 * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
	 * or {@link org.springframework.core.type.MethodMetadata method} being checked.
	 * @return {@code true} if the condition matches and the component can be registered
	 * or {@code false} to veto registration.
	 */
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

6. 组件注册-@Import-给容器中快速导入一个组件

注册bean的方式
向Spring容器中注册bean通常有以下几种方式:

  1. 包扫描+给组件标注注解(@Controller、@Servcie、@Repository、@Component),但这种方式比较有局限性,局限于我们自己写的类
  2. @Bean注解,通常用于导入第三方包中的组件
  3. @Import注解,快速向Spring容器中导入一个组件

我们先来看一下@Import注解的源码,如下图所示:

image-20230313213718030

注意:@Import注解只允许放到类上面,不允许放到方法上。

1)、@Import(要导入到容器中的组件):容器中就会自动注册这个组件,id默认是全类名

image-20230313213847239

2)、@ImportSelector:返回需要导入的组件的全类名数组;–>Springboot用 的多

ImportSelector接口是Spring中导入外部配置的核心接口,在Spring Boot的自动化配置和@EnableXXX(功能性注解)都有它的存在。我们先来看一下ImportSelector接口的源码,如下所示。

public interface ImportSelector {

	/**
	 * Select and return the names of which class(es) should be imported based on
	 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);

}

image-20230313215219400

3)、@ImportBeanDefinitionRegistrar:手动注册bean到容器中

ImportBeanDefinitionRegistrar需要配合@Configuration和@Import这俩注解,其中,@Configuration注解定义Java格式的Spring配置文件,@Import注解导入实现了ImportBeanDefinitionRegistrar接口的类。

public interface ImportBeanDefinitionRegistrar {

	/**
	 * Register bean definitions as necessary based on the given annotation metadata of
	 * the importing {@code @Configuration} class.
	 * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
	 * registered here, due to lifecycle constraints related to {@code @Configuration}
	 * class processing.
	 * @param importingClassMetadata annotation metadata of the importing class
	 * @param registry current bean definition registry
	 */
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

}

image-20230313221819133

4)、使用Spring提供的 FactoryBean(工厂Bean)

  • 1)、默认获取到的是工程bean调用getObject创建的对象
  • 2)、要获取工程Bean本身,我们需要给id前面加一个&
    &colorFactoryBean

首先,创建一个ColorFactoryBean类,它得实现FactoryBean接口,如下所示。

//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {

    //返回一个Color对象,这个对象会添加到容器中
    @Override
    public Color getObject() throws Exception {
        System.out.println("ColorFactoryBean....");
        return new Color();
    }

    @Override
    public Class<?> getObjectType() {
        return Color.class;
    }

    //是否单例?
    //true:这个bean是单实例,在容器中只保存一份
    //false:这个bean是多实例,每次获取都会创建一个新的bean
    @Override
    public boolean isSingleton() {
        return false;
    }
}

然后,我们在MainConfig2配置类中加入ColorFactoryBean的声明,如下所示。

 @Test
    public void testImport(){
        printBeans(applicationContext);
        Blue bean = applicationContext.getBean(Blue.class);
        System.out.println(bean);

        //工厂Bean获取的是调用getObject创建的对象
        Object bean2 = applicationContext.getBean("colorFactoryBean");
        Object bean3 = applicationContext.getBean("colorFactoryBean");
        System.out.println("bean的类型:"+bean2.getClass());
        System.out.println(bean2==bean3);

        Object bean4 = applicationContext.getBean("&colorFactoryBean");
        System.out.println(bean4.getClass());

    }

//ColorFactoryBean....
//ColorFactoryBean....
//bean的类型:class com.xjz.pojo.Color
//false
//class com.xjz.pojo.ColorFactoryBean
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xjz_2002

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值