spring-ioc笔记

1、Spring-IOC

程序的耦合:

  • 耦合:程序间的依赖关系。包括:

    • 类之间的依赖

    • 方法间的依赖

  • 解耦:降低程序间的依赖关系

  • 实际开发:

    • 应该做到,编译期不依赖,运行时才依赖

  • 解耦思路:

    • 第一步:使用反射来创建对象,而避免使用new关键字。

    • 第二步:通过读取配置文件来获取要创建的对象全限定类名。

 

1.1、bean工厂

自己建立一个简单的beanfactory(bean工厂)

  1. BeanFactory image-20200803221707382

     /**
      * 一个创建Bean对象的工厂
      *
      * Bean:在计算机英语中,有可重用组件的含义。
      * JavaBean:用java语言编写的可重用组件。
      *     javvabean >> 实体类(javabean远远大于实体类)
      * 他就是创建我们的service和dao对象的。
      *
      * 第一个:需要一个配置文件来配置我们的service和dao
      *     配置的内容:唯一标识=全限定类名(key=value)
      * 第二个:通过读取配置文件中配置内容,反射创建对象
      *
      * 我的配置文件可以是xml也可以是properties
      */
     public class BeanFactory {
         //定义一个Properties对象
         private static Properties props;
     
         //使用静态代码块位properties对象赋值
         static{
             try {
                 //实例化对象
                 props = new Properties();
                 //获取properties文件的流对象
                 //InputStream in = new FileInputStream("");     //这是要输入具体地址,一般要采用相对地址,也就是下面这个
                 InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
                 props.load(in);
            }catch (Exception e){
                 System.out.println("导入错误");
            }
        }
     
         public static Object getBean(String beanName){
             Object bean = null;
             try {
                 String beanPath = props.getProperty(beanName);
                 bean = Class.forName(beanPath).newInstance();  //每次都会调用默认构造函数创建对象
            }catch (Exception e){
                 System.out.println("初始化错误");
            }
             return bean;
        }
     
     }
     

     

  2. UserMapper接口(相当与UserDao) image-20200803222045191

     public interface UserMapper {
         void queryUser();
     }

    UserMapperImpl(相当与UserDaoImpl)

     public class UserMapperImpl implements UserMapper {
         public void queryUser() {
             System.out.println("查询");
        }
     }

     

  3. UserService接口 image-20200803222054959

     public interface UserService {
         void queryUser();
     }

    UserServiceImpl

     public class UserServiceImpl implements UserService {
         //private UserMapper userMapper = new UserMapperImpl();
         private UserMapper userMapper = (UserMapper) BeanFactory.getBean("UserMapper");
         public void queryUser() {
             userMapper.queryUser();
        }
     }

     

  4. Client(测试类) image-20200803222108718

     public class Client {
     
         public static void main(String[] args) {
             //UserService userService = new UserServiceImpl();
             UserService userService = (UserService)BeanFactory.getBean("UserService");
             userService.queryUser();
        }
     
     }

     

  5. bean.properties image-20200803222435042

     UserService=com.gzk.service.UserServiceImpl
     UserMapper=com.gzk.mapper.UserMapperImpl

     

  6. 运行Client

image-20200803222248856

1.2、单例,原型

我们的1.1中的bean工厂是单例的还是原型的呢?

有代码来解释:

 public class UserServiceImpl implements UserService {
     private UserMapper userMapper = (UserMapper) BeanFactory.getBean("UserMapper");
     private int i = 1;
     public void queryUser() {
         userMapper.queryUser();
         System.out.println(i++);
    }
 }

 

 public class Client {
 
     public static void main(String[] args) {
         for(int i=0;i<5;i++) {
             UserService userService = (UserService) BeanFactory.getBean("UserService");
             System.out.println(userService);
             userService.queryUser();
        }
    }
 }

image-20200803223455708

由代码测试可得,我们这个工厂是原型的

原型(也就是说,每调用一次就new一个对象【一般用在存在线程安全问题的情况下使用】)

单例(也就是说,无论调用多少次都只有一个实例对象【只被创建你一次,对象只会初始化一侧】)

对象被创建多次,执行效率就会比较低,所以我们一般情况采用单例

所以我们要将这个工厂改造成单例的

 /**
  * 一个创建Bean对象的工厂
  *
  * Bean:在计算机英语中,有可重用组件的含义。
  * JavaBean:用java语言编写的可重用组件。
  *     javvabean >> 实体类(javabean远远大于实体类)
  * 他就是创建我们的service和dao对象的。
  *
  * 第一个:需要一个配置文件来配置我们的service和dao
  *     配置的内容:唯一标识=全限定类名(key=value)
  * 第二个:通过读取配置文件中配置内容,反射创建对象
  *
  * 我的配置文件可以是xml也可以是properties
  */
 public class BeanFactory {
     //定义一个Properties对象
     private static Properties props;
     //创建一个HashMap容器
     private static Map<String ,Object> beans;
 
     //使用静态代码块位properties对象赋值
     static{
         try {
             //实例化对象
             props = new Properties();
             //获取properties文件的流对象
             //InputStream in = new FileInputStream("");     //这是要输入具体地址,一般要采用相对地址,也就是下面这个
             InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
             props.load(in);
             //实例化容器
             beans = new HashMap<String, Object>();
             //取出配置文件中所有的key
             Enumeration keys = props.keys();
             //遍历枚举
             while(keys.hasMoreElements()){
                 //取出每一个key
                 String key = keys.nextElement().toString();
                 //根据key获取value
                 String beanPath = props.getProperty(key);
                 //通过反射创建对象
                 Object value = Class.forName(beanPath).newInstance();
                 //把key和value存入容器中
                 beans.put(key ,value);
            }
        }catch (Exception e){
             System.out.println("错误");
        }
    }
 
     public static Object getBean(String beanName){
         return beans.get(beanName);
    }
 
 }

image-20200803230351756

1.3、IOC控制反转

传统方式

image-20200803234422204

IOC方式

image-20200803234345298

控制反转(Inversion of Control,英文缩写IOC)把创建对象的权利交给框架,是框架的重要特征,并非面向对象编程的专业术语,它包括依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。

除main函数之外,其他代码都在【1.1、bean工厂】中。

Client

 public class Client {
 
     /**
      * 获取spring的IOC核心容器,并根据id获取对象
      *
      * ApplicationContext的三个常用实现类
      *     ClassPathXmlApplicationContext     --> 它可以加载路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了【相对路径】
      *     FileSystem.XmlApplicationContext   --> 它可以加载磁盘任意路径下的配置文件(必须要有访问权限)【绝对路径】
      *     AnnotationConfigApplicationContext --> 它是用于读取注解创建容器的
      *
      * 核心容器的两个接口引发的问题
      * ApplicationContext:
      *     它在构建核心容器时,创建对象采取的策略时采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。(单例时)
      *     它在构建核心容器时,创建对象采取的策略时采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。(原型时)
      * BeanFactory:
      *     它在构建核心容器时,创建对象采取的策略时采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
      * @param args
      */
 
     public static void main(String[] args) {
         //1.获取核心容器对象
         //ApplicationContext context = new FileSystemXmlApplicationContext("D:\\idea-workspace\\my-site\\spring02-study\\spring-01\\src\\main\\resources\\bean.xml");
         ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
         //2.根据id查找对应的bean实例
         //UserService userService = (UserService) context.getBean("userService");
         UserService userService = context.getBean("userService", UserService.class);
         userService.queryUser();
    }
 
 }

 

 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
         https://www.springframework.org/schema/beans/spring-beans.xsd">
     <bean id="userMapper" class="com.gzk.mapper.UserMapperImpl"/>
 beans>

1.4、bean的三种创建方式

 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
         https://www.springframework.org/schema/beans/spring-beans.xsd">
 
     
     
     
     
 
     
 
 
     
 
 
 beans>

1.5、bean对象的作用范围

bean标签的scopc属性:

  • 作用:用于指定bean的作用范围

  • 取值:

    • singleton:单例的(默认值)

    • prototype:原型的

    • request:作用于web应用的请求范围

    • session:作用于web应用的会话范围

    • global-session:作用于集群环境的会话范围【当有多台服务器时(全局会话范围)】,当不是集群环境时,它就是session

 <bean id="userService" class="com.gzk.service.UserServiceImpl" scope="prototype"/>

 

bean对象的生命周期

  • 单例对象:

    • 出生:当容器创建时对象出生

    • 活着:只要容器还在,对象一直活着

    • 死亡:容器销毁,对象消亡

    • 总结:单例对象的生命周期和容器相同

  • 原型对象(多例):

    • 出生:当我们使用对象时spring框架为我们创建

    • 活着:对象只要是在使用过程中就一直活着

    • 死亡:当对象长时间不用,且没有别的对象引用时,由Java垃圾回收器回收

 public class AccountServiceImpl implements AccountService {
 
     public AccountServiceImpl(){
         System.out.println("AccountServiceImpl的无参构造函数");
    }
 
     public void init(){
         System.out.println("对象初始化!");
    }
     public void getAccountService(){
         System.out.println("方法执行中");
    }
     public void destroy(){
         System.out.println("对象销毁!");
    }
 }
 public interface AccountService {
     void init();
     void getAccountService();
     void destroy();
 }
 
 public class Client {
     public static void main(String[] args) {
         ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
         AccountService accountService = null;
         for(int i=0;i<5;i++) {
             accountService = context.getBean("accountService", AccountService.class);
             System.out.println(accountService);
             accountService.getAccountService();
             System.out.println();
        }
 }

 

 
 <bean id="accountService" class="com.gzk.service.AccountServiceImpl" scope="singleton" init-method="init" destroy-method="destroy"/>
 
 
 

 

 

单例:

image-20200804104107871

原型:

image-20200804104009413

1.6、spring的依赖注入

依赖注入:Dependency Injection

IOC的作用:

  • 降低程序间的耦合(依赖关系)

依赖关系的管理:

  • 以后都交给spring来维护

在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明

依赖注入:

  • 能注入的数据:有三类

    • 基本类型和String

    • 其他bean类型(在配置文件中或者注解配置过的bean)

    • 复杂类型/集合类型

  • 注入的方式:有三种

    • 使用构造函数

    • 使用set方法

    • 使用注解

1.6.1、构造函数注入:

  • 使用的标签:constructor-arg

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

  • 标签中的属性

    • type:用于指定要注入大的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型

    • index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始

    • name:用于指定给构造函数中指定名称的参数赋值 =====以上三个用于指定给构造函数中某些参数赋值=====

    • value:用于提供基本类型和String类型的数据

    • ref:用于指定其他的bean类型数据。他指的就是再spring的IOC核心容器中出现过的bean对象

  • 优势:

    • 在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。

 <bean id="constructorService" class="com.gzk.service.ConstructorService">
         <constructor-arg index="0" value="111"/>
         <constructor-arg index="1" value="张三"/>
         <constructor-arg index="2" ref="dateTime"/>
     bean>
 
     <bean id="dateTime" class="java.util.Date"/>

 

1.6.2、set方法注入

  • 使用的标签:property

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

  • 标签中的属性

    • name:用于指定注入时所调用的set方法

    • value:用于提供基本类型和String类型的数据

    • ref:用于指定其他的bean类型数据。他指的就是再spring的IOC核心容器中出现过的bean对象

  • 优势

    • 创建对象时,没有明确的限制

  • ss

  •  

 <bean id="constructorService2" class="com.gzk.service.ConstructorService2">
         <property name="id" value="111"/>
         <property name="name" value="张三"/>
         <property name="dateTime" ref="dateTime"/>
     bean>
     
     <bean id="dateTime" class="java.util.Date"/>

 

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

    • 用于给List结构【set,List,String[]】集合注入的标签:

      • list array set【三者可以混用,无明确规定】

    • 用于给Map结构【Map,Properties】集合注入的标签:

      • map props【二者可以混用,无明确规定】

 public class SetService {
     private String name;
     private String[] stringList;
     private List<String> list;
     private Set<String> set;
     private Map<String ,Object> map;
     private Properties properties;
 
     public void setName(String name) {
         this.name = name;
    }
 
     public void setStringList(String[] stringList) {
         this.stringList = stringList;
    }
 
     public void setList(List<String> list) {
         this.list = list;
    }
 
     public void setSet(Set<String> set) {
         this.set = set;
    }
 
     public void setMap(Map<String, Object> map) {
         this.map = map;
    }
 
     public void setProperties(Properties properties) {
         this.properties = properties;
    }
 
     @Override
     public String toString() {
         return "SetService{" +
                 "name='" + name + '\'' +
                 ", stringList=" + Arrays.toString(stringList) +
                 ", list=" + list +
                 ", set=" + set +
                 ", map=" + map +
                 ", properties=" + properties +
                 '}';
    }
 }

 

 <bean id="setService" class="com.gzk.service.SetService">
     <property name="name" value="张三"/>
     <property name="stringList">
         <list>
             <value>AAAvalue>
             <value>BBBvalue>
             <value>CCCvalue>
         list>
 
     property>
 
     <property name="list">
         <array>
             <value>AAAvalue>
             <value>BBBvalue>
             <value>CCCvalue>
         array>
     property>
 
     <property name="set">
         <set>
             <value>AAAvalue>
             <value>BBBvalue>
             <value>CCCvalue>
         set>
     property>
 
     <property name="map">
         <props>
             <prop key="1">DDDprop>
             <prop key="2">EEEprop>
             <prop key="3">FFFprop>
         props>
 
     property>
 
     <property name="properties">
         <map>
             <entry key="1" value="DDD"/>
             <entry key="2">
                 <value>EEEvalue>
             entry>
         map>
     property>
 bean>

 

1.6.3、注解注入

  • 用于创建对象的

    • 它们的作用就和在XML配置文件中编写一个标签实现的功能是一样的

    • @Component:

      • 作用:用于把当前类对象存入spring容器中

      • 属性:

        • value:用于指定bean的id。当我们不写时,默认为当前类名,且首字母小写

    • @Controller:一般用于表现层

    • @Service:一般用于业务层

    • @Repository:一般用于持久层

    • 以上三个注解的作用和属性与Component是一摸一样的

    • 他们三个是spring框架为我们提供明确的三层使用的注解,是我们三层结构更加清晰

    • 在配置注解时,要注意:要在配置文件中配置扫描的包

     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
             https://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/context
             https://www.springframework.org/schema/context/spring-context.xsd">
     
         <context:component-scan base-package="com.gzk"/>
     
     beans>

     

     //@Component("userService")
     @Service("userService")
     public class UserServiceImpl implements UserService {
         private UserMapper userMapper = new UserMapperImpl();
     
         public UserServiceImpl(){
             System.out.println("被创建了");
        }
     
         public void queryUser() {
             userMapper.queryUser();
        }
     }

     

  • 用于注入数据的

    • 他们的作用就和在xml配置文件中的bean标签中写一个标签的作用是一样的

    • @Autowired

      • 作用:自动按照类型注入。只要容器中有唯一的bean对象类型和注入的变量类型匹配,就可以注入成功

      • 出现位置:

        • 可以是变量上,也可以是方法上

      • 细节:

        • 在使用注解注入是,set方法就不是必须的。

      • Autowired查找过程:

        • key存放bean对象的唯一ID值,value存放ID的类型。

        • 第一步,在Map容器中查找value值;(如:UserMapper在下面这个容器有两个,所以,匹配导两个;【注意:如果只匹配导一个,那么就直接取出对应的key值赋值在被注解的参数,不用走第二步了】)

        image-20200804165138558

        • 第二步,在Map容器中查找value对应的key值,(如果有多个value值,则会查出多个key值,那么key值要与id相互匹配,要不然会报错【注意:要是只有一个,ID值与key值可以互不相同】),然后取出对应的key值赋值在被注解的参数。

     @Repository("userMapper")
     public class UserMapperImpl implements UserMapper {
         public void queryUser() {
             System.out.println("查询");
        }
     }

     

     @Service("userService")
     public class UserServiceImpl implements UserService {
     
         @Autowired
         private UserMapper userMapper;
     
         public UserServiceImpl(){
             System.out.println("被创建了");
        }
     
         public void queryUser() {
             userMapper.queryUser();
        }
     }
    • @Qualifier

      • 作用:在按照类中注入的基础之上再按照名称注入。他在给类成员注入时不能单独使用。但是在给方法参数注入时可以

      • 属性

        • value:用于指定注入bean的id

    • @Resource

      • 作用:直接按照bean的id注入。可以单独使用

      • 属性:

        • name:用于指定bean的id

    • 以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。另外,集合类型的注入只能通过XML来实现

    • @Value

      • 作用:用于注入基本类型和String类型的数据

      • 属性:

        • value:用于指定数据的值。他就可以i使用spring中SpEL(也就是sprinng的el表达式)SpEL的写法:${表达式}

  • 用于改变作用范围的

    • 他们的作用就和在bean标签中使用scope属性实现的功能时一样的

    • @Scope:

      • 作用:用于指定bean的作用范围

      • 属性:

        • value:指定范围的取值。常用取值:singleton,prototype

 @Service("userService")
 @Scope("prototype")
 public class UserServiceImpl implements UserService {
 
     @Autowired
     private UserMapper userMapper;
 
     public UserServiceImpl(){
         System.out.println("被创建了");
    }
 
     public void queryUser() {
         userMapper.queryUser();
    }
 }

 

  • 和生命周期相关的

    • 他们的作用就和在bean标签中使用init-method和destroy-method的作用是一样的

    • @PreDestroy

      • 作用:用于指定销毁方法

    • @PostConstruct

      • 作用:用于指定初始化方法

 @Repository("userMapper")
 public class UserMapperImpl implements UserMapper {
     public void queryUser() {
         System.out.println("查询");
    }
 
     @PostConstruct
     public void init(){
         System.out.println("初始化");
    }
 
     @PreDestroy
     public void destroy(){
         System.out.println("销毁");
    }
 }

 

1.7、spring的新注解

  • @Configuration:

    • 作用:指定当前类是一个配置类

    • 细节:

      • 当AnnotationConfigApplicationContext指定某一个配置类时,那个配置类不加@Configuration也可以

      • 或者在SpringConfig配置类中加入@Import注解,指向具体的类时,那个类也可以不加@Configuration注解

      • 以上两种情况都不成立的话,那个配置类就需要加@Configuration注解,且要被扫描到。

 ApplicationContext application = new AnnotationConfigApplicationContext(SpringConfig.class);
  • ttt

  • @ComponentScan

    • 作用:用于通过注解指定spring在创建容器时要扫描的包

    • 属性:

      • valur:他和basePackages的作用一样的,都是用于指定创建容器时要扫描的包。

     <context:component-scan base-package="com.gzk"/>
  • @Bean

    • 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中

    • 属性:

      • name:用于指定bean的id。当不写时,默认值是当前方法的名称

    • 细节:

      • 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。

      • 查找方式和@Autowried注解的作用时一样的

  • @Import

    • 作用:用于导入其他的配置类

    • 属性:

      • value:用于指定其他配置类的字节码

    • 当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类

  • PropertySource

    • 作用:用于指定properties文件的位置

    • 属性:

      • value:指定文件的名称和路径。

        • 关键字:classpath,表示类路径下

纯注解:

可以将xml配置文件删掉,但要自己定义一个config配置文件

1.建立一个包【config】,包中建立一个主配置类SpringConfig

 //在测试时,会将该配置类放入AnnotationConfigApplicationContext(SpringConfig.class),所以@Configuration可有可无
 //@Configuration
 @ComponentScan(basePackages = "com.gzk")
 @Import(JdbcConfig.class)
 @PropertySource("classpath:db.properties")
 public class SpringConfig {
 }

2.建立一个功能子类,专门做特定的事务的:JdbcConfig

 //因为在主配置类中注解了@Import(JdbcConfig.class),所以@Configuration可有可无
 //又因为在主配置类中注解了@PropertySource("classpath:db.properties"),所以,子类继承父类,所以可以通过@Value直接取db.properties中的值。
 public class JdbcConfig {
     @Value("${driver}")
     private String driver;
     @Value("${url}")
     private String url;
     @Value("${name}")
     private String name;
     @Value("${password}")
     private String password;
 
     //配置queryRunner
     @Bean("queryRunner")
     @Scope("prototype")
     public QueryRunner queryRunner(DataSource dataSource) {
         //注入数据源
         return new QueryRunner(dataSource);
    }
 
     //配置数据源
     @Bean(name = "dataSource")
     public DataSource dataSource() {
         ComboPooledDataSource dataSource = new ComboPooledDataSource();
         try {
             //这是连接数据库必要的数据
             dataSource.setDriverClass(driver);
             dataSource.setJdbcUrl(url);
             dataSource.setUser(name);
             dataSource.setPassword(password);
        } catch (PropertyVetoException e) {
             e.printStackTrace();
        }
         return dataSource;
    }
 
 }

3.测试类

 public class UserTest {
 //   private ApplicationContext application = new ClassPathXmlApplicationContext("bean.xml");
     private ApplicationContext application = new AnnotationConfigApplicationContext(SpringConfig.class);
     private UserService userService = application.getBean("userService" ,UserService.class);
 
     @Test
     public void queryUserList() {
         List<User> userList = userService.queryUserList();
         for (User user : userList){
             System.out.println(user);
        }
    }
 
     @Test
     public void queryUser() {
         System.out.println(userService.queryUser(1));
    }
 
     @Test
     public void updateUser() {
         User user = new User(5 ,"张三" ,"123" ,"[user:update]");
         boolean flag = false;
         if(userService.updateUser(user) > 0){
             flag = true;
        }
         System.out.println(flag);
    }
 
     @Test
     public void insertUser() {
         User user = new User(5 ,"张三" ,"123456789" ,"user:add");
         boolean flag = false;
         if(userService.insertUser(user) > 0){
             flag = true;
        }
         System.out.println(flag);
    }
 
     @Test
     public void deleteUser() {
         boolean flag = false;
         if(userService.deleteUser(5) > 0){
             flag = true;
        }
         System.out.println(flag);
    }
 
 }

 

你可能感兴趣的:(spring-ioc笔记)