Spring IOC实验

实验1:通过IOC容器创建对象,并为属性赋值★

HelloWorld 实现

实验2:根据bean的类型从IOC容器中获取bean的实例★

如果IOC容器这个类型的bean有多个,查找会报错
报错org.springframework.beans.factory.NoUniqueBeanDefinitionException

1
2
3
4
5
6
      /*Person bean = ioc.getBean(Person.class);
System.out.println(bean);
*/
// 指定类之后不需要强转
Person bean = ioc.getBean("person02", Person.class);
System.out.println(bean);

实验3:

  • 通过构造器为bean的属性赋值(index,type属性介绍)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
       <!-- 必须为Person创建全参构造 -->
    <!-- 方法一 -->
    <bean id="person01" class="xyz.lyhcc.bean.Person">
    <constructor-arg name="lastName" value="KL"></constructor-arg>
    <constructor-arg name="name" value="KJJJL"></constructor-arg>
    <constructor-arg name="gender" value="F"></constructor-arg>
    <constructor-arg name="email" value="KL@123.com"></constructor-arg>
    </bean>
    <!-- 方法二 -->
    <!-- 注意:下面的顺序必须按照构造函数的参数顺序,但可以使用index指定 -->
    <bean id="person01" class="xyz.lyhcc.bean.Person">
    <constructor-arg value="KL1"></constructor-arg>
    <constructor-arg value="KJJ1JL"></constructor-arg>
    <constructor-arg value="M"></constructor-arg>
    <constructor-arg value="KLw@123.com"></constructor-arg>
    </bean>
    <!-- 构造函数重载时,如果需要指定调用,可以使用type="java.lang.Integer" -->
  • 通过p名称空间为bean赋值,导入p命名空间
    1
    2
    3
    4
    5
    6
    7
       <!-- 名称控件是用来防止标签重复的 -->
    <!-- 1)导入命名空间 2)写带前缀的标签 -->
    xmlns:p="http://www.springframework.org/schema/p"
    <!-- 遇到的问题:在对应的spring configuration xml中没有namespace选项,将文件关掉,点击explore上面找到对应的配置文件,右键->open with ->spring config editor -->
    <bean id="person01" class="xyz.lyhcc.bean.Person"
    p:gender="F" p:email="abc@123.com" p:lastName="AC" p:name="CB">
    </bean>

    实验4:正确的为各种属性赋值

  • 测试使用null值
    1
    2
    3
    4
    5
    6
    7
      <!-- 默认不赋值也为null -->
    <bean id="person01" class="xyz.lyhcc.bean.Person">
    <property name="lastName">
    <!-- 复杂标签赋值 -->
    <null></null>
    </property>
    </bean>
  • 引用类型赋值(引用其他bean、引用内部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
    <!-- 为Person类增加一个Person属性,并生成getter/setteer方法 -->
    <!-- 引用外部 -->
    <bean id="person01" class="xyz.lyhcc.bean.Person">
    <property name="lastName" value="李四"></property>
    <property name="name" value="王五"></property>
    <property name="gender" value="男"></property>
    <property name="email" value="abc@123.com"></property>
    </bean>
    <bean id="person02" class="xyz.lyhcc.bean.Person">
    <property name="lastName" value="AS"></property>
    <property name="name" value="QW"></property>
    <property name="gender" value="F"></property>
    <property name="email" value="abcds@123.com"></property>
    <property name="person" ref="person01"></property>
    </bean>
    <!-- 内部 说明:内部bean不能直接获取 -->
    <bean id="person02" class="xyz.lyhcc.bean.Person">
    <property name="lastName" value="AS"></property>
    <property name="name" value="QW"></property>
    <property name="gender" value="F"></property>
    <property name="email" value="abcds@123.com"></property>
    <property name="person">
    <bean id="person01" class="xyz.lyhcc.bean.Person">
    <property name="lastName" value="李四"></property>
    <property name="name" value="王五"></property>
    <property name="gender" value="男"></property>
    <property name="email" value="abc@123.com"></property>
    </bean>
    </property>
    </bean>
  • 集合类型赋值(List、Map、Properties)
    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
    46
    47
    48
    49
    50
    51
    52
    53
    <!-- List -->
    <!-- Person添加friends属性 -->
    <bean id="person01" class="xyz.lyhcc.bean.Person">
    <property name="lastName" value="李四"></property>
    <property name="name" value="王五"></property>
    <property name="gender" value="男"></property>
    <property name="email" value="abc@123.com"></property>
    </bean>
    <bean id="person02" class="xyz.lyhcc.bean.Person">
    <property name="lastName" value="AS"></property>
    <property name="name" value="QW"></property>
    <property name="gender" value="F"></property>
    <property name="email" value="abcds@123.com"></property>
    <property name="friends">
    <list>
    <bean class="xyz.lyhcc.bean.Person" p:lastName="AC" p:email="as@123.com"></bean>
    <ref bean="person01"/>
    </list>
    </property>
    </bean>

    <!-- Map -->
    <!-- Person中添加map属性 -->
    <bean id="person02" class="xyz.lyhcc.bean.Person">
    <property name="lastName" value="AS"></property>
    <property name="name" value="QW"></property>
    <property name="gender" value="F"></property>
    <property name="email" value="abcds@123.com"></property>
    <property name="map">
    <!-- new LinkedHashMap -->
    <map>
    <entry key="A" value="1"></entry>
    <entry key="B" value="2"></entry>
    <entry key="D" value="3"></entry>
    <entry key="C" value="4"></entry>
    </map>
    </property>
    </bean>
    <!-- Properties -->
    <!-- 在Person中添加Properties属性 -->
    <bean id="person02" class="xyz.lyhcc.bean.Person">
    <property name="lastName" value="AS"></property>
    <property name="name" value="QW"></property>
    <property name="gender" value="F"></property>
    <property name="email" value="abcds@123.com"></property>
    <property name="properties">
    <!-- new Properties() -->
    <props>
    <prop key="username">WSX</prop>
    <prop key="passwd">AS</prop>
    </props>
    </property>
    </bean>
  • util名称空间创建集合类型的bean

    1
    2
    3
    4
    5
    6
    7
    8
    <!-- util名称空间创建集合的bean,方便引用 -->
    <util:map id="myMap">
    <entry key="A" value="1"></entry>
    <entry key="B" value="2"></entry>
    <entry key="C" value="3"></entry>
    <entry key="D" value="4"></entry>
    </util:map>
    <util:list id="mylist"></util:list>
  • 级联属性赋值(级联属性表示属性的属性)

====================工厂方式创建bean==========

实验5:配置通过静态工厂方法创建的bean、实例工厂方法创建的bean、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
36
37
38
39
40
41
42
43
44
45
46
47
<!-- 使用Book类,里面有name,author,price三个属性,并生成getter/setter方法 -->
<!-- bean的创建默认就是框架通过利用反射new出来的 -->
<!-- 工厂模式:工厂帮我们去创建对象
AirPlane ap = AirPlaneFactory.getAirPlane(String airPlane)

静态工厂:工厂本身不要创建对象,通过静态调用
对象 = 工厂类.工厂方法()
实例工厂:工厂本省不需要创建对象
对象 = (new 工厂类)工厂方法()
-->

<!-- 静态工厂通过
class: 指定静态工厂的全类名
factory-method=“getAirPlane” 指定工厂方法
constructor-arg 进行传参
-->
<!-- 工厂方法定义:
public class BookStaticFactory {
public static Book getBookInstance(String name) {
System.out.println("静态工厂被调用...");
return new Book(name);
}
}

-->
<bean id="book1" class="xyz.lyhcc.factory.BookStaticFactory" factory-method="getBookInstance">
<constructor-arg name="name" value="计算机网络"></constructor-arg>
</bean>

<!-- 实例工厂
factory-method 指定这个实例工厂的工厂方法
factory-bean 指定当前对象黄建使用哪个工厂
1)先配置出实例化工厂对象
2)配置我们要创建的类使用哪个工厂
-->
<!-- 工厂方法
public class BookFactory {
public Book getInstance(String name) {
System.out.println("实例工厂被调用");
return new Book(name);
}
}
-->
<bean id="factory" class="xyz.lyhcc.factory.BookFactory"></bean>
<bean id="book02" class="xyz.lyhcc.bean.Book" factory-bean="factory" factory-method="getInstance">
<constructor-arg value="操作系统"></constructor-arg>
</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
<!-- FactoryBean 是Spring规定的一个接口,只要这个接口的实现类, Spring都认为是一个工厂,Spring会自动调用工厂方法 

1. 定义一个FactoryBean实现类
2. 在配置文件中注册
3. ioc容器启动时不会创建实例(无论单实例还是多实例)
4. FactoryBean 获取的时候才创建实例
<bean id="book03" class="xyz.lyhcc.factory.MyFactoryBeanImpl"></bean>
-->
public class MyFactoryBeanImpl implements FactoryBean<Book> {

/**
* 返回创建的对象
*/
@Override
public Book getObject() throws Exception {
return new Book(UUID.randomUUID().toString());
}

/**
* 返回创建对象的类型
* spring自动创建这个方法来确认创建的对象是什么类型
*/
@Override
public Class<?> getObjectType() {
return Book.class;
}

/**
* 是否为单实例
*/
@Override
public boolean isSingleton() {
return false;
}

}

实验6:通过继承实现bean配置信息的重用

1
2
3
4
5
6
7
8
9
<bean id="person01" class="xyz.lyhcc.bean.Person">
<property name="lastName" value="李四"></property>
<property name="name" value="王五"></property>
<property name="gender" value="男"></property>
<property name="email" value="abc@123.com"></property>
</bean>
<bean id="person02" class="xyz.lyhcc.bean.Person" parent="person01">
<property name="name" value="SDTYFUGHJGJHGFGHJHG"></property>
</bean>

实验7:通过abstract属性创建一个模板bean

1
2
3
4
5
6
7
8
无法获取报异常 org.springframework.beans.factory.BeanIsAbstractException
<!-- abstract="true", 声明这个bean只能被继承,不能被获取 -->
<bean id="person01" class="xyz.lyhcc.bean.Person" abstract="true">
<property name="lastName" value="李四"></property>
<property name="name" value="王五"></property>
<property name="gender" value="男"></property>
<property name="email" value="abc@123.com"></property>
</bean>

实验8:bean之间的依赖

1
2
3
4
<!-- 默认是按照配置文件的顺序创建 -->
<!-- 通过depends-on=""改变创建顺序 创建Person之前先创建Person2-->
<bean id="person01" class="xyz.lyhcc.bean.Person" depends-on="person02"></bean>
<bean id="person02" class="xyz.lyhcc.bean.Person2"></bean>

实验9:测试bean的作用域,分别创建单实例和多实例的bean★

1
2
3
4
5
6
7
8
9
10
11
12
<!-- bean的作用域:指定bean是否为单实例,默认单实例 -->
<!-- 使用scope=""指定
prototype:多实例
1)容器启动默认不会创建多实例
2)获取时被创建
3)每次获取时创建一个对象
singleton:单实例
1)在容器启动之前就已经创建好对象,保存在容器中
2)任何时候获取都是之前创建好的那个对象
request:在web环境下,同一次请求创建一个Bean实例(没用)
session: 在web环境下,同一个会话中创建一个Bean实例(没用)
-->

实验10:创建带有生命周期方法的bean

生命周期:bean的创建到销毁

  1. 单例Bean, 容器启动的时候就会创建好,容器关闭也会销毁创建的bean
  2. 多实例bean, 获取的时候才创建
    自定义一些生命周期的方法,spring在创建或者销毁的时候就会调用指定的方法
    自定义初始化方法和销毁方法,可以抛任意异常,但不能有参数
    Bean的生命周期
  • 单实例:构造器–>初始化方法–>(容器关闭)销毁方法
  • 多实例:获取bean(构造器–>初始化方法)–>容器关闭不会调用bean的销毁方法
1
2
3
<bean id="book04" class="xyz.lyhcc.bean.Book" init-method="init" destroy-method="destory"></bean>

<bean id="book04" class="xyz.lyhcc.bean.Book" scope="prototype" init-method="init" destroy-method="destory"></bean>

实验11:测试bean的后置处理器

Spring有一个接口(BeanPostProcessor);后置处理器,可以在bean初始化前调用 初始化方法, 后置处理器可以干扰初始化
无论bean是否有初始化方法,后置处理器都会默认有,并继续执行
BeanPostProcessor 有两个抽象方法
postProcessBeforeInitialization 初始化之前调用
postProcessAfterInitialization 初始化之后调用

使用方法

  1. 编写后置处理器的实现类
  2. 将后置处理器注册在配置文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// beanName是在bean配置文件中的id
public class MyBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization: ["+beanName+"]初始化即将开始调用..."+bean);
return bean;
}

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

System.out.println("postProcessBeforeInitialization ["+beanName+"]初始化方法调用完成..."+bean);
//返回值是初始化之后的bean,返回的是什么,容器保存的就是什么
return bean;
}

}

//注册后置处理器
<bean class="xyz.lyhcc.processor.MyBeanPostProcessor"></bean>

实验12:引用外部属性文件★

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- 数据库连接池作为单实例是最好的,一个项目一个池,管理多个连接,连接从连接池中获取,可以让Spring创建连接池对象 -->
<!-- ${jdbc.username}用户名 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>

<!-- 引入外部属性文件 -->
1. 将context命名空间引入
xmlns:context="http://www.springframework.org/schema/context"
2. 引入配置文件
<context:property-placeholder location="classpath:dbconfig.properties"/>
3. 使用
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="driverClass" value="${jdbc.driver}"></property>
</bean>


注意:

使用时需要三个jar包, c3p0、mchange、mysql

1
2
3
mchange-commons-java-0.2.11.jar 
mysql-connector-java-5.1.27.jar
c3p0-0.9.5.2.jar

org.springframework.beans.factory.BeanCreationException:
出现这个问题是需要一个包,具体参考


实验13:基于XML的自动装配(自定义类型自动赋值)

JavaBean(基本类型)
(自定义类型是一个对象,这个对象可能存在容器中)
自动装配:

1
2
3
4
5
6
7
8
9
10
11
12
autowire="default/no", 不自动装配 
一下的是按照某种规则自动赋值
byName 以属性名作为id去容器中找到这个组件,给其赋值,不存在则为null
ioc.getBean("id")
byType 以属性的类型作为依据去容器找这个组件,
如果容器中有多个这种类型的组件,会报错
找不到,返回null
ioc.getBean(*.class)
constructor 按照构造器进行赋值
1)先按照有参构造器的类型进行装配,没有装配null
2)按照类型找多个,参数名作为id继续匹配,找到就装配,否则null
3)不会报错,如果是一个列表就会将容器所有符合的对象装配进去

实验14:[SpEL(Spring Expression Language)测试I]

  • 在SpEL中使用字面量
  • 引用其他bean
  • 引用其他bean的某个属性值
  • 调用非静态方法
  • 调用静态方法
  • 使用运算符
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <bean id="person01" class="xyz.lyhcc.bean.Person">
    <!-- 字面量 和使用运算符 -->
    <property name="age" value="#{12*3}"></property>
    <!-- 引用其他bean -->
    <property name="person" value="#{person02}"></property>
    <!-- 应用其他bean的属性值 -->
    <property name="lastName" value="#{person02.lastName}"></property>
    <!-- 非静态方法的调用 -->
    <property name="name" value="#{person02.getName()}"></property>
    <!-- 静态方法调用#{T(类名).静态方法} -->
    <property name="email" value="#{T(java.util.UUID).randomUUID().toString()}"></property>

    </bean>

实验15:通过注解分别创建Dao、Service、Controller(控制器,控制网站跳转逻辑Servlet)★

通过给bean上添加注解,可以快速的将bean加入ioc容器中
Spring的四个注解添加任何一个注解都能快速将这个组件注册到ioc容器中

  1. @Controller 控制器 我们推荐给控制层的组件(Servlet)添加这个注解
  2. @Service 业务逻辑,我们推荐业务逻辑层的组件添加这个注解,
  3. @Resposity 给持久化层组件添加这个注解(Dao层)
  4. @Component 不属于以上几层组件

注意: Spring底层不会去验证你的这个组件,是否如你所说的是一个Servlet层或者Dao层组件
使用注解快速添加组件到容器需要几步

  • 添加注释(四个的一个)
  • 告诉Spring,自动扫描加了注释的组件,依赖context命名空间

    <context:component-scan base-package=””>
    base-package 指定扫描的基础包 把基础包及他下面所有加了注释的类,自动注册到ioc容器中

  • 导入aop包之后才会支持注解,否则报异常org.springframework.beans.factory.BeanDefinitionStoreException:

使用注解加入到容器中的组件,和使用配置加入到容器中的㢟行为都是默认一样的

  • 组件的id,默认就是类名首字母小写

    @Repository(“bookdao123”)

  • 组件的作用域,默认是单例

    @Scope(value=”prototype”)

1
<!-- Dao层(数据库访问)、Service层(业务处理)、Web层(页面控制action类) -->

实验16:使用context:include-filter指定扫描包时要包含的类

使用需禁用默认规则use-default-filters="false"
1
2
3
4
<context:component-scan base-package="xyz.lyhcc" use-default-filters="false">

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

实验17:使用context:exclude-filter指定扫描包时不包含的类

type="annotation" 指定排除规则,按照注解进行排除,标注了指定注解组件不需要
    expression="" 注解的全类名   如 org.springframework.stereotype.Controller
type="assignable" 排除某个具体的类   
    expression="" 类的全类名

后面一般不使用
type="custom" 自定义一个 org.springframework.core.type.filter.TypeFilter 
        里面只有一个match方法,该方法返回true就要

type="aspectj"    aspectj表达式

type="regex"    还可以使用正则表达式
1
2
3
4
5
<context:component-scan base-package="xyz.lyhcc">
<!-- 排除一些不需要的组件 -->

<context:exclude-filter type="regex" expression=""/>
</context:component-scan>

实验18:使用@Autowired注解实现根据类型实现自动装配★

使用@Autowired注解,Spring会自动为这个属性赋值

  1. 首先进行告诉Spring自动扫描组件

    <context:component-scan base-package=”xyz.lyhcc”>

  2. 使用@Autowired
    • BookServlet.java
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      @Controller
      public class BookServlet {
      @Autowired
      private BookService bookService;

      public void doGet() {
      System.out.println("保存数据中...");
      bookService.save();
      }
      }
    • BookDao.java
      1
      2
      3
      4
      5
      6
      7
      @Repository
      public class BookDao {

      public void saveBook() {
      System.out.println("保存图书...");
      }
      }
    • BookService.java
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      @Service
      public class BookService {

      @Autowired
      private BookDao bookDao;

      public void save() {
      System.out.println("BookService正在调用DAO存储数据");
      bookDao.saveBook();
      }
      }

说明:

  • 先按照类型去找相应的组件: bookSevice = ioc.getBean(BookServlet.class)
    • 找到一个就直接赋值
    • 没找到,报异常
    • 找到多个?装配上
      • 按照变量名作为id,继续匹配,
        • 匹配上?装配
        • 没有匹配?报异常 (原因是按照变量名作为id进行匹配,@Qualifier可以指定id名)
          • 找到装配上,没找到报异常
  • @Autowired标注的自动装配的属性默认一定装配上的,找到就装配,找不到就不装配
    • @Autowired(required=false)

实验19:如果资源类型的bean不止一个,

默认根据@Autowired注解标记的成员变量名作为id查找bean,进行装配★

实验20:如果根据成员变量名作为id还是找不到bean,

可以使用@Qualifier注解明确指定目标bean的id★

实验21:在方法的形参位置使用@Qualifier注解

1
2
3
4
5
6
7
8
9
10
11
//ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE
//@Autowired可以放到构造方法上,变量上,方法上,注解上
/**
* 方法上有@AutoWired的话
* 1)这个方法会在bean创建的时候自动运行
* 2)该方法的每一个形参都会自动注入
*/
@Autowired
public void doIt(@Qualifier("bookDao") BookDao bookDao) {
System.out.println("正在运行此方法----"+bookDao);
}

实验22:@Autowired注解的required属性指定某个属性允许不被设置

见以上说明

实验23:测试泛型依赖注入★


与@Autowired等同的还有,@Resource,@Inject
@Autowired: 最强大,离开Spring不能使用
@Resource: java标准,扩展性更强, 如果 切换在其他的容器框架 仍可以使用
@Inject : EJB


Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×