Spring学习笔记-IOC/DI

Spring Framework系统架构图

在这里插入图片描述

IoC/DI

  • 代码书写现状:service接口更换实现类需要重新创建对象,导致代码耦合度偏高。

  • 解决方案:IoC(Inversion of Control)控制反转思想

    • 使用对象时,由主动new对象转换为外部提供对象
    • 对象创建控制权转移给Ioc容器
  • spring技术提供IoC容器对IoC思想进行了实现

    • IoC容器负责对象的创建、初始化
    • 被创建和管理的对象在IoC容器中称为bean
  • DI(Dependency Injection)依赖注入

    • 在IoC容器中建立bean与bean之间的依赖关系
  • IoC实现

    • 在pom.xml导入spring坐标

      • <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.10.RELEASE</version>
        </dependency>
        
    • 在resource目录下创建Spring Config配置文件

      在这里插入图片描述

    • 在applicationContext.xml配置文件中配置bean

      • <bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl"></bean>
        <bean id="bookService" name="service" class="com.itheima.service.impl.BookServiceImpl"></bean>
        <!-- 
        	配置bean:使用<bean>配置
            	class属性:表示给bean定义类型(最终路径为实现类)
            	id属性:表示给bean起名
            	name属性:给bean起别名,可以起多个,作用与id相同
            	scope属性:指定bean单例对象、非单例对象,默认是单例对象
        			singleton:单例
        			prototype:非单例
        -->
        
    • 在启动类的main方法中获取Ioc容器

      • public class App2 {
            public static void main(String[] args) {
                //获取容器对象,ApplicationContext是接口
                ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
                //调用getBean方法获取对象
                BookDao bookDao = (BookDao)ctx.getBean("bookDao");
            }
        }
        
  • DI实现

    • 删除业务层中使用new的方式创建的dao对象

    • 提供dao对象的set方法

      • public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;
        }
        
    • 在applicationContext.xml配置文件中配置service与dao的关系

      • <bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl"></bean>
        <bean id="bookService" name="service" class="com.itheima.service.impl.BookServiceImpl">
            <property name="bookDao" ref="bookDao"/>  <!--dao对象注入service层-->
        </bean>
        <!-- 
        	配置service和dao的关系(DI)
        		<property>:配置当前bean的属性
            	name属性:java程序中对象名
            	ref属性:参照bean的id
        -->
        

bean实例化四种方式

  • 无参构造方法实例化

    • 提供无参构造方法

      • public class BookDaoImpl implements BookDao {
        	//无参构造方法
            public BookDaoImpl() {
                System.out.println("book dao constructor is running ....");
            }
        
            public void save() {
                System.out.println("book dao save ...");
            }
        
        }
        
    • 在主函数中获取bean

      • //通过无参构造方法获取bean
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
        
  • 静态工厂实例化

    • 提供工厂类的静态get方法返回bean

      • //静态工厂创建对象
        public class OrderDaoFactory {
            public static OrderDao getOrderDao(){
                System.out.println("factory setup....");
                return new OrderDaoImpl();
            }
        }
        
    • 在applicationContext.xml配置文件中配置静态工厂bean

      • <bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
        <!--
                方式二:使用静态工厂(静态方法)实例化bean:需要工厂类和静态get方法
                1.class属性:指向工厂类
                2.factory-method属性:指向工厂类的静态get方法名
        -->
        
    • 在主函数中获取bean

      • //通过静态工厂获取bean
        OrderDao orderDao = OrderDaoFactory.getOrderDao();
        orderDao.save();
        
  • 实例工厂实例化

    • 提供实例工厂类的非静态方法返回bean

      • //实例工厂创建对象
        public class UserDaoFactory {
            public UserDao getUserDao(){
                return new UserDaoImpl();
            }
        }
        
    • 在applicationContext.xml配置文件中配置实例工厂bean

      • <bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
        <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
        <!--
                方式三:使用实例工厂(非静态方法)实例化bean
                1.配置工厂bean
                    a.class属性:指向工厂类
                2.配置业务层bean
                    a.factory-method属性:实例工厂的get方法名
                    b.factory-bean属性:工厂bean
        -->
        
    • 在主函数中获取bean

      •     //创建实例工厂对象
            UserDaoFactory userDaoFactory = new UserDaoFactory();
            //通过实例工厂对象创建对象
            UserDao userDao = userDaoFactory.getUserDao();
            userDao.save();
        
  • FactoryBean实例化

    • 提供FactoryBean类的getObject方法和getObjectType方法

      • public class UserDaoFactoryBean implements FactoryBean<UserDao> {//实现FactoryBean接口并指定类型
            //代替原始实例工厂中创建对象的方法
            public UserDao getObject() throws Exception {
                return new UserDaoImpl();
            }
        	//返回bean对象类型
            public Class<?> getObjectType() {
                return UserDao.class;
            }
        
        }
        
    • 在applicationContext.xml配置文件中配置FactoryBean

      • <bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
        
    • 在主函数中获取bean

      • ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
        orderDao.save();
        

bean生命周期

  • bean生命周期:bean从创建到销毁的整体过程

  • bean初始化和销毁

    • 使用配置文件配置初始化和销毁方法

      • //表示bean初始化对应的操作(需要配置)
            public void init(){
                System.out.println("init...");
            }
        
            public void save() {
                System.out.println("book dao save ...");
            }
            //表示bean销毁前对应的操作(需要配置)
            //destory方法关闭容器才能执行
            public void destory(){
                System.out.println("destory...");
            }
        
    • 在applicationContext.xml配置文件中配置bean初始化和销毁方法

      • <!--
            init-method属性:设置bean初始化生命周期回调函数
            destroy-method属性:设置bean销毁生命周期回调函数,仅适用于单例对象,必须关闭容器才能执行
        -->
        <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
        
    • 执行销毁方法需要先关闭容器:

      • 暴力关闭

        • //关闭容器需要ClassPathXmlApplicationContext接口
          ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
          
          //关闭容器(暴力关闭):暴力关闭后不能获取bean对象
          ctx.close();
          
      • 注册关闭钩子函数,在虚拟机退出前自动关闭容器

        • //注册关闭钩子函数,动态关闭
          ctx.registerShutdownHook();
          
    • 实现接口重写初始化和销毁方法

      • public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
        	public void setBookDao(BookDao bookDao) {
                System.out.println("set .....");
                this.bookDao = bookDao;
            }
        
            public void save() {
                System.out.println("book service save ...");
                bookDao.save();
            }
        
            //销毁bean之前执行
            public void destroy() throws Exception {
                System.out.println("service destroy");
            }
        
            //初始化bean方法,在set方法执行后再执行
            public void afterPropertiesSet() throws Exception {
                System.out.println("service init");
            }
        }
        

依赖注入

  • setter注入

    • 简单类型

      • public class BookDaoImpl implements BookDao {
        
            private String databaseName;
            private int connectionNum;
            //setter注入需要提供要注入对象的set方法
            public void setConnectionNum(int connectionNum) {
                this.connectionNum = connectionNum;
            }
            //setter注入需要提供要注入对象的set方法
            public void setDatabaseName(String databaseName) {
                this.databaseName = databaseName;
            }
        
            public void save() {
                System.out.println("book dao save ..."+databaseName+","+connectionNum);
            }
        }
        
      • <!--
            setter注入简单类型
            name属性:设置注入的属性名,实际是set方法对应的名称
            value属性:设置注入简单类型数据值
        -->
        <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
            <!--property标签:设置注入属性-->
            <property name="connectionNum" value="100"/>
            <property name="databaseName" value="mysql"/>
        </bean>
        
    • 引用类型

      • private BookDao bookDao;
        private UserDao userDao;
        //setter注入需要提供要注入对象的set方法
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
        //setter注入需要提供要注入对象的set方法
        public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;
        }
        
      • <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
            <!--property标签:设置注入属性-->
            <property name="bookDao" ref="bookDao"/>
            <property name="userDao" ref="userDao"/>
        </bean>
        
  • 构造器注入

    • 简单类型

      • public class BookDaoImpl implements BookDao {
            private String databaseName;
            private int connectionNum;
        
            public BookDaoImpl(String databaseName, int connectionNum) {
                this.databaseName = databaseName;
                this.connectionNum = connectionNum;
            }
        
            public void save() {
                System.out.println("book dao save ..."+databaseName+","+connectionNum);
            }
        }
        
      • <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
            <!--按方法形参名注入,代码耦合度过高-->
            <constructor-arg name="connectionNum" value="10"/>
            <constructor-arg name="databaseName" value="mysql"/>
        </bean>
        
        <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
            <!--按类型注入,但类型不能重复-->
            <constructor-arg type="int" value="10"/>
            <constructor-arg type="java.lang.String" value="mysql"/>
        </bean>
        
        <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
            <!--根据参数位置注入-->
            <constructor-arg index="0" value="mysql"/>
            <constructor-arg index="1" value="100"/>
        </bean>
        
    • 引用类型

      • public class BookServiceImpl implements BookService{
            private BookDao bookDao;
            private UserDao userDao;
        	//使用有参构造方法注入引用类型
            public BookServiceImpl(BookDao bookDao, UserDao userDao) {
                this.bookDao = bookDao;
                this.userDao = userDao;
            }
        
            public void save() {
                System.out.println("book service save ...");
                bookDao.save();
                userDao.save();
            }
        }
        
      • <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
            <!--constructor-arg-->
            <constructor-arg name="userDao" ref="userDao"/>
            <constructor-arg name="bookDao" ref="bookDao"/>
        </bean>
        

依赖自动装配

  • IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配

  • 自动装配:只能注入引用类型,优先级低于setter注入和构造器注入。

    • 按类型(常用):

      • <!--需要注入的bean也要在spring配置(可以不起名),类型必须唯一-->
        <bean class="com.itheima.dao.impl.BookDaoImpl"/>
        <!--自动装配需要提供setter方法-->
        <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
        
    • 按名称

      • <!--按名称装配:需要注入的bean的配置id与java程序一致(耦合)-->
        <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
        <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byName"/>
        
    • 按构造方法

    • 不启用自动装配

集合注入

  • 在applicationContext.xml配置文件中配置注入类型

    • 数组

      • <!--name属性:成员变量名-->
        <!--数组注入-->
        <property name="array">
            <array>
                <value>100</value>
                <value>200</value>
                <value>300</value>
            </array>
        </property>
        
    • List(常用)

      • <!--list集合注入-->
        <property name="list">
            <list>
                <value>itcast</value>
                <value>itheima</value>
                <value>boxuegu</value>
                <value>chuanzhihui</value>
            </list>
        </property>
        
    • Set

      • <!--set集合注入-->
        <property name="set">
            <set>
                <value>itcast</value>
                <value>itheima</value>
                <value>boxuegu</value>
                <value>boxuegu</value>
            </set>
        </property>
        
    • Map(常用)

      • <!--
            map集合注入
            <map>标签:Z
                <entry>标签:
                1.key属性
                2.value属性
        -->
        <property name="map">
            <map>
                <entry key="country" value="china"/>
                <entry key="province" value="henan"/>
                <entry key="city" value="kaifeng"/>
            </map>
        </property>
        
    • Properties

      • <!--
            Properties注入
            <props>标签:
                <prop>标签:
                1.key属性
                2.value写在标签中
        -->
        <property name="properties">
            <props>
                <prop key="country">china</prop>
                <prop key="province">henan</prop>
                <prop key="city">kaifeng</prop>
            </props>
        </property>
        
  • 在代码中测试注入是否成功:

    • public class BookDaoImpl implements BookDao {
      
          private int[] array;
      
          private List<String> list;
      
          private Set<String> set;
      
          private Map<String,String> map;
      
          private Properties properties;
      
      
      
      
          public void setArray(int[] array) {
              this.array = array;
          }
      
          public void setList(List<String> list) {
              this.list = list;
          }
      
          public void setSet(Set<String> set) {
              this.set = set;
          }
      
          public void setMap(Map<String, String> map) {
              this.map = map;
          }
      
          public void setProperties(Properties properties) {
              this.properties = properties;
          }
      
      
      
      
          public void save() {
              System.out.println("book dao save ...");
      
              System.out.println("遍历数组:" + Arrays.toString(array));
      
              System.out.println("遍历List" + list);
      
              System.out.println("遍历Set" + set);
      
              System.out.println("遍历Map" + map);
      
              System.out.println("遍历Properties" + properties);
          }
      }
      

数据源对象管理

  • 整合druid:

    • 导入druid坐标:

      • <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>
        
    • 在applicationContext.xml配置数据源对象:

      • 	<bean class="com.alibaba.druid.pool.DruidDataSource">
                <!--查看DruidDataSource类确定setter注入-->
                <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/spring_db"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </bean>
        
  • 整合c3p0:

    • 导入c3p0坐标:

      • <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        
    • 在applicationContext.xml配置数据源对象:

      • <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.jdbc.Driver"/>
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/>
            <property name="user" value="root"/>
            <property name="password" value="root"/>
            <property name="maxPoolSize" value="1000"/>
        </bean>
        

加载properties文件

  • 在applicationContext.xml的beans标签下开启命名空间:

    • 在这里插入图片描述
  • 在applicationContext.xml使用context空间加载properties文件:

    • <context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
      <!--system-properties-mode属性:是否加载系统属性-->
      <!--
      	location后可以指定多个配置文件,按,分割
      	使用*.properties可以加载多个配置文件
      	classpath:*.properties是标准写法
      -->
      
  • 使用属性占位符 $(key) 访问properties文件中的属性:

    • <bean class="com.alibaba.druid.pool.DruidDataSource">
          <property name="driverClassName" value="${jdbc.driver}"/>
          <property name="url" value="${jdbc.url}"/>
          <property name="username" value="${jdbc.username}"/>
          <property name="password" value="${jdbc.password}"/>
      </bean>
      

容器

  • bean加载方式

    • //1.按名称加载(需要强转)
      BookDao bookDao = (BookDao) ctx.getBean("bookDao");
      
      //2.按名称和类型加载
      BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
      
      //3.按类型加载
      BookDao bookDao = ctx.getBean(BookDao.class);
      
  • 容器层次结构图

    • 在这里插入图片描述

    • BeanFactory创建完毕后,所有的bean都是延迟加载

    • ApplicationContext创建完毕后,所有的bean都是立即加载

核心容器总结

  • 容器总结

    • 在这里插入图片描述
  • bean相关

    • 在这里插入图片描述
  • 依赖注入相关

    • 在这里插入图片描述

注解开发定义bean

  • 使用@Component注解定义bean

    • //@Component需要按类型来获取bean
      @Component("bookDao")//按名称来获取bean
      public class BookDaoImpl implements BookDao {
          public void save() {
              System.out.println("book dao save ...");
          }
      }
      
  • 在applicationContext.xml开启注解扫描:

    • <context:component-scan base-package="com.itheima"/>
      
  • Spring提供@Component注解的三个衍生注解

    • @Controller:用于表现层bean定义
    • @Service:用于业务层bean定义
    • @Repository:用于数据层bean定义

纯注解开发

  • 创建配置类:替换applicationContext.xml配置文件

    • @Configuration:声明当前类为Spring配置类

    • @ComponentScan:设置注解扫描路径,多个路径书写为字符串数组格式

    • @Configuration//设置当前类为配置类
      @ComponentScan("com.itheima")//扫描需要配置bean的类
      public class SpringConfig {
      }
      
  • public class AppForAnnotation {
        public static void main(String[] args) {
            //AnnotationConfigApplicationContext注解开发实现类
            ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);//加载配置类
            
            //按id获取bean
            BookDao bookDao = (BookDao) ctx.getBean("bookDao");
            System.out.println(bookDao);
            
            //按类型获取bean
            BookService bookService = ctx.getBean(BookService.class);
            System.out.println(bookService);
        }
    }
    
  • 设置单例和非单例对象

    • @Scope(“prototype”)非单例
    • @Scope(“singleton”)单例
  • 设置生命周期方法

    • @PostConstruct 初始化方法
    • @PreDestroy 销毁前方法
  • 设置依赖注入

    • @Autowired:按类型注入引用类型

      • 不需要提供set方法,源码底层使用暴力反射直接注入。
    • @Qualifier(“对象名”):按名称注入引用类型

    • @Value:配合配置文件注入简单类型

      • 需要在Spring配置类添加@PropertySource注解

        • @Configuration//设置当前类为配置类
          @ComponentScan("com.itheima")//扫描需要配置bean的类
          
          //@PropertySource加载properties配置文件{,,,}可以添加多个配置文件,配置文件名允许用*
          @PropertySource("jdbc.properties")
          public class SpringConfig {
          }
          
      • 使用@Value(“${}”)注入简单类型

        • @Value("${name}")//@Value:注入简单类型(无需提供set方法)
          private String name;
          
  • 管理第三方bean

    • 创建jdbc的配置类

      • @Configuration
        public class JdbcConfig {
            @Value("root")
            private String username;
            @Value("root")
            private String password;
            @Value("jdbc:mysql://localhost:3306/spring_db")
            private String url;
            @Value("com.mysql.jdbc.Driver")
            private String driverClassName;
        
            /**
             * 配置第三方bean方法
             * @param bookDao 参数自动装配注入引用类型
             * @return
             */
            @Bean
            public DataSource dataSource(BookDao bookDao){
                System.out.println(bookDao);
                DruidDataSource druidDataSource = new DruidDataSource();
                druidDataSource.setUsername(username);
                druidDataSource.setPassword(password);
                druidDataSource.setUrl(url);
                druidDataSource.setDriverClassName(driverClassName);
                return druidDataSource;
            }
        }
        
    • 将第三方bean的配置类加入核心配置

      • 方式一:导入式(第三方bean的配置类不需要添加@Configuration)

        • @Configuration
          @Import(JdbcConfig.class)//导入其他配置类,{,,,}导入多个
          public class SpringConfig {
          }
          
      • 方式二:扫描式

        • @Configuration
          @ComponentScan("com.itheima.config")//扫描式导入(不推荐)
          public class SpringConfig {
          }
          

注解开发总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值