Spring学习(三):IOC容器详解

文章目录

    • 什么是IOC
    • 底层原理
    • XML注入实现IOC
      • 注入常规类型属性
        • set方法注入
        • 有参构造注入
        • P空间注入(了解)
      • 注入特殊类型属性
        • 空值
        • 特殊符号
        • 外部Bean
        • 内部Bean和级联配置
          • 内部Bean配置
          • 级联配置
      • 注入集合属性
        • 普通注入
        • 注入对象集合
      • 自动装配
      • 外部属性文件
    • 工厂Bean
    • Bean的作用域
    • Bean的生命周期
    • 注解方式实现IOC
      • 注解概述
      • Spring针对 Bean管理中创建对象提供的注解
      • 实现对象创建
      • 扫描细节配置
      • 实现属性注入
        • @Autowired
        • @Qualifier
        • @Resource
        • @Value
          • 注入普通属性
          • 注入List
      • 完全注解开发

什么是IOC

Inversion of Control的缩写,中文译为控制反转,简单来水就是把对象创建和对象之间的调用过程,交给 Spring 进行管理。
创建对象实例的控制权从代码控制剥离到IOC容器控制,实际就是你在xml文件控制,侧重于原理

底层原理

  • XML解析
    使用dom4j解析
  • 工厂模式
  • 反射创建类对象
Class clazz=Class.forName(classValue);
UserService service=clazz.newInstance();
return service;

XML注入实现IOC

注入常规类型属性

set方法注入

  • 第一步 在类中定义属性和对应的 set 方法
  • 第二步 在XML文件中添加property标签

<bean id="user" class="com.spring5.User">
    
    <property name="name" value="小明">property>
bean>

有参构造注入

  • 第一步 在类中定义有参构造方法
  • 第二步 在XML文件中添加constructor-arg标签

<bean id="user" class="com.spring5.User">
   
   <constructor-arg  name="name"  value="张三">constructor-arg>
bean>

P空间注入(了解)

  • 第一步 在类中定义属性和对应的 set 方法
  • 第二步 修改XML文件
    在beans中添加xmlns:p属性;在bean中添加p:name属性

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="user" class="com.spring5.User" p:name="李四">bean>
beans>

注入特殊类型属性

空值

<property name="name">
    <null>null>
property>

特殊符号

  • 方法一 把<>进行转义

    <property name="name" value="<<王五>>">
    </property>
    
  • 把带特殊符号内容写到 CDATA

    <property name="name">
        <value>>]]>value>
    property>
    

外部Bean

现有类UserService、接口UserDao、类UserDaoImpl,其中UserDaoImpl实现了接口UserDao。代码结构如下:

 src
    │  bean.xml
    │
    └─com
        └─spring5
            │  Test.java 
            │
            ├─dao
            │      UserDao.java
            │      UserDaoImpl.java
            │
            └─service
                    UserService.java

如果类UserService要调用类UserDaoImpl的方法,传统做法是创建UserDao对象然后调用其方法,这种做法耦合太高。用spring注入的步骤如下:

  • 在UserService中创建属性UserDao,并写好set方法
  • 在XML中配置UserService和UserDao对象
  • 通过property标签注入UserDao对象

示例代码如下:
接口UserDao:

public interface UserDao {
     
    public void update();
}

类UserDaoImpl:

public class UserDaoImpl implements UserDao {
     
    @Override
    public void update() {
     
        System.out.println("I am dao update...");
    }
}

类UserService:

public class UserService {
     
   private UserDao userDao;

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

    @Test
    public void add(){
     
        System.out.println("I am service add...");
        userDao.update();
    }
}

bean.xml:

<bean id="userDaoImpl" class="com.spring5.dao.UserDaoImpl">bean>
    
<bean id="userService" class="com.spring5.service.UserService">
    <property name="userDao" ref="userDaoImpl">
    property>
bean>

内部Bean和级联配置

简单来说就是bean标签里面嵌套bean标签。在员工和部门,一个部门有多个员工,一个员工属于一个部门。示例代码如下:
Dept类:

public class Dept {
     
	private String  dname;
	public void setDname(String dname) {
     
		this. dname = dname;
	}
}

Emp类

public class Emp {
     
	private String  ename;
	private String  gender;
	//员工属于某一个部门,使用对象形式表示
	private Dept  dept;
	public void setDept(Dept dept) {
     
		this. dept = dept;
	}
	public void setEname(String ename) {
     
		this. ename = ename;
	}
	public void setGender(String gender) {
     
		this. gender = gender;
	}
}
内部Bean配置


	
	property>
	property>
	
	
		
			property>
		bean>
	property>
bean>
级联配置

Emp类需要生成getDep的方法

public Dept getDept(){
     
	return dept;
}

XML文件中dept.dname获取属性



	
	property>
	property>
	
	property>
bean>

注入集合属性

普通注入

新建类User,包含集合属性和相关set方法

public class User {
     

    private String[]  arrs;// 数组
    private List<String> list;// list
    private Map<String,String> maps;// map
    private Set<String> sets;// set

    public void setArrs(String[] arrs) {
     
        this.arrs = arrs;
    }

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

    public void setMaps(Map<String, String> maps) {
     
        this.maps = maps;
    }

    public void setSets(Set<String> sets) {
     
        this.sets = sets;
    }
}

XML配置


    
    
        <array>
            <value>JAVA课程value>
            <value>mySQL课程value>
        array>
    property>
    
    
        <list>
            <value>张三value>
            <value>李四value>
        list>
    property>
    
    
        <map>
            entry>
            entry>
        map>
    property>
    
    
        <set>
            <value>MySQLvalue>
            <value>MySQLvalue>
            <value>Oraclevalue>
            <value>Oraclevalue>
        set>
    property>
bean>

注入对象集合

Dept类

public class Dept {
     
    //部门有多个员工
    private List<Emp> empList;

    public void setEmpList(List<Emp> empList) {
     
        this.empList = empList;
    }
}

Emp类

public class Emp {
     
    private String eName;

    public void seteName(String eName) {
     
        this.eName = eName;
    }
}

XML配置


    
    <property name="empList">
        <list>
            <ref bean="emp1">ref>
            <ref bean="emp2">ref>
            <ref bean="emp3">ref>
        list>
    property>
bean>
<bean id="emp1" class="com.spring5.Emp">
    <property name="eName" value="张三">property>
bean>
<bean id="emp2" class="com.spring5.Emp">
    <property name="eName" value="李四">property>
bean>
<bean id="emp3" class="com.spring5.Emp">
    <property name="eName" value="王五">property>
bean>

自动装配

Spring可以根据属性名称或者类型进行自动装配,使用byType时相同类型的Bean不能配置多个


	
bean>
bean>

外部属性文件

一、创建外部属性文件jdbc.properties,填写数据库信息,key随便写

prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root

二、把外部 properties 属性文件引入到 spring 配置文件中

  • 引入 context 名称空间
    xmlns:context=“http://www.springframework.org/schema/context”
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring- - context.xsd"

  • 在 spring 配置文件使用标签引入外部属性文件

<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="${prop.driverClass}">property>
        <property name="url" value="${prop.url}">property>
        <property name="username" value="${prop.userName}">property>
        <property name="password" value="${prop.password}">property>
    bean>
beans>

工厂Bean

Spring 有两种类型:普通 bean和工厂bean(FactoryBean)。普通 bean在配置文件中定义的bean类型和返回类型一样,而工厂bean在配置文件定义 bean类型可以和返回类型不一样。实现工厂Bean的步骤如下:

  • 第一步 创建类,让这个类作为工厂 bean,实现FactoryBean接口
  • 第二步 重写接口里面的方法,在方法中定义返回的bean类型

示例代码如下:
MyBean和User类

public class MyBean implements FactoryBean<User> {
     

    @Override
    public User getObject() throws Exception {
     
        User user = new User();
        user.setName("张三");
        return user;
    }

    @Override
    public Class<User> getObjectType() {
     
        return null;
    }

    @Override
    public boolean isSingleton() {
     
        return false;
    }
}

public class User {
     

    private String name;

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

    public void test(){
     
        System.out.println("I am test...");
    }


}

bean.xml

<bean id="myBean" class="com.spring5.MyBean">bean>

Test类

public class Test {
     

    @org.junit.Test
    public void test() {
     
        //1 加载 spring 配置文件
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean.xml");
        //2 获取配置创建的对象
        User user = context.getBean("myBean", User.class);
        System.out.println(user);
        user.test();
    }
}

运行结果:
com.spring5.User@42d8062c
I am test…

Bean的作用域

作用域 说明
单例(singleton) 默认)每一个Spring IoC容器都拥有唯一的一个实例对象
原型(prototype) 一个Bean定义,任意多个对象
请求(request) 一个HTTP请求会产生一个Bean对象,也就是说,每一个HTTP请求都有自己的Bean实例。只在基于web的Spring ApplicationContext中可用
会话(session) 限定一个Bean的作用域为HTTPsession的生命周期。同样,只有基于web的Spring ApplicationContext才能使用
全局会话(global session) 限定一个Bean的作用域为全局HTTPSession的生命周期。通常用于门户网站场景,同样,只有基于web的Spring ApplicationContext可用

Bean的生命周期

  • 第一步,执行无参构造创建Bean实例

  • 第二步,调用类的set方法设置属性值

  • 第三步,调用预初始化方法postProcessBeforeInitialzation()
    需要有Bean实现了BeanPostProcessor接口

  • 第四步,调用bean的初始化的方法
    需要在XML的bean属性中进行配置

    init-method="初始化调用方法名"
    
  • 第五步,调用后初始化方法postProcessAfterInitialization()
    需要有Bean实现了BeanPostProcessor接口

  • 第六步,对象获取到了,bean可以使用了

  • 第七步,当容器关闭时调用bean的销毁的方法
    需要在XML的bean属性中进行配置

    destroy-method="销毁调用方法名"
    

实例代码如下:
User类

public class User {
     

    private String name;

    public User() {
     
        System.out.println("第一步 执行无参构造创建Bean实例");
    }

    public void setName(String name) {
     
        this.name = name;
        System.out.println("第二步 调用set方法设置属性值");
    }
    public void initMethod(){
     
        System.out.println("第四步 调用bean的初始化的方法");
    }
    public void destroyMethod(){
     
        System.out.println("第七步 当容器关闭时调用bean的销毁的方法");
    }
    public void test(){
     
        System.out.println("I am test...");
    }
}

MyBeanPost类

public class MyBeanPost implements BeanPostProcessor {
     
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
     
        System.out.println("第三步,调用预初始化方法postProcessBeforeInitialzation()");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
     
        System.out.println("第五步,调用后初始化方法postProcessAfterInitialization()");
        return bean;
    }
}

Test类

public class Test {
     

    @org.junit.Test
    public void test() {
     
        //1 加载 spring 配置文件
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("bean.xml");
        //2 获取配置创建的对象
        User user = context.getBean("user", User.class);
        System.out.println(user);
        System.out.println("第六步 对象获取到了,bean可以使用了");
        user.test();
        context.close();
    }
}

bean.xml

<!--配置User对象-->
<bean id="user" class="com.spring5.User" init-method="initMethod" destroy-method="destroyMethod">
   <!--name的值对应于javaBean中的属性值-->
   <property name="name" value="小明"></property>
</bean>
<bean id="myBeanPost" class="com.spring5.MyBeanPost"></bean>

注解方式实现IOC

注解概述

  • 注解是代码特殊标记
    语法:@注解名称(属性名称=属性值, 属性名称=属性值…)
  • 怎么用注解
    注解可以用在类、方法和属性上面
  • 使用注解目的
    简化 xml 配置

Spring针对 Bean管理中创建对象提供的注解

  • @Component
  • @Service
  • @Controller
  • @Repository
    上面四个注解功能是一样的,都可以用来创建 bean 实例,在服务层用@Service、在控制层用@Controller只是方便管理

实现对象创建

  • 第一步 引入依赖
    把spring-aop-5.2.6.RELEASE.jar包引入到IDEA中
  • ​ 第二步 开启组件扫描
    需要引入context名称空间:xmlns:context=“http://www.springframework.org/schema/context”、http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"

<context:component-scan base-package="com.spring5">context:component-scan>
  • 第三步 创建类,在类上面添加注解
    注解中的value值可以省略,默认值是类名首字母小写
@Component(value = "userService")
public class UserService {
     
    public void add(){
     
        System.out.println("I am UserService add...");
    }
}

扫描细节配置

  • 不加use-default-filters="false"表示会扫描包下面的所有注解
  • context:include-filter 配置扫描哪些内容
  • context:exclude-filter配置不扫描哪些内容

示例一


<context:component-scan base-package="com.spring5" use-default-filters="false">
	<context:include-filter type="annotation"
           expression="org.springframework.stereotype.Controller"/>
context:component-scan>

示例二


<context:component-scan base-package="com.spring5">
	<context:exclude-filter type="annotation"
        	expression="org.springframework.stereotype.Controller"/>
context:component-scan>

实现属性注入

@Autowired、@Qualifier和@Resource用于"对象"属性,@Value用于普通属性。

@Autowired

现有类UserService、接口UserDao、类UserDaoImpl,其中UserDaoImpl实现了接口UserDao。代码结构如下:

 src
    │  bean.xml
    │
    └─com
        └─spring5
            │  Test.java 
            │
            ├─dao
            │      UserDao.java
            │      UserDaoImpl.java
            │
            └─service
                    UserService.java

如果类UserService要调用类UserDaoImpl的方法,用spring注解实现的步骤如下:

  • 在 UserService和 UserDaoImpl类添加创建对象注解
  • 在 UserService类添加UserDao类型属性,并在属性上面使用注解

示例代码如下:
接口UserDao:

public interface UserDao {
     
    public void update();
}

类UserDaoImpl:

@Repository(value = "userDaoImpl")
public class UserDaoImpl implements UserDao {
     
    @Override
    public void update() {
     
        System.out.println("I am dao update...");
    }
}

类UserService:

@Service(value = "userService")
public class UserService {
     

    @Autowired
    private UserDao userDao;

    public void add(){
     
        System.out.println("I am UserService add...");
        userDao.update();
    }
}

bean.xml:

<context:component-scan base-package="com.spring5">
context:component-scan>

@Qualifier

@Qualifier注解是根据名称进行注入。如果接口有很多实现类用@Autowired就不行,和@Qualifier一起使用就可以解决。使用示例:

@Autowired
@Qualifier(value = "userDaoImpl2")
private UserDao userDao;

@Resource

@Resource是javax.annotation.Resource包中的注解,既可以根据名称注入又可以根据类型注入。
示例:

// 根据类型注入
@Resource
private UserDao userDao;

// 根据名称注入
@Resource(name = "userDaoImpl")
private UserDao userDao;

@Value

@Value用于普通属性。

注入普通属性
@Value(value = "张三丰")
private String name;
注入List

步骤如下:

  • 在src下简历属性文件application.properties
my.names=zhangsan,lisi,wangwu,zhaoli
  • 在类UserService中添加@PropertySource和@Value注解
@Service(value = "userService")
@PropertySource(value="classpath:application.properties")
public class UserService {
     

    @Resource(name = "userDaoImpl")
    private UserDao userDao;

    @Value("#{'${my.names}'.split(',')}")
    private List<String> names;

    public void add(){
     
        System.out.println("I am UserService add...");
        userDao.update();
    }
}

参考链接

完全注解开发

  • 创建配置类,替代 xml 配置文件
@Configuration
@ComponentScan(basePackages = {
     "com.spring5"})
public class SpringConfig {
     
}
  • 编写测试类
    用AnnotationConfigApplicationContext加载spring的java配置文件
@org.junit.Test
public void test() {
     
    //1 加载spring的java配置文件
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    //2 获取创建的对象
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
}

这个过程在实际环境中会用SpringBoot替代。

你可能感兴趣的:(spring,spring,5,ioc)