SpringIOC复习

153 阅读8分钟

1.Spring两大核心

两大核心:

IOC(Inverse Of Control:控制反转)

AOP(Aspect Oriented Programming:面向切面编程)。

IOC不是什么技术,而是一种设计思想。它的目的是指导我们设计出更加松耦合的程序。

控制:在java中指的是对象的控制权限(创建、销毁)

反转:指的是对象控制权由原来 由开发者在类中手动控制 反转到 由Spring容器控制

2.Spring相关API

2.1 BeanFactory

BeanFactory是IOC容器的核心接口,它定义了IOC的基本功能。

特点:在第一次调用getBean()方法时,创建指定对象的实例

2.2 ApplicationContext

应用上下文对象,可以获得spring中IOC容器的Bean对象。

特点:在spring容器启动时,加载并创建所有对象的实例

2.3 BeanFactory与ApplicationContext区别

  • BeanFactory是Spring框架中IoC容器的顶层接⼝,它只是⽤来定义⼀些基础功能,定义⼀些基础规范,⽽ApplicationContext是它的⼀个⼦接⼝,所以ApplicationContext是具备BeanFactory提供的全部功能的。

  • 通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的⾼级接⼝,⽐BeanFactory要拥有更多的功能,⽐如说国际化⽀持和资源访问(xml,java配置类)等等

  • ApplicationContext 加载的同时就创建了bean对象存到容器中

  • BeanFactory 不会创建bean对象存到容器中 调用getBean方法时 才会加载到ioc容器中

/**
 * @Author 振帅
 * @Date 2021/06/17 21:11
 */
public class SpringTest {

    @Test
    public void test1() {

        //获取spring上下文对象,借助上下对象可以获取到IOC容器中的bean对象,加载的同时就创建了bean对象存到容器中
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        //使用上下文对象从IOC容器中获取到了bean对象
        IUserDao userDao = (IUserDao) applicationContext.getBean("userDao");

        userDao.save();
    }

    @Test
    public void test2() {

        //BeanFactory 核心接口 不会创建bean对象存到容器中
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

        //调用getBean方法时 才会加载到ioc容器中
        IUserDao userDao = (IUserDao) beanFactory.getBean("userDao");
        userDao.save();
    }
}

3.spring配置文件

3.1 什么是bean

在 Spring 中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。bean是一个由Spring IoC容器实例化、组装和管理的对象。

3.2 Bean标签基本配置

<bean id="" class="" scope=""  init-method="" destroy-method="" ></bean>
  • 默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。
  • id:Bean实例在Spring容器中的唯一标识
  • class:Bean的全限定名
  • scope属性指对象的作用范围
    • singleton 默认值,单例的
    • prototype 多例的
  • init-method:指定类中的初始化方法名称
  • destroy-method:指定类中销毁方法名称

3.2.1 当scope的取值为singleton时

Bean的实例化个数:1个

Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例

Bean的生命周期:

对象创建:当应用加载,创建容器时,对象就被创建了

对象运行:只要容器在,对象一直活着 

对象销毁:当应用卸载,销毁容器时,对象就被销毁了 

3.2.2 当scope的取值为prototype时

Bean的实例化个数:多个

Bean的实例化时机:

当调用getBean()方法时实例化Bean 

Bean的生命周期:

对象创建:当使用对象时,创建新的对象实例 

对象运行:只要对象在使用中,就一直活着 

对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了

3.3 Bean实例化三种方式

3.3.1 无参构造方法实例化

它会根据默认无参构造方法来创建类对象,如果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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 方式一:无参构造方法进行实例化 -->
    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" ></bean>

</beans>

3.3.2 工厂静态方法实例化

应用场景

依赖的jar包中有个A类,A类中有个静态方法m1,m1方法的返回值是一个B对象。如果我们频繁使用 B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类 中的m1方法,直接从IOC容器获得。

<!-- 方式二:工厂静态方法实例化 -->
<bean id ="userDao" class="com.lagou.factory.StaticFactoryBean" factory-method="createUserDao"></bean>
package com.lagou.factory;

import com.lagou.dao.IUserDao;
import com.lagou.dao.impl.UserDaoImpl;

/**
 * @Author 振帅
 * @Date 2021/06/17 23:00
 */
public class StaticFactoryBean {

    public static IUserDao createUserDao() {
        return new UserDaoImpl();
    }
}

3.3.3 工厂普通方法实例化

应用场景

依赖的jar包中有个A类,A类中有个普通方法m1,m1方法的返回值是一个B对象。如果我们频繁使用 B对象, 此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1 方法,直接从IOC容器获得。

<!-- 方式三:工厂普通方法实例化 -->
<bean id="dynamicFactoryBean" class="com.lagou.factory.DynamicFactoryBean"></bean>
<bean id="userDao" factory-bean="dynamicFactoryBean" factory-method="createUserDao"></bean>
package com.lagou.factory;

import com.lagou.dao.IUserDao;
import com.lagou.dao.impl.UserDaoImpl;

/**
 * @Author 振帅
 * @Date 2021/06/17 23:07
 */
public class DynamicFactoryBean {

    public IUserDao createUserDao() {
        return new UserDaoImpl();
    }
}

3.4 Bean的依赖注入方式

依赖注入 DI(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。

在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IOC 解耦只是降低他们的依赖关系,但不会消除。

例如:业务层仍会调用持久层的方法。 那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。简单的说,就是通 过框架把持久层对象传入业务层,而不用我们自己去获取。

3.4.1 自己维护业务层和持久层的依赖关系

(1)UserServiceImpl.java

/**
 * @Author 振帅
 * @Date 2021/06/17 23:26
 */
public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        //调用userDao的方法
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        IUserDao userDao = (IUserDao) applicationContext.getBean("userDao");
        userDao.save();
    }
}

(2) applicationContext.xml

<!-- 配置UserService -->
<bean id="useService" class="com.lagou.service.impl.UserServiceImpl"></bean>

(3) Test.java

@Test
public void test() {

    //获取spring上下文对象,借助上下对象可以获取到IOC容器中的bean对象,加载的同时就创建了bean对象存到容器中
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

    //使用上下文对象从IOC容器中获取到了bean对象
    UserService userService = (UserService) applicationContext.getBean("useService");

    userService.save();
}
    

3.4.2 构造方法注入

(1) UserServiceImpl.java

public class UserServiceImpl implements UserService {

    private IUserDao userDao;

    public UserServiceImpl(IUserDao userDao){
        this.userDao = userDao;
    }

    @Override
    public void save() {
        userDao.save();
    }
}

(2) applicationContext.xml

index 构造方法参数下标位置

type 构造方法参数类型

ref 依赖的bean

<!-- 配置UserDao -->
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" ></bean>
<!-- 配置UserService -->
<bean id="useService" class="com.lagou.service.impl.UserServiceImpl">
    <constructor-arg index="0" type="com.lagou.dao.IUserDao" ref="userDao" />
</bean>

可以简化配置 name 参数名称

 <!-- 配置UserDao -->
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" ></bean>
<!-- 配置UserService -->
<bean id="useService" class="com.lagou.service.impl.UserServiceImpl">
    <constructor-arg name="userDao"  ref="userDao" />
</bean>
    

3.4.3 set方法注入

(1) UserServiceImpl.java

public class UserServiceImpl implements UserService {

    private IUserDao userDao;

    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {
        userDao.save();
    }
}

(2) applicationContext.xml

name set方法 setUserDao 后面的UserDao首字母小写

ref 依赖的bean

<!-- 配置UserDao -->
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" ></bean>
<!-- 配置UserService -->
<bean id="useService" class="com.lagou.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao" />
</bean>

3.4.4 P命名空间注入

P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件 中,如下:

首先,需要引入P命名空间:

xmlns:p="http://www.springframework.org/schema/p"

其次,需要修改注入方式:

<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl" p:userDao-ref="userDao"/>

3.5 Bean注入普通数据类型

3.5.1 普通数据类型

package com.lagou.domain;

/**
 * @Author 振帅
 * @Date 2021/06/18 21:07
 */
public class User {

    private String username;
    private String age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAge() {
        return age;
    }

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

value用于普通数据类型的注入

ref用于引用数据类型的注入

 <bean id="user" class="com.lagou.domain.User">
    <property name="username" value="yangzhen" />
    <property name="age" value="20" />
</bean>

3.5.2 引用数据类型

同上set方法注入

3.5.3 集合数据类型

(1) list/set/Array

更换标签即可

/**
 * @Author 振帅
 * @Date 2021/06/18 21:21
 */
public class UserGroup {

    private String groupName;

    List<Object> userList;

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }

    public void setUserList(List<Object> userList) {
        this.userList = userList;
    }

}
 <bean id="user" class="com.lagou.domain.User">
    <property name="username" value="yangzhen" />
    <property name="age" value="20" />
</bean>

<bean id="userGroup" class="com.lagou.domain.UserGroup">
    <!-- 普通类型注入 -->
    <property name="groupName" value="TFboys" />
    <!-- 进行List集合数据类型的注入 -->
    <property name="userList">
        <list>
            <!--list中的第一个元素 String类型 -->
            <value>yang1</value>
            <!--list中的第二个元素 引用类型 -->
            <ref bean="user"></ref>
        </list>
    </property>
</bean>

(2) Map

/**
 * @Author 振帅
 * @Date 2021/06/18 21:21
 */
public class UserGroup {

    private String groupName;

    List<Object> userList;

    private Map<String,Object> map;

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

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }

    public void setUserList(List<Object> userList) {
        this.userList = userList;
    }

    public Map<String, Object> getMap() {
        return map;
    }
}

<bean id="user" class="com.lagou.domain.User">
        <property name="username" value="yangzhen" />
        <property name="age" value="20" />
    </bean>

    <bean id="userGroup" class="com.lagou.domain.UserGroup">
        <!-- 普通类型注入 -->
        <property name="groupName" value="TFboys" />
        <!-- 进行List集合数据类型的注入 -->
        <property name="userList">
            <list>
                <value>yang1</value>
                <ref bean="user"></ref>
            </list>
        </property>
        <!-- 进行Map集合数据类型的注入 -->
        <property name="map">
            <map>
                <!--普通类型用value-->
                <entry key="k1" value="k1Value"></entry>
                <!--引用类型用value-ref-->
                <entry key="k2" value-ref="user"></entry>
            </map>
        </property>
    </bean>

(3) Properties

public class UserGroup {

    private Properties properties;

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public Properties getProperties() {
        return properties;
    }
   
}
<bean id="userGroup" class="com.lagou.domain.UserGroup">
    <!-- 普通类型注入 -->
    <property name="groupName" value="TFboys" />
    <!-- 进行List集合数据类型的注入 -->
    <property name="userList">
        <list>
            <value>yang1</value>
            <ref bean="user"></ref>
        </list>
    </property>
    <!-- 进行Map集合数据类型的注入 -->
    <property name="map">
        <map>
            <!--普通类型用value-->
            <entry key="k1" value="k1Value"></entry>
            <!--引用类型用value-ref-->
            <entry key="k2" value-ref="user"></entry>
        </map>
    </property>
    <property name="properties">
        <props>
            <prop key="k1">v1</prop>
            <prop key="k2">v2</prop>
        </props>
    </property>
</bean>

3.6 配置文件模块化

配置文件模块化:将部分 配置拆解到其他配置文件中

3.6.1 并列的多个配置文件

ApplicationContext act = new ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");

3.6.2 并列的多个配置文件

<import resource="applicationContext-xxx.xml"/>

注意:

  • 同一个xml中不能出现相同名称的bean,如果出现会报错。
  • 多个xml如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean

3.6.3 抽取jdbc配置文件

applicationContext.xml加载jdbc.properties配置文件获得连接信息。

需要引入context命名空间和约束路径

<?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 http://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="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

</beans>

4.spring注解开发

4.1常用注解

Spring常用注解主要是替代 的配置

注解说明
@Component使用在类上用于实例化Bean
@Controller使用在web层类上用于实例化Bean
@Service使用在service层类上用于实例化Bean
@Repository使用在dao层类上用于实例化Bean
@Autowired使用在字段上用于根据类型依赖注入
@Qualifier结合@Autowired一起使用,根据名称进行依赖注入
@Resource相当于@Autowired+@Qualifier,按照名称进行注入
@Value注入普通属性
@Scope标注Bean的作用范围
@PostConstruct使用在方法上标注该方法是Bean的初始化方法
@PreDestroy使用在方法上标注该方法是Bean的销毁方法

说明:

JDK11以后完全移除了javax扩展导致不能使用@resource注解

需要maven引入依赖

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId> 
    <version>1.3.2</version>
</dependency>

注意

使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包 下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。

<!--注解的组件扫描--> 
<context:component-scan base-package="com.lagou"></context:component-scan>

示例

@Service
@Scope("singleton")
public class UserServiceImpl implements UserService {

    @Value("注入普通数据") 
    private String str; 
    @Value("${jdbc.driver}")
    private String driver;
    
    @Autowired
    private UserDao userDao;
    
    @PostConstruct
    public void init(){
        System.out.println("初始化方法...."); 
    }
    
    @PreDestroy
    public void destroy(){
        System.out.println("销毁方法....."); 
    }
}

4.2 Spring新注解

使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:

  • 非自定义的Bean的配置:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>
  • 加载properties文件的配置:
<context:property-placeholder location="classpath:jdbc.properties"/>
  • 组件扫描的配置:
<!--注解的组件扫描--> 
<context:component-scan base-package="com.lagou"></context:component-scan>
  • 引入其他文件:
<import resource="applicationContext-xxx.xml"/>
注解说明
@Configuration用于指定当前类是一个Spring 配置类,当创建容器时会从该类上加载注解
@Bean使用在方法上,标注将该方法的返回值存储到 Spring 容器中
@PropertySource用于加载 properties 文件中的配置
@ComponentScan用于指定 Spring 在初始化容器时要扫描的包
@Import用于导入其他配置类

4.2.1 编写Spring核心配置类

@Configuration
@ComponentScan("com.lagou")
@Import(DataSourceConfig.class)
public class SpringConfig {

    @Bean("queryRunner") 
    public QueryRunner getQueryRunner(@Autowired DataSource dataSource) { 
        return new QueryRunner(dataSource); 
    } 
}

4.2.2 编写数据库配置信息类

@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {

    @Value("${jdbc.driver}") 
    private String driver; 
    
    @Value("${jdbc.url}") 
    private String url; 
    
    @Value("${jdbc.username}") 
    private String username; 
    
    @Value("${jdbc.password}") 
    private String password;
    
    @Bean("dataSource")
    public DataSource getDataSource() { 
    
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource; 
        
    } 
    
}

5.Spring整合Junit

5.1普通Junit测试问题

在普通的测试类中,需要开发者手动加载配置文件并创建Spring容器,然后通过Spring相关API获得 Bean实例。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

AccountService accountService = applicationContext.getBean(AccountService.class);

5.2Spring整合Junit步骤

    1. 导入spring集成Junit的坐标
    1. 使用@Runwith注解替换原来的运行器
    1. 使用@ContextConfiguration指定配置文件或配置类
    1. 使用@Autowired注入需要测试的对象
    1. 创建测试方法进行测试
<!--此处需要注意的是,spring5 及以上版本要求 junit 的版本必须是 4.12 及以上--> 
<dependency> 
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.5.RELEASE</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class SpringJunitTest { 

    @Autowired
    private AccountService accountService; 
    
    //测试查询 
    @Test 
    public void testFindById() { 
        Account account = accountService.findById(3);
        System.out.println(account); 
    }
}