天天看点

Spring框架IOC容器详解(Spring IOC笔记)

一、入门案例

创建步骤:

  1. 创建java项目
  2. 导入Spring5的相关jar包(只做基本功能Beans、Core、Context、expression,还需要一个额外的日志包)
  3. 创建普通类
  4. 创建Spring配置文件,在配置文件中配置创建的对象
  5. 编写测试代码

    Spring有两个核心部分

导入jar包步骤:一级目录下创建lib文件夹,将需要的jar包复制进去,打开项目的Project Structure,在Modules中选择Dependencies将复制进去的jar包导入(点+号找到jar包导入,不需要打勾)

创建普通类步骤:在src里面创建包com.atguigu.spring5,创建User普通类

public class User{
    public void add(){
        System.out.println("add.....");
    }
}
           

创建配置文件步骤:

  1. 在src下创建xml文件,名字bean1.xml
  2. 配置User对象创建,bean标签中class对应全路径(包+类名),id是给对向创建名字

编写测试代码步骤:在com.atguigu.spring5包下新建testdemo包,在包里面新建TestSpring5类,测试成功会输出user对象的地址,调用啊add()方法会输出相应的语句

Public class TsetSpring{
    @Test
    public void testAdd(){
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml")
        //获取配置创建的对象
        User user=context.getBean("user",user.class);
        
        System.out.println(user);
        user.add();
    }
}
           

二、底层原理

IOC容器

  1. IOC容器底层原理
  2. IOC接口(BeanFactory)
  3. IOC操作Bean管理(基于xml)
  4. IOC操作Bean管理(基于注解)

什么是IOC

通过控制反转,对向在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

  1. 控制反转:把对象创建和对象之间调用的过程,交给Spring进行管理
  2. 使用IOC目的:为了耦合度降低
  3. 做入门案例就是IOC实现

IOC底层原理

  1. xml解析、工厂模式、反射(得到字节码文件,控制字节码文件进行操作)
  2. 原始方式耦合度高
class UserService{
	 execute(){
         UserDao dao = new UserDao();
         dao.add();
     }
}
class UserDao{
    add(){
        ...
    }
}
           

工厂模式

class UserService{
    execute(){
        UserDao dao = UserFactory.getDao();
        dao.add();
    }
}
class UserDao{
    add(){
        ...
    }
}
class UserFactory{
    public static UserDao getDao(){
        return new UserDao();
    }
}
           

IOC过程

  1. xml配置文件,配置创建的对象
  1. 有service类和dao类,创建工厂类
class UserFactory{
	public static UserDao getDao(){
        String classCalue = class属性值 //1、xml解析
        Class clazz = Class.forName(classValue)//2、通过反射创建对象
        return (UserDao)clazz.newInstance();
    }
}
           

容器本质上就是对象工厂

  1. IOC的思想基于IOC容器完成,IOC容器底层就是对象工厂
  2. Spring提供IOC容器实现的两种方式:(两个接口)
    1. BeanFactory: IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用(加载配置文件的时候不会去创建对象,在获取(使用)对象才会去创建对象)
    2. ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般右开发人员进行使用(加载配置文件的时候就会把对配置文件对象进行创建)
  3. ApplicationContext接口有两个实现类
    1. FileSystemXmlApplicationContext实现类对应的变量是盘符下的路径
    2. ClassPathXmlApplictionContext对应的是

三、 Bean管理XML方式

1、什么叫Bean管理,Bean管理指的是两个操作

  1. Spring创建对象
  2. Spring注入属性

2、Bean管理操作有两种方式

  1. 基于xml配置文件方式实现
  2. 基于注解方式实现
  3. 基于xml方式注入属性

1、创建对象和注入属性

1、 基于xml方式创建对象

  • 在Spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建
  • 在bean标签中有很多属性,介绍一些常用属性
    • id属性:唯一标识(给对象起一个名字)
    • class属性:创建对象所在类的全路径
    • name属性:和id属性类似,id不可以加特殊符号,name可以加特殊符号
  • 创建对象的时候,默认执行无参数构造方法完成对象创建

2、基于xml方式注入属性

DI:依赖注入,注入属性

  1. 使用set()注入
  2. 使用有参构造注入

第一种注入方式:使用set方法进行注入

  1. 创建类,定义属性和对应的set方法
public class Book{
       //创建属性
       private String bname;
       private String bauthor;
       //创建属性对应的set方法
       public void setname(String bname){
           this.bname=bname;
       }
       public void setname(String bauthor){
           this.bauthor=bauthor;
       }    
   }
           
  1. 在spring配置文件配置对象创建,配置属性注入
<!--set方法注入属性-->
<bean id="book" class="com.atguigu.spring5.Book">
    <!--使用property玩车个属性的注入
		name:类里面属性名称
		value:向属性注入值
	-->
    <property name="bname" value="易筋经"></property>
    <property name="bauthor" value="达摩老祖"></property>
</bean>
           

第二种注入方式:使用有参构造注入

<!--有参构造输入属性-->
<bean id="orders" class="com.stguigu.spring5.Orders">
    <constructor-arg name="oname" value="abc"></constructor-arg>
    <constructor-arg name="address" value="China"></constructor-arg>
    <!--根据索引值注入-->
    <constructor-arg index="0" value="China"></constructor-arg>
</bean>
           

2、注入空值和符号

1、字面量

  1. null值
  2. 属性值包含特殊符号
<!--null值-->
<property name="address">
    <null/>
</property>
<!--属性值包含特殊符号
1、把<>进行转义
2、把带符号内容写到CDATE
-->
<property name="address">
    <value><![CDATA[<<南京>>]]>
</property>
           

3、外部注入

  1. 创建两个类service类和DAO类
  2. 在service调用dao里面的方法
  3. 在spring配置文件中进行配置
public class UserService{
    //创建UserDao类型属性,生成set方法
    private UserDao userDao;
    public void setUserDao(UserDao userDao){
        this.userDao = userDao;
    }
    public void add(){
        System.out.println("service add......");
        userDao.update();
    }
}
           
<!--service和dao对象创建-->
<bean id="userService" class="com.atguigu.spring5.service.UserService">
<!--注入userDAo对象
name属性值:类里面属性名称
ref属性:创建userDao对象bean标签id值
-->
    <property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDao" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>
           

3、注入属性-内部bean和级联赋值

  1. 一对多关系:部门和员工
  2. 在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示
public class Dept{
    private String dname;
    public void setDame(String dname){
        this.dname = dname;
    }
}

public class Emp{
    private String ename;
    private String gender;
    //员工属于某一个部门,使用对象形式表示
    private Dept dept;
    public void setEname(String ename){
        this.ename = ename;
    }
    public void setGender(String gender){
        this.gender = gender;
    }
}
           
  1. 在spring配置文件中进行配置
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
    <!--先设置两个普通的属性-->
    <property name="ename" value="lucy"></property>
    <property name="gender" value="女"></property>
     <!--设置对象类型属性-->
    <property name="dept">
        <bean id="dept" class="com.atguigu.spring5.bean.Dept">
            <property name="dname" value="财务部"></property></bean></property>
</bean>
           

4. 注入属性——级联赋值

  1. 第一种方式(下面第二种方式需要在Emp类中设置getDept()方法)
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
    <!--先设置两个普通的属性-->
    <property name="ename" value="lucy"></property>
    <property name="gender" value="女"></property>
     <!--级联赋值-->
    <property name="dept" ref="dept"></property>
</bean>
        <bean id="dept" class="com.atguigu.spring5.bean.Dept">
            <property name="dname" value="财务部"></property>
</bean>
           
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
    <!--先设置两个普通的属性-->
    <property name="ename" value="lucy"></property>
    <property name="gender" value="女"></property>
     <!--级联赋值-->
    <property name="dept" ref="dept"></property>
    <property name="dept.dname" name="技术部"></property>
</bean>
        <bean id="dept" class="com.atguigu.spring5.bean.Dept">
            <property name="dname" value="财务部"></property>
</bean>
           

5.xml注入集合属性

1、注入数组类型的属性

2、注入list集合类型的属性

3、注入Map集合类型的属性

  1. 创建类,定义数组、list、map、set类型属性,生成对应set方法
public class Stu{
   //1、数组类型属性
   private String[] courses;
   //2 list集合类型属性
   private List<String> list;
   //3、Map集合类型的属性
   private Map<String,String> maps;
   //4、set集合类型的属性
   private set<String> sets;
   public void setCourses(String[] courses){
       this.courses=courses;
   }
   public void setList(List<String> courses){
       this.courses=courses;
   }
   public void setMaps(Map<String,String> courses){
       this.courses=courses;
   }
   public void test(){
       System.out.println(Arrays.toString(courses));
       System.out.println(list);
       System.out.println(maps);
       System.out.println(sets);
       
   }
   
}
           
  1. 在spring配置文件进行配置
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
    <!--数组类型属性注入-->
    <property name="courses">
        <array>
            <value>java课程</value>
            <value>数据库课程</value>
        </array>
    </property>
    <!--list类型属性注入-->
    <property name="list">
        <list>
            <value>张三</value>
            <value>小三</value>
        </list>
    </property>
    <!--map类型属性注入-->
    <property name="list">
        <map>
            <entry key="JAVA" value="java"></entry>
            <entry key="PHP" value="php"></entry>
        </map>
    </property>
     <!--set类型属性注入-->
    <property name="list">
        <set>
            <value>MySQL</value>
            <value>Redis</value>
        </map>
    </property>
</bean>
           

6.在集合里面设置对象类型的值

public class Course{
    private String[] course;
}
           
<!--注入list集合类型,值是对象-->
<property name="courseList">
    <list>
        <ref bean="course1"></ref>
        <ref bean="course2"></ref>
    </list>
</property>
<!--创建多个course对象-->
<bean id="course1" class="com.atguigu.spring5.collectiontype.Course">
    <property name="cname" value="Spring5框架"></property>
</bean>
<bean id="course2" class="com.atguigu.spring5.collectiontype.Course">
    <property name="cname" value="Mybatis框架"></property>
</bean>
           

7.把集合注入部分提取出来

  1. 在spring配置文件中引入名称空间
  2. 使用util标签完成属性名称注入
<!--1、提取list集合类型属性注入-->
<util:list id="book.list">
    <value>易筋经</value>
    <value>九阴真经</value>
    <value>九阳神功</value>
</util:list>
<!--2、提取list集合类型属性注入使用-->
<bean id="book" class="com.atguigu.spring5.collectiontype.Book">
    <property name="list" ref="booklist"></property>
</bean>
           

8.Bean的作用域

1、在Spring里面可以设置创建bean实例还是多实例

2、在Spring里面在默认情况下,bean是单实例对象

3、如何设置单实例还是多实例

  1. 在Spring配置文件bean标签里面有属性(scope)用于设置单实例还是多实例
  2. scope属性的值

    第一个值 默认值,singleton,表示是单实例对象

    第二个值prototype,表示是多实例对象

4、singleton和prototype的区别

第一singleton单实例,prototype多实例

第二设置scope值是singleton时候,加载spring配置文件时候就会创建单实例对象

​ 设置scope值是prototype时候,不是在加载spring配置文件时候创建对象,在调用getBean方法的时候创建多实例对象

9.bean生命周期

1、生命周期

  1. 从对象创建到对象销毁的过程

2、bean生命周期

  1. 通过构造器创建bean实例(执行无参数构造)
  2. 为bean的属性设置值和对其他bean引用(调用set方法)
  3. 把bean实例传递bean后置处理器的方法
    • postProcessBeforeInitialization
  4. 调用bean的初始化的方法(需要进行配置)
  5. 把bean实例传递bean后置处理器方法
    • postProcessAfterInitialization
  6. bean可以使用了(对象获取到了)
  7. 当容器关闭时候,调用bean的销毁方法(需要进行配置销毁的方法)

3、演示bean生命周期

public class Orders{
    private String oname;
    public Orders(){
        System.out.println("第一步 执行无参数构造创建bean实例");
    }
    public void setOname(){
        this.oname=oname;
        System.out.println("第二部 调用set方法设置属性值");
    }
    //创建执行初始化的方法
    public void initMethod(){
        System.out.println("第三步 执行初始化方法");
    }
    //创建执行销毁的方法
    public void destroy(){
        ystem.out.println("第五步 执行销毁的方法");
    }
}
           
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
    <property name="oname" value="手机"></property>
</bean>
           
public void testBean3(){
    //ApplicationContext context=new ClassPathXmlApplicationContext("bean4.xml");ApplicationContext无close方法,用它的实现类
    ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("bean4.xml");
    Orders orders=context.getBean("orders",Orders.class);
    System.out.println("第四步 获取创建bean实例对象");
    System.out.println(orders);
    
    //手动让bean实例销毁
    context.close();
}
           

4、bean的后置处理器,bean生命周期有七步

5、演示添加后置处理器效果

  1. 创建类,实现接口BeanPostProcessor,创建后置处理器
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
    <property name="oname" value="手机"></property>
</bean>
<!--配置后置处理器-->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
           

10.自动装配

1、什么是自动装配

  1. 根据指定装配规则(属性类型或者属性名称),Spring自动将匹配的属性值进行注入

2、演示自动装配过程

  1. 根据属性名称自动装配
public class Emp{
    private Dept dept;
    public void setDept(Dept dept){
        this.dept=dept;
    }
    @override
    public String toString(){
        return "Emp{"+"dept="+dept+'}';
    }
    piblic void test(){
        System.out.println(dept);
    }
}
           
<bean id="emp" class="com.atguigu.spring5.autowire.Emp">
    <!--手动装配-->
    <property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>

           
<!--实现自动装配
	bean标签属性autowire,配置自动装配
	autowire属性常用两个值:
	byName根据属性名称进行注入,注入值的bean的id和属性名称一样
	byTime根据属性类型进行注入
-->
<!--根据名称byName-->
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName"></bean>
<!--根据类型byType-->
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType"></bean>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
           

11.外部属性文件

1、直接配置数据库信息

  1. 配置德鲁伊连接池
  2. 引入德鲁伊连接池jar包
<!--直接配置连接池-->
<bean id="" class="com.alibaba.duid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
</bean>
           

2、引入外部属性文件配置数据库连接池

  1. 创建外部
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root
           
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties">
    
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.duid.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>
           

四、Bean管理注解方式

1、什么是注解

  1. 注解是代码里面特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值)
  2. 使用注解,注解作用在类上面,方法上面,属性上面
  3. 使用注解目的:简化xml配置

1.创建对象

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

  1. @Component
  2. @Service
  3. @Controller
  4. @repository

上面四个注解功能是一样的,都可以用来创建bean实例

2、基于注解方式实现对象创建

第一步:引入依赖 spring-aop

第二步 开启组件扫描

先加入context名称空间

<!--开启组件扫描
	1、如果扫描多个包,多个包逗号隔开
	2、扫描包上层目录
-->
<context:component-scan base-package="com.atguigu.spring5.dao,comatguigu.spring.service"></context:component-scancontext:component>
           

第三步 创建类

//在注解里面value属性值可以不写,默认值是类名称,首字母小写
//UserService -- userSvice
@Component(Value="userService")//<bean id="userService" class="...">
public class UserService{
	public void add(){
        System.out.println("service add......");
	}
}
           

2.组件扫描配置

3、开启组件扫描

<!--示例1
	use-default-filters="false"表示现在不使用默认filter,自己配置filter
 	context:include-filter,设置扫描哪些内容
-->
<context:component-scan base-package="com.atguigu" use-default-filters="false">
    <context:include-filter type="annotation"
                            expression="org.springframwork.stereotype.Controller"/>
</context:component-scan>

<!--示例2
	use-default-filters="false"表示现在不使用默认filter,自己配置filter
 	context:exclude-filter,设置不扫描哪些内容
-->
<context:component-scan base-package="com.atguigu">
    <context:exclude-filter type="annotation"
                            expression="org.springframwork.stereotype.Controller"/>
</context:component-scan>
           

3.注入属性@Autowired和Qualifier

5、基于注解方式实现属性注入

  1. @Autowired
    • 根据属性类型进行自动装配

      第一步把service和dao对象创建,在service和dao的类上添加创建对象注解

      第二步在service注入dao对象,在service类添加dao类型属性,在属性上面使用注解

@Service
public class UserService{
    //定义dao属性
    //不需要添加set方法
    //添加注入属性注解
    @Autowired //根据类型进行注入
    private UserDao userDao;
    public void add(){
        System.out.println("service add......");
        userDao.add();
    }     
}

           
  1. @Qualifier
    • 根据属性名称进行注入
    • @Qualifier注解的使用,和上面@Autowired一起使用
    @Service
    public class UserService{
        //定义dao属性
        //不需要添加set方法
        //添加注入属性注解
        @Autowired //根据类型进行注入
        @Qualifier(value = "userDaoImpl1")//根据名称进行注入
        private UserDao userDao;
        public void add(){
            System.out.println("service add......");
            userDao.add();
        }     
    }
               
  2. @Resource
    • 可以根据类型注入,可以根据名称注入
    @Service
    public class UserService{
       // @Resource //根据类型进行注入
        @Resource(name="userDaoImpl1") //根据名称进行注入
        private UserDao userDao;
        public void add(){
            System.out.println("service add......");
            userDao.add();
        }     
    }
               
  3. @Value:注入普通类型属性
@Service
public class UserService{
    @Value(Value="abc")
    private String name;
    public void add(){
        System.out.println("service add......"+name);
        userDao.add();
    }     
}
           

4.Bean管理XML方式(完全注解开发)

6、完全注解开发

  1. 创建配置类。替代XML配置文件
@Configuration//作为配置类
@ComponentScan(basePackages = {"com.atguigu"})
public class Springconfig{
    
}
           
  1. 编写配置类
@Test
public void testService(){
    //加载配置类
    ApplicationContext comtext=new 			AnnnotationConfigAppllicationContext(SpringConfig.class)
        UserService userService=context.getBean("userService",UserService.class);
    System.out.prinln(userService);
    userService.add
}