Spring框架(二)


接上回。。。

依赖注入(DI配置)

依赖注入的两种方式

  • setter注入
    简单类型
    引用类型(很常用)
  • 构造器注入
    简单类型
    引用类型

setter方式注入

引用类型:

  • 在bean中定义引用类型属性并提供可访问的set方法
  • Application.xml中使用property标签ref属性注入引用类型

详情见Spring框架(一)的DI入门案例

简单类型:

  • 在bean中定义引用类型属性并提供可访问的set方法
  • Application.xml中使用property标签value属性注入简单数据类型

代码演示:

  • 新添加UserDao接口和BookDao一样
  • BookDaoImpl提供要注入对象的set方法
public class BookDaoImpl implements BookDao {

    private String databaseName;
    private int connectionNum;
    //setter注入需要提供要注入对象的set方法
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }
    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }

    public void save() {
        System.out.println("book dao save ..."+databaseName+","+connectionNum);
    }
}
  • 配置文件注入简单数据类型
<?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="bookDao" class="com.yang.dao.impl.BookDaoImpl">
        <!--property标签:设置注入属性-->
        <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
        <!--value属性:设置注入简单类型数据值-->
        <property name="connectionNum" value="100"/>
        <property name="databaseName" value="mysql"/>
    </bean>

    <bean id="userDao" class="com.yang.dao.impl.UserDaoImpl"/>

    <!--注入引用类型-->
    <bean id="bookService" class="com.yang.service.impl.BookServiceImpl">
        <!--property标签:设置注入属性-->
        <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
        <!--ref属性:设置注入引用类型bean的id或name-->
        <property name="bookDao" ref="bookDao"/>
        <property name="userDao" ref="userDao"/>
    </bean>

</beans>
  • App主函数实现
public class AppForDISet {
    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}
  • 运行结果:

构造器注入

构造器注入不需要set方法

生成构造方法,与setter方式注入相比,只是换个方法,还是参数往里传

  • BookServiceImpl添加构造方法
public class BookServiceImpl implements BookService {

    private BookDao bookDao;
    private UserDao userDao;
    public BookServiceImpl(BookDao bookDao, UserDao userDao) {
        this.bookDao = bookDao;
        this.userDao = userDao;
    }
    @Override
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
        userDao.save();
    }
}
  • 同理BookDaoImpl建立构造方法
public class BookDaoImpl implements BookDao {
    private String databaseName;
    private int connectionNum;

    public BookDaoImpl(String databaseName, int connectionNum) {
        this.databaseName = databaseName;
        this.connectionNum = connectionNum;
    }
    @Override
    public void save() {
        System.out.println("book dao save ..."+databaseName+","+connectionNum);
    }
}
  • ApplicationContext使用constructor-arg 标签
<?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">
<!--                    此方法使形参名不耦合,改形参名不影响type
<bean id = "bookDao" class = "com.yang.dao.Impl.BookDaoImpl">-->
<!--        <constructor-arg type="int" value="100"/>-->
<!--        <constructor-arg type="java.lang.String" value="mysql"/>-->
<!--    </bean>-->
<!--    <bean id = "bookService" class = "com.yang.service.Impl.BookServiceImpl">-->
<!--        <constructor-arg name="bookDao" ref="bookDao"/>-->
<!--        <constructor-arg name="userDao" ref="userDao"/>-->
<!--    </bean>-->
<!--    <bean id="userDao" class="com.yang.dao.Impl.UserDaoImpl"/>-->
    <!--解决参数类型重复问题,使用位置解决参数匹配-->
    <bean id="bookDao" class="com.yang.dao.Impl.BookDaoImpl">
        <!--根据构造方法参数位置注入-->
        <constructor-arg index="0" value="mysql"/>
        <constructor-arg index="1" value="100"/>
    </bean>
    <bean id="userDao" class="com.yang.dao.Impl.UserDaoImpl"/>

    <bean id="bookService" class="com.yang.service.Impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>
</beans>
  • App类不变

依赖注入方式选择

  1. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
  2. 可选依赖使用setter注入进行,灵活性强
  3. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
  4. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
  5. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
  6. 自己开发的模块推荐使用setter注入

依赖自动装配

自动装配概念

  • IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
  • 自动装配方式
    按类型(常用)
    按名称
    按构造方法
    不启用自动装配

自动装配类型

依赖自动装配

配置中使用bean标签autowire属性设置自动装配的类型,有按名称或者类型(主要是按类型)

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
依赖自动装配特征
  1. 自动装配用于引用类型依赖注入,不能对简单类型进行操作
  2. 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
  3. 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
  4. 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

集合注入

注入数组类型数据

<property name="array">
    <array>
        <value>100</value>
        <value>200</value>
        <value>300</value>
    </array>
</property>

注入List类型数据

<property name="list">
    <list>
        <value>itcast</value>
        <value>itheima</value>
        <value>boxuegu</value>
        <value>chuanzhihui</value>
    </list>
</property>

注入Set类型数据

<property name="set">
    <set>
        <value>itcast</value>
        <value>itheima</value>
        <value>boxuegu</value>
        <value>boxuegu</value>
    </set>
</property>

注入Map类型数据

<property name="map">
    <map>
        <entry key="country" value="china"/>
        <entry key="province" value="henan"/>
        <entry key="city" value="kaifeng"/>
    </map>
</property>

注入Properties类型数据

<property name="properties">
    <props>
        <prop key="country">china</prop>
        <prop key="province">henan</prop>
        <prop key="city">kaifeng</prop>
    </props>
</property>

说明:property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写<array>、<list>、<set>、<map>、<props>标签

注解开发

注解相当于一个接口,程序可以通过反射来获取指定程序元素的注解对象。

注解并不影响程序正常运行。

在Spring中,xml配置Bean对象有些繁琐,可以使用注解简化Bean对象的定义:在类或者方法上加入特定的注解(@XXX),完成特定功能的开发。

通过注解的方式,在功能的调用者和提供者之间达成约定,进而进行功能的调用。因为注解更为方便灵活,所以在现实的开发中,更推荐使用注解的形式完成

代码演示:

  • 在类上使用@Component注解定义Bean。
@Component("bookDao")            // 这里类似配置了一个bean,name叫“bookDao”
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("book dao save....");
    }
}
//同理在BookServiceImpl一样定义注解
@Component
public class BookServiceImpl implements ServiceDao {
   	 private BookDao bookDao;

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}
  • 注解定义后,得让Spring知道有这个注解,就得在xml的配置文件添加,让其扫描这个目录
<context:component-scan base-package="com.yang"/>  
  • 在测试类中获取bean
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");   //按id寻找bean
        System.out.println(bookDao);
        ServiceDao bookService = ctx.getBean(ServiceDao.class);   //按类型寻找bean,所以BookServiceImpl类里没有id
        System.out.println(bookService);
    }
}
  • 结果

能够看到两个bean的地址,成功。

在测试类中不要调用bookService的save方法,因为还没有给ServiceImpl中的bookDao赋值(在setter方法当中),调用bookService的save方法会出现空指针异常。

@Component三个衍生注解

都是Component注解使得开发难以看懂,所以衍生了三个注解。他们和Component一样,只是换个名字

  • @Controller:用于表现层bean定义
  • @Service:用于业务层bean定义
  • @Repository:用于数据层bean定义

纯注解开发模式

没有xml配置文件全是注解。

  • Spring3.0开启了纯注解开发模式,使用Java类替代配置文件
  • Java类代替Spring核心配置文件
  • @Configuration注解用于设定当前类为配置类
  • @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式

代码演示:

  • 定义配置类代替配置文件
//声明当前类为Spring配置类
@Configuration
//Spring注解扫描,相当于<context:component-scan base-package="com.yang"/>
@ComponentScan("com.yang")
//设置bean扫描路径,多个路径书写为字符串数组格式
//@ComponentScan({"com.yang.service","com.yang.dao"})
public class SpringConfig {
}
  • 在APP测试类中加载配置类,获取Bean对象并使用
public class AppForAnnotation {
    public static void main(String[] args) {
        //AnnotationConfigApplicationContext加载Spring配置类初始化Spring容器
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
        //按类型获取bean
        BookService bookService = ctx.getBean(BookService.class);
        System.out.println(bookService);
    }
}

未完待续。。。


文章作者: 小小星仔
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小小星仔 !
评论
  目录