zoukankan      html  css  js  c++  java
  • 三、spring成长之路——springIOC容器详解(上)

    一、springIOC

    控制反转和依赖注入

    ​ 简单的说就是将对象的创建,属性的的设置交给spring容器进行管理,而不再由用户自己创建,当用户需要使用该接口或者类的时候,直接注入就可以了,spring容器会自动帮助用户创建对象。

    1.创建maven应用程序

    【pom.xml】

    ​ 1.引入spring依赖,junit依赖

    ​ 2.引入maven插件——java编译插件

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.itcloud</groupId>
      <artifactId>resource</artifactId>
      <version>1.0-SNAPSHOT</version>
      <packaging>jar</packaging>
      <name>resource</name>
      <url>http://maven.apache.org</url>
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>
    
      <dependencies>
    
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>4.3.15.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
             <version>3.7.0</version>
            <configuration>
              <source>1.8</source>
              <target>1.8</target>
              <encoding>UTF-8</encoding>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </project>
    

    该依赖会下载下面jar

    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    
    • org.springframework:spring-aop:4.3.15.RELEASE
    • org.springframework:spring-beans:4.3.15.RELEASE
    • org.springframework:spring-core:4.3.15.RELEASE
    • org.springframework:spring-expression:4.3.15.RELEASE

    2.springIOC基础

    基本概念:springIOC主要作用是用来管理javaBean的容器,将java对象以及对象和对象之间的关系交由Spring容器管理。

    在没有spring容器之前对接口或者类进行实例化的时候都需要使用new关键字,来进行对象的创建,那么自从有了spring,那么这些事情就交给了spring来做了。

    2.1.了解spring的几种注入方式

    【Teacher.java】

    ​ getter和setter方法在这里都会被省略。

    一个老师对应多个学生,老师pojo类中包含setter注入,和List集合注入

    public class Teacher implements Serializable {
        private Long id;
        private String name;
        private Integer age;
        private List<Student> students;
    }
    

    【Student.java】

    一个学生对应一个老师,学生包含多种注入方式,有setter,Properties类注入,map注入以及构造方法注入

    注意点,

    ​ 1.如果添加了有参构造方法(没有参构造),那么在进行注入的时候必须要进行构造方法的注入

    ​ 2.如果既有有参构造和无参构造可以不进行构造方法的注入

    public class Student implements Serializable {
        private Long id;
        private String name;
        private Teacher teacher;
    
        private Properties pro;
    
        private Map<String,Object> map;
        public Student(){}
        public Student(Long id, String name){
            this.id = id;
            this.name = name;
        }
    }
    

    【applicationContext.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="student" class="com.itcloud.pojo.Student">
            <property name="id" value="1"/>
            <property name="name" value="小明"/>
        </bean>
    
        <bean id="student2" class="com.itcloud.pojo.Student">
            <!-- 构造方法注入 -->
            <!--每一个标签都代表一个构造方法的属性,按照参数在构造方法中的顺序进行注入-->
            <constructor-arg value="2"/>
            <constructor-arg>
                <value>张三</value>
            </constructor-arg>
            
            
            <property name="teacher" ref="teacher" />
    		<!-- map注入 -->
            <property name="map">
                <map>
                    <entry key="1" value="语文" />
                    <entry key="2" value="数学" />
                </map>
            </property>
    	<!-- Properties注入 -->
            <property name="pro">
                <props>
                    <prop key="身高">1.8</prop>
                    <prop key="体重">70kg</prop>
                </props>
            </property>
        </bean>
    
    
        <bean id="teacher" class="com.itcloud.pojo.Teacher">
    
            <property name="id" value="100023" />
            <property name="name" value="王老师" />
            <property name="age" value="30" />
    		<!-- list集合注入 -->
            <property name="students">
                <list>
                    <ref bean="student2" />
                    <ref bean="student" />
                </list>
            </property>
        </bean>
    </beans>
    

    【TestIOC.java】进行数据的测试,debug观察数据

    package com.itcloud.pojo;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestIOC {
    
        //加载spring容器
        private ApplicationContext context =
                new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
    
        @Test
        public void testStudent() {
            //context.getBean()获取spring容器中管理的bean,参数为Id
            Student stu1 = (Student) context.getBean("student");
    
            Student stu2 = context.getBean("student2", Student.class);
    
            Teacher teacher = context.getBean("teacher", Teacher.class);
    
            System.out.println("---------------------");
        }
    }
    
    2.2.p标签和c标签的注入方式

    ​ p代表的就是属性,c代表的就是构造方法。

    添加标签头,引入p和c

    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:c="http://www.springframework.org/schema/c"
    

    实例

    c:id="3" c:name="smith"就是构造方法中的两个参数 id和name,p:teacher-ref="teacher"为类中中的属性teacher

    <bean id="student3" class="com.itcloud.pojo.Student" c:id="3" c:name="smith" p:teacher-ref="teacher"/>
    
    2.3.bean之间的继承关系

    两个关键字段parentabstract

    1.此时student3会继承student中的id,和name属性,这个是parent的作用,非必须标签

    2.abstract表示student这个bean无法被实例化,即无法再代码中获取这个bean,也无法被外部所引用,非必须标签

    ​ 例如:ref="student"是错的

    <bean id="student" class="com.itcloud.pojo.Student" abstract="true">
    	<property name="id" value="1"/>
    	<property name="name" value="小明"/>
    </bean>
    <bean id="student3" class="com.itcloud.pojo.Student" parent="student" p:teacher-ref="teacher"/>
    
    2.4.bean的作用域

    概述

    作用域 描述
    单例(singleton) (默认)每一个Spring IoC容器都拥有唯一的一个实例对象
    原型(prototype) 一个Bean定义,任意多个对象

    scope="singleton"默认值,只会产生一个实例化对象

    scope="prototype"原型,每次获取bean的时候都会获取一个新的实例化对象

    <bean id="student4" class="com.itcloud.pojo.Student" parent="student" p:teacher-ref="teacher" scope="singleton"/>
    
    2.6.bean的生命周期

    两个关键点:

    ​ 1.<bean/>标签中的字段:init-method=""表示bean初始化(构造方法)之后调用的方法 destroy-method=""容器关闭之后调用的方法.

    ​ 2. bean的后置处理器,需要实现方法,BeanPostProcessor这个类,两个方法:

    postProcessBeforeInitialization():在每个bean初始化后(构造方法)调用一次(在init-method方法之前被调用)。

    postProcessAfterInitialization():在init-method之后被调用,destroy-method之前被调用

    实现案例0001

    【LifeCycle.java】

    package com.itcloud.pojo;
    
    public class LifeCycle {
    
        public LifeCycle(){
            System.out.println("构造方法初始化..........");
        }
    
        public void init(){
            System.out.println("init()初始化方法.......");
        }
    
        public void destory(){
            System.out.println("destory()销毁方法.......");
        }
    }
    

    【applicationContext.xml】

    init-method="init" destroy-method="destory"

    <bean id="lifeCycle" class="com.itcloud.pojo.LifeCycle" init-method="init" destroy-method="destory"></bean>
    

    【TestIOC.java】测试

    public class TestIOC {
    
        //加载spring容器
        private ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
    
        @Test
        public void testStudent() {
         
            LifeCycle lifeCycle = context.getBean("lifeCycle", LifeCycle.class);
            context.close();
            //测试结果
    		/*
    信息: Loading XML bean definitions from class path resource [spring/applicationContext.xml]
    构造方法初始化..........
    init()初始化方法.......
    
    四月 08, 2018 10:09:34 上午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
    
    信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@27ddd392: startup date [Sun Apr 08 10:09:33 CST 2018]; root of context hierarchy
    destroy()销毁方法.......
    		*/
        }
    }
    

    第二个关键点实现案例0002

    【CycleProcessor.java】

    package com.itcloud.pojo;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.stereotype.Component;
    
    
    public class CycleProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
            System.out.println("CycleProcessor start....." + name);
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
            System.out.println("CycleProcessor end....." + name);
            return bean;
        }
    }
    

    【TestIOC.java】测试类不变,测试结果:

    /*
    构造方法初始化..........
    CycleProcessor start.....lifeCycle
    init()初始化方法.......
    CycleProcessor end.....lifeCycle
    四月 08, 2018 10:13:31 上午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
    
    信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@58c1670b: startup date [Sun Apr 08 10:13:00 CST 2018]; root of context hierarchy
    destroy()销毁方法.......
    */
    
    2.7.工厂注入(了解即可)

    注意点,如果工厂方法有参数,通过<constructor-arg value="xxx"></constructor-arg>进行参数匹配

    静态工厂注入

    public class StudentFactory {
    
        public static Student getInstance(){
            Student stu = new Student();
            stu.setId(10L);
            stu.setName("小十");
            return stu;
        }
    }
    
    <bean id="student6" class="com.itcloud.pojo.StudentFactory" factory-method="getInstance"></bean>
    

    测试

    Student stu = context.getBean("student6", Student.class);
    

    实例工厂注入

    package com.itcloud.pojo;
    
    public class TeacherFactory {
        public Teacher getInstance(Long id, String name){
            Teacher teacher = new Teacher();
            teacher.setId(id);
            teacher.setName(name);
            return teacher;
        }
    }
    
    <!-- 实例工厂必须单独配置一个bean -->
        <bean id="teacherFactory" class="com.itcloud.pojo.TeacherFactory"/>
        <bean id="teacher2" factory-bean="teacherFactory" factory-method="getInstance">
            <constructor-arg>
                <value>222</value>
            </constructor-arg>
            <constructor-arg name="name" value="张老师" />
    
        </bean>
    

    测试

    Teacher teacher = context.getBean("teacher2", Teacher.class);
    

    FactoryBean配置

    跳转标志

    package com.itcloud.pojo;
    
    import org.springframework.beans.factory.FactoryBean;
    
    public class TeacherFactoryBean implements FactoryBean {
    
        private String name;
    
        @Override
        public Object getObject() throws Exception {
            Teacher teacher = new Teacher();
            teacher.setName(name);
            return teacher;
        }
    
        @Override
        public Class<?> getObjectType() {
            return Teacher.class;
        }
    
        @Override
        public boolean isSingleton() {
            return true;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    
    <!--FactoryBean方法配置bean-->
        <bean id="teacherBean" class="com.itcloud.pojo.TeacherFactoryBean">
            <property name="name" value="李老师"/>
        </bean>
    
    Teacher teacher = context.getBean("teacherBean", Teacher.class);
    

    3.spring注解注入

    ​ 三个注解将类注入到spring容器中,注意点:注解默认注入的Id为当前类的名称首字母小写

    • @Repository主要用于dao,数据访问层
    • @Service用于Service层,调用数据访问层的方法,进行逻辑操作
    • @Component用户普通类的注册,用户自己定义的组件

    我们知道Service层一定会调用dao层的相关方法,dao层已经被注册到Spring容器之中,这是后就需要使用Spring为我们提供的注解来引用对应的实例

    • @Autowired按照类型进行匹配,如果一个接口存在两个子类,可以配合@Qualifier注解来使用
    • @Resource按照名称进行匹配,
    3.1简单应用应用案例

    【applicationContext-annotation.xml】支持注解配置

    <?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:p="http://www.springframework.org/schema/p"
           xmlns:c="http://www.springframework.org/schema/c" 		             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">
    	<!-- 此配置表示com.itcloud包及其子包支持注解配置 -->
        <context:component-scan base-package="com.itcloud">
    		
        </context:component-scan>
    </beans>
    

    【UserDAO.java】

    package com.itcloud.dao;
    
    public interface UserDAO {
        //用户更新
        int update();
    }
    

    【UserDAOImpl.java】注意点:@Repository的value的值默认是(userDAOImpl)

    @Repository
    public class UserDAOImpl implements UserDAO {
    
    
        @Override
        public int update() {
            System.out.println("进行数据库语句编写");
            return 0;
        }
    }
    

    【UserService.java】

    package com.itcloud.service;
    
    public interface UserService {
        int doUpdate();
    }
    

    【UserServiceImpl.java】@Service的value的默认值是:userServiceImpl

    ackage com.itcloud.service.impl;
    
    import com.itcloud.dao.UserDAO;
    import com.itcloud.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDAO userDAO;
    
        @Override
        public int doUpdate() {
            System.out.println("UserServiceImpl update()方法开始....");
            userDAO.update();
            return 0;
        }
    }
    

    测试

    context.getBean("userDAOImpl");//结果:userDAOImpl@127 即:获取到UserDAO的对象
    

    理解bean之间的相互调用

    我们在UserServiceImpl这个类中可以看到如下这句话,这句话表示的意思就是引用外部bean,在没有Spring之前我们引入外部bean的过程是:private UserDAO userDAO = new UserDAOImpl(),在有了Spring之后,spring会自动帮我们进行对象的创建,以及维护对象之间的关系。

     	@Autowired
        private UserDAO userDAO;
    

    测试Autowired

     	@Test
        public void testAnnotation(){
            UserService userService = context.getBean(UserService.class);
            userService.doUpdate();
        }
    //结果
    /*
    	UserServiceImpl update()方法开始....
    	进行数据库语句编写
    */
    

    前面提到,@Autowired是根据类型进行注入的,此时因为UserDAO只有一个子类,但是如果有两个子类要怎么书写呢:

    	//方案一,官方推荐
    	@Autowired
    	@Qualifier("userDAOImpl")
    	private UserDAO userDAO;	
    	//方案二
    	@Resource(name = "userDAOImpl")
        private UserDAO userDAO;
    	//或者
    	@Resource
        private UserDAO userDAOImpl;
    
    3.2理解开启注解支持配置

    【applicationContext-annotation.xml】

    <context:component-scan base-package="com.itcloud">
    
    </context:component-scan>
    

    这里也可以添加子元素,对注解数据进行过滤

    最常用的过滤方式

    <context:component-scan base-package="com.itcloud" use-default-filters="false">
            <!--只包含Service注解的bean,其他注解不会被扫描-->
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        
        </context:component-scan>
    

    use-default-filters="false"不使用默认过滤方式,如果为true的话,<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/> 表示的意思只是将@Service包含进来,其他注解也会包含的

    <!--表示不扫描Repository注解-->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    

    4.spring引用外部属性文件

    4.1 applicationContext.xml文件配置bean并且注入属性文件中的内容

    【value.properties】定义一个外部属性文件

    value.driverName=com.mysql.jdbc.Driver
    value.url=jdbc:mysql://localhost:3306/test
    value.username=root
    value.password=123456
    

    【DataSource.java】定义属性类

    package com.itcloud.value;
    
    public class DataSource {
    
        private String driverName;
    
        private String username;
    
        private String password;
    
        private String url;
        //getter setter方法略
    
    }
    
    

    【applicationContext-annotation.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"
           xmlns:p="http://www.springframework.org/schema/p"
           xmlns:c="http://www.springframework.org/schema/c" 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">
        <context:property-placeholder location="classpath:properties/value.properties"/>
        <bean id="dataSource" class="com.itcloud.value.DataSource">
            <property name="username" value="${value.username}"/>
            <property name="password" value="${value.password}"/>
            <property name="driverName" value="${value.driverName}"/>
            <property name="url" value="${value.url}"/>
        </bean>
    </beans>
    

    此时,当spring容器加载的时候,DataSource.java 被实例化, value.properties属性文件中的内容会被注入到DataSource中。

    4.2不通过配置文件的方式读取属性

    【applicationContext-annotation.xml】开启注解配置

    <context:component-scan base-package="com.itcloud"/>
    <context:property-placeholder location="classpath:properties/value.properties"/>
    <!--<bean id="dataSource" class="com.itcloud.value.DataSource">-->
            <!--<property name="username" value="${value.username}"/>-->
            <!--<property name="password" value="${value.password}"/>-->
            <!--<property name="driverName" value="${value.driverName}"/>-->
            <!--<property name="url" value="${value.url}"/>-->
    
    <!--</bean>-->
    

    【DataSource.java】 此时可以没有setter方法

    package com.itcloud.value;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    @Component
    public class DataSource {
    
        @Value("${value.driverName}")
        private String driverName;
    
        @Value("${value.username}")
        private String username;
    
        @Value("${value.password}")
        private String password;
    
        @Value("${value.url}")
        private String url;
    }
    
  • 相关阅读:
    CDN与缓存的归纳理解
    CSRF介绍与应对以及Java代码示例
    java严格验证日期是否正确的代码
    consul(一)什么是consul
    服务发现--初识Consul
    Consul 快速入门
    spring cloud: 使用consul来替换eureka
    Eureka&Zookeeper&Consul 原理与对比
    服务发现框架选型,Consul还是Zookeeper还是etcd
    腾讯运维10年,我斩获了这5把“杀手锏”
  • 原文地址:https://www.cnblogs.com/hu1056043921/p/8878238.html
Copyright © 2011-2022 走看看