【Spring】史上最全的spring IoC使用讲解

目录

一、配置spring容器(将bean装配到spring容器中)

1.1、XML方式

1.2、注解方式

1.3、JavaConfig

1.4、混合使用

1.5、实现bean的单独注册

1.6 插一句

二、自动装配

2.1、使用XML来完成自动装配

2.1.1、byType

2.1.2、byName

2.1.3、no和default

2.1.4、constructor

2.2、使用JavaConfig和注解来进行自动装配

2.2.1 @Autowired

2.2.2 @Resource

三、spring懒加载

3.1 使用XML设置

3.2 使用注解设置

3.3 懒加载与非懒加载的优缺点

四、spring作用域

4.1 Singleton Beans with Prototype-bean Dependencies

4.1.1 ApplicationContextAware接口

4.1.2 Lookup Method Injection

五、spring生命周期和回调

5.1 Initialization Callbacks(初始化回调)

5.2 Destruction Callbacks(销毁回调)

5.3 Default Initialization and Destroy Methods(默认初始化和销毁方法)

5.3.1 XML(init-method和destroy-method)

5.3 2 注解(@PostConstruct和@PreDestory)

六、其他

6.1 @ComponentScan标签的其他用法

6.2 byType多个同类型的Bean冲突解决办法

6.2.1、@Primary

6.2.2、第二种方法使用限定注解@Qualifier

6.3 加速spring扫描类的速度

6.4 @Bean注解

配置类

6.5 @Profile标签


一、配置spring容器(将bean装配到spring容器中)

使用ioc首先就要配置好spring容器,要告诉容器我需要它管理的bean是什么,以及bean之间的依赖关系

配置spring(初始化spring环境)的方式有三种(spring的编程风格):

  1. schemal-based-------xml
  2. annotation-based-----annotation
  3. java-based----java Configuration

 

我们首先创建好项目

IndexDao接口:

package priv.cy.dao;
public interface IndexDao {
    public void test();
}

 

IndexDao实现类:

package priv.cy.dao;
public class IndexDaoImpl implements IndexDao {
    private String str;

    @Override
    public void test() {
        System.out.println(str);
        System.out.println("Impl");
    }
    public void setStr(String str) {
        this.str = str;
    }
    public String getStr() {
        return str;
    }
}

Service类:

package priv.cy.dao;
public class IndexService {
    private IndexDao dao;

    public IndexService(IndexDao dao) {
        this.dao = dao;
    }
    public void service() {
        dao.test();
    }
}

 

 

1.1XML方式

首先讲第一种,使用XML

先在resources创建一个XML,XML保存到resource包中,XML文件的名字可以任意,这里命名为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"
       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">
    <!--上面这些叫做命名空间-->

       <!--在bean标签中指定要装配给spring容器的类,在class属性写好类的全限定名,设置其id,如果不设置name默认name和id相同-->
    <bean id="dao" class="priv.cy.dao.IndexDaoImpl">
        <property name="str" value="aaa"></property>
    </bean>

    <bean id="service" class="priv.cy.dao.IndexService">
        <!--使用构造方法注入-->
        <constructor-arg ref="dao"></constructor-arg>
	        <!--使用set方法注入,这里配置的是service这个类依赖的对象,ref中写的是xml文件中定义的该对象的id-->
                   <property name="dao" ref="dao"></property>
    </bean>
</beans>

 

在这个XML文件中来指定要委托给spring容器管理的对象,然后指定对象之间的依赖关系,依赖关系是通过注入的方式来实现的,这里再讲一下注入方式,一共有三种

  1. 构造方法注入。
  2. setter方法注入。
  3. 接口注入

接口注入在spring3提供的,现在的spring版本已经取消了

 

上面展示了setter方法注入(使用<property>标签)和构造方法注入(使用<constructor-arg>标签),这两种选一种使用就可以。注意,如果依赖的是已经交给spring容器管理的bean(即已经在xml中配置过的bean),依赖关系用ref,等号右边写定义的name。配置bean的时候如果只定义了id,那么它的name默认和id一样。

 

如果注入的不是被spring容器管理的bean,就不要用ref,比如indexDaoImpl依赖一个String对象,就用value后面直接写String的值就可以了。

 

通过上面的配置,spring容器已经接管了我们的bean

创建测试类进行测试,注意创建spring容器对象时构造方法传入的是classpath:spring.xml。

package priv.cy.dao;
public class Test {
    public static void main(String[] args) {
        /**
         * XML是放到resource包下的,这样编译后才会被复制到classpath路径下,XML的编译就是原样复制,.java的编译时编程.class再放到classpath目录中
         */
        // 如果使用了XML,就需要用这个类来获取Spring容器,通过该对象来获取bean
        ClassPathXmlApplicationContext classPathXmlApplicationContext
                = new ClassPathXmlApplicationContext("classpath:spring.xml");

        IndexService service = (IndexService) classPathXmlApplicationContext.getBean("service");
        service.service();
    }
}

通过spring容器获取bean,实现了IOC控制反转

 

1.2、注解方式

再讲第二种。使用注解的方式来配置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"
       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">
    <!--上面这些网址叫做命名空间-->

    <!--刚刚使用的是XML的方式来管理spring容器,下面讲解用注解的方式管理容器-->
    <!--还可以使用注解来管理spring容器,但是spring默认是关闭注解支持的,并且不会去扫描注解,这么做是为了节约性能-->
    <!--开启支持注解解析-->
    <context:annotation-config></context:annotation-config>
    <!--开启扫描com包下面的所有注解-->
    <context:component-scan base-package="priv"></context:component-scan>

      <!--以前spring要使用注解管理spring容器这两条必须要写,但是现在把第一条合并到第二条了,所以现在写下面这一条就可以了-->
 
</beans>

 

Dao类:

通过注解,将IndexDaoImpl装配到Spring容器中去,可以通过@Value标签给依赖的String对象注入常量值

package priv.cy.dao;
import org.springframework.stereotype.Component;

// 这里可以指定放入容器之后这个类的id是dao,如果不写的话默认就是类名
@Component("dao")
public class IndexDaoImpl implements IndexDao {
    @Value("aaa")
    private String str;
    @Override
    public void test() {
        System.out.println(str);
        System.out.println("Impl  0");
    }
    public void setStr(String str) {
        this.str = str;
    }
    public String getStr() {
        return str;
    }
}

 

Service类:

通过注解将该类装配到Spring容器中,并且直接通过@Autuwired注解将依赖的对象注入给IndexDaoImpl

package priv.cy.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
// 可以在括号里指定他的id,如果不指定id默认就是类名
@Service
public class IndexService {
    @Autowired // 默认使用的是构造方法进行注入
    private IndexDao dao;

    public IndexService(IndexDao dao) {
        this.dao = dao;
    }
    public void service() {
        dao.test();
    }
}

 

测试类:

也就是使用解析XML的那个类来获取spring容器对象,进而来获取bean

package priv.cy.dao;
public class Test {
    public static void main(String[] args) {
        /**
         * XML是放到resource包下的,这样编译后才会被复制到classpath路径下,XML的编译就是原样复制,.java的编译时编程.class再放到classpath目录中
         */
        // 如果使用了XML,就需要用这个类来获取Spring容器,通过该对象来获取bean
        ClassPathXmlApplicationContext classPathXmlApplicationContext
                = new ClassPathXmlApplicationContext("classpath:spring.xml");

        IndexService service = (IndexService) classPathXmlApplicationContext.getBean("service");
        service.service();
    }
}

AnnotationConfigApplicationContext和ClassPathXmlApplicationContext这两个对象的getBean方法传入的参数可以是beanname,也可以是xxx.class,即可以通过name来获取bean,也可以通过type来获取bean。如果传入的是xx.class,那么返回的对象就是这个类型,不需要强转,如果传入的是name,那么返回的类型就是Object,需要强转,除非在传入的参数中添加一个class参数指名bean的类型。例如:

IndexDaodao = annotationConfigApplicationContext.getBean("indexDao", IndexDao.class);

 

1.3JavaConfig

最后讲一下javaConfig的当时来管理spring容器,因为从上面两种可以看出,都是需要有XML文件的,即使是注解方式,也需要有XML文件来开启支持注解。但是javaConfig方法,就可以完全不需要XML文件。

package priv.cy.dao;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
 * 前面讲的是XML和注解的管理spring的方式,可是注解的方式本身还需要通过XML来开启注解支持,所以本质还是离不开XML
 * 所以就可以使用Javaconfig的方式,直接创建一个Java类来管理spring,完全不需要XML
 */
// 将该类设置为一个配置类,该类就相当于一个XML
@Configuration
// 开启扫描,指定要扫描哪些包,就和XML中开启扫描的作用是一样的,如果不开启就没有办法使用注解。必须要指定扫描位置
@ComponentScan("priv")
public class Spring {
}

通过这个JavaConfig类,也就开启了注解支持,后面的描述bean之间的依赖关系就可以直接使用注解了,完全不需要XML

 

在获取spring容器对象的时候会有些不同,因为spring解析XML和解析javaConfig功能的实现是使用的不同的类,所以通过javaConfig配置spring容器的话,获取spring容器对象的时候需要使用另一个对象,注意传入的是spring.class。

AnnotationConfigApplicationContext(通过注解的方式初始化应用程序上下文)这个类本身是支持解析注解的功能的,之前的ClassPathXmlApplicationContext(通过XML的方式初始化应用程序上下文)不支持解析注解,所以在之前如果要使用注解,还需要在XML开启支持解析注解但是AnnotationConfigApplicationContext类本身就支持解析注解,所以也就不用再从XML中开启了。只需要在javaConfig类中指定要扫描的位置就可以了

测试类:

package priv.cy.dao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
    public static void main(String[] args) {
        //如果没有使用XML,使用的Javaconfig的方式来进行管理spring的,就不能使用上面的方法来获取Spring容器了,要使用这个类来获取
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(Spring.class);
        IndexService service1 = (IndexService) annotationConfigApplicationContext.getBean("service");
        service.service();
    }
}

 

1.4、混合使用

也可以混合使用,即用Javaconfig也用XML,不用注解将indexDaoImpl装载到spring容器了,也不用注解来标识各个bean之间的依赖关系,而是直接通过XML来用bean标签进行装载

然后在Javaconfig那个类上再加一个注解@ImportResource("classpath:spring.xml"),表示也根据xml的配置来管理spring容器,解析这个XML,进行混合使用

package priv.cy.dao;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("priv")
@ImportResource("classpath:spring.xml")
public class Spring {
}

我不是很喜欢写XML,所以我一般就是使用javaConfig的方式和注解的方式来配置spring容器

 

 

1.5、实现bean的单独注册

所谓的初始化spring环境就是将我们交给spring管理的类实例化

这里注册的意思其实就是装配

 

我们已经讲了上面几个初始化spring的环境的方法

1、xml    ClassPathXmlApplicationContext   提供了类的扫描和单独bean的注册(就是在xml定义一个类的bean标签,spring容器就会完成这个类的声明和注册,也就是实例化)这两个功能,xml可以不用扫描代码就可以完成对指定bean的装配,只要是在xml声明了,它就会对这个类完成注册,实现实例化。

2、annotation  使用注解

3、javaConfig  AnnotationConfigAoolicationContext    类的扫描    类的定义(Java代码)     不能单独注册一个类,就是说必须进行扫描,才可以实现类的定义,类的扫描和类的定义是不能分开的。从代码层面就是说如果我们不开启扫描注解,我们添加的注解就是无效的。

 

但实际上spring给Annotation提供了单独注册方法,只不过用的很少,可以不使用扫描直接去将指定的类在spring容器中注册,完成bean的装配。我们可以使用register这个方法来完成对bean的单独注册

 

先说一下register这个方法:

javaConfig:

@Configuration
@ComponentScan("priv")
public class AppConfig {
}

 

service:

@Service
public class IndexService {
    public void service() {
        System.out.println("service");
    }
}

 

测试类:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext();

        // 实现类的单独注册,不需要扫描注解就可以将其来spring容器中实例化
         annotationConfigApplicationContext.register(AppConfig.class);
        // 使用register方法来实现类的单独注册必须要refresh
        annotationConfigApplicationContext.refresh();

        System.out.println(annotationConfigApplicationContext.getBean(IndexService.class).getClass().getName());
    }
}

 执行结果:

 

我们发现直接将配置JavaConfig配置文件直接注册进register方法也是可以实现相同的效果,扫描注解,实现装配,但是使用register方法进行注册必须要手动refreshsh。这些都可以从源码中找到原因。

 

这个是AnnotationConfigApplicationContext类的构造方法源码,也就是我们平时将javaConfig的class传入进行spring容器配置的方法。

public  AnnotationConfigApplicationContext(Class<?>...annotatedClasses) {
	this();
	register(annotatedClasses);
	refresh();
}

我们发现原有的构造方法本质也是调用的register方法实现的配置文件注册,并且帮我们refresh了,所以如果我们直接调用register方法,我们还需要自己手动refresh。

那么如何使用register实现单独bean注册呢,很简单

我们不传入javaConfig类,并且将service类上的@Service注解去掉,看看如何在无注解和不进行扫描的情况下手动将我们的bean装配到spring容器中

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext();

        // 实现类的单独注册,不需要扫描注解就可以将其来spring容器中实例化
        annotationConfigApplicationContext.register(IndexService.class);
        // 使用register方法来实现类的单独注册必须要refresh
        annotationConfigApplicationContext.refresh();

        System.out.println(annotationConfigApplicationContext.getBean(IndexService.class).getClass().getName());
    }
}

执行结果:

很简单,我们只需要将要装配的类的class传入到register方法中就实现了单独注册装配,没有添加javaConfig配置,不需要扫描,也不需要加注解。

如果阅读了register源码,我们就能发现register支持注册两种bean,一个是普通bean,一个就是javaConfig的bean,所以它不光支持读取我们的配置类,也支持让我们单独注册bean到spring容器当中

 

1.6 插一句:

XML中还有一个depends-on 这个可以规定bean在容器中的创建顺序。

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

比如上面的这种配置,通过depends-on配置,那么manager就会先于beanOne创建,但是这两个bean之间是没有依赖关系的。这种操作的应用场景就是如果beanOne的初始化需要用到manager的一些信息或者beanOne中的一些静态方法需要用到manager的信息,那么就得保证manager先于beanOne创建。但是这两个bean之间并没有依赖关系,所以只能使用depends-on的方法

 

二、自动装配

下面我们再说一下自动装配这个问题

定义还是要定义的,就是说要将哪个类作为bean装配给spring容器管理,但是通过自动装配的技术bean之间的依赖关系不需要我们来设置了,比如如果使用XML来配置,我们就不需和上面一样还要在bean标签中再嵌套设置该bean所依赖的对象了。

只需要把所有要装配给spring容器的对象在XML文件中设置好或者用注解指定好,然后spring会直接根据定义类时的代码获取到依赖关系,自动给需要依赖的对象进行装配,这就大大减少了描述依赖关系的配置。

 

手动装配的优先级高于自动装配

自动装配的优点:

  • 大大减少我们描述依赖关系是的大篇幅配置
  • 如果对象依赖关系更新,也不需要重新配置

 

自动装配的方法:

  • no/default
  • byName
  • byType
  • constructor

 

指定自动装配的方式有两种:

  • 全局配置:全局都用指定的自动装配方法,如全局都用byName
  • 局部配置:指定的自动装配方法只在局部有效

 

2.1、使用XML来完成自动装配

全局指定,直接在XML头部设置自动装配方式

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"
        default-autowire="byType">

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>

    <bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>

 

2.1.1byType

就是通过类型来查找相应的依赖关系

 

一上面的XML配置文件为例

开启了自动装配之后,首先spring先扫描XML文件中设置的要装配给spring容器管理的bean,然后扫描到了indexService这个bean之后,会先去扫描定义这个类的代码,发现里面有一个类型是IndexDao的依赖对象,然后就会返回来扫描XML配置文件,查看有没有交给spring容器管理的这个类型的bean,最后发现了id为dao的这个bean,然后就直接将这个bean注入给indexService对象了,这就是通过类型来进行自动装配。

 

XML中配置的bean,底层都会讲他们添加到一个map中,所以上面扫描bean的过程在底层就是遍历这个map。

 

但是上述方法有一个问题,刚才的例子只是将一种IndexDao接口的实现类装配到spring容器中去管理,如果将两种IndexDao接口的实现类装配到spring容器中,就会出现报错,因为spring容器发现这两种类型都可以注入给IndexService,不知道应该使用哪个了

 

例如定义两个IndexDao实现类:

package priv.cy.dao;

public class IndexDaoImpl implements IndexDao {
    @Override
    public void test() {
        System.out.println("Impl 0");
    }

}

package priv.cy.dao;

public class IndexDaoImpl1 implements IndexDao {
    @Override
    public void test() {
        System.out.println("Impl 1");
    }
}

 

将其交给spring容器管理:

<?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"
        default-autowire="byType">

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
    <bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>

    <bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>

运行时报错如下:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'priv.cy.dao.IndexDao' available: expected single matching bean but found 2: dao,dao1

上面的意思就是说应该注入IndexDao类型的对象,但是发现了两个bean,dao和dao1都可以注入,这才会报错。这时就需要用byName来解决

而且还有一点要注意,使用自动装配的时候默认使用的是无参构造方法来创建对象,所以要确保所有的bean都有无参构造方法,要么不显示地写构造方法,要么显示写出无参构造方法(如果写了有参构造方法必须也写上无参构造方法,因为有参构造方法会使默认的隐式无参构造方法失效)

 

 

2.1.2byName

这里就是通过指定bean的Name来进行自动装配

<?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"
        default-autowire="byName">

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
    <bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
    <bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>

 

前面说过,如果不指定bean的name,那么name默认和id一样,使用byName进行自动装配的时候,使用的是类的set方法名来进行Name匹配的,如:

package priv.cy.dao;

public class IndexService {
    private IndexDao dao;
    public void service() {
        dao.test();
    }
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
}

比如上面的service类,setter方法的方法名是setDao,那么再进行自动装配的时候就会自动将方法名前面的set去掉,将剩下的第一个字母变小写,得到的name去和XML中设置的bean的name进行比较,找到name为dao的这个bean,将其注入到service类中。此时运行service方法,就会输出Impl 0

所以这里要识别的名字只和set方法中的哪个后缀名字有关,和service类中定义的依赖对象的名字 private IndexDao dao;,也就是dao是没有关系的。

 

比如我将 

<bean id="dao1" name="aaa" class="priv.cy.dao.IndexDaoImpl1"></bean>

IndexDaoImpl1这个类的name改成aaa,然后将service中的setter改为setAaa 

public class IndexService {
    private IndexDao dao;
    public void service() {
        dao.test();
    }
    public void setAaa(IndexDao dao) {
        this.dao = dao;
    }
}

 

这个时候输出的就是Impl  1而不是Impl  0l了,说明byName只和bean的name值以及setter方法的名字有关系。和定义类的时候给依赖对象起的名字无关。

 

2.1.3nodefault

<?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"
        default-autowire="default">

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
    <bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
    <bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>

这两个作用一样,都是使用手动装配,如果没有手动装配的话,会报空指针错误

 

 

2.1.4constructor

<?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"
        default-autowire="constructor">

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
    <bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
    <bean id="service" class="priv.cy.dao.IndexService"></bean>
</beans>

 

这个和byType差不多,都是依靠类型来进行自动装配,区别就是byType根据的是定义的成员属性的类型来进行自动装配的

public class IndexService {
    private IndexDao dao;
    public void service() {
        dao.test();
    }
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
}

它是根据里面private IndexDao dao; 这里面定义的依赖对象的类型IndexDao来spring容器中的bean进行类型匹配的

 

而constructor使用的是构造方法的类型来进行匹配的,所以使用这种方式要设置好构造方法

public class IndexService {
    private IndexDao dao;

    public IndexService (IndexDao dao) {
        this.dao = dao;
    }

    public void service() {
        dao.test();
    }
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
}

他就会根据构造方法中传入的参数类型进行匹配,取得了构造方法中传入的参数类型是IndexDao,然后再去spring容器中找这个类型的bean,找到后将其注入到IndexService的bean中。

 

注意使用这种方法如果没有设置好构造方法,就会发生错误。

 

上面说的都是全局,自动装配也可以局部配置

<?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="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
    <bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
    <bean id="service" class="priv.cy.dao.IndexService" autowire="byName"></bean>
</beans>

像这种,在需要自动装配的bean标签中,添加autowire属性,等号右边是自动装配的方式。这样只有service这个bean使用自动装配,其他的bean都是用手动装配。

 

但是局部配置的最广泛的用处还是与全局配置混合使用,局部配置的优先级要高于全局配置

<?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"
        default-autowire="byType">

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl"></bean>
    <bean id="dao1" class="priv.cy.dao.IndexDaoImpl1"></bean>
    <bean id="service" class="priv.cy.dao.IndexService" autowire="byName"></bean>
</beans>

 向这种写法就可以实现service这个bean使用byName进行自动装配,其他的bean使用byType进行自动装配。这样就可以防止service的bean使用byType时发现有多个相同类型的bean都可以注入到service中,出现冲突报错了

 

2.2、使用JavaConfig和注解来进行自动装配

下面我们使用JavaConfig和注解来进行自动装配

 

这里就需要说一下使用注解的时候,bean的命名规则:

  1. 在使用@Component、@Repository、@Service、@Controller等注解创建bean时,如果指定bean的name,则是指定的名称.
  2. 如果不指定bean的name,bean名称的默认规则是类名的首字母小写,如SysConfig - sysConfig,Tools - tools。
  3. 如果类名前两个或以上个字母都是大写,那么bean名称与类名一样,如RBACUserLog - RBACUserLog,RBACUser - RBACUser,RBACRole - RBACRole。

 

这里和使用XML时的命名规则有些不同,使用XML来配置spring容器的时候bean标签写上id,然后如果不指定name的话,那么bean的name默认和id一样。

 

这里插一条,spring中bean的命名规则是通过BeanNameGenerator这个接口来实现的,我们可以通过实现这个接口来完成自己的命名规则,编写好命名规则类,然后告诉在告诉spring要扫描位置的同时,指定命名规则类就可以了。配置方法如下:

MyNameGenerator是我们自己编写的命名规则类

 

javaConfig:

@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
    // ...
}

XML:

 <beans>
        <context:component-scan base-package="org.example"
            name-generator="org.example.MyNameGenerator" />
  </beans>

 

javaConfig类:

package priv.cy.dao;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("priv")
public class Spring {
}

 

service:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class IndexService {
    @Autowired
    private IndexDao indexDaoImpl;

    public void service() {
        indexDaoImpl.test();
    }
    public void setDao(IndexDao dao) {
        this.indexDaoImpl = dao;
    }
}

 

dao:

import org.springframework.stereotype.Repository;
@Repository
public class IndexDaoImpl implements IndexDao {
    @Override
    public void test() {
        System.out.println("Impl 0");
    }

}

import org.springframework.stereotype.Repository;
@Repository
public class IndexDaoImpl1 implements IndexDao {
    @Override
    public void test() {
        System.out.println("Impl 1");
    }

}

上面这些代码通过注解将类交给spring容器去管理,默认的bean name就是他们的首字母小写的类名

 

先讲一下@Autowired@Resource的区别

  • @Autowired默认先使用的是byType,byType使用的是属性的类型,如果byType会出现报错的话,再就会再使用byName,这里使用的name也是属性的属性名,用属性的属性名来去spring容器中匹配查找相同的bean name
  • @Resource默认是使用的是byName,但是这里的byName和之前讲XML自动装配的byName不太一样,之前的byName都是通过setter方法名来匹配name的,但是@Resource使用的是属性名来匹配name的,和setter方法没有关系。@Resource可以指定使用byName还是byType。

以上两种自动装配的注解都不需要使用setter方法

 

2.2.1 @Autowired

例子:

@Service
public class IndexService {
    @Autowired
    private IndexDao indexDaoImpl;

    public void service() {
        indexDaoImpl.test();
    }
    public void setDao(IndexDao dao) {
        this.indexDaoImpl = dao;
    }
}

按道理说如果使用byType会出现报错,因为有两个IndexDao接口的实现类,但是它不会直接报错,@Autowired会再换成byName再试一次,这个时候的name使用的是属性名的name,也就是用这个indexDaoImpl去查找,如果byName也没有找到,就会报之前那个有两个bean的冲突错误了。

 

所以注解(@Autowired和@Resource)使用byName都是直接使用依赖的对象的属性名来进行查找的,和setter方法没有任何关系,比如上面的这个代码在spring容器中代理的IndexDaoImpl.class这个bean默认的name时indexDaoImpl,上面的代码中setter方法名时setDao,按之前的道理IndexService取到的依赖类的名字应该是dao才对,但是这里依旧能将bean注入,但是此时spring容器中IndexDaoImpl这个类的bean的name是indexDaoImpl(默认命名规则)。说明此时IndexService是用indexDaoImpl这个name来从spring容器中进行查找匹配的(也就是使用的private IndexDao indexDaoImpl 里面这个对象的名字来进行匹配的,如果此时吧这个对象的名字改成aaa,也就会报错出现问题了

 

2.2.2 @Resource

使用@Resource的话如果不指定的话,就默认是byName,如果是默认byName,那么过程和上面的@Autowired一样,但是@Resource可以指定使用byName还是byType

指定byType:

@Service
public class IndexService {
    @Resource(type = IndexDaoImpl1.class)
    private IndexDao dao;

    public void service() {
        dao.test();
    }
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
}

直接指定使用byType,并且使用IndexDaoImpl1.class这个类,这样不管怎么样service这个类注入的已经是IndexDaoImpl1这个实现类

 

也可以指定byName

@Service
public class IndexService {
    @Resource(name = "indexDaoImpl1")
    private IndexDao dao;

    public void service() {
        dao.test();
    }
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
}

这样就通过bean的name来进行查找,查找spring容器中bean的name是indexDaoImpl1的这个bean,将其注入给IndexService对象

 

三、spring懒加载

bean在调用的时候才会被加载

使用懒加载的前提是这个bean的作用域必须是单例的

 

3.1 使用XML设置:

用lazy-init。告诉spring容器是否以懒加载的方式创造对象。用的时候才加载构造,不用的时候不加载

取值:true(懒,真正调用到的时候再加载)、false(非懒,已启动spring容器就创建对象)、default(懒)

 <bean id="dao" class="cn.java.ioc.MW" lazy-init="default" ></bean>

 

3.2 使用注解设置:

@Lazy
@Bean("dao")
public Dao dao() {
    System.out.println("dao");
    return new Dao();
}

 

3.3 懒加载与非懒加载的优缺点:

  • 懒加载:对象使用的时候才去创建,节省资源,但是不利于提前发现错误。
  • 非懒加载:容器启动的时候立刻创建对象。消耗资源。利于提前发现错误。

 

当scope=“prototype” (多例)时,默认以懒加载的方式产生对象。

当scope=“singleton” (单例)时,默认以非懒加载的方式产生对象。

 

 

四、spring作用域

spring容器中的bean可以分为五个范围。所有范围的名称都是说明的,

1.singleton:这种bean范围是默认的,这种范围确保不管接受到多个请求,每个容器中有一个bean的实例,单例模式由bean factory自身来维护。

2.prototype:原先通过范围与单例范围相反,为每一个bean请求提供一个实例。

3.request:在请求bean范围内会为每一个来自客户端的网络请求创建一个实例,在请求完成之后,bean会失效并被垃圾回收器回收。该属性仅对HTT请求产生作用,使用该属性定义Bean时,每次HTTP请求都会创建一个新的Bean,适用于WebApplicationContext环境。

4.session:与请求范围类似,确保每个session中的bean的实例在session过期后bean会随之消失。该属性仅用于HTTP Session,同一个Session共享一个Bean实例。不同Session使用不同的实例。Session

5.global-session:global-session和portlet公用全局存储变量的话,那么这全局变量需要存储在global-session中。该属性仅用于HTTP Session,同session作用域不同的是,所有的Session共享一个Bean实例。

 

下面重点讨论singleton、prototyp作用域,request、session和global-session类作用域放到Spring MVC章节讨论,这里不再做详细讲述。

 

可以通过@Scope(" ")注解来设置

spring容器bean的作用域默认是单例,非懒加载的

 

 

4.1 Singleton Beans with Prototype-bean Dependencies

意思是在Singleton 当中引用了一个Prototype的bean的时候引发的问题,依赖的这个对象的原型作用域失效,也变成了单例了

 

其实这个问题就可以总结为一个类依赖另一个类,但是这两个的作用域是不同的,就会出现问题。

 

比如下面这一套代码:

javaConfig

@Configuration
@ComponentScan("priv")
public class Spring {
}

 

service

@Service
@Scope("singleton")
public class IndexService {
    @Autowired
    private IndexDao dao;

    public void service() {
        System.out.println("service" + this.hashCode());
        System.out.println("dao" + dao.hashCode());
    }

    public void setDao(IndexDao dao) {
        this.dao = dao;
    }
}

 

DAO

@Repository
@Scope("prototype")
public class IndexDaoImpl implements IndexDao {
    @Override
    public void test() {
        System.out.println("Impl 0");
    }
}

 

Test:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(Spring.class);
        IndexService service = (IndexService) annotationConfigApplicationContext.getBean("indexService");
        IndexService service1 = (IndexService) annotationConfigApplicationContext.getBean("indexService");
        IndexService service2 = (IndexService) annotationConfigApplicationContext.getBean("indexService");
        service.service();
        service1.service();
        service2.service();
    }
}

 

结果:

可以发现service是单例的,而它依赖的dao是原型的,但是最终的结果确实dao的原型作用域失效,这就是作用域不同进行依赖错产生的问题。因为service是单例的,他在spring容器中只会创建一次,而它依赖的对象是随着它的加载而加载的,所以service只加载一次,即使dao是原型的,也只会被加载一次。

我们的解决办法只能是在service调用dao的时候想办法重新获取一次新的dao对象,这样既能保证service对象是单例,也能保证每一次调用dao都是新产生的对象。

如果这个对象要依赖的类和自己的作用域不同,spring提供了两种解决方法:

 

4.1.1 ApplicationContextAware接口

一种是让这个对象实现ApplicationContextAware接口,这个还需要覆写一个set方法,和引入ApplicationContext对象

这样的解决办法就是在service类每一次需要使用依赖的这个dao时,就通过ApplicationContext对象重新取一次这个bean,这样dao就可以依旧保持原型的作用域,每一次使用它就会是一个新bean,而service对象一直保持单例不变。

 

javaConfig

@Configuration
@ComponentScan("priv")
public class Spring {
}

 

service

@Service
@Scope("singleton")
public class IndexService implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Autowired   //加不加这个注解都无所谓了,反正最后使用的也不是这个dao对象  用ApplicationContext这个Autowired可加可不加
    private IndexDao dao;

    public void service() {
        System.out.println("service" + this.hashCode());
		//每次调用都使用applicationContext对象重新获取IndexDaoImpl的bean
        System.out.println("dao" + applicationContext.getBean("indexDaoImpl").hashCode());
    }
    public void setDao(IndexDao dao) {
        this.dao = dao;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

 

DAO

@Repository
@Scope("prototype")
public class IndexDaoImpl implements IndexDao {
    @Override
    public void test() {
        System.out.println("Impl 0");
    }
}

 

Test:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(Spring.class);
        IndexService service = (IndexService) annotationConfigApplicationContext.getBean("indexService");
        IndexService service1 = (IndexService) annotationConfigApplicationContext.getBean("indexService");
        IndexService service2 = (IndexService) annotationConfigApplicationContext.getBean("indexService");
        service.service();
        service1.service();
        service2.service();
    }
}

 

结果:

 

但是这种实现spring接口的方法,会大大增加项目的耦合度,侵入性太强,使代码严重spring的API,spring的设计理念之一就是非侵入性,所以spring还提供了Lookup Method Injection这个方法来解决Singleton Beans with Prototype-bean Dependencies问题

 

 

4.1.2 Lookup Method Injection

使用@Lookup注解,就可以不需要依赖spring的API了

 

其他代码和之前还是一样,只需要修改service代码:

@Service
@Scope("singleton")
public class IndexService {
//  @Autowired     使用Lookup必须要将Autowired去掉,否则会出错
    private IndexDao dao;

    public void service() {
        System.out.println("service" + this.hashCode());
        System.out.println("dao" + getDao().hashCode());
    }

    @Lookup
    public IndexDao getDao() {
        return null;
    }
}

这里只需要添加一个get方法,用来每一次调用时获取全新的dao对象,方法名不重要,自定义就行,返回值就是你要获取的对象类型,只需要在这个方法上面添加@Lookup注解,这个方法内部什么逻辑代码都不需要写,直接写一个return null就可以了。在每次获取dao对象的位置调用这个方法就可以了。还有一点就是因为是通过这个方法来获取daobean了,所以也就不需要@Autowired这个注解进行注入了,要将其删除,不删除的话会出现报错

 

@Lookup是通过方法的返回值类型,根据这个类型从spring容器中查找这个bean,如果找到了就直接通过这个方法返回回来。但是这里就有可能出现前面讲的多个相同类型的bean冲突问题,如果IndexDao是一个接口,而这个接口有多个实现类被委托给spring容器进行管理,那么这个时候如果还通过类型来查找就找到多个bean,进而出现错误

Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'priv.cy.dao.IndexDao' available: expected single matching bean but found 2: indexDaoImpl,indexDaoImpl1

 

解决这个问题就还需要通过byName来进行查找,方法就是@Lookup("indexDaoImpl"),直接在这个注解指定bean的name,然后他就会通过这个name去spring容器中查找这个对应的bean,这样就不会起冲突了

@Service
@Scope("singleton")
public class IndexService {
//    @Autowired   
    private IndexDao dao;
    public void service() {
        System.out.println("service" + this.hashCode());
        System.out.println("dao" + getDao().hashCode());
    }

    @Lookup("indexDaoImpl")
    public IndexDao getDao() {
        return null;
    }
}

 

 

五、spring生命周期和回调

就相当于finally

生命周期回调有三种:

  • Initialization Callbacks(初始化回调)
  • Destruction Callbacks(销毁回调)
  • Default Initialization and Destroy Methods(默认初始化和销毁方法)

 

 

5.1 Initialization Callbacks(初始化回调)

在初始化的时候回调,初始化是在实例化方法执行之后,需要实现InitializingBean接口,然后覆写afterPropertiesSet()方法,这样每次初始化的时候就会回调afterPropertiesSet方法

代码:

@Repository
public class IndexDaoImpl1 implements IndexDao , InitializingBean {
    public IndexDaoImpl1() {
        System.out.println("Constructor");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("init");
    }
}

 

测试类:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(Appconfig.class);
    }
}

 

执行结果:

 

 

5.2 Destruction Callbacks(销毁回调)

当容器被销毁的时候,会进行回调。需要实现DisposableBean这个接口,然后覆写afterPropertiesSet()方法,这样容器被销毁的时候就会回调afterPropertiesSet方法

代码:

@Repository
public class IndexDaoImpl1 implements IndexDao , InitializingBean , DisposableBean {
    public IndexDaoImpl1() {
        System.out.println("Constructor");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("init");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("destroy");
    }
}

 

5.3 Default Initialization and Destroy Methods(默认初始化和销毁方法)

可以自定义回调方法,不需要去实现接口,不依赖spring的API,降低了系统的耦合,非侵入性。

可以指定一个初始化方法和销毁方法来实现方法回调

 

有两种设置方式,XML和注解

 

5.3.1 XMLinit-methoddestroy-method

 

dao:

@Repository
public class IndexDaoImpl1 implements IndexDao {
    public IndexDaoImpl1() {
        System.out.println("Constructor");
    }
    public void init() {
        System.out.println("init");
    }
    public void destory() {
        System.out.println("destory");
    }
}

 

test:

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext
                = new ClassPathXmlApplicationContext("classpath:spring.xml");
    }
}

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"

       default-init-method="init"
       default-destroy-method="destory" >

    <bean id="dao" class="priv.cy.dao.IndexDaoImpl1"></bean>
</beans>

 就是直接在需要添加回调方法的类中编写自己想要的方法就行了,方法名自定义,只需要在XML配置文件中指定初始化回调方法和销毁回调方法的方法名就可以了

 

 

5.3 2 注解(@PostConstruct@PreDestory):

 

也可以直接使用注解,不需要用XML配置

@PostConstruct 由JSR-250提供,在构造函数执行完之后执行,等价于xml配置文件中bean的initMethod

@PreDestory 由JSR-250提供,在Bean销毁之前执行,等价于xml配置文件中bean的destroyMethod

使用以上两个注解直接用在回调方法上,也能实现自定义的回调方法

 

例如:

@Repository
public class IndexDaoImpl1 implements IndexDao {
    public IndexDaoImpl1() {
        System.out.println("Constructor");
    }

    @PostConstruct
    public void init() {
        System.out.println("init");
    }

    @PreDestory
    public void destory() {
        System.out.println("destory");
    }
}

 

 

懒加载

只有在调用的时候才会被加载

使用@Lazy标签

 

例如将上面的回调方法代码加上@Lazy标签:

@Repository
@Lazy
public class IndexDaoImpl1 implements IndexDao {
    public IndexDaoImpl1() {
        System.out.println("Constructor");
    }
    @PostConstruct
    public void init() {
        System.out.println("init");
    }
    public void destory() {
        System.out.println("destory");
    }
}

 

Test:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(Appconfig.class);

        //IndexDaoImpl1 dao = (IndexDaoImpl1) annotationConfigApplicationContext.getBean(IndexDao.class);
    }
}

 

执行结果:

 

这个代码的构造方法和回调方法都不会执行了,说明只有当调用这个bean的时候这个bean在容器中才会被实例化

 

 

六、其他

6.1 @ComponentScan标签的其他用法

它不光可以指定要spring要扫描注解的范围,还可以指定spring不扫描的位置

 

使用注解的时候如果向注解中传入的参数只有一个,也就是value,那么就可以不用写value="",直接在括号里面写上参数就可以。但是如果传入的参数大于1,那么就必须表明每一个参数要传给谁

例如:

@Configuration
@ComponentScan(value = "priv", excludeFilters = {@ComponentScan.Filter(type = FilterType.REGEX,pattern = "priv.cy.service.*")})
public class Appconfig {
}

上面的注解意思就是扫描priv下面的所有包,但是priv.cy.service包下的内容除外,如果使用注解来装配bean的话,priv.cy.service包下的bean就会全部装配失败。type表示的是后面pattern表达式的类型,像REGEX类型的表达式后面必须要带*,其他类型的相关简介请看官方文档https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-scanning-filters

 

还有就是可以可以指定必须要扫描哪些位置,使用includeFilters 参数

例如:

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

它的应用场景比如我对A包里面除了其中一个子包a需要扫描之外,其余的都不需要扫描,就需要用到includeFilters。

 

实现以上功能还可以使用XML配置

<beans>
    <context:component-scan base-package="org.example">
        <context:include-filter type="regex"
                expression=".*Stub.*Repository"/>

        <context:exclude-filter type="annotation"
                expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>

 

 

6.2 byType多个同类型的Bean冲突解决办法

再来说一下之前讲过使用byType会出现多个相同类型的bean出现冲突无法自动装配的解决方法,有两种

 

6.2.1@Primary

这个是主数据源标签,出现冲突的情况下优先使用这个bean进行装配

例子:

service:

@Service
public class IndexService {
    @Autowired
    private IndexDao indexDao;
}

dao:

@Repository
public class IndexDaoImpl1 implements IndexDao {
    public IndexDaoImpl1() {
        System.out.println("Constructor 1");
    }
    @PostConstruct
    public void init() {
        System.out.println("init 1");
    }
    public void destory() {
        System.out.println("destory 1");
    }
}

@Repository
public class IndexDaoImpl2 implements IndexDao {
    public IndexDaoImpl2() {
        System.out.println("Constructor 2");
    }
    @PostConstruct
    public void init() {
        System.out.println("init 2");
    }
    public void destory() {
        System.out.println("destory 2");
    }
}

不使用@Primary标签,报错

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'priv.cy.dao.IndexDao' available: expected single matching bean but found 2: indexDaoImpl1,indexDaoImpl2

 

 

对IndexDaoImpl1使用@Primary标签

@Repository
@Primary
public class IndexDaoImpl1 implements IndexDao {
    public IndexDaoImpl1() {
        System.out.println("Constructor 1");
    }
    @PostConstruct
    public void init() {
        System.out.println("init 1");
    }
    public void destory() {
        System.out.println("destory 1");
    }
}

就可以正常注入了,会优先将IndexDaoImpl1注入到IndexService对象中

 

6.2.2、第二种方法使用限定注解@Qualifier

@Qualifier这个注解可以直接指定要注入哪个bean,它通过name去指定bean

代码:

@Service
public class IndexService {
    @Qualifier("indexDaoImpl1")
    private IndexDao indexDao;
}

直接指定使用indexDaoImpl1这个name的bean去注入

Java自带的一些注解可以替代spring的注解来完成相同的功能,比如@Resource,这个就属于jdk的注解,但是也能实现自动装配的功能。

 

 

6.3 加速spring扫描类的速度

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.2.4.RELEASE</version>
        <optional>true</optional>
    </dependency>
</dependencies>

添加了这个之后spring项目在编译的时候就会将所有要扫描的类添加到一个索引当中,以后再扫描就不需要使用类路径一个个扫描了,而是直接使用索引进行扫描,这样大大加快了扫描速度。

 

6.4 @Bean注解

@Bean 注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上),也就是将方法的返回值所谓一个bean交给spring容器管理

比如一些必须要通过一个方法返回出来的对象想把它交给spring容器去管理,我们就需要用这个办法,在这个方法上面添加@Bean标签,这个方法的返回值就交给spring容器管理了。

 

配置类

@Configuration
public class SpringConfiguration {
    /**
     * 在JavaConfig配置类中使用@Bean标签来完成bean的注册,效果就和在XML中配置<bean>标签是一样的
     */
    @Bean
    public HollowController hollowController(){
        return new HollowController();
    }
}

 

 

6.5 @Profile标签

这个标签用来切换环境

dao:

@Repository
@Profile("dao1")
public class IndexDaoImpl1 implements IndexDao {
    public IndexDaoImpl1() {
        System.out.println("Constructor 1");
    }
    @PostConstruct
    public void init() {
        System.out.println("init 1");
    }
    public void destory() {
        System.out.println("destory 1");
    }
}


@Repository
@Profile("dao2")
public class IndexDaoImpl2 implements IndexDao {
    public IndexDaoImpl2() {
        System.out.println("Constructor 2");
    }
    @PostConstruct
    public void init() {
        System.out.println("init 2");
    }
    public void destory() {
        System.out.println("destory 2");
    }
}

 

给两个dao设置了对应的环境名

测试类:

public class Test {
    public static void main(String[] args) {
        // 创建AnnotationConfigApplicationContext类的时候先不指定配置类
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext();

        // 获取annotationConfigApplicationContext对象的环境并对其进行修改
        annotationConfigApplicationContext.getEnvironment().setActiveProfiles("dao2");

        // 指定完环境之后再注册配置类
        annotationConfigApplicationContext.register(Appconfig.class);

        // 注册完成后刷新annotationConfigApplicationContext
        annotationConfigApplicationContext.refresh();

        // 再取bean就是在dao2这个环境下获取了
        annotationConfigApplicationContext.getBean(IndexService.class);
    }
}

此时service注入的就是dao2环境下的bean了

这个注解的用途就是可以设置很多种环境,可以通过切换不同的环境来使不同的bean交给spring容器管理,或者设置多个配置类环境,可以根据需要任意切换。


其他文章:【Spring】面试官,请别再问Spring Bean的生命周期了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章