• Spring学习01:控制反转(IOC)与依赖注入(DI)


    程序的耦合和解耦

    耦合:程序间的依赖关系,在开发中应该做到编译时不依赖,运行时才依赖

    解耦:使用反射来创建对象,避免使用new关键字,并通过读取配置文件来获取要创建对象的全限定类名

    Spring基于XML的IOC配置

    1.创建Maven项目并在pom.xml中导入依赖坐标

    <dependencies>
        	<!-- 引入核心容器的依赖-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
    </dependencies>
    

    2.在resources目录下创建bean.xml文件,把对象的创建交给spring来管理

     <?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="accountService" class="com.chenpeng.service.impl.AccountServiceImpl"></bean>
         <bean id="accountDao" class="com.chenpeng.dao.impl.AccountDaoImpl"></bean>
     </beans>
    

    每个<bean>标签对应一个类,其中的属性:

    • id:该类的唯一标识,其他类可以引用
    • class:该类的全限定类名

    3.在测试类中通过容器创建对象

    //1.获取容器
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    //2.创建对象
    AccountService as = ac.getBean("accountService", AccountService.class);
    

    常用的容器有三种:

    1. ClassPathXmlApplicationContext:从类的根路径下加载配置文件创建容器
    2. FileSystemXmlApplicationContext:从磁盘路径上加载配置文件创建容器
    3. AnnotationConfigApplicationContext:读取注解创建容器

    bean标签

    • 作用:配置spring管理的对象,默认调用类的无参构造函数创建对象
    • 属性:
      • id:类的唯一标识,其他类可以引用,也可作为getBean()方法的参数创建对象
      • class:该类的全限定类名
      • scope:指定对象的作用范围,可选值如下:
        • singleton:单例对象,默认值
        • prototype:多例对象
        • request:作用于web应用的请求范围
        • session:作用于web应用的会话范围
        • global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
      • init-method:指定类中的初始化方法名称,在对象创建成功后执行
      • destroy-method:指定类中销毁方法名称(对多例对象无效)

    bean的作用范围和生命周期

    1. 单例对象:scope=singleton
      • 作用范围:每个应用只有一个该对象的实例,它的作用范围就是整个应用
      • 生命周期:单例对象的生命周期和容器相同
        • 出生:当容器创建时出生
        • 活着:只要容器还在,对象就一直活着
        • 死亡:容器销毁时,对象消亡
    2. 多例对象:scope=prototype
      • 作用范围:每次访问对象时,都会重新创建对象实例
      • 生命周期:多例对象的创建与销毁不受容器控制
        • 出生:当我们使用对象时spring框架为我们创建
        • 活着:对象只要是在使用过程中就一直活着
        • 死亡:当对象长时间不用,且没有别的对象引用时,由java的垃圾回收器回收

    创建bean的三种方式

    1. 使用默认构造函数创建,在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建

      <bean id="accountService" class="com.chenpeng.service.impl.AccountServiceImpl"></bean>
      
    2. 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)

      创建普通工厂如下

      public class InstanceFactory {
      	public IAccountService getAccountService(){
      		return new AccountServiceImpl();
      	}
      }
      
      <bean id="instanceFactory" class="com.chenpeng.factory.InstanceFactory"/>
      <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"/>
      

      其中<bean>标签的属性:

      • factory-bean属性:指定普通工厂的id
      • factory-method属性:指定普通工厂中生产对象的方法
    3. 使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)

      创建静态工厂如下

      public class StaticFactory {
      	public static IAccountService getAccountService(){
      		return new AccountServiceImpl();
      	}
      }
      
      <bean id="accountService" class="com.chenpeng.factory.StaticFactory" factory-method="getAccountService"/>
      

      其中<bean>标签的属性:

      • factory-method属性::指定静态工厂中生产对象的静态方法

    Spring的依赖注入

    依赖注入

    通过控制反转,对象在创建的时候,由spring为我们将对象所依赖的对象的引用传递给它

    注入数据的分类

    1. 基本类型和String
    2. 其他bean类型(在配置文件中或者注解配置过的bean)
    3. 复杂类型/集合类型

    依赖注入的方法

    构造函数注入

    • 使用的标签<constructor-arg>

    • 标签出现的位置:<bean>标签的内部

    • 标签中的属性:

      • type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
      • index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引的位置是从0开始
      • name:用于指定给构造函数中指定名称的参数赋值
      • value:用于提供基本类型和String类型的数据
      • 用于指定其他的bean类型数据(指的是在spring的IOC核心容器中出现过的bean对象)
    • 优势:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功

    • 弊端:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供

      public class AccountServiceImpl implements AccountService {
      
          //如果是经常变化的数据,并不适用于注入的方式
          private String name;
          private Integer age;
          private Date birthday;
      
          public AccountServiceImpl(String name, Integer age, Date birthday) {
              this.name = name;
              this.age = age;
              this.birthday = birthday;
          }
      }
      
      <bean id="now" class="java.util.Date" scope="prototype"></bean>
      
      <bean id="accountService" class="cn.maoritian.service.impl.AccountServiceImpl">
      	<constructor-arg name="name" value="myname"></constructor-arg>
      	<constructor-arg name="age" value="18"></constructor-arg>
      	<constructor-arg name="birthday" ref="now"></constructor-arg>
      </bean>
      

    set方法注入(更常用)

    • 使用的标签:<property>

    • 标签出现的位置:<bean>标签的内部

    • 标签中的属性:

      • name:用于指定注入时所调用set方法名称
      • value:用于提供基本类型和String类型的数据
      • ref:用于指定其他的bean类型数据(指的是在spring的IOC核心容器中出现过的bean对象)
    • 优势:创建对象时没有明确的限制,可以直接使用默认构造函数

    • 弊端:如果有某个成员必须有值,则获取对象时有可能set方法没有执行

      public class AccountServiceImpl implements IAccountService {
      
      	private String name;
      	private Integer age;
      	private Date birthday;
      
      	public void setName(String name) {
      		this.name = name;
      	}
      	public void setAge(Integer age) {
      		this.age = age;
      	}
      	public void setBirthday(Date birthday) {
      		this.birthday = birthday;
      	}
      }
      
      <bean id="now" class="java.util.Date" scope="prototype"></bean>
      
      <bean id="accountService" class="cn.maoritian.service.impl.AccountServiceImpl">
      	<property name="name" value="myname"></property>
      	<property name="age" value="21"></property>
      	<property name="birthday" ref="now"></property>
      </bean>
      

    复杂类型/集合类型的注入

    集合字段及其对应的标签按照集合的结构分为两类,相同结构的集合标签之间可以互相替换

    1. 只用键的结构:

      • 数组字段: <array>标签表示数组,<value>标签表示数组内的成员
      • List字段: <list>标签表示集合,<value>标签表示集合内的成员
      • Set字段: <set>标签表示集合,<value>标签表示集合内的成员
    2. 键值对的结构:

      • Map字段: <map>标签表示集合,<entry>标签表示集合内的键值对,其key属性表示键,value属性表示值
      • Properties字段: <props>标签表示集合,<prop>标签表示键值对,其key属性表示键,标签内的内容表示值
      public class AccountServiceImpl implements AccountService {
      	
      	private String[] myArray;
      	private List<String> myList;
      	private Set<String> mySet;
      	private Map<String,String> myMap;
      	private Properties myProps;
      
      	public void setMyStrs(String[] myArray) {
      		this.myArray = myArray;
      	}
      	
      	public void setMyList(List<String> myList) {
      		this.myList = myList;
      	}
      	
      	public void setMySet(Set<String> mySet) {
      		this.mySet = mySet;
      	}
      	
      	public void setMyMap(Map<String, String> myMap) {
      		this.myMap = myMap;
      	}
      	
      	public void setMyProps(Properties myProps) {
      		this.myProps = myProps;
      	}
      	
      	@Override
      	public void saveAccount() {
      		System.out.println(Arrays.toString(myArray));
      		System.out.println(myList);
      		System.out.println(mySet);
      		System.out.println(myMap);
      		System.out.println(myProps);
      	}
      }
      
      <bean id="accountService" class="cn.maoritian.service.impl.AccountServiceImpl">
      	<property name="myStrs">
      		<array>
      			<value>value1</value>
      			<value>value2</value>
      			<value>value3</value>
      		</array>
      	</property>
      
      	<property name="myList">
      		<list>
      			<value>value1</value>
      			<value>value2</value>
      			<value>value3</value>
      		</list>
      	</property>
      
      	<property name="mySet">
      		<set>
      			<value>value1</value>
      			<value>value2</value>
      			<value>value3</value>
      		</set>
      	</property>
      
      	<property name="myMap">
      		<map>
      			<entry key="key1" value="value1"></entry>
      			<entry key="key2">
      				<value>value2</value>
      			</entry>
      			
      		</map>
      	</property>
      
      	<property name="myProps">
      		<props>
      			<prop key="key1">value1</prop>
      			<prop key="key2">value2</prop>
      		</props>
      	</property>
      </bean>
      

    Spring基于注解的IOC配置

    常用注解

    用于创建对象的注解

    • @Component
      • 作用:用于把当前类对象存入spring容器中
      • 属性:value:用于指定bean的id,不写时它的默认值是当前类名且首字母小写
    • @Controller:一般用在表现层
    • @Service:一般用在业务层
    • @Repository:一般用在持久层
    • @Controller,@Servic和@Repository的作用和属性和@Component一样,这些注解的作用相当于bean.xml中的<bean>标签

    用于注入数据的注解

    • @Autowired
      • 作用:自动按照类型注入,只要容器中有唯一一个bean对象类型和要注入的变量类型匹配,就可以注入成功
      • 注入过程:
        • 如果IOC容器中没有任何bean的类型和要注入的变量类型匹配,则报错
        • 如果IOC容器中有多个类型匹配时会报错,可以指定要注入的bean对象的id
      • 出现位置:可以是变量上,可以是方法上
      • 在使用注解注入时,set方法不是必须的
    • @Qualifier
      • 作用:在按照类注入的基础上再按照名称注入,在给类成员注入时不能单独使用,但是在给方法参数注入时可以
      • 属性:
        • value:用于指定注入的bean的id
    • @Resource
      • 作用:直接按照bean的id注入,可以独立使用
      • 属性:
        • name:用于指定注入bean的id
    • @Value
      • 作用:用于注入基本类型和String类型的数据
      • 属性:
        • value:用于指定数据的值,使用spring中的el表达式--SpEL
    • 这些注解的作用相当于bean.xml中<bean>标签中的<property>标签
    • 注意:集合的类型注入只能通过xml实现

    用于改变作用范围的注解

    • @Scope
      • 作用:用于指定bean的作用范围
      • 属性:
        • value:指定范围的取值,常用取值:Singleton和Prototype
    • 该注解的作用相当于bean.xml中<bean>标签中的<scope>标签

    和生命周期相关的注解

    • @ PostConstruct
      • 作用:用于指定初始化方法
    • @PreDestroy
      • 作用:用于指定销毁方法

    Spring的半注解配置和纯注解配置

    半注解配置

    半注解配置时,仍然使用ClassPathXmlApplicationContext类加载配置文件,但是需要在配置文件中告知spring创建容器时要扫描的包,然后将spring的注解加在类的定义上或者类对象上

    <?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">
    
        <!--告知spring在创建容器时要扫描的包,需要导入context的名称空间和约束-->
        <context:component-scan base-package="cn.maoritian"></context:component-scan>
    </beans>
    

    纯注解配置

    纯注解配置时,使用配置类代替bean.xml,使用AnnotationConfigApplicationContext类从spring配置类中读取IOC配置

    配置类中使用的注解

    • @Configuration
      • 作用:指定当前类是一个配置类
      • 当被配置的类作为AnnotationConfigApplicationContext的参数时,可以不写
    • @ComponentScan
      • 作用:用于通过注解指定spring在创建容器时要扫描的包
      • 属性:
        • value:和basePackage的作用一样,都是用于指定创建容器时要扫描的包
    • @Bean
      • 作用:用于把当前方法中的返回值作为bean对象存入spring的IOC容器中
      • 属性:
        • name:用于指定bean的id,默认值是当前方法的名称
      • 细节:使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象,查找的方式和Autowired注解的作用是一样的
    • @Import
      • 作用:用于导入其他的配置类
    • @PropertySource
      • 作用:用于指定properties的位置
      • 属性:
        • value:指定文件的名称和路径,关键字:classpath,表示类路径下

    使用纯注解配置

    1. dao层实现类

      @Repository("accountDao")
      public class AccountDaoImpl implements AccountDao {
      	@Autowired//自动按照类型注入
      	private QueryRunner runner;
      	
      	public List<Account> findAll() {
      		try {
      			return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
      		} catch (SQLException e) {
      			throw new RuntimeException(e);
      		}
      	}
      }
      
    2. service层实现类

      @Service("accountService")
      public class AccountServiceImpl implements AccountService {
      	@Autowired
      	private AccountDao accountDao;
      	
      	public List<Account> findAll() {
      		return accountDao.findAll();
      	}
      }
      
    3. 创建config包,在其中创建配置类

      SpringConfiguration主配置类

      @Configuration
      @ComponentScan("com.chenpeng")//创建容器时要扫描的包
      @Import(JdbcConfig.class)//引入配置类,可以引入多个
      @PropertySource("classpath:jdbc.properties")//从类的根路径下读取配置文件
      public class SpringConfiguration {
      
      }
      

      JDBCConfig配置类

      /**
       * 数据库连接的配置类
       */
      
      public class JdbcConfig {
          @Value("${jdbc.driver}")
          private String driver;
      
          @Value("${jdbc.url}")
          private String url;
      
          @Value("${jdbc.user}")
          private String user;
      
          @Value("${jdbc.password}")
          private String password;
          /**
           * 创建一个QueryRunner对象
           * @param dataSource
           * @return
           */
          @Bean(name="runner")
          @Scope("prototype")
          public QueryRunner createQueryRunner(DataSource dataSource){
              return new QueryRunner(dataSource);
          }
      
          /**
           * 创建数据源对象
           * @return
           */
          @Bean("dataSource")
          public DataSource createDataSource(){
              ComboPooledDataSource ds = new ComboPooledDataSource();
              try {
                  ds.setDriverClass(driver);
                  ds.setJdbcUrl(url);
                  ds.setUser(user);
                  ds.setPassword(password);
                  return ds;
              } catch (PropertyVetoException e) {
                  throw new RuntimeException(e);
              }
          }
      }
      
  • 相关阅读:
    【NOIP2013】 华容道 bfs预处理+bfs
    【NOIP2017】逛公园 最短路+DP
    NOIP上机测试注意事项
    【NOIP2013】货车运输 最大生成树+倍增
    【NOIP2013】 火柴排队 贪心+splay
    【NOIP2013】转圈游戏 快速幂
    【xsy1143】 兔子的数字 搜索
    【xsy1172】 染色 dp
    【NOIP2017】 宝藏 状压dp
    【NOIP2017】列队 splay
  • 原文地址:https://www.cnblogs.com/codeDD/p/12696326.html
Copyright © 2020-2023  润新知