基于XML配置方式组件管理

基于XML配置方式组件管理

1.组件信息声明配置

定义XML配置文件,声明组件类信息

1.1 基于无参构造函数

先准备一个普通的类,里面默认包含无参构造

package com.ztong.ioc_01;
​
public class HappyComponent {
​
    //默认包含无参构造
    public void dowork(){
        
    }
}

写一个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">
​
    <bean id="happyComponent" class="com.ztong.ioc_01.HappyComponent"/>
</beans>
  • id 表示组件的标识名

  • class 是 组件的类的全限定符

默认是单例模式,如果class 是相同的两个组件,那么就会实例化两个组件对象

 

1.2 静态工厂方法实例化

先来一个工厂类

package com.ztong.ioc_01;
​
public class ClientService {
    private static ClientService service = new ClientService();
​
    private ClientService(){
​
    }
    public static ClientService createInstance(){
        return service;
    }
}

在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">
​
<!--    无参构造-->
    <bean id="happyComponent" class="com.ztong.ioc_01.HappyComponent"/>
​
<!--    静态工厂-->
    <bean id="clientService" class="com.ztong.ioc_01.ClientService" factory-method="createInstance"/>
</beans>

静态工厂的bean 标签属性含义

  • id 组件标识名

  • class =“工厂类的全限定符”

  • factory-method = "静态工厂方法"

 

1.3 非静态工厂方法实例化(实例工厂)

先准备一个工厂类,里面的方法是非静态的

package com.ztong.ioc_01;
​
public class DefaultServiceLocator {
    private static ClientServiceImpl clientService = new ClientServiceImpl();
​
    public ClientServiceImpl createClientServiceImpl(){
        return clientService;
    }
}

在xml中,配置bean 需要两步

先配置工厂类的组件信息。因为非静态方法,需要实例化对象去调用

第二步是用 factory-bean 指定工厂类

factory-method 指定非静态方法

<?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="defaultServiceLocator" class="com.ztong.ioc_01.DefaultServiceLocator"/>
<!--    通过指定工厂对象和非静态方法 来配置组件信息-->
    <bean id="clientService2" factory-bean="defaultServiceLocator" factory-method="createClientServiceImpl"/>
</beans>

 

2.组件依赖注入配置

通过配置文件实现IOC容器中 Bean之间的引用,或者说给类的属性赋值

这里由两种方式:一是通过构造函数,二是通过setter方法

2.1 基于构造函数的依赖注入

首先准备两个类UserService 和 UserDao,在UserService中用到了UserDao,所以要给UserService注入UserDao

public class UserDao {
}
​
public class UserService {
    private UserDao userDao;
    private String name;
    
    public UserService(String name, UserDao userDao){
        this.name = name;
        this.userDao = userDao;
    }
}

首先需要先声明这两个bean,在声明UserDao的时候用的是自闭和标签,在声明UserService时,标签内要使用 constructor-arg 标签来注入 属性 name 和 UserDao

constructor-arg 标签的属性

  • name 就是要注入的属性的名字

  • value 是 要注入普通属性的值

  • ref 是要注入的对象属性的 bean id

  • index 是要注入的属性的索引

<?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="userDao" class="com.ztong.ioc_02.UserDao"/>
​
    <bean id="userService" class="com.ztong.ioc_02.UserService">
        <constructor-arg name="name" value="wzt"/>
        <constructor-arg name="userDao" ref="userDao"/>
    </bean>
</beans>

构造方法只有一个参数时,可以只有 value 或 ref

当有多个参数,只写value 或 ref ,要按照参数的顺序进行注入

如果不按顺序,就可以用name 或 index 来指定注入的是哪一个属性

 

2.2 基于setter 方法的依赖注入

同样准备两个类,MovieFinder 和 SimpleMovieLister,在后者中注入前者,通过 setter方法

package com.ztong.ioc_02;
​
public class MovieFinder {
}
​
public class SimpleMovieLister {
    private MovieFinder movieFinder;
    private String movieName;
​
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
​
    public void setMovieName(String movieName) {
        this.movieName = movieName;
    }
}

同样还是先声明bean,不同的是在SimpleMovieLister的bean中,用的标签是property

property的属性

  • name 去掉set后的方法名(注意首字母要小写)

  • ref

  • value

<bean id="simpleMovieLister" class="com.ztong.ioc_02.SimpleMovieLister">
    <property name="movieFinder" ref="movieFinder"/>
    <property name="movieName" value="jlczzwc"/>
</bean>

 

3 .IOC容器的创建

先准备一个类,并把这个类注册到 ioc容器中

package com.ztong.ioc_03;
​
public class SadComponent {
    public void dowork(){
        System.out.println("I am sad");    
    }
}

这里使用无参构造来注册

<?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="sadComponent" class="com.ztong.ioc_03.SadComponent"/>
</beans>

 

创建IOC容器可以直接通过实例化四个IOC容器实现类即可

这里选用的是 ClassPathXmlApplicationContext,有两种方式:

  • 直接在创建的时候传入要读取的配置文件

  • 先实例化对象,再传入配置文件,再刷新

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
​
public class SpringIocTest {
​
    public void creareIOC(){
        //方式一:直接传入配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-03.xml");
​
        //方式二:先创建,再传配置文件,再刷新
        ClassPathXmlApplicationContext applicationContext1 = new ClassPathXmlApplicationContext();
        //传入配置文件用setConfigLocations方法
        applicationContext1.setConfigLocations("spring-03.xml");
        //刷新
        applicationContext1.refresh();
    }
}

注意:刷新是必不可少的,因为方式一中在源码里也进行了刷新

创建完ioc后,来获取bean组件,也有三种方式,都是调用 ioc容器的getBean方法

  • 直接传入 bean 的id

  • 传入bean 的id 和 bean 的类型

  • 传入bean的类型

public void creareIOC(){
​
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-03.xml");
​
    //方式一:传入beanid,返回的类型是 Object,需要强制类型转换
    SadComponent sadComponent = (SadComponent) applicationContext.getBean("sadComponent");
​
    //方式二:传入beanid 和bean的类型,直接返回对应的类型
    SadComponent sadComponent1 = applicationContext.getBean("sadComponent", SadComponent.class);
​
    //方式三:传入bean的类型
    SadComponent sadComponent2 = applicationContext.getBean(SadComponent.class);
}

需要注意的是方式三,必须确保ioc容器内只能有一个该类型的bean,否则就会报

NoUniqueBeanDefinitionException 一场

方式二和方式三中,也可以传入该类型实现的接口,但是在xml文件里只能是类

在底层是通过 instanceOf来判断传入的接口和配置的类的关系

 

4 .组件的生命周期方法及作用域

4.1生命周期方法

我们可以在组件类中定义方法,当ioc容器实例化和销毁的时候调用这两个方法。那么这两个方法就类似于生命周期方法。可以在这两个方法中完成一些初始化和释放资源的操作。

首先定义一个组件类,和两个方法

这两个方法必须是 public 并且返回值是void

package com.ztong.ioc_04;
​
public class JavaBean {
​
    //初始化方法
    public void start(){
        System.out.println("JavaBean.start");
    }
​
    //销毁方法
    public void end(){
        System.out.println("JavaBean.end");
    }
}

在ioc容器中注册bean的时候,在标签中加入如下属性

  • init-method 注册初始化方法,值是方法名

  • destroy-mothod 注册销毁方法,值是方法名

<?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="javaBean" class="com.ztong.ioc_04.JavaBean" init-method="start" destroy-method="end"/>
</beans>

需要注意当ioc容器销毁时才会调用 销毁方法,所以要主动调用ioc容器的close方法

 

4.2作用域

当在bean标签中声明组件时,是将组件的信息配置给Spring-IOC容器。

在IoC容器中,这些bean标签对应的信息转成Spring内部的 BeanDefinition对象,在这个对象内,包含传进来的这些信息(id,class,init-method等)

SpringIOC容器根据BigDefinition对象反射创建Bean的对象实例

在传入 BigDefinition 的属性中 有一个属性 scope 能够决定去创建几个 bean对象

在ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、AnnotationConfigApplicationContext 中 Scope有两个值

  • singleton 创建的bean对象为单例,是在IOC容器初始化时创建

  • prototype 创建的bean对象为多例,是在获取bean时创建

在 WebApplicationContext 中还会有另外两个值

  • request 请求范围内有效的实例,每次请求时创建

  • session 会话范围内有效的实例,每次会话时创建

默认值是 singleton

<bean id="javaScope" class="com.ztong.ioc_04.JavaScope" scope="prototype"/>
<!--默认值是 singleton-->

 

5 .FactoryBean

在创建bean对象时,有一种方式是 通过工厂模式创建,在工厂类中,写一个工厂方法返回要创建的对象。在ioc容器中,去创建工厂类的bean就行了,factory-method的值是工厂方法。

FactoryBean 是一个标准化工厂组件接口,用于配置复杂的bean对象,可以将创建过程存储在 getObject方法中,不用去写工厂方法和 factory-method。

FactoryBean<T> 提供三个方法

  • T getObject() 返回该工厂创建对象的实例,该返回值会被储存到IOC容器,我们可以在这个方法中进行一些复杂的操作

  • boolean isSingleton() 如果FactoryBean返回的是单例,该方法返回true,否则返回false

  • Class<?> getObjectType 返回getObject方法返回的对象类型,如果不知道类型,则返回null

 

FactoryBean的使用场景是:创建代理类、整合第三方框架、实例化复杂对象等

例如Mybatis框架

正常创建流程是:

  • 先 创建SqlSessionFactoryBuilder 对象

  • 调用getResourceAsStream() 方法读取配置文件

  • 调用SqlSessionFactoryBuilder 对象的 builder方法,获得SqlSeqqsionFactory对象

  • 调用SqlSeqqsionFactory 的 openSession 方法,获得 SqlSession对象

MyBatis 整合到spring有一个类是SqlSessionFactoryBean,这个类实现了FactoryBean接口,把这些步骤封装到 getObject,我们在springioc配置文件中,只需要配置 SqlSessionFactoryBean,就能拿到想要的SqlSession对象

 

下面看看如何使用FactoryBean

首先准备一个要创建的类

package com.ztone.ioc_05;
​
public class LearnComponent{
​
    private String name;
​
    public void setName(String name) {
        this.name = name;
    }
}

然后创建FactoryBean实现类,需要实现FactoryBean接口,并且传入要创建类的泛型,重写两个方法

package com.ztone.ioc_05;
​
import org.springframework.beans.factory.FactoryBean;
​
public class MyFactoryBean implements FactoryBean<LearnComponent> {
    @Override
    public LearnComponent getObject() throws Exception {
        //可以用new的方式创建对象,返回该对象
        LearnComponent learnComponent = new LearnComponent();
        return learnComponent;
    }
​
    @Override
    public Class<?> getObjectType() {
        //返回要创建类的类型
        return LearnComponent.class;
    }
}

在配置文件中,创建配置MyFactoryBean 即可

<bean id="learnComponent" class="com.ztone.ioc_05.MyFactoryBean"/>

注意点:id是要创建的类的id,即LearnComponent,而MyFactoryBean的id 是 &id

在这个标签中 用property 给属性赋值时,是给MyFactoryBean的属性赋值,如果想给LearnComponent的属性赋值,可以在MyFactoryBean的getObject方法中,调用 对应属性的set方法,把bean中获得的值传入

 

FactoryBean 和 BeanFactory的区别:

  • FactoryBean 是Spring中的一种特殊的bean,可以在getObject() 工厂方法自定义的逻辑创建Bean,是一种能够生产其他bean的bean

  • BeanFactory 是Spring框架的基础,它是顶级接口,定义了容器的基本行为,例如管理bean的生命周期,配置文件的加载解析,bean的装配和依赖注入等。

 

6. Spring 三层架构案例

  1. 导入依赖,mysql、druid、spring-jdbc

    <!-- 数据库驱动和连接池-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.25</version>
    </dependency>
    ​
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.8</version>
    </dependency>
    ​
    <!-- spring-jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>6.0.6</version>
    </dependency>
  2. 准备实体类Student

    package com.ztone.pojo;
    ​
    public class Student {
        private Integer id;
        private String name;
        private String gender;
        private Integer age;
        private String classes;
    ​
        public Integer getId() {return id;}
    ​
        public void setId(Integer id) {this.id = id;}
    ​
        public String getName() {return name;}
    ​
        public void setName(String name) { this.name = name;}
    ​
        public String getGender() {return gender;}
    ​
        public void setGender(String gender) { this.gender = gender;}
    ​
        public Integer getAge() { return age;}
    ​
        public void setAge(Integer age) { this.age = age;}
    ​
        public String getClasses() { return classes;}
    ​
        public void setClasses(String classes) {this.classes = classes; }
        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", gender='" + gender + '\'' +
                    ", age=" + age +
                    ", classes='" + classes + '\'' +
                    '}';
        }
    }

     

  3. 准备controller,service,dao层

    package com.ztone.controller;
    
    import com.ztone.pojo.Student;
    import com.ztone.service.StudentService;
    
    import java.util.List;
    
    public class StudentController {
        private StudentService studentService;
    
        public void setStudentService(StudentService studentService) {
            this.studentService = studentService;
        }
        public void searchStudent(){
            List<Student> students = studentService.queryAllStudent();
            System.out.println(students);
    
        }
    }
    package com.ztone.service;
    
    import com.ztone.pojo.Student;
    
    import java.util.List;
    
    public interface StudentService {
        List<Student> queryAllStudent();
    }
    ----------------------------------------
    package com.ztone.service;
    
    import com.ztone.dao.StudentDao;
    import com.ztone.pojo.Student;
    
    import java.util.List;
    
    public class StudentServiceImpl implements StudentService{
        private StudentDao studentDao;
        @Override
        public List<Student> queryAllStudent() {
            return studentDao.queryAllStudent();
        }
    
        public void setStudentDao(StudentDao studentDao) {
            this.studentDao = studentDao;
        }
    }
    package com.ztone.dao;
    
    import com.ztone.pojo.Student;
    
    import java.util.List;
    
    public interface StudentDao {
        List<Student> queryAllStudent();
    }
    ----------------------------------------
    package com.ztone.dao;
    
    import com.ztone.pojo.Student;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    import java.util.List;
    
    public class StudentDaoImpl implements StudentDao{
    
        private JdbcTemplate jdbcTemplate;
        public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
        @Override
        public List<Student> queryAllStudent() {
            String sql = "select id,name,gender,age,class as classes from students";
            List<Student> students = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class));
            return students;
        }
    }

    在StudentDaoImpl中,调用JdbcTemplate的query方法去执行sql语句,返回一个集合,然后在service中调用dao,在controller中调用service

  4. 在xml中配置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"
           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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!--导入外部属性文件-->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--    配置数据连接池-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="url" value="${url}"/>
            <property name="driverClassName" value="${driver}"/>
            <property name="username" value="${username1}"/>
            <property name="password" value="${password}"/>
        </bean>
    <!--    配置jdbcTemplate-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
    <!--    配置三层架构-->
        <bean id="studentDao" class="com.ztone.dao.StudentDaoImpl">
            <property name="jdbcTemplate" ref="jdbcTemplate"/>
        </bean>
        <bean id="studentService" class="com.ztone.service.StudentServiceImpl">
            <property name="studentDao" ref="studentDao"/>
        </bean>
        <bean id="studentController" class="com.ztone.controller.StudentController">
            <property name="studentService" ref="studentService"/>
        </bean>
    </beans>

     

    遇到的问题:

    • 配置dataSource的 driver,name只能是 driverClassName

    • 配置dataSource的 username时,value的名字不能是 username,因为username是spring的key中的一个关键字,在使用 username时,就是调用了这个关键字,而不是读取jdbc.properties文件中的username属性值

    • 配置三层架构的bean时,property标签的name属性必须是 对应的set方法的方法名

posted @ 2024-07-15 20:04  GrowthRoad  阅读(4)  评论(0编辑  收藏  举报