IOC 实验

1、实验一[Debug 查看对象的属性是如何设置进去的]

Debug 查看对象的属性是如何设置进去的

  • 结论 1:创建 HappyFactory 对象并设置属性值是发生在 IOC 容器对象创建的过程中,而不是 getBean()的时候。
  • 结论 2:Spring IOC 容器内部使用 BeanFactory 创建 bean 的对象,对外提供 ApplicationContext 给开发者使用。
  • 结论 3:bean 的属性通过 setXxx()方法注入到 bean 对象中

2、实验二[重要,根据类型获取 bean]

根据类型获取 bean

1
2
3
4
5
6
7
8
9
@Test
public void testGetBeanByType() {

// 根据类型获取bean对象
HappyFactory factory = iocContainer.getBean(HappyFactory.class);

System.out.println("factory = " + factory);

}

此时如果同样类型的 bean 在 IOC 容器中不只一个对象,那么会抛出下面异常:

1
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.atguigu.spring.component.HappyFactory] is defined: expected single matching bean but found 2: happyFactory,happyFactory2

3、实验三(属性赋值,能看懂就行)

使用构造器给属性赋值。(能看懂就行)。

必须按照顺序赋值,否则使用 index。

1
2
<!-- 如果constructor-arg标签的顺序和构造器实际的顺序不同,那么会抛出下面的异常: -->
<!-- org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'happyFactory3' defined in class path resource [applicationContext.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities) -->

上面创建的顺序:

1
2
3
4
5
public class HappyFactory {

private String factoryName;
private Double registerMoney;
private Integer factoryAge;

所以使用构造器给属性赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 通过构造器给bean的属性赋值,按照顺序 -->
<bean id="happyFactory3" class="com.atguigu.spring.component.HappyFactory">
<!-- 通过constructor-arg标签给构造器传入参数 -->
<constructor-arg value="goodFactory"/>
<constructor-arg value="8877.66"/>
<constructor-arg value="99"/>
</bean>

<!-- 通过构造器给bean的属性赋值,打乱顺序 -->
<bean id="happyFactory4" class="com.atguigu.spring.component.HappyFactory">
<!-- 通过constructor-arg标签给构造器传入参数 -->
<constructor-arg value="66" index="2"/>
<constructor-arg value="5544.33" index="1"/>
<constructor-arg value="helloFactory" index="0"/>
</bean>

一般的赋值方式:

1
2
3
4
5
6
<bean id="happyFactory" class="com.atguigu.spring.component.HappyFactory">
<!-- 通过配置property标签给对象设置属性 -->
<property name="factoryName" value="dreamFactory"/>
<property name="factoryAge" value="20"/>
<property name="registerMoney" value="500"/>
</bean>

4、实验四(属性赋值)

使用 p 名称空间给属性赋值

1
2
3
4
5
6
7
8
<!-- 使用p名称空间给bean的属性赋值 -->
<bean
id="happyFactory5"
class="com.atguigu.spring.component.HappyFactory"
p:factoryName="appleFactory"
p:factoryAge="33"
p:registerMoney="666.66"
/>

5、实验五[null 值和级联属性]

null 值和级联属性

级联属性使用ref标签,声明级联属性的具体对象的位置。

1
2
public class HappyFactory {
private Machine machine;
1
2
3
public class Machine {
private String machineName;
private Double machinePrice;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- 给属性设置不同的值 -->
<bean id="happyFactory6" class="com.atguigu.spring.component.HappyFactory">
<!-- 通过配置property标签给对象设置属性 -->
<property name="factoryName">
<!-- 使用null标签作为property标签的子标签给属性设置null值 -->
<null/>
</property>
<property name="factoryAge" value="20"/>
<property name="registerMoney" value="500"/>

<!-- 给级联属性赋值之前,必须先让级联的属性有一个具体的对象,即需要首先给级联属性装配一个bean-->
<!-- 在property标签中使用ref属性关联另外一个bean -->
<property name="machine" ref="machine"/>

<!-- 给级联属性赋值 -->
<property name="machine.machineName" value="iceCreamMachine"/>
<property name="machine.machinePrice" value="50.50"/>
</bean>

<!-- 级联属性的具体对象的位置 -->
<bean id="machine" class="com.atguigu.spring.component.Machine"/>

6、实验六[重要,给级联属性赋值,引用另外一个 bean]

给属性赋值的时候,引用另外一个 bean。(这个实验中配置的方式是符合 Spring 使用习惯的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 在给属性设置值的时候引用另外一个bean -->
<!-- 配置machine -->
<bean id="machine2" class="com.atguigu.spring.component.Machine">
<!-- value属性用于给属性设置字面量形式的值 -->
<property name="machineName" value="bananaMachine"/>
<property name="machinePrice" value="555.66"/>
</bean>

<!-- 配置HappyFactory -->
<bean id="happyFactory7" class="com.atguigu.spring.component.HappyFactory">
<property name="factoryName" value="bananaFactory"/>
<property name="factoryAge" value="33"/>
<property name="registerMoney" value="33.44"/>
<!-- ref属性指定的是另一个bean的id,如果不小心把ref属性写成了value属性(value属性指定的都是字面量),那么Spring不会把value的值看成是一个bean的id,而就是一个普通的字符串,赋值的时候很可能会出错 -->
<!-- 如果ref属性写成了value属性,会抛出异常:java.lang.IllegalStateException: Cannot convert value of type -->
<property name="machine" ref="machine2" />
</bean>

7、实验七[比较重要,以内部 bean 的形式给属性赋值]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 以内部bean的形式给Machine属性赋值 -->
<!-- Machine的bean的声明位置位于happyFactory8的内部 -->
<bean id="happyFactory8" class="com.atguigu.spring.component.HappyFactory">
<property name="factoryName" value="bananaFactory"/>
<property name="factoryAge" value="33"/>
<property name="registerMoney" value="33.44"/>

<!-- 配置machine属性 -->
<property name="machine">
<!-- 内部bean仅限于当前位置使用,外部无法使用,所以可以不用写id属性 -->
<bean class="com.atguigu.spring.component.Machine">
<property name="machineName" value="catMachine"/>
<property name="machinePrice" value="66.33"/>
</bean>
</property>
</bean>

8、实验八[给集合属性赋值,list 标签内部引用外部 bean]

1
2
public class HappyFactory {
private List<ProductLine> productLineList;
1
2
public class ProductLine {
private String productName;
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="happyFactory9" class="com.atguigu.spring.component.HappyFactory">
<property name="factoryName" value="bananaFactory"/>
<property name="factoryAge" value="33"/>
<property name="registerMoney" value="33.44"/>

<!-- 给集合属性赋值 -->
<property name="productLineList">
<!-- 使用list标签配置List类型的数据给productLineList属性赋值 -->
<list>
<!-- list标签内部可以使用内部bean的形式,也可以使用ref标签引用外部bean -->
<bean class="com.atguigu.spring.component.ProductLine">
<property name="productName" value="lineOne"/>
</bean>

<bean class="com.atguigu.spring.component.ProductLine">
<property name="productName" value="lineTwo"/>
</bean>

<bean class="com.atguigu.spring.component.ProductLine">
<property name="productName" value="lineThree"/>
</bean>
<!-- 引用外部bean -->
<ref bean="productLine"/>
</list>
</property>
</bean>

<bean id="productLine" class="com.atguigu.spring.component.ProductLine">
<property name="productName" value="lineFour"/>
</bean>

9、实验九[集合类型的 bean,直接配置集合类型]

1
2
public class HappyFactory {
private List<Manager> managerList;
1
2
3
public class Manager {
private String managerName;
private String managerSalary;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 直接配置集合类型 -->
<util:list id="managerTeamId">
<bean class="com.atguigu.spring.component.Manager">
<property name="managerName" value="tom"/>
<property name="managerSalary" value="500.500"/>
</bean>
<bean class="com.atguigu.spring.component.Manager">
<property name="managerName" value="jerry"/>
<property name="managerSalary" value="600.500"/>
</bean>
<bean class="com.atguigu.spring.component.Manager">
<property name="managerName" value="bob"/>
<property name="managerSalary" value="700.500"/>
</bean>
</util:list>

<bean id="happyFactory10" class="com.atguigu.spring.component.HappyFactory">
<!-- 引用外部集合类型的bean-->
<property name="managerTeam" ref="managerTeamId"/>
</bean>

10、实验十[比较重要,FactoryBean]

学习这个接口是为了理解 Spring 整合其他第三方框架时采取的策略。

①FactoryBean 接口说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.springframework.beans.factory;

// 如果我们在Spring的配置文件中配置FactoryBean类型的bean
// 实际从IOC容器中获取这个bean对应的对象时,实际拿到的是getObject()返回的对象
// 泛型T指定的就是当前这个“工厂”要生产的bean,我们要让这个“工厂”生产什么产品,这里的T就写什么类型
public interface FactoryBean<T> {
// 工厂生产出来的产品对象,也是从IOC容器中真正获取到的那个对象
T getObject() throws Exception;

// 产品对象的类型
Class<?> getObjectType();

// 工厂生产的产品对象是否是单一实例的
boolean isSingleton();
}

举例:Spring 整合 Mybatis 时,配置的是 SqlSessionFactoryBean,但是获取到的实际的对象是 SqlSessionFactory。SqlSessionFactoryBean 就实现了 FactoryBean 接口。

所以,FactoryBean 形式适用于整合第三方框架时,去创建复杂的核心对象。

② 创建 FactoryBean

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
public class HappyFactoryBean implements FactoryBean<HappyFactory> {
// 提供了getXxx()、setXxx()方法的成员变量,可以在配置bean的时候指定具体值
private String factoryName;

public String getFactoryName() {
return factoryName;
}

public void setFactoryName(String factoryName) {
this.factoryName = factoryName;
}
@Override
public HappyFactory getObject() throws Exception {

// 编写创建HappyFactory对象的具体过程
HappyFactory happyFactory = new HappyFactory();
happyFactory.setFactoryName(factoryName);
happyFactory.setRegisterMoney(555.55);
happyFactory.setFactoryAge(20);
happyFactory.setMachine(new Machine("uuuMachine", 555.22));
happyFactory.setManagerTeam(Arrays.asList(new Manager("tom",55.22),new Manager("jerry",66.33)));

return happyFactory;
}

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

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

③ 配置 FactoryBean

1
2
3
4
5
6
7
<!-- 在class属性配置的是HappyFactoryBean -->
<!-- 配置的bean:MyHappyFactoryBean类型(实现FactoryBean接口的类) -->
<!-- 得到的bean:HappyFactory类型(实现FactoryBean接口后getObject()方法的返回值) -->
<!-- 这个bean在IOC容器中真正创建的对象是HappyFactory -->
<bean id="happyFactory11" class="com.atguigu.spring.component.HappyFactoryBean">
<property name="factoryName" value="haoziweizhi"/>
</bean>

11、实验十一[比较重要,测试 bean 的作用域]

测试 bean 的作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 按照默认作用域配置manager -->
<!-- scope属性:配置bean的作用域 -->
<!-- scope属性可选值:singleton表示当前bean是单实例的,默认值,在IOC容器对象初始化过程中创建对象 -->
<bean id="managerSingleton" class="com.atguigu.spring.component.Manager">
<property name="managerName" value="justin"/>
<property name="managerSalary" value="5000.55"/>
</bean>

<!-- scope属性可选值:prototype表示当前bean是多实例的,在每次IOC对象调用getBean()方法时创建对象 -->
<!-- 通过scope属性将bean设置为多实例 -->
<bean id="managerPrototype" scope="prototype" class="com.atguigu.spring.component.Manager">
<property name="managerName" value="justin"/>
<property name="managerSalary" value="5000.55"/>
</bean>

junit 测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testScope() {
Object managerSingleton01 = iocContainer.getBean("managerSingleton");
Object managerSingleton02 = iocContainer.getBean("managerSingleton");

System.out.println("managerSingleton01.hashCode() = " + managerSingleton01.hashCode());
System.out.println("managerSingleton02.hashCode() = " + managerSingleton02.hashCode());

// 打印结果如下:证明是同一个对象,说明bean默认是单例的
// managerSingleton01.hashCode() = 265119009
// managerSingleton02.hashCode() = 265119009

Object managerPrototype01 = iocContainer.getBean("managerPrototype");
Object managerPrototype02 = iocContainer.getBean("managerPrototype");

System.out.println("managerPrototype01.hashCode() = " + managerPrototype01.hashCode());
System.out.println("managerPrototype02.hashCode() = " + managerPrototype02.hashCode());
}

通过 debug 跟踪源码,发现:

  • scope 属性为 singleton 时:bean 的实例对象在 IOC 容器初始化时创建
  • scope 属性为 prototype 时:bean 的实例对象在调用 getBean()方法时创建

实用价值:将来在 SpringMVC 中,使用 Controller 类处理请求,此时有个问题——SpringMVC 会不会为每一个请求单独创建一个 Controller 对象?基于默认值配置,Controller 对象也是单实例的。所以不要在处理请求的过程中修改 Controller 的成员变量———-会有线程安全问题。

12、实验十二[应付面试,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
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(
// bean是被处理的对象
Object bean,
// beanName是被处理对象的bean的id
String beanName) throws BeansException {

System.out.println("Before bean = " + bean);
System.out.println("Before beanName = " + beanName);

// 这里要把经过处理的bean返回
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

System.out.println("After bean = " + bean);
System.out.println("After beanName = " + beanName);

return bean;
}
}

② 注册后置处理器类

1
<bean id="myBeanPostProcessor" class="com.atguigu.spring.component.MyBeanPostProcessor"/>

③ 执行的时机

  • postProcessBeforeInitialization():在初始化操作前
  • postProcessAfterInitialization():在初始化操作后

13、实验十三[应付面试,bean 的初始化方法和销毁方法]

① 在 bean 所属的 Java 类中创建初始化方法和销毁方法

1
2
3
4
5
6
7
8
9
// 对象创建好之后执行初始化操作
public void doInit() {
System.out.println("小孩儿刚生下来要洗澡");
}

// 对象销毁前执行清理操作
public void doDestroy() {
System.out.println("告别之前留几句话");
}

② 配置 bean 时指定初始化方法和销毁方法

1
2
3
4
5
6
7
<!-- 配置bean的初始化方法和销毁方法 -->
<!-- 使用init-method属性指定bean的初始化方法 -->
<!-- 使用destroy-method属性指定bean的销毁方法 -->
<bean id="managerInitAndDestroy"
init-method="doInit"
destroy-method="doDestroy"
class="com.atguigu.spring.component.Manager"></bean>

③junit 测试

1
2
3
4
5
6
@Test
public void testBeanLifeCycle() {
Object managerInitAndDestroy = iocContainer.getBean("managerInitAndDestroy");
System.out.println("managerInitAndDestroy = " + managerInitAndDestroy);
((ClassPathXmlApplicationContext)iocContainer).close();
}

打印效果:

1
2
3
4
5
6
7
8
9
10
Manager constructor
Before bean = Manager{managerName='null', managerSalary=null}
Before beanName = managerInitAndDestroy
小孩儿刚生下来要洗澡
After bean = Manager{managerName='null', managerSalary=null}
After beanName = managerInitAndDestroy
managerInitAndDestroy = Manager{managerName='null', managerSalary=null}
十二月 02, 2020 4:43:54 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4141d797: startup date [Wed Dec 02 16:43:54 CST 2020]; root of context hierarchy
告别之前留几句话

④bean 的生命周期

  • 创建 bean 的对象
  • 设置 bean 的属性
  • 调用 bean 后置处理器的 before 方法(如果有的话)
  • 调用 bean 的初始化方法(如果有的话)
  • 调用 bean 后置处理器的 after 方法(如果有的话)
  • bean 可用
  • 调用 bean 的销毁方法(如果有的话)
  • 关闭 IOC 容器

14、实验十四[重要,引用外部属性文件]

① 准备外部属性文件(jdbc.properties)

1
2
3
4
wechat.dev.driver=com.mysql.jdbc.Driver
wechat.dev.url=jdbc:mysql://localhost:3306/mybatis0223
wechat.dev.username=root
wechat.dev.password=root

② 在 Spring 配置文件(applicationContext.xml)中引入外部属性文件

1
2
3
4
5
    <!-- 引入context名称空间 -->
<!-- 使用context:property-placeholder引入外部属性文件 -->
<!-- 在location属性中指定外部属性文件的路径 -->
<!-- classpath:表示从类路径的根目录下开始查找,后面写外部属性文件以类路径根目录为基准的相对路径 -->
<context:property-placeholder location="classpath:jdbc.properties"/>

③ 基于外部属性文件配置数据源

1
2
3
4
5
6
7
<!-- 配置数据源,引用外部属性文件中保存的数据 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${wechat.dev.driver}"/>
<property name="url" value="${wechat.dev.url}"/>
<property name="username" value="${wechat.dev.username}"/>
<property name="password" value="${wechat.dev.password}"/>
</bean>

④junit 测试

1
2
3
4
5
6
@Test
public void testDBConnection() throws SQLException {
DataSource dataSource = iocContainer.getBean(DataSource.class);
Connection connection = dataSource.getConnection();
System.out.println("connection = " + connection);
}

打印结果:connection = com.mysql.jdbc.JDBC4Connection@3745e5c6

15、实验十五[基于 XML 的自动装配]

① 概念

自动装配:在 IOC 容器中,组件 A 需要组件 B;同时组件 B 在 IOC 容器中也正好存在,IOC 容器替我们自动把这两个组件组装好。

组装:

② 实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 配置EmpDao对应的bean -->
<bean id="empDao3" class="com.atguigu.spring.component.EmpDao"/>
<bean id="empDao2" class="com.atguigu.spring.component.EmpDao"/>

<!-- 配置EmpService,并在EmpService中自动装配EmpDao -->
<!-- 使用autowire属性设置装配规则,使用autowire属性配置自动装配 -->
<!-- autowire="byType":根据所需要的类型,到IOC容器中查找符合这个类型的bean。此时要求目标bean必须是单实例的。 -->
<!-- autowire="byName":根据bean的id进行装配。装配的规则是:需要装配的属性的属性名[setXxx()方法定义的属性]和目标bean的id一致。 -->
<bean id="empService"
class="com.atguigu.spring.component.EmpService"
autowire="byName"/>

<!-- 手动装配 -->
<!--<property name="empDao" ref="empDao"/>-->
</bean>
<!-- 被装配的bean -->
<!--<bean id="empService" class="com.atguigu.spring.component.EmpService">
-->

16、实验十六[重要,通过注解配置 bean]

① 组件相关的注解

  • @Controller:控制器(注入服务)

  • @Service:对应业务逻辑层的 Service 类(注入 dao)

  • @Repository:对应持久化层的 Dao 或 Mapper(实现 dao 访问)

  • @Component:相当于配置文件中的<bean id=”” class=””/>(实现 bean 的注入)

    @Component 泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services 等的时候),我们就可以使用@Component 来标注这个类

从程序运行的角度来说,上面四个注解的作用是一样的,即使标记在了不对应的类上,Spring 在运行过程中也不会报错。但是为了我们自己开发过程中不造成不必要的误解,我们还是要标记在正确的类上。

② 在类上标记注解

1
2
3
@Controller
public class EmpController {
}
1
2
3
@Service
public class EmpService {
}
1
2
3
@Repository
public class EmpDao {
}

但是仅仅把注解标记在类上,还不足以让 IOC 容器创建它们的对象。

初学者的误区:

认为注解对应的功能都是在注解的代码里写的。但其实并没有。

例如:@Controller 注解代码

1
2
3
4
5
6
7
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
String value() default "";
}

@Controller 注解的代码中其实有效的就是声明了一个 value 属性。

注解本身和它里面可以设置的属性一起告诉框架,我们在标记了注解的这个地方要干嘛。

具体干活还是框架去干。

但是框架怎么知道这有个注解?

答案是:扫描。

③ 配置自动扫描的包

加入 spring-aop-4.0.0.RELEASE.jar 包。

1
<context:component-scan base-package="com.atguigu.spring.component"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  <!-- context:component-scan配置基本情况1 -->
<context:component-scan base-package="com.atguigu.spring.component"/>

<!-- context:component-scan配置基本情况2 -->
<context:component-scan base-package="com.atguigu.spring.component,com.atguigu.spring.toy"/>

<!-- context:component-scan配置基本情况3 -->
<context:component-scan base-package="com.atguigu.spring.*" resource-pattern="Stu*.class"/>

<!-- context:component-scan配置基本情况4 -->
<context:component-scan base-package="com.atguigu.spring.*">
<!-- 在子标签中使用context:exclude-filter排除指定的类 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<!-- context:component-scan配置基本情况5 -->
<!-- 使用use-default-filters="false"取消默认规则,配合context:include-filter追加规则才能实现“仅包含”效果 -->
<context:component-scan base-package="com.atguigu.spring.*" use-default-filters="false">

<!-- context:include-filter表示追加规则,不会取消原有规则 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

④junit 测试

1
2
3
4
5
6
7
8
9
10
11
12
private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");

@Test
public void testGetAnnotationBean() {
EmpController empController = iocContainer.getBean(EmpController.class);
EmpService empService = iocContainer.getBean(EmpService.class);
EmpDao empDao = iocContainer.getBean(EmpDao.class);

System.out.println("empController = " + empController);
System.out.println("empService = " + empService);
System.out.println("empDao = " + empDao);
}

⑤bean 的 id

[1]默认情况

使用上面注解将类加入 IOC 容器后,bean 的 id 是类名首字母小写。例如:下面代码中,这个类在 IOC 容器中的 bean 的 id 是 taskServiceSimpleImpl

1
2
3
@Service
public class TaskServiceSimpleImpl implements TaskService {
}

[2]指定 bean 的 id

1
2
3
@Service(value = "zhutou")
public class TaskServiceSimpleImpl implements TaskService {
}

1
2
3
@Service("zhutou")
public class TaskServiceSimpleImpl implements TaskService {
}

⑥bean 的范围

和基于 XML 的情况一样,通过注解加入 IOC 容器的 bean,默认是单一实例的。如果使用@Scope(value = “prototype”)设置为多实例对象,那么每次 getBean()会创建新的对象

1
2
3
4
5
6
7
// 使用@Scope注解可以指定bean是单一实例还是多实例
@Scope(value = "prototype")

// 使用value属性可以指定bean的id
@Controller(value = "fengLaoShiZhenShuai")
public class EmpController {
}

17、实验十七[通过注解自动装配]

① 使用@Autowired 注解的基本情况[重要]

下面的代码是最基本的用法,但是以后我们用的就是这个最基本的用法。

1
2
3
4
5
6
7
8
9
10
11
@Controller
public class EmpController {

@Autowired
private EmpService empService;

public String getMessage() {
return empService.getMessage();
}

}
1
2
3
4
5
6
7
8
9
10
@Service
public class EmpService {

@Autowired
private EmpDao empDao;

public String getMessage() {
return empDao.selectMessage();
}
}

② 新旧代码对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class EmpServlet extends BaseServlet {

private EmpService empService = new EmpService();

}

----------------华丽的分割线------------------

@Controller
public class EmpController {

@Autowired
private EmpService empService;

}

18、实验十八[自动装配特殊情况]

① 可以加@Autowired 注解的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Controller
public class EmpController {

@Autowired
private EmpService empService;

@Autowired
public EmpController(EmpService empService) {
System.out.println("EmpController构造器中:empService = " + empService);
}

@Autowired
public void setEmpService(EmpService empService) {
System.out.println("EmpController的set方法中:empService = " + empService);
}

②@Autowired 注解设置 required 属性

required 属性可选值:

  • true:表示当前位置必须注入成功,否则会抛出装配失败的异常
  • false:不是必须装配,允许装配不成功
1
2
3
4
// 默认情况下@Autowired要求必须装配成功,如果目标bean找不到会抛出异常
// 如果目标bean可有可无,不装配也行,那么可以设置required = false
@Autowired(required = false)
private DeptService deptService;

但是实际开发时,但凡是写了@Autowired 注解,那就肯定是需要装配成功的。

③@Autowired 注解查找目标 bean 的顺序

  • 第一步:根据类型查找
  • 第二步:如果根据类型找到的是多个,那么根据变量名或方法名作为 bean 的 id 继续查找

上面两种方式都找不到会抛出异常

④@Qualifier 注解

帮助@Autowired 注解指定目标 bean 的名称

1
2
3
4
@Autowired
// 使用@Qualifier注解指定目标bean的id
@Qualifier(value = "zhutou")
private TaskService taskServiceSimpleImpl;

19、实验十九[扫描包相关特殊情况]

① 扫描多个包可以使用逗号分开

1
2
<context:component-scan
base-package="com.atguigu.spring.component,com.atguigu.spring.toy"/>

② 使用 resource-pattern 属性进行过滤

此时只有符合条件的类会被扫描

1
2
3
4
5
<!-- 配置自动扫描的包 -->
<context:component-scan
base-package="com.atguigu.spring.component,com.atguigu.spring.toy"
resource-pattern="Shop*.class"
/>

③ 排除特定类

1
2
3
4
5
6
7
8
9
<!-- 特殊情况3:在base-package基础上排除掉一些类 -->
<context:component-scan base-package="com.atguigu.spring.component">
<!-- 使用context:exclude-filter标签配置排除一些类的规则 -->
<!-- type="annotation"表示排除标记了特定注解的类 -->
<!-- expression本身是表达式的意思,如果type属性指定的规则是annotation,那么这里配置特定注解的全类名 -->
<context:exclude-filter
type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

④ 仅扫描特定类

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 特殊情况4:在base-package基础上“仅”扫描某些类 -->
<!-- 配置context:include-filter标签还不足以实现“仅”的效果 -->
<!-- 还必须配置use-default-filters="false"让默认扫描规则失效 -->
<context:component-scan
base-package="com.atguigu.spring.component"
use-default-filters="false"
>

<!-- 使用context:include-filter标签配置在base-package基础上要包含到扫描规则中的特定规则 -->
<context:include-filter
type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>