SSM框架学----Spring(1)

1. Spring概述

1.1. Spring是什么

Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IoC(Inverse Of Control: 反转控制)和AOP(Aspect Oriented Programming 面向切面变成)为内核,提供展现层Spring MVC和持久层Spring JDBC以及业务层等管理众多的企业级应用技术,还能整合开源世界的第三方框架和类库。

1.2. Spring的优势

  • 方便解耦,简化开发
    通过Spring提供的IoC容器,可以将对象间的依赖关系交给Spring进行控制,避免硬编码所造成的过度程序耦合,用户也不必再为单例模式类,属性文件解析等这些很底层的需求编写代码,更专注于上层的应用。

  • AOP编程的支持
    通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。

  • 声明式事务的支持
    可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。

  • 方便程序的测试
    可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。

  • 方便继承各种优秀框架
    Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。

  • 降低Java EE API的使用难度
    Spring 对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。

  • Java源码才是yyds
    Spring 的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对 Java 设计模式灵活运用以及对 Java 技术的高深造诣。它的源代码才是 Java 技术的最佳实践的范例。

1.3. Spring体系架构

请添加图片描述

2. IoC的概念和作用

2.1. 什么是程序的耦合

耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。 划分模块的一个准则为高内聚低耦合

  • 类之间的依赖
  • 方法之间的依赖

2.2 解决程序耦合的思路

解耦:降低程序之间的依赖关系

实际开发中,应该做到:编译器不依赖,运行期才依赖

解耦的思路:

  • 使用反射来创建对象,避免使用new关键字
    public static void main(String[] args) throws SQLException, ClassNotFoundException {

        Connection conn = null;
        PreparedStatement stat = null;
        ResultSet res = null;
        // 使用反射来创建对象,避免使用new关键字,仅仅依赖的是字符串的内容
        Class.forName("com.mysql.cj.jdbc.Driver");
        try {
            // 依赖的是DriverManager驱动类
//            DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatisquery?serverTimezone=UTC&useSSL=false",
                    "root", "123456");
            stat = conn.prepareStatement("select * from account");
            res = stat.executeQuery() ;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        System.out.println("账户id: ");
        while(res.next()){
            System.out.println(res.getInt("id"));
        }
        if(res != null){
            res.close();
        }
        if(stat != null){
            stat.close();
        }
        if(conn != null){
            conn.close();
        }
    }

对于上面例子,使用DriverManager驱动类来new一个对象,一旦在pom.xml中删除掉Mysql的依赖,那么执行该程序将会在编译期报错,而使用Class.forName反射得到的对象,编译期是不会报错的,而不是在运行期报错:ClassNotFoundException

  • 读取配置文件来获取需要创建的对象全限定类名

由于反射读取的是字符串,并且仅仅适用于MySQL数据库,一旦改变数据库,需要重新配置,因此使用读取外部配置文件的操作来创建对象;以及获取url,username和password这些都可以在外部配置中进行设置。

2.3. 工厂模式解耦

在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。那么,这个读取配置文件,创建和获取三层对象的类就是工厂

案例分析:由于使用Spring解决依赖关系,并不是真正的做CRUD操作,所以没必要创建对应的实体类。在Dao包下创建account并且创建Account接口的实现类AccountDaoImpl,在Service包下创建AccountService并且创建Service接口的实现类AccountServiceImpl,模拟业务层去调用持久层中的方法,在ui包下创建Client类模拟表现层去调用业务层的方法

  • AccountInterface
/**
 * 账户的持久层接口
 */
public interface AccountInterface {

    void saveAccount();
    
}
  • AccountInterfaceImpl
/**
 * 账户的持久层实现类
 */
public class AccountDaoImpl implements AccountInterface {
    @Override
    public void saveAccount() {
        System.out.println("Saved Account...");
    }
}
  • AccountService
/**
 * 账户的业务层接口
 */
public interface AccountService {

    void saveAccount();

}
  • AccountServiceImpl
public class AccountServiceImpl implements AccountService {
    private AccountInterface accountDao = new AccountDaoImpl();
    private int flag = 1; // 进行多例对象的测试
    // 使用工厂模式创建对象
    // private AccountInterface accountDao = (AccountInterface) BeanFactory.getBean1("AccountDao");
    // 得到的是单例对象
    // private AccountInterface accountDao = (AccountInterface) BeanFactory.getBean2("AccountDao");
    @Override
    public void saveAccount() {
       // int flag = 1; // 将可能改变的变量定义在方法内,而不是作为成员变量
        accountDao.saveAccount();
        System.out.println(flag++);
    }
}
  • Cilent
public class Client {

    public static void main(String[] args) {
        AccountService as = new AccountServiceImpl();
       // for (int i = 0; i < 5; i++) {
            // 此时会得到5个不同的AccountServiceImpl对象 -> 多例
       //     AccountService as = (AccountService) BeanFactory.getBean1("AccountService");
       //    得到单例对象 
       //     AccountService as = (AccountService) BeanFactory.getBean2("AccountService");
       //     System.out.println(as);
       //     as.saveAccount();
       // }
        as.saveAccount();

注意这里存在两个new操作,在Clien中创建AccountService对象,在AccountServiceImpl中创建AccountDao对象,虽然可以正确执行,但是是主动创建对象,其中的耦合性较高。


现采用工厂类来帮助创建对象,是被动创建对象,定义配置文件bean.properties用来提供给工厂来创建对象:

AccountService=com.huzhen.service.impl.AccountServiceImpl
AccountDao=com.huzhen.dao.impl.AccountDaoImpl

public class BeanFactory {
    // 定义Properties对象用于读取配置文件
    private static Properties pro;
    // 定义一个Map(容器)用于存放需要创建的对象,避免多例对象的产生
    private static Map<String, Object> beans;

    // 使用静态代码块给pro对象赋值, 创建单例对象, 并且添加到容器中
    static {
        pro = new Properties();
        // 使用类加载器来读取配置文件
        InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            pro.load(is);
            // 实例化容器
            beans = new HashMap<>();
            // 取出配置文件中的key,全限定类名的name
            Enumeration keys = pro.keys();
            while(keys.hasMoreElements()){
                String key = keys.nextElement().toString(); // 获取name:AccountDao
                String path = pro.getProperty(key); // 获取全限定类名:com.huzhen.dao.impl.AccountDaoImpl
                Object value = Class.forName(path).getDeclaredConstructor().newInstance(); // 反射创建对象
                beans.put(key, value);              // 保存到容器中
            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError("Initial properties failed....");
        }
    }
   
    /**
     * 根据bean的名称获取bean对象
     * @param beanName
     * @return Object
     */
    public static Object getBean1(String beanName){
        Object bean=null;
        // 根据beanName得到对应的全限定类名
        String path = pro.getProperty(beanName);
        try {
            // 根据全限定类名反射得到创建对象,每次都会根据构造函数创建对象,导致多例对象的创建,执行效率低
            bean = Class.forName(path).getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }

    /**
     * 使用HashMap根据每个全限定类名创建单例对象,然后再从容器中取出
     */
    public static Object getBean2(String beanName){
        if(beans.containsKey(beanName) != false) {
            return beans.get(beanName);
        }else{
            throw new ExceptionInInitializerError("BeanName is illegal...");
        }
    }
}

使用properties对象来读取配置文件,通过类加载器的方式;获取property中的路径(全限定类名),通过反射得到bean对象

如果使用上述的getBean1方法来获取对象,也就是根据全限定类名反射得到创建对象,每次都会根据构造函数创建对象,导致多例对象的创建,执行效率低。在Client类和ServiceImpl类中调用getBean1方法进行测试:

请添加图片描述

对于多例对象的创建,每次创建对象都不同,虽然在成员方法中给类变量进行赋值,但是每次输出的flag依旧是1
对于单例对象而言,无论调用几次,类中的成员只会被初始化一次,因此每次创建时的成员变量属性会被正确赋值
但是对象被创建多次,执行效率没有单例对象高,但是Dao和Service接口中并没有将成员变量可以被方法中修改,因此
使用单例对象不存在线程安全问题, 如果有建议将类变量定义到成员方法中


使用getBean2方法时,由于在静态代码块中将properties配置文件的全限定类名反射得到的对象已经保存在成员变量beans(容器)中,只需要根据beanName就可以得到对应的对象。此时创建的是单例对象:
请添加图片描述

2.4. 控制反转-Inversion Of Control

2.4.1 容器

由于获取很多对象,肯定要找个集合来存。这时候有 Map 和 List 供选择,到底选 Map 还是 List 就看有没有查找需求。如果有查找需求,就选 Map。所以答案就是在应用加载时,创建一个 Map,用于存放三层对象。把这个 map 称之为容器

2.4.2 工厂

工厂就是负责从容器中获取指定对象的类。这时候获取对象的方式发生了改变

  • 之前在获取对象时,采用new的方式,是主动创建对象
    请添加图片描述

  • 现在获取对象时,只需要告诉需求或者是创建对象的一个描述,有工厂帮助查找以及创建对象,是被动创建对象
    请添加图片描述

这种被动接收的方式获取对象的思想就是控制反转,它是 spring 框架的核心之一。
现在可以明确IoC的作用:削减计算机程序的耦合(降低代码中的依赖关系)

3. 使用Spring的IoC解决程序耦合

3.1. Spring核心容器的两大接口

与之前Mybatis一样,也需要在pom.xml中进行导包,这里使用的是Spring-context 5.3.14:

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.14</version>
        </dependency>

继续使用2.4章节的案例,在使用Spring来帮助创建对象,这里需要配置文件bean.xml,也就是将创建对象的描述告诉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">

    <!-- 将对象的创建交给Spring来管理 id属性表示impl的name class为全限定类名-->
    <bean id="accountService" class="com.huzhen.service.impl.AccountServiceImpl"></bean>
    <bean id="accountDao" class="com.huzhen.dao.impl.AccountDaoImpl"></bean>
 </beans>       

关于bean.xml的创建,可以右击包名 -> new -> XML Configuration file -> Spring Config来得到头文件。id属性表示impl的name,class为对应的全限定类名。

在Client类中进行测试:

 public static void main(String[] args) {
        // 获取核心容器对象ac,根据id获取对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        // ApplicationContext ac2 = new FileSystemXmlApplicationContext("");
        AccountService accountService = (AccountService) ac.getBean("accountService");
        AccountInterface accountDao = ac.getBean("accountDao", AccountInterface.class);

        System.out.println(accountService);
        System.out.println(accountDao);

        System.out.println("===========Bean Factory=============");
        Resource resource = new ClassPathResource("bean.xml");
        BeanFactory factory = new XmlBeanFactory(resource);
        AccountService as = factory.getBean("accountService", AccountService.class);
        AccountInterface ad = factory.getBean("accountDao", AccountInterface.class);
        System.out.println(as);
        System.out.println(ad);
    }

获取Spring的IOC核心容器,并且根据id获取对象。ApplicationContext接口的三个常用实现类:

  • ClasspathXmlApplicationContext:可以加载类路径下的配置文件,要求配置文件必须在类路径下(较为常用)
  • FileSystemXmlApplicationContext:可以加载磁盘任意路径下的配置文件(必须有访问权限)
  • AnnotationConfigApplicationContext:用于读取注解创建容器的

请添加图片描述
可以看到这里都能正确打印出两个对象,但是使用ApplicationContext接口和BeanFactory接口创建对象的时间点不一样,可以使用debug来调试发现。通过Spring工厂的类结构图:

请添加图片描述

核心容器的两个接口各自引发的问题:

  • ApplicationContext:在构建核心容器时,创建对象使用立即加载的方式,只要配置文件读取,立马就创建对象。适用于单例对象,由于是BeanFactory接口的子接口,功能更加完善,常用该接口。
  • BeanFactory: 在构建核心容器时,创建对象使用延迟加载方式,什么时候用才真正的创建对象。适用于多例对象,是顶层接口。

3.2. IOC中bean标签和管理对象细节

bean标签的作用:用于配置对象让 spring 来创建的。默认情况下它调用的是类中的无参构造函数, 如果没有无参构造函数则不能创建成功。

Spring对bean的管理细节:

3.2.1. 创建bean的三种方式
  • 使用默认构造函数创建,在spring配置文件中使用bean标签,配好id和class属性之后,且没有其他属性和标签时,此时采用的是默认构造函数来创建bean对象,如果类中没有默认构造函数,则对象无法创建

可以看到,在ServiceImpl中加入一个有参的构造函数,此时继续使用3.1章节的方法来创建bean对象,会报如下错:
请添加图片描述

  • 使用普通工厂中的方法创建对象(使用某个类中的方法去创建对象,并存入Spring容器)
    模拟工厂类定义如下:
/**
 * 模拟一个工厂类(该类可能是存在某个jar包中的,无法通过修改源码的方式来提供默认构造函数)
 * 类中存在普通方法返回的对象才是需要创建的
 */
public class InstanceFactory {

    public AccountService getAccountService(){
        return new AccountServiceImpl();
//        return new AccountServiceImpl("Zed", 20, new Date());
    }
}

此时需要在bean.xml中进行配置:

 <bean id="instanceFactory" class="com.huzhen.factory.InstanceFactory"></bean>
 <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

先反射得到该工厂类InstanceFactory的对象,再通过factory-bean指定从哪里获取这个类对象,根据factory-method反射得到该方法的对象

  • 使用静态工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入Spring容器)

模拟工厂类定义如下,此时类中的成员方法是静态方法:

public class StaticFactory {

    public static AccountService getAccountService(){
     //   return new AccountServiceImpl();
        return new AccountServiceImpl("大司马", 35, new Date("1990--5-12"));
    }
}

此时不再需要进行和上例中两步走的方式得到类中方法的对象,直接通过该类反射得到:

<bean id="accountService" class="com.huzhen.factory.StaticFactory" factory-method="getAccountService"></bean>
3.2.2. bean对象的作用范围以及生命周期

bean标签的scope属性,用于指定bean的作用范围,取值有:

singleton:一个应用只有一个对象的实例,创建单例对象(默认值)

prototype:每次访问对象时,都会创建新的对象即多例对象

​ request:作用于web应用的请求范围

​ session:作用域web应用的会话范围

​ global-session:作用于集群环境的会话范围(全局会话范围),针对于分布式中的负载均衡情况,多台服务器公用一个session即为global-session。如果不是集群环境时,即为session


bean对象的生命周期

  1. 对于单例对象,当应用加载,创建容器时,对象被创建(立即加载,同一个全限定类名创建的对象,无论创建多少次只有一个);只要容器在,对象一直存活;当应用卸载,容器销毁时,对象消亡。单例对象与容器相辅相成。
  2. 对于多例对象,当使用对象时,创建新的对象实例(延迟加载,只要再次调用无论是否是同一个类,都是新的对象);只要对象在使用中,就一直存活;当对象长时间不用时,被 java 的垃圾回收器回收后对象消失。

为了演示,在ServiceImpl类中增加两个方法,init和destroy方法:

public class AccountServiceImpl implements AccountService {
    // 业务层在调用持久层的情况,体现出此时三层架构的强依赖性,是不推荐的
    // private AccountInterface accountDao = new AccountDaoImpl();

    // 构造函数注入
    private String name;
    private Integer age;
    private Date birthday;
    public AccountServiceImpl(String name, Integer age, Date birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }
    // set方法注入
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public AccountServiceImpl(){
        System.out.println("created Object...");
    }
    @Override
    public void saveAccount() {
//        accountDao.saveAccount();
        System.out.println("Service业务层中的方法实现了 " + "我叫:" + name + "," + age + "," + birthday);
    }

    public void init(){
        System.out.println("对象initial...");
    }

    public void destroy(){
        System.out.println("对象destroy...");
    }
}

并且再xml中配置单例模式以及初始化方法等:

    <bean id="accountService" class="com.huzhen.service.impl.AccountServiceImpl"
            scope="prototype"  init-method="init" destroy-method="destroy"></bean>

在测试类中进行测试:

public static void main(String[] args) {
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        AccountInterface dao1 = ac.getBean("accountDao", AccountInterface.class);
        AccountService service1 = ac.getBean("accountService", AccountService.class);

        System.out.println(dao1);
        System.out.println(service1);
            
        AccountInterface dao2 = ac.getBean("accountDao", AccountInterface.class);
        System.out.println(dao1 == dao2);
        System.out.println("=======================================");
        // 测试单例对象的生命周期
        service1.saveAccount();
        // 当main方法结束后,程序运行也就结束,没来得及调用service中的destroy方法, 对象就已经消失了
        ac.close(); // 调用子类得到的容器对象,来销毁容器,可以看到destroy的正常执行
        // 对于多例对象,是不会被执行destroy方法的,多例对象与容器不相关
    }

请添加图片描述
可以看到,对于多例对象,即使容器运行destroy方法,也不会去执行,因为多例对象与容器是不相关的。而采用单例对象,在xml中的scope属性设置为singleton,再次调用mian方法得到的输出结果为:
请添加图片描述可以看到,单例对象的与容器是共存亡的,随着容器的产生,单例对象被创建,随着容器的销毁,对象也消亡了。

3.3. Spring的依赖注入

依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现。我们的程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。

IoC的作用:ioc 解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法

依赖关系的管理:这种业务层和持久层的依赖关系,在使用 spring 之后,就让 spring 来维护了。在当前类需要用到其他类的对象,由Spring提供,只需要在配置文件中说明依赖关系的维护,称之为依赖注入。简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

可以注入的数据类型有:

  • 基本类型和String
  • 其他bean类型(配置文件中或者注释配置过的bean)
  • 复杂类型/集合类型
3.3.1. 构造函数注入

使用的标签:constructor-arg
标签出现的位置:在bean标签内部
标签中的属性:

  • type:用于指定注入的数据的数据类型,也是构造函数中某个或者某些参数的类型,不能独立的实现注入功能,对于相同数据类型的参数不能进行区分
  • index:用于指定注入的数据给构造函数中指定索引位置的参数进行赋值(索引位置从0开始)
  • name:用于给构造函数中给指定名称的参数赋值 (常用)

  • value:用于提供基本类型和String类型的数据
  • ref:用于指定其他的bean类型数据,也就是在Spring的IoC核心容器中出现过的bean对象,如Date类型

Impl的类定义已经在3.2.2章节中给出,对于全参的构造函数的bean.xml配置,如下:

<bean id="accountServiceConstruct" class="com.huzhen.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="大司马"></constructor-arg>
        <constructor-arg name="age" value="35"></constructor-arg>
        <!--对于Date类型,不能直接赋值一个字符串, 只能配置一个日期对象进行调用-->
        <constructor-arg name="birthday" ref="date"></constructor-arg>
    </bean>
    <!--定义一个日期对象到容器中-->
    <bean id="date" class="java.util.Date"></bean>

对于基本类型可以直接使用name + value属性进行配置,对于其他的bean类型,如Date类型,必须在配置文件中重新定义一个日期对象存储到容器中,才能通过ref去调用该bean数据类型。

在测试类演示并输出结果:

    public static void main(String[] args) {
        // 构造函数注入测试
        ApplicationContext ac1 = new ClassPathXmlApplicationContext("bean.xml");
        AccountService service1 = ac1.getBean("accountServiceConstruct", AccountService.class);
        service1.saveAccount();
    }

请添加图片描述
可以看到,数据已经被正确的注入到对象中。
构造函数注入的优势:在获取bean对象时,注入数据是必须的操作,否则bean对象无法创建
构造函数注入的弊端:改变了bean对象的实例化方式,使得在创建对象时,即使用不到某些参数的数据,也必须提供

3.2.2. set方法注入(常用)

使用的标签:property
标签出现的位置:bean标签内部
标签中的属性:

  • name:找的是类中 set 方法后面的部分,与类变量名称无关
  • vlaue:用于提供基本类型和String类型的数据
  • ref:用于指定其他的bean类型数据,也就是在Spring的IoC核心容器中出现过的bean对象,如Date类型

为了演示方便,在service包下新建一个实现类:

public class AccountServiceImpl2 implements AccountService {

    private String name;
    private Integer age;
    private Date birthday;


    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }


    @Override
    public void saveAccount() {
        System.out.println("AccountService的实现类方法执行.." + "," +name + "," +age+ "," + birthday);
    }
}

在bean.xml中进行配置:

    <!--依赖注入方式2:set方法注入-->
    <bean id="accountServiceSet" class="com.huzhen.service.impl.AccountServiceImpl2">
        <property name="name" value="五五开"></property>
        <property name="age" value="36"></property>
        <property name="birthday" ref="date"></property>
    </bean>

测试类中进行演示:

public static void main(String[] args) {
        // set方法注入测试
        ApplicationContext ac2 = new ClassPathXmlApplicationContext("bean.xml");
        AccountService service2 = ac2.getBean("accountServiceSet", AccountService.class);
        service2.saveAccount();
}

请添加图片描述
set方法注入的优势:创建对象时没有明确的限制,可以直接使用默认构造函数,全参构造函数可以不写
set方法注入的弊端:如果某个成员变量必须要赋值,则获取对象是有可能发生set方法没有执行

3.2.3. 使用 p 名称空间注入数据(本质还是调用set 方法)

此种方式是通过在 xml 中导入 p 名称空间,使用 p:propertyName 来注入数据,它的本质仍然是调用类中的set 方法实现注入功能。

依旧使用property属性,其中包括复杂类型的注入/集合类型的注入:

  • 用于给集合结构注入的标签有:
    • list
    • array
    • set
  • 用于给map接口注入的标签有:
    • map
    • props

同一种结构的标签可以互换,是不影响注入的结果的。如:map结构中的map标签和props标签是可以互换使用的
同样,在service包下再定义一个实现类:

public class AccountServiceImpl3 implements AccountService {

    private String[] strArray;
    private List<String> list;
    private Set<String> set;
    private Map<String, String> map;
    private Properties pros;
    
    public void setStrArray(String[] strArray) {
        this.strArray = strArray;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setPros(Properties pros) {
        this.pros = pros;
    }

    @Override
    public void saveAccount() {
        System.out.println("AccountService的实现类方法执行...");
        System.out.println(Arrays.toString(strArray));
        System.out.println(list);
        System.out.println(set);
        System.out.println(map);
        System.out.println(pros);
    }
}

在bean.xml的配置如下:

<!--复杂对象的注入-->
    <bean id="accountServicePName" class="com.huzhen.service.impl.AccountServiceImpl3">
        <property name="strArray">
            <array>
                <value>大司马</value>
                <value>五五开</value>
                <value>马飞飞</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value>123</value>
                <value>AAA</value>
                <value>*--*</value>
            </list>
        </property>
        <property name="set">
            <set>
                <value>123</value>
                <value>AAA</value>
                <value>*#*</value>
            </set>
        </property>
        <property name="map">
            <map>
                <entry key="金牌讲师" value="大司马"></entry>
                <entry key="开挂达人" value="五五开"></entry>
                <entry key="引流之主">
                    <value>Uzi</value>
                </entry>
            </map>
        </property>
        <property name="pros">
            <props>
                <prop key="testA">这是一个Properties属性对象!</prop>
            </props>
        </property>
    </bean>

在测试类中进行测试:

public static void main(String[] args) {
	    // 复杂类型的注入测试
        ApplicationContext ac3 = new ClassPathXmlApplicationContext("bean.xml");
        AccountService service3 = ac3.getBean("accountServicePName", AccountService.class);
        service3.saveAccount();
}

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从现在开始壹并超

你的鼓励,我们就是hxd

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值