目录

IOC

IOC概念原理

概念

什么是IOC:IOC即控制反转,把对象创建和对象之间的调用过程交给 Spring 进行管理

目的:降低耦合度

底层原理

底层技术:xml解析、工厂模式、反射

IOC过程:

  1. 创建xml配置文件,配置要创建的对象
  2. 创建工厂类
  3. 工厂类通过xml解析和反射创建对象

工厂模式可以降低耦合度,通过IOC使用工厂结合xml解析和反射进一步降低了耦合度

对象工厂接口

IOC思想是基于IOC容器完成,IOC容器底层就是对象工厂

spring提供了两种方式(接口)实现IOC容器

  1. BeanFactory

    IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用

    加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象

  2. ApplicationContext

    BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用

    加载配置文件时候就会把在配置文件中的对象进行创建

    ApplicationContext使用了BeanFactory其他实现类的功能,例如利用其他实现类创建Bean,获取Bean等。

    https://gitee.com/lienhui68/picStore/raw/master/null/20201012002330.png

Bean管理概念

Bean管理概念

Bean管理就是通过spring容器创建对象和注入属性

Bean管理方式

  • 基于xml配置文件方式
  • 基于注解方式

基于XML方式的Bean管理

基于xml方式创建对象

  • 在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建
  • 在 bean 标签有很多属性,介绍常用的属性
    • id 属性:唯一标识
    • class 属性:类全路径(包类路径)
  • 创建对象时候,默认也是执行无参数构造方法完成对象创建

基于xml方式注入属性

DI

DI:依赖注入,就是注入属性,是IOC的具体实现

  • 使用set方法进行注入

  • 使用构造器进行注入

  • p名称空间注入

    了解即可,用的不多

    1. 添加p标签xsd namespace

      1
      
      xmlns:p="http://www.springframework.org/schema/p"
      
    2. 进行属性注入,在 bean 标签里面进行操作

      1
      
      <bean id="helloWorld" class="com.eh.eden.spring.demo.HelloWorld" p:name="xxx"/>
      

注入字面量

null值

1
2
3
<property name="address">
	<null/>
</property>

包含特殊符号

1
2
3
4
5
6
7
<!--属性值包含特殊符号
1 把<>进行转义 &lt; &gt;
2 把带特殊符号内容写到 CDATA
-->
<property name="address">
    <value><![CDATA[<<南京>>]]></value>
</property>

注入属性——外部bean

1
2
3
4
5
6
7
8
9
    <!--1 service 和 dao 对象创建-->
    <bean id="userService" class="com.atguigu.spring5.service.UserService">
        <!--注入 userDao 对象
        name 属性:类里面属性名称
        ref 属性:创建 userDao 对象 bean 标签 id 值
        -->
        <property name="userDao" ref="userDaoImpl"></property>
    </bean>
    <bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>

注入属性——内部bean

内部bean也可以通过使用外部bean ref的方式实现,在实际中建议使用外部bean的方式看起来结构更清晰一点。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!--内部 bean-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
    <!--设置两个普通属性-->
    <property name="ename" value="lucy"></property>
    <property name="gender" value="女"></property>
    <!--设置对象类型属性-->
    <property name="dept">
        <bean id="dept" class="com.atguigu.spring5.bean.Dept">
            <property name="dname" value="安保部"></property>
        </bean>
    </property>
</bean>

注入属性——级联赋值

1
2
3
4
5
6
7
8
9
<!--级联赋值-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
    <!--设置两个普通属性-->
    <property name="ename" value="lucy"></property>
    <property name="gender" value="女"></property>
    <!--级联赋值-->
    <property name="dept" ref="dept"></property>
    <property name="dept.dname" value="技术部"></property>
</bean>

注意:级联赋值, 属性对象Dept一定要提供get方法。

注入集合属性

  1. 创建类,定义数组、list、map、set 类型属性,生成对应 set 方法
  2. 在 spring 配置文件进行配置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
    <!--数组类型属性注入-->
    <property name="courses">
        <!--数组也支持使用list标签-->
        <array>
            <value>java课程</value>
            <value>数据库课程</value>
        </array>
    </property>
    <!--list类型属性注入-->
    <property name="list">
        <list>
            <value>张三</value>
            <value>小三</value>
        </list>
    </property>
    <!--map类型属性注入-->
    <property name="maps">
        <map>
            <entry key="JAVA" value="java"></entry>
            <entry key="PHP" value="php"></entry>
        </map>
    </property>
    <!--set类型属性注入-->
    <property name="sets">
        <set>
            <value>MySQL</value>
            <value>Redis</value>
        </set>
    </property>
</bean>

集合里面存放的是对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<!--创建多个course对象-->
<bean id="course1" class="com.atguigu.spring5.collectiontype.Course">
    <property name="cname" value="Spring5框架"></property>
</bean>
<bean id="course2" class="com.atguigu.spring5.collectiontype.Course">
    <property name="cname" value="MyBatis框架"></property>
</bean>

<!--注入list集合类型,值是对象-->
<property name="courseList">
    <list>
        <ref bean="course1"></ref>
        <ref bean="course2"></ref>
    </list>
</property>

将集合提取出来

  1. 在 spring 配置文件中引入名称空间 util

    1
    2
    3
    4
    5
    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:util="http://www.springframework.org/schema/util" <!--添加util名称空间-->
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    
  2. 使用 util 标签完成 list 集合注入

    1
    2
    3
    4
    5
    
    <util:list id="bookList">
            <value>易筋经</value>
            <value>九阴真经</value>
            <value>九阳神功</value>
        </util:list>
    
  3. 提取list集合进行注入

    1
    2
    3
    
    <bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype">
            <property name="list" ref="bookList"></property>
        </bean>
    

工厂bean

Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)

  • 普通 bean:在配置文件中定义 bean 类型就是返回类型
  • 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean 第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.eh.eden.spring.demo;

import org.springframework.beans.factory.FactoryBean;

public class MyBean implements FactoryBean<Course> {

    @Override
    public Course getObject() throws Exception {
        // spring底层在使用工厂bean的时候用的是工厂+反射,这里只做简单演示
        return new Course();
    }

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

    @Override
    public boolean isSingleton() {
        return false;
    }
}

bean.xml

1
<bean id="myBean" class="com.eh.eden.spring.demo.MyBean"/>

接受的时候使用实际类型Course

1
2
3
4
5
6
7
8
@Test
    public void testSay() {
        // 1. 加载spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        // 2. 获取配置创建的对象
        Course course = context.getBean("myBean", Course.class);
        System.out.println(course);
    }

Bean作用域

在 Spring 里面,默认情况下,bean 是单实例对象,下面进行作用域设置:

  • 在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例

  • scope 属性值 第一个值 默认值,singleton,表示是单实例对象 第二个值 prototype,表示是多实例对象

  • 此外还有两个scope 类型

    • request 一次请求
    • session 一次会话
  • singleton 和 prototype 区别

    设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象 ;设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建对象,在调用 getBean 方法时候创建多实例对象

Bean生命周期

所谓生命周期就是从对象创建到对象销毁的过程

bean生命周期主要过程:

  1. 通过构造器创建 bean 实例(无参数构造)
  2. 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
  3. 调用 bean 的初始化的方法(需要进行配置初始化的方法)
  4. bean 可以使用了(对象获取到了)
  5. 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

演示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.eh.eden.spring.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.concurrent.atomic.AtomicInteger;

public class HelloWorld {
    public static final Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    public static final AtomicInteger step = new AtomicInteger(0);

    private String name;


    public HelloWorld() {
        logger.info(String.format("第%d步:构造方法", step.addAndGet(1)));
    }

    public void setName(String name) {
        this.name = name;
        logger.info(String.format("第%d步:set方法", step.addAndGet(1)));
    }

    public void init() {
        logger.info(String.format("第%d步:init方法", step.addAndGet(1)));
    }

    public void destroy() {
        logger.info(String.format("第%d步:destroy方法", step.addAndGet(1)));
    }

    public static void main(String[] args) {
        // 1. 加载spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        // 2. 获取配置创建的对象
        HelloWorld helloWorld = context.getBean("helloWorld", HelloWorld.class);
        if (helloWorld != null) {
            logger.info(String.format("第%d步:使用容器里的HelloWorld对象", step.addAndGet(1)));
        }
        // 3. 关闭容器
        ((ClassPathXmlApplicationContext) context).close();
    }
}

bean.xml

1
2
<bean id="helloWorld" class="com.eh.eden.spring.demo.HelloWorld" p:name="spring" init-method="init"
      destroy-method="destroy"/>

执行结果:

1
2
3
4
5
20201012 15:41:33 [main] INFO  com.eh.eden.spring.demo.HelloWorld - 第1步构造方法
20201012 15:41:33 [main] INFO  com.eh.eden.spring.demo.HelloWorld - 第2步set方法
20201012 15:41:33 [main] INFO  com.eh.eden.spring.demo.HelloWorld - 第3步init方法
20201012 15:41:33 [main] INFO  com.eh.eden.spring.demo.HelloWorld - 第4步使用容器里的HelloWorld对象
20201012 15:41:33 [main] INFO  com.eh.eden.spring.demo.HelloWorld - 第5步destroy方法

bean的生命周期共有七步,还包括后置处理器BeanPostProcessor,后置处理器配置在bean.xml,对所有bean生效。如下

  1. 通过构造器创建 bean 实例(无参数构造)
  2. 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
  3. 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
  4. 调用 bean 的初始化的方法(需要进行配置初始化的方法)
  5. 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
  6. bean 可以使用了(对象获取到了)
  7. 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

演示:

MyBeanPostProcessor.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.eh.eden.spring.demo;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import static com.eh.eden.spring.demo.HelloWorld.logger;
import static com.eh.eden.spring.demo.HelloWorld.step;

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        logger.info(String.format("第%d步:postProcessBeforeInitialization,此时对象是%s", step.addAndGet(1), bean.getClass().getSimpleName()));
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        logger.info(String.format("第%d步:postProcessAfterInitialization,此时对象是%s", step.addAndGet(1), bean.getClass().getSimpleName()));
        return bean;
    }
}

beans.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="helloWorld" class="com.eh.eden.spring.demo.HelloWorld" p:name="spring" init-method="init"
          destroy-method="destroy"/>

    <bean class="com.eh.eden.spring.demo.MyBeanPostProcessor"/>
</beans>

执行结果:

1
2
3
4
5
6
7
20201012 15:50:07 [main] INFO  com.eh.eden.spring.demo.HelloWorld - 第1步构造方法
20201012 15:50:07 [main] INFO  com.eh.eden.spring.demo.HelloWorld - 第2步set方法
20201012 15:50:07 [main] INFO  com.eh.eden.spring.demo.HelloWorld - 第3步postProcessBeforeInitialization此时对象是HelloWorld
20201012 15:50:07 [main] INFO  com.eh.eden.spring.demo.HelloWorld - 第4步init方法
20201012 15:50:07 [main] INFO  com.eh.eden.spring.demo.HelloWorld - 第5步postProcessAfterInitialization此时对象是HelloWorld
20201012 15:50:07 [main] INFO  com.eh.eden.spring.demo.HelloWorld - 第6步使用容器里的HelloWorld对象
20201012 15:50:07 [main] INFO  com.eh.eden.spring.demo.HelloWorld - 第7步destroy方法

Bean生命周期涉及到的主要后置处理器

第一次后置处理器

InstantiationAwareBeanPostProcessor后置处理器,调用的是:postProcessBeforeInstantiation方法,第一次后置处理器的作用是:给后置处理器一个返回代理对象的机会。这个方法用来在对象实例化前直接返回一个对象(如代理对象)来代替通过内置的实例化流程创建对象,该方法在创建对象之前会先掉用,如果有返回实例则直接使用不会去走下面创建对象的逻辑。

第二次后置处理器

SmartInstantiationAwareBeanPostProcessor后置处理器,执行的是determineCandidateConstructors方法,第二次后置处理器的作用是推断出使用哪个构造函数。

第三次执行后置处理器

MergedBeanDefinitionPostProcessor后置处理器,执行的是postProcessMergedBeanDefinition方法,第三次后置处理器的作用是:可以用来修改merged BeanDefinition的一些properties 或者用来给后续回调中缓存一些meta信息使用。

第四次后置处理器

SmartInstantiationAwareBeanPostProcessor后置处理器,执行的是:getEarlyBeanReference方法,第四次后置处理器的作用是获取早期引用,也即是将singletonFactory加入到三级缓存中,主要是为了解决循环依赖。

第五次后置处理器 InstantiationAwareBeanPostProcessor后置处理器,调用的是:postProcessAfterInstantiation方法,第五次后置处理器的作用是判断是否需要进行后续的属性填充。我们想自定义实现属性填充,这里是个很好的调用时机,我们可以在此实现我们自己的填充,返回false,那么就不会接着下面的spring给我们安排的填充了,默认是true。

第六次后置处理器 InstantiationAwareBeanPostProcessor后置处理器,执行的是:postProcessPropertiesValues方法,第六次后置处理器作用是进行属性填充。

第七次后置处理器 BeanPostProcessor后置处理器,执行的是:postProcessBeforeInitialization方法,第七次后置处理器的作用是在执行生命周期回调的init方法(实现方式有三种:1:加@postConstruct注解,2:配置文件中配置init-method=“init”,3:实现InitializingBean 接口,重写afterPropertiesSet方法)之前执行此逻辑。

第八次后置处理器 BeanPostProcessor后置处理器,执行的是:postProcessAfterInitialization方法,在生命周期回调init方法执行之后执行。

第九次后置处理器 DestructionAwareBeanPostProcessor后置处理器,postProcessBeforeDestruction方法,生命周期回调的销毁方法之前执行的逻辑。生命周期的回调方法中的destroy方法(三种实现,1:使用@PreDestroy 注解。2:实现DisposableBean 接口,重写destroy方法。3:配置文件中定义destroy-method方法)。

自动装配

根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入

自动装配属性有6个值可选,分别代表不同的含义:

  • byName ->从Spring环境中获取目标对象时,目标对象中的属性会根据名称在整个Spring环境中查找<bean>标签的name属性值。如果有相同的,那么获取这个对象,实现关联。整个Spring环境:表示所有的spring配置文件中查找,那么name不能有重复的。

  • byType ->从Spring环境中获取目标对象时,目标对象中的属性会根据类型在整个spring环境中查找<bean>标签的class属性值。如果有相同的,那么获取这个对象,实现关联。

    缺点:如果存在多个相同类型的bean对象,会出错;如果属性为单一类型的数据,那么查找到多个关联对象会发生错误;如果属性为数组或集合(泛型)类型,那么查找到多个关联对象不会发生异常。

  • constructor ->使用构造方法完成对象注入,其实也是根据构造方法的参数类型进行对象查找,相当于采用byType的方式。

  • autodetect ->自动选择:如果对象没有无参数的构造方法,那么自动选择constructor的自动装配方式进行构造注入。如果对象含有无参数的构造方法,那么自动选择byType的自动装配方式进行setter注入。

  • no ->不支持自动装配功能

  • default ->表示默认采用上一级标签的自动装配的取值。如果存在多个配置文件的话,那么每一个配置文件的自动装配方式都是独立的。

演示:

1
2
3
4
5
6
7
8
<!--实现自动装配
bean 标签属性 autowire,配置自动装配
autowire 属性常用两个值:
byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
byType 根据属性类型注入 注意如果一个类型对应多个bean编译就会报错
-->
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName"/>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"/>

引入外部属性文件

  1. 引入context名称空间

    1
    2
    3
    4
    5
    6
    7
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:p="http://www.springframework.org/schema/p"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
  2. 引入外部属性文件

    1
    
    <context:property-placeholder location="classpath:db.properties"/>
    
  3. 使用属性

    1
    2
    3
    4
    5
    6
    7
    
    <!--配置连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driver}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${username}"></property>
        <property name="password" value="${password}"></property>
    </bean>
    

基于注解方式的Bean管理

注解的目的是简化xml配置

创建对象

创建对象又4个注解

  • @Component
  • @Service
  • @Controller
  • @Repository

注意:4个注解都可以用来创建对象,混用也是ok的,各层使用自己的注解只是为了让开发人员更清楚当前组件在应用开发中中的角色

基于注解方式实现对象创建步骤:

  1. spring中使用注解需要依赖spring-context.jar

  2. 开启组件扫描

    1
    2
    3
    4
    
    <!--
            如果扫描多个包,多个包可以使用逗号隔开或者扫描包上层目录
        -->
        <context:component-scan base-package="com.eh.eden.spring.demo"/>
    
  3. 添加注解

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    package com.eh.eden.spring.demo;
       
    import lombok.Data;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.stereotype.Component;
       
    @Component
    @Data
    public class CreateAnnotationDemo extends BaseDemo {
        private String name;
       
        public void test() {
            logger.info("CreateAnnotationDemo========test");
        }
       
        public static void main(String[] args) {
            // 1. 加载spring配置文件
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            // 2. 获取配置创建的对象
            CreateAnnotationDemo demo = context.getBean("createAnnotationDemo", CreateAnnotationDemo.class);
            demo.test();
        }
    }
    

注解扫描配置

filter: include/exclude

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!--示例 1
use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
context:include-filter ,设置扫描哪些内容
-->
<context:component-scan base-package="com.xxx" use-defaultfilters="false">
    <!--代表只扫描Controller注解的类-->
    <context:include-filter type="annotation"
                            expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例 2
 下面配置扫描包所有内容
 context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.xxx">
    <!--表示Controller注解的类之外一切都进行扫描-->
    <context:exclude-filter type="annotation"
                            expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

注入属性

三种方式:

  • @Autowired:根据属性类型进行自动装配

  • @Qualifier:根据名称进行注入

    这个@Qualifier 注解的使用,和上面@Autowired 一起使用,在接口和实现类一对多的时候

    1
    2
    3
    
    @Autowired //根据类型进行注入
    @Qualifier(value = "userDaoImpl1") //根据名称进行注入
    private UserDao userDao;
    
  • @Resource:可以根据类型注入,也可以根据名称注入

    1
    2
    3
    4
    
    //@Resource  //根据类型进行注入
    @Resource(name = "userDaoImpl1")
    //根据名称进行注入
    private UserDao userDao;
    

    注意:@Resource 是javax包下的,但是也能达到同样的效果。建议使用Autowire和Quilifier注解。

上面三种注入方式是关于对象类型的,还有一种简单类型的注入方式@Value,如下:

1
2
@Value(value = "abc")
private String name;

纯注解开发

所谓纯注解开发就是创建一个配置类,使用注解的方式替代xml配置文件。

演示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package com.eh.eden.spring.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = {"com.eh.eden.spring.demo"})
public class SpringConfig {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        CreateAnnotationDemo demo = context.getBean("createAnnotationDemo", CreateAnnotationDemo.class);
        demo.test();
    }
}

组合注解

组合注解就是将多个注解组合到一块生成一个新的注解,使用这个新的注解就相当于使用了该组合注解中所有的注解。这个特性还是蛮有用的,接下来我们就来看一下如何创建和使用组合注解。

组合注解的创建

在之前的Spring配置类中,我们经常使用到@Configuration和@ComponentScan这两个注解,接下来,我们将其进行组合封装,从而形成一个新的注解。

下方这个CombinationConfiguration注解就是我们组合的新的注解,该注解中使用了@Configuration和@ComponentScan进行修饰,也就说明@CombinationConfiguration注解兼有@Configuration和@ComponentScan这两个注解的功能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.eh.combination.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@ComponentScan
public @interface CombinationConfiguration {
    String[] value() default{};
}

组合注解的使用

创建完相应的组合注解后就到了使用的时候了,上面注解的使用和一般的注解没有什么区别。只是这个注解表示之前写的@Configuration和@ComponentScan这两个注解。下方代码截图就是该组合注解的使用方式。

1
2
3
4
5
package com.eh.combination.annotation;

@CombinationConfiguration
public class DemoConfig {
}