Spring IOC

IOC

  • IOC容器概述
    ApplicationContext是Spring IoC容器实现的代表,它负责实例化,配置和组装Bean。容器通过读取配置元数据获取有关实例化、配置和组装哪 些对象的说明 。配置元数据可以使用XML、Java注解或Java代码来呈现。它允许你处理应用程序的对象与其他对象之间的互相依赖关系。
  • 容器的实例化
    对象在Spring容器创建完成的时候就已经创建完成,不是需要用的时候才创建
  • 容器的使用
    ApplicationContext是能够创建bean定义以及处理相互依赖关系的高级工厂接口,使用方法T getBean(String name, Class<T> requiredType)获取容器实例
    // 创建spring上下文 加载所有的bean 
    ApplicationContext context = new ClassPathXmlApplicationContext("service s.xml", "daos.xml"); 
    // 获取bean 
    PetStoreService service = context.getBean("petStore", PetStoreService.cla ss); 
    // 使用bean的对象
    List<String> userList = service.getUsernameList();
    

IOC基于XML的使用

编写XML配置文件

在这里插入图片描述

Bean

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

Bean的命名

在xml中使用id或name属性来指定标识符。id是唯一的,若同时使用了name则视为别名,name也可以通过 (空格)、,(逗号)、;(分号)分隔来设置多个别名。若没有指定任何标识符,则Spring会自动生成一个唯一的标识符。

<bean class="zt.demo.beans.User" id="user" name="user2 user3,user4;user5">
</bean>

也可以使用alias设置别名

<alias name="user" alias="demo"></alias>

实例化Bean

  • 使用构造器实例化 (默认,无法干预实例化过程)

    • 空构造器(指定的类必须存在无参构造函数)
    <bean class="zt.demo.beans.User" id="user"/>
    
    • 带参构造器(指定的类必须存在有参构造函数)
      1. 基于name属性设置构造函数参数
      2. 可以只有value属性
      3. 如果省略name属性 一定注意参数顺序
      4. 如果参数顺序错乱
        可以使用name,
        还可以使用index:设置参数的下标 从0开始
        还可以使用type: 在错乱的参数类型一致的情况下不能使用
    public class User {
        private Integer id;
        private String username;
        private String realname;
    
    	、、、(set、get方法)
    
    	public User(Integer id, String username, String realname) {
            this.id = id;
            this.username = username;
            this.realname = realname;
    	}
    }
    
    <bean class="zt.demo.beans.User" id="user"/>
    	<constructor-arg name="id" value="1"/>
        <constructor-arg index="1" value="z"/>
        <constructor-arg type="java.lang.String" value="x"/>
    </bean>
    

    在这里插入图片描述

  • 使用静态工厂方法实例化
    在类中添加静态工厂方法,可以创建自身或者子类。通过factory-method来调用方法,同时静态工厂也能指定参数。

    public static  Person createPersonFactory(){
        Child child = new Child();
        child.setName("Son");
        return child;
    }
    
    public static  Person createPersonFactory1(String name){
        Person person = new Person();
        person.setName(name);
        return person;
    }
    
    <bean class="zt.demo.beans.Person" id="person" factory-method="createPersonFactory" >
    </bean>
    
    <bean class="cn.tulingxueyuan.beans.Person" id="person" factory-method="createPersonFactory1" >
        <constructor-arg name="name" value="JUGG"/>
    </bean>
    

    在这里插入图片描述

  • 使用实例工厂方法实例化
    需要额外的指定一个工厂bean,使用时class需要指定工厂类,也可创建自身或子类。同时要指定工厂方法作为factory-bean。

    <!-- 先注入工厂方法类 -->
    <bean class="zt.demo.beans.PersonFacotry" id="personFacotry"></bean>
    <bean class="zt.demo.beans.beans.Person" id="person"
          factory-bean="personFacotry"
          factory-method="createPersonFacotryMethod" >
    </bean>
    
    // 工厂方法类
    public class PersonFacotry {
        // public Person createPersonFacotryMethod() {
        //     Child child = new Child();
        //     child.setName("儿子");
        //     return child;
        // }
        public Person createPersonFacotryMethod() {
            Person person = new Person();
            person.setName("0.0");
            return person;
        }
    }
    

Bean的作用域

  • singleton单例的作用域(默认就是单例)
    • 不管使用多少次,同一个id都只会加载一次bean
    • 可以节省内存消耗。
    • 单例模式下容易遇到线程安全问题-数据脏读,当一个共享的一个bean的内容,在多个线程共同读写的情况下容易出现数据的覆盖。
  • protorype原型(多例)的作用域
    • 每一次使用时,都会加载一次bean创建一个新的出来。
    • 通过scope="protorype"设置
  • 下列几种作用域需基于web的spring Application Context才可用
    • request
      每一个请求创建一个新的bean
    • session
      每一个对话创建一个bean
    • application
      每一个应用创建一个bean
    • websocket
      每一个长链接创建一个bean

Bean的生命周期回调

ioc来控制bean的生命周期

  • 实现Initializing Bean和Disposable Bean回调接口
    public class Person implements  InitializingBean,DisposableBean {
    	...
    	// 实例化
        public void afterPropertiesSet() throws Exception {
            System.out.println("实例化Person1");
        }
    
        // 销毁
        public void destroy() throws Exception {
            System.out.println("销毁Person1");
        }
    }
    
  • 自定义init()和destroy()方法
    public class Person implements  InitializingBean,DisposableBean {
    	...
    	 //实例化
        public void initByConfig() throws Exception {
            System.out.println("实例化Person2");
        }
    
        // 销毁
        public void destroyByConfig() throws Exception {
            System.out.println("销毁Person2");
        }
    }
    
    <bean class="zt.demo.beans.Person" id="person" init-method="initByConfig" destroy-method="destroyByConfig"></bean>
    
  • 什么时候会销毁
    在spring容器关闭的时候close()或者使用ConfigurableApplicationContext.registerShutdownHook方法优雅的关闭。
    @Test
    public  void test() {
        Person person = ioc.getBean("person", Person.class);
        System.out.println(person);
        // ioc.close();
        ioc.registerShutdownHook();
    }
    
    在这里插入图片描述

Bean定义的继承

一个bean继承另一个bean,可以使用parent属性指定父类bean
如果想让父类bean不能被实例化可以加上abstract="true"

<bean class="zt.demo.beans.User" id="user2">
    <property name="idxx" value="1"/>
    <property name="username" value="RTZ"/>
    <property name="realname" value="dove"/>
</bean>

<bean class="zt.demo.beans.User" id="user" parent="user2">
    <property name="username" value="EG"/>
</bean>

在这里插入图片描述

依赖注入

基于setter方法的依赖注入

  • 因为基于setter方法,所以首先属性要有set方法
  • name是根据set方法的名字来的,比如方法名字是: setxx ‐> name=“xx”
    public class Person {
        private Integer id;
        private String name;
        
        public void setId(Integer id) {
        	this.id = id;
    	}
    	
    	public void setNameggg(String name) {
        	this.name = name;
    	}
    }
    
    <bean class="zt.demo.beans.Person" id="person">
        <property name="id" value="1"></property>
        <property name="nameggg" value="ggg"></property>
    </bean>
    
    在这里插入图片描述

基于构造函数依赖注入

  • 将会调用自定义构造函数来实例化对象,就不会调用默认的无参构造函数
  • name是根据构造函数的参数名来的, 比如:User(String idxx) ‐> name=“idxx”
  • name属性可以省,可以只有value属性,但是要注意参数的位置
  • 参数顺序错乱时,可以使用 name 或者 index 或者 type
    • index 是参数的下标 从0开始
    • type 在错乱的参数类型一致的情况下不能使用
    public class User {
        private Integer id;
        private String username;
        private String realname;
    	
    	public User(Integer id, String username, String realname) {
            this.id = id;
            this.username = username;
            this.realname = realname;
        }
    }
    
    <bean class="zt.demo.beans.User" id="user7">
        <!-- <constructor-arg name="id" value="1"/> -->
        <!-- <constructor-arg name="username"  value="ame"></constructor-arg> -->
        <!-- <constructor-arg name="realname"  value="萧瑟"></constructor-arg> -->
        <!-- <constructor-arg index="1" value="maybe"/> -->
        <!-- <constructor-arg type="java.lang.String" value="土堆"/> -->
        <constructor-arg value="2"/>
        <constructor-arg value="dc"/>
        <constructor-arg value="灿"/>
    </bean>
    
    在这里插入图片描述

复杂数据类型的依赖注入

<!-- <bean class="zt.demo.beans.Wife" id="wife"> -->
<!--     <property name="age" value="22"/> -->
<!--     <property name="name" value="kun"/> -->
<!-- </bean> -->
<!--复杂数据类型的依赖注入-->
<bean class="zt.demo.beans.Person" id="person">
    <property name="id" value="1"/>
    <!--设置null-->
    <property name="name">
        <null/>
    </property>
    <property name="gender" value=""/>
    <!-- 引用外部Bean, ref为外部bean的ID-->
    <!-- <property name="wife" ref="wife"/> -->
    <!-- 使用内部bean 依赖注入其他bean -->
    <property name="wife">
        <bean class="zt.demo.beans.Wife">
            <property name="age" value="33"/>
            <property name="name" value="你干嘛"/>
        </bean>
    </property>
    <!--list 注入:
        如果泛型是基本数据类型<value>
        如果泛型是bean  <bean>-->
    <property name="hobbies">
        <list>
            <value></value>
            <value></value>
            <value>rap</value>
            <value>篮球</value>
        </list>
    </property>
    <property name="others">
        <list>
            <bean class="zt.demo.beans.User">
                <property name="idxx" value="1"/>
                <property name="username" value="1"/>
            </bean>
            <bean class="zt.demo.beans.Child">
                <property name="name" value="12"/>
                <property name="id" value="12"/>
            </bean>
        </list>
    </property>
    <!--map 注入
        如果value是基本数据类型<entry key="1" value="Java"></entry>
        如果value是bean  value-ref-->
    <property name="course">
        <map>
            <entry key="1" value="练习"/>
            <entry key="2" value="两年半"/>
        </map>
    </property>
</bean>

使用c命名空间简化基于构造函数的XML

需要有bean有构造函数

<bean class="zt.demo.beans.User" id="user" c:id="2" c:realname="kun" c:username="jinitaimei">
</bean>

使用p命名空间简化基于setter属性注入XML配置

p:按Alt+Enter 自动加上命名空间
设置基本数据类型 或者p:wife-ref引用外部bean
如果有集合类型就不支持,需要额外配置<property>

<bean class="zt.demo.beans.Wife" id="wife2" p:age="44" p:name="44">
</bean>
<bean class="zt.demo.beans.Person" id="person" p:wife-ref="wife2" >
    <property name="hobbies">
        <list>
            <value></value>
            <value></value>
        </list>
    </property>
</bean>

depends on

depends on可以控制bean加载顺序
bean的加载顺序是先加载bean,然后加载方法
当一个bean想让另一个bean在它之前加载可以设置depends-on

<bean class="zt.demo.beans.User" id="user" depends-on="wife"></bean>
<bean class="zt.demo.beans.Wife" id="wife"></bean>

懒加载

不会在spring容器加载的时候加载该bean,而是在使用的时候加载。
当某个bean实例在使用的时候才加载。不用的时候就不会加载。
适用于某些bean不需要在ioc初始化的时候加载。
可以在<bean>上也可以在<beans>上加lazy-init = true

<bean class="zt.demo.beans.User" id="user" lazy-init="true"></bean>

自动注入

autowire自动注入的方式有三种

  • byType
    根据类型去自动匹配,当出现多个类型或者匹配到类型则会报错
    <bean class="zt.demo.beans.Person" id="person" autowire="byType">
    </bean>
    
    <bean class="zt.demo.beans.Wife" id="wife2">
        <property name="name" value="mina"></property>
    </bean>
    

在这里插入图片描述

  • by Name
    根据set方法的名字去自动匹配
    // 这里是setWife 所以优先匹配id是wife的
    public void setWife(Wife wife) {
        this.wife = wife;
    }
    
    <bean class="zt.demo.beans.Person" id="person" autowire="byName">
    </bean>
    
    <bean class="zt.demo.beans.Wife" id="wife2">
        <property name="name" value="mina"></property>
    </bean>
    
    <bean class="zt.demo.beans.Wife" id="wife">
        <property name="name" value="apex"></property>
    </bean>
    

在这里插入图片描述

  • construct
    根据构造器去匹配,优先会去根据构造器的参数名字去匹配,若名字没有匹配到,就会根据参数类型去匹配
    会根据构造函数的参数,进行完整的匹配注入,当构造函数的参数是Person(Wife wife, User user),那么ioc容器中必须要有上述两个bean
    名字没有匹配到会根据类型匹配,类型加入出现多个时,会注入失败,单不会报错。
    当根据类型匹配到多个,可以使用 设置某个bean为主要bean:primary=“true”
    或者设置不需要自动注入的bean:autowire-candidate=‘false’以此忽略注入。
    // 这里参数名是wife3 所以优先匹配id是wife3的
    public Person(Wife wife3) {
        this.wife = wife3;
    }
    
    <bean class="zt.demo.beans.Person" id="person" autowire="constructor">
    </bean>
    
    <bean class="zt.demo.beans.Wife" id="wife">
        <property name="name" value="mina"></property>
    </bean>
    
    <bean class="zt.demo.beans.Wife" id="wife3">
        <property name="name" value="apex"></property>
    </bean>
    
    正常匹配
    在这里插入图片描述
    设置autowire-candidate="false"
    <bean class="zt.demo.beans.Person" id="person" autowire="constructor">
    </bean>
    
    <bean class="zt.demo.beans.Wife" id="wife2">
        <property name="name" value="mina"></property>
    </bean>
    <!-- 设置autowire-candidate="false"后,忽略此处自动注入,然后通过名字匹配不到,按类型会匹配到wife2 -->
    <bean class="zt.demo.beans.Wife" id="wife3" autowire-candidate="false">
        <property name="name" value="apex"></property>
    </bean>
    
    在这里插入图片描述
    设置primary="true"
    <bean class="zt.demo.beans.Person" id="person" autowire="constructor">
    </bean>
    
    <bean class="zt.demo.beans.Wife" id="wife2">
        <property name="name" value="mina"></property>
    </bean>
    <!-- 设置primary="true"后,优先注入wife4 -->
    <bean class="zt.demo.beans.Wife" id="wife4" primary="true">
        <property name="name" value="apex"></property>
    </bean>
    
    在这里插入图片描述

引用第三方bean

<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
	<property name="username" value="root"></property>
	<property name="password" value="123456"></property>
	<property name="url" value="jdbc:mysql://localhost:3306/demo"></property>
	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>

这种类似的配置一般会放在一个配置文件中。
利用以下方法引入外部属性资源文件
<context:property-placeholder location=“db.property”></context:property-placeholder>
文件内容:
mysql.username=root
引入后可以直接:

<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
	<property name="username" value="${mysql.username}"></property>
	<property name="password" value="123456"></property>
	<property name="url" value="jdbc:mysql://localhost:3306/demo"></property>
	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>

IOC基于注解的使用

基础注解

  • @Controller 标记在控制层的类注册为Bean主键
  • @Service 标记在业务逻辑层的类注册为Bean主键
  • @Repository 标记在数据访问层的类注册为Bean主键
  • @Component 标记在非三层的普通类注册为Bean主键

这些虽然不是一定要对应上对应的层,但是一般建议还是对应上,利于管理,增加可读性

使用注解将一个类注册为bean

  1. 设置扫描包context:component-scan
  2. 在对应的类名加上对应的注解

使用上面注解会自动将类名的首字母小写设置为Bean的名字,即

@Controller
public class UserController {
}

等价于

<bean class="zt.demo.controller.UserController" id="userController"></bean>

配置XML文件

IOC基于JavaConfig的使用

绑定Java与XML配置

新建一个类并在类上使用@Configuration注解来标记此类为SpirngIoc配置类,相当于上文的xml配置文件。
@ComponentScan注解用来标记需要扫描的包。相当于xml中的 <context:component>

	@Configuration // 相当于 xml文件  <beans></beans>
	@ComponentScan(basePackages = "demo")
	public class IOCJavaConfig {
	
		@Bean
	    public User user3(){
	        return new User();
	    }
	}

@Bean注解

  • 使用@Bean注解将一个实例注册为bean,且会自动将返回值作为bean的类型,将方法名作为bean的id。
    • @Bean(name = “xx”) 设置bean的名字,会替换原名
    • @Bean(name = {“dataSource”,“dd”}) 设置多个名字
    @Bean
    /** 
     * @Bean(name = "xx") 可以用此方法设置bean的名字(替换)
     * @Bean(name = {"dataSource","dd"}) 也可以设置多个名字
     */
    public Role role() {
        return new Role();
    }
    
    • @Bean(initMethod = “”, destroyMethod = “”) 用此方法设置生命周期回调函数,相当于xml中的<bean class="xx" id="xx" init-method="xxx" destroy-method="xxx"></bean>。(销毁回调函数需要在单例模式下,关闭IOC的时候会出现)
    // 方法
    @Bean(initMethod = "initPerson", destroyMethod = "desPerson")
    public Person person() {
        return new Person();
    }
    
    // 对象类
    @Data
    @Getter
    @Setter
    @Component
    @Scope("singleton")
    public class Person {
    
        @Value("zt")
        private String name;
    
        public Person (){
            System.out.println("Person 加载");
        }
    
        private void initPerson() {
            System.out.println("init Person");
        }
    
        private void desPerson() {
            System.out.println("destory Person");
        }
    }
    
  • 依赖其他bean的方法
    • 当需要自动依赖外部bean时:直接在方法里面写上需要依赖的参数即可,不需要使用@Autowired注解
    @Bean
    public Role role(User user) {
    	System.out.println(user.getName());
        return new Role();
    }
    
    • 当需要自动依赖内部Bean时:直接调用方法即可
    @Bean
    public Role role() {
    	System.out.println(user1());
        return new Role();
    }
    
    @Bean
    public User user1() {
        return new User();
    }
    

引入外部资源文件

使用@PropertySource 引入外部属性资源文件,并使用@value来干预bean的实例化过程

@Configuration
@ComponentScan(basePackages = "zt.demo")
@PropertySource("classpath:db.properties")
public class IoCJavaConfig {
	@Value("${mysql.username}")
    private String name;
    @Value("${mysql.password}}")
    private String password;
    @Value("${mysql.url}")
    private String url;
    @Value("${mysql.driverClassName}")
    private String driverClassName;
	
	public DruidDataSource dataSource(){
        DruidDataSource dataSource=new DruidDataSource();
        dataSource.setName(name);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        dataSource.setDriverClassName(driverClassName);
        return dataSource;
    }
}

使用AnnotationConfigApplicationContext初始化Spring容器

public class JavaConfigTest {

	AnnotationConfigApplicationContext ioc;
	
	@Before
    public void before(){
        ioc = new AnnotationConfigApplicationContext(IOCJavaConfig.class);
    }

	@Test
    public void test4(){
        Person bean = (Person) ioc.getBean("person");
        System.out.println(bean);
        ioc.close();
    }
}

@Import注解

就像在Spring XML文件中使用元素来帮助模块化配置一样, @Import 注解允许从另一个配置类加载@Bean定义

  • 引入其他的配置类
    @Import(IOCJavaConfig2.class)
    public class IOCJavaConfig {
    }
    
  • 将类注册为Bean
    @Import(Role.class)
    public class IOCJavaConfig {
    }
    
  • 导入ImportSelector接口实现类(可注册多个bean)
    // 自定义ImportSelector接口实现类
    @Component
    public class MyImportSelector implements ImportSelector {
      	@Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
        	// 可以以字符串的形式注册多个Bean,字符串必须是类的完整限定名,getBean不能根据名字获取,必须要根据类型获取
            return new String[]{"demo.beans.Person", Role.class.getName()};
        }
    }
    
    // 配置文件
    @Import(MyImportSelector.class)
    public class IOCJavaConfig {
    }
    
  • 导入ImportBeanDefinitionRegistrar接口实现类(可注册多个BeanDefinition)
    // 自定义ImportBeanDefinitionRegistrar接口实现类
    @Component
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        	// 使用bean定义的方式
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClass(Person.class);
            registry.registerBeanDefinition("person", beanDefinition);
    
    		GenericBeanDefinition beanDefinition1 = new GenericBeanDefinition();
            beanDefinition1.setBeanClass(User.class);
            registry.registerBeanDefinition("user", beanDefinition1);
        }
    }
    
    
    // 配置文件
    @Import(MyImportBeanDefinitionRegistrar.class)
    public class IOCJavaConfig {
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值