2.spring5-IOC

IOC

IOC是inversion of Control的简写,译为"控制反转"。它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。

控制反转是一种编程思想,将对象的创建权利和对象之间的关系的维护权交出去,由第三方容器负责。实现控制反转的思想是依赖注入(DI)

IOC 容器

Spring 通过IOC 容器 来管理所有Java 对象的实例化、初始化和组装的对象 ,Spring IoC 容器管理的对象称为bean。Bean初始化信息以及它们之间的依赖关系,反映在容器使用的配置元数据中(xml或者注解)。在容器内部,这些 bean 定义表示为 BeanDefinition 对象。

org.springframework.beans 和 org.springframework.context 包是 Spring Framework IoC 容器的基础

BeanFactory 接口提供了一种高级配置机制,能够管理任何类型的对象。ApplicationContext 是 BeanFactory 的子接口,增加了:

  • 更轻松地与 Spring 的 AOP 特性集成
  • 消息资源处理(用于国际化)
  • 事件发布
  • 应用程序层特定上下文,例如用于 Web 应用程序的 WebApplicationContext。

简而言之,BeanFactory 提供了配置框架和基本功能,而 ApplicationContext 增加了更多企业级功能。

org.springframework.context.ApplicationContext 接口代表 Spring IoC 容器,负责实例化、配置和组装 bean。容器通过读取配置元数据来获取有关要实例化、配置和组装的组件的指令。

配置元数据可以表示为带注解、java类、外部 XML 文件

ApplicationContext 接口的几个实现是核心 Spring 的一部分。在独立应用程序中,通常创建 AnnotationConfigApplicationContext 或 ClassPathXmlApplicationContext 的实例。

依赖注入

DI (Dependency Injection) : 依赖注入,依赖注入实现了控制反转的思想。指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。依赖注入常见的实现方式包括两种:

  • 第一种: set注入

  • 第二种: 构造注入

Bean管理说的是: Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)

IOC的实现过程

Spring中的容器是通过map来实现的,具体是ConcurrentHashMap,默认大小是256,主要存储Bean的描述类

  1. 在bean.xml加载后生产BeanDefinition对象,此对象为bean的描述对象.

  2. 在spring中,有BeanDefinitionReader一个接口,通过这个接口实现不同的类来加载bean.xml配置文件。

    上面的例子就是通过XmlBeanDefinitionReader类来加载bean.xml文件。

    java 复制代码
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }
  3. 加载完成bean.xml配置之后会创建BeanDefinition对象,然后通过BeanFactory工厂+反射来进行实例化的。BeanFactory接口下分别有三种接口:ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory,上述示例中是通过ListableBeanFactory工厂进行创建对象的。

    AbstractRefreshableApplicationContext.java内容:

    java 复制代码
    protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            //把创建bean对象,然后放入容器中.
            loadBeanDefinitions(beanFactory);
            this.beanFactory = beanFactory;
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

基于xml管理Bean

xml配置

别名

和id一样,只是起另一个别名。同样可以获取cat类

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="cat" class="com.light.automatic_assembly.Cat">
        <property name="name" value="哭哭啼啼的大脸猫" />
    </bean>
    <alias name="cat" alias="catNew" />
</beans>

bean

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 
	id:唯一标识符
    class:对象所对应该的全限定名 包名 + 类型
    name: 也是别名
	-->
    <bean id="cat" class="com.light.automatic_assembly.Cat" name="cat1,cat2">
        <property name="name" value="哭哭啼啼的大脸猫" />
    </bean>
</beans>

import

跟导入文件一样,这里是导入配置文件

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <import resource="bean.xml" />
    <import resource="bean2.xml" />
    <import resource="bean3.xml" />
    <!-- 
	id:唯一标识符
    class:对象所对应该的全限定名 包名 + 类型
    name: 也是别名
	-->
    <bean id="cat" class="com.light.automatic_assembly.Cat" name="cat1,cat2">
        <property name="name" value="哭哭啼啼的大脸猫" />
    </bean>
</beans>

获取Bean

java 复制代码
public class TestPerson {
    public void testAdd(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //根据id获取bean
        Person person = (Person) context.getBean("person");
        person.add();

        //根据类型获取bean
        Person bean = context.getBean(Person.class);
        bean.add();

        //根据id和类型获取bean
        Person bean1 = context.getBean("person", Person.class);
        bean1.add();
    }
}

当根据类型获取bean时,要求配置文件中指定类型的bean有且只能有一个。如果有多个会抛出异常:org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.light.www.Person' available: expected single matching bean but found 2: person,person1

expected single matching bean but found 2: person,person1 这句翻译为期望匹配一个单实例的bean, 但是发现了两个:person,person1。

组件类实现接口,获取对象

UserDao.java

java 复制代码
public interface UserDao {
    public void run();
}

配置bean: <bean id="UserDaoImpl" class="com.light.www.UserDaoImpl"/>

UserDaoImpl.java

java 复制代码
public class UserDaoImpl implements UserDao {
    @Override
    public void run() {
        System.out.println("run...");
    }
}
  • 如果组件类实现了接口,根据接口类型可以获取 bean 吗?

    可以,前提是bean唯一

  • 如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类型可以获取 bean 吗?

    不行,因为bean不唯一

依赖注入

book类

java 复制代码
package com.light.di;

public class Book {
    private String name;
    private String author;
    private String other;
    private String xml;

    private String cdata;

    public Book() {
        System.out.println("使用setter注入,首先创建无惨对象,然后调用Setter方法");
    }

    public Book(String name, String author) {
        System.out.println("使用构造器注入,直接使用构造方法注入");
        this.name = name;
        this.author = author;
    }

    public String getOther() {
        return other;
    }

    public void setOther(String other) {
        this.other = other;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getXml() {
        return xml;
    }

    public String getCdata() {
        return cdata;
    }

    public void setCdata(String cdata) {
        this.cdata = cdata;
    }

    public void setXml(String xml) {
        this.xml = xml;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", other='" + other + '\'' +
                ", xml='" + xml + '\'' +
                ", cdata='" + cdata + '\'' +
                '}';
    }
}

基于Setter注入, xml配置:

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="book" class="com.light.di.Book">
        <property name="name" value="java从入门到入坑" />
        <property name="author" value="小毛驴" />
       <!-- null值-->
        <property name="other">
            <null />
        </property>
        <!-- 小于号在XML文档中用来定义标签的开始,不能随便使用 -->
        <!-- 解决方案一: 使用XML实体来代替 -->
        <property name="xml" value="a &lt; b" />
        <!-- C[DATA]节-->
        <property name="cdata">
            <!-- 解决方案二:使用CDATAT -->
            <!-- CDATA中的C代表character,是文本、字符的含义,CDATA就表示纯文本数据 -->
            <!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
            <!-- 所以CDATA节中写什么符号都随意 -->
            <value><![CDATA[a < b]]></value>
        </property>
    </bean>
</beans>

基于构造器注入,xml配置:

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bookCon" class="com.light.di.Book">
        <constructor-arg name="name" value="c++从入门到秃头" />
        <!-- constructor-arg的index表示构造器中参数的位置,必须对应,所以一般使用name比较好-->
        <constructor-arg index="1" value="light" />
    </bean>
</beans>

其他特殊值注入:

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="book" class="com.light.di.Book">
        <!-- 字面量 -->
        <property name="name" value="java从入门到入坑" />
        <property name="author" value="小毛驴" />
       <!-- null值-->
        <property name="other">
            <null />
        </property>
        <!-- 小于号在XML文档中用来定义标签的开始,不能随便使用 -->
        <!-- 解决方案一: 使用XML实体来代替 -->
        <property name="xml" value="a &lt; b" />
        <!-- C[DATA]节-->
        <property name="cdata">
            <!-- 解决方案二:使用CDATAT -->
            <!-- CDATA中的C代表character,是文本、字符的含义,CDATA就表示纯文本数据 -->
            <!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
            <!-- 所以CDATA节中写什么符号都随意 -->
            <value><![CDATA[a < b]]></value>
        </property>
    </bean>
</beans>

测试:

java 复制代码
public class TestBook {

    @Test
    public void testSetter(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-di.xml");
        //setter注入
        Book bean = context.getBean("book", Book.class);
        System.out.println(bean);
        //构造器注入
        Book bookCon = context.getBean("bookCon", Book.class);
        System.out.println(bookCon);
    }

}

对象、数组、集合类型注入

以部门和员工为例:

Employs.java 代码:

java 复制代码
package com.light.di;

public class Employs {
    private String name;
    private Integer age;
    private Dept dept;
    private EmpInfo empInfo;
    public Employs() {
    }

    public Employs(String name, Integer age, Dept dept) {
        this.name = name;
        this.age = age;
        this.dept = dept;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public EmpInfo getEmpInfo() {
        return empInfo;
    }

    public void setEmpInfo(EmpInfo empInfo) {
        this.empInfo = empInfo;
    }

    @Override
    public String toString() {
        return "Employs{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", dept=" + dept +
                ", empInfo=" + empInfo +
                '}';
    }
}

Dept.java

java 复制代码
package com.light.di;

import java.util.List;
import java.util.Map;

public class Dept {
    private String name;
    private List<Employs> employs;
    private Map<String, Leader> leaderMap;

    public Dept() {
    }

    public Dept(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public Map<String, Leader> getLeaderMap() {
        return leaderMap;
    }

    public void setLeaderMap(Map<String, Leader> leaderMap) {
        this.leaderMap = leaderMap;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Employs> getEmploys() {
        return employs;
    }

    public void setEmploys(List<Employs> employs) {
        this.employs = employs;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "name='" + name + '\'' +
                ", employs=" + employs +
                ", leaderMap=" + leaderMap +
                '}';
    }
}

empInfo.java

java 复制代码
package com.light.di;

public class EmpInfo {
    private String province;
    private String city;
    private String county;
    private String detailAddr;
    private String telephone;
    private String Idcard;

    public EmpInfo() {
    }
    public EmpInfo(String province, String city, String county, String detailAddr, String telephone, String idcard) {
        this.province = province;
        this.city = city;
        this.county = county;
        this.detailAddr = detailAddr;
        this.telephone = telephone;
        Idcard = idcard;
    }
    public String getProvince() {
        return province;
    }
    public void setProvince(String province) {
        this.province = province;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getCounty() {
        return county;
    }
    public void setCounty(String county) {
        this.county = county;
    }
    public String getDetailAddr() {
        return detailAddr;
    }
    public void setDetailAddr(String detailAddr) {
        this.detailAddr = detailAddr;
    }
    public String getTelephone() {
        return telephone;
    }
    public void setTelephone(String telephone) {
        this.telephone = telephone;
    }
    public String getIdcard() {
        return Idcard;
    }
    public void setIdcard(String idcard) {
        Idcard = idcard;
    }
    @Override
    public String toString() {
        return "EmpInfo{" +
                "province='" + province + '\'' +
                ", city='" + city + '\'' +
                ", county='" + county + '\'' +
                ", detailAddr='" + detailAddr + '\'' +
                ", telephone='" + telephone + '\'' +
                ", Idcard='" + Idcard + '\'' +
                '}';
    }
}

leader.java

java 复制代码
package com.light.di;

public class Leader {
    private String name;
    private String position;

    public Leader() {
    }

    public Leader(String name, String position) {
        this.name = name;
        this.position = position;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPosition() {
        return position;
    }

    public void setPosition(String position) {
        this.position = position;
    }

    @Override
    public String toString() {
        return "Leader{" +
                "name='" + name + '\'' +
                ", position='" + position + '\'' +
                '}';
    }
}

注入对象xml配置:

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="dept-safe" class="com.light.di.Dept">
        <property name="name" value="安保部" />
    </bean>
    <bean id="employs" class="com.light.di.Employs">
        <!--普通属性注入-->
        <property name="name" value="lucy" />
        <property name="age" value="30" />
        <!--对象属性注入-->
        <!--第一种方式 外部注入: ref="dept" ref表示引入id为dept的对象-->
        <property name="dept" ref="dept-safe" />
        <!-- 级联赋值 -->
        <property name="dept.name" value="市场部" />
        <!-- 第二种方式:内部注入 在property内部在定义个Bean标签-->
        <property name="empInfo" >
            <bean id="empInfo" class="com.light.di.EmpInfo">
                <property name="city" value="xi an" />
                <property name="county" value="xin cheng" />
                <property name="province" value="shanxi" />
                <property name="detailAddr" value="cheng bei 188 number." />
                <property name="idcard" value="EM1002120"/>
                <property name="telephone" value="18828250022" />
            </bean>
        </property>
        <!--数组注入-->
        <property name="hobby">
            <array>
                <value>吃饭</value>
                <value>睡觉</value>
                <value>敲代码</value>
            </array>
        </property>
    </bean>
    <!--员工-->
    <bean id="employs-one" class="com.light.di.Employs">
        <property name="name" value="tech-one" />
        <property name="age" value="30" />
    </bean>
    <bean id="employs-two" class="com.light.di.Employs">
        <property name="name" value="tech-two" />
        <property name="age" value="20" />
    </bean>
    <!--领导-->
    <bean id="leader-one" class="com.light.di.Leader">
        <property name="name" value="张xx" />
        <property name="position" value="技术总监" />
    </bean>
    <bean id="leader-two" class="com.light.di.Leader">
        <property name="name" value="王大壮" />
        <property name="position" value="项目经理" />
    </bean>

    <bean id="dept-tech" class="com.light.di.Dept">
        <property name="name" value="技术部" />
        <!--注入list集合-->
        <property name="employs">
            <list>
                <!--如果list里面是普通的值,那么就可以使用value标签<value></value>-->
                <!--我们这里list里面的对象,所以需要使用ref引入外部类-->
                <ref bean="employs-one"/>
                <ref bean="employs-two" />
            </list>
        </property>
        <!--注入map集合-->
        <!--<property name="leaderMap">
            <map>
                <entry>
                    <key>
                        <value>10020</value>
                    </key>
                     <value>
                        <ref bean="leader-one" />
                    </value>
                </entry>
                <entry>
                    <key>
                        <value>10021</value>
                    </key>
                    <value>
                        <ref bean="leader-two" />
                    </value>
                </entry>
            </map>
        </property>-->
        <property name="leaderMap">
            <map>
                <!--如果是普通的值 直接使用value属性-->
                <entry key="10010" value-ref="leader-two" />
                <entry key="10012" value-ref="leader-two" />
            </map>
        </property>
    </bean>
</beans>

测试:

java 复制代码
package com.light.di;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestBook {

    @Test
    public void testSetter(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-di.xml");
        //setter注入
        Book bean = context.getBean("book", Book.class);
        System.out.println(bean);
        //构造器注入
        Book bookCon = context.getBean("bookCon", Book.class);
        System.out.println(bookCon);
    }

    @Test
    public void testEmploys(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-di-employ.xml");
        Employs bean = context.getBean(Employs.class);
        System.out.println(bean);
    }
    @Test
    public void testDept(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-di-employ.xml");
        Dept bean = context.getBean("dept-tech", Dept.class);
        System.out.println(bean);

    }
}

引入资源

factorys.java

java 复制代码
package com.light.di;

import java.util.List;
import java.util.Map;

public class Factorys {
    private String name;
    private List<Employs> employsList;
    private Map<String, Leader> leaderMap;

    public Factorys() {
    }

    public Factorys(String name, List<Employs> employsList, Map<String, Leader> leaderMap) {
        this.name = name;
        this.employsList = employsList;
        this.leaderMap = leaderMap;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Employs> getEmploysList() {
        return employsList;
    }

    public void setEmploysList(List<Employs> employsList) {
        this.employsList = employsList;
    }

    public Map<String, Leader> getLeaderMap() {
        return leaderMap;
    }

    public void setLeaderMap(Map<String, Leader> leaderMap) {
        this.leaderMap = leaderMap;
    }

    @Override
    public String toString() {
        return "Factorys{" +
                "name='" + name + '\'' +
                ", employsList=" + employsList +
                ", leaderMap=" + leaderMap +
                '}';
    }
}

注入对象xml配置:

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:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--员工-->
    <bean id="employs-one" class="com.light.di.Employs">
        <property name="name" value="tech-one" />
        <property name="age" value="30" />
    </bean>
    <bean id="employs-two" class="com.light.di.Employs">
        <property name="name" value="tech-one" />
        <property name="age" value="30" />
    </bean>
    <!--领导-->
    <bean id="leader-one" class="com.light.di.Leader">
        <property name="name" value="张xx" />
        <property name="position" value="技术总监" />
    </bean>
    <bean id="leader-two" class="com.light.di.Leader">
        <property name="name" value="王大壮" />
        <property name="position" value="项目经理" />
    </bean>
    <!--引入资源-->
    <util:map id="leaders-map">
        <entry key="10010" value-ref="leader-one" />
        <entry key="10011" value-ref="leader-two" />
    </util:map>
    <util:list id="emp-list">
        <ref bean="employs-one" />
        <ref bean="employs-two" />
    </util:list>
    <bean id="factorys" class="com.light.di.Factorys">
        <!--注入属性-->
        <property name="name" value="面包厂"/>
        <property name="leaderMap" ref="leaders-map" />
        <property name="employsList" ref="emp-list" />
    </bean>
    <!--p命名空间-->
    <bean id="factorysp" class="com.light.di.Factorys" p:name="袜子厂" p:leaderMap-ref="leaders-map" p:employsList-ref="emp-list" />
    <!--引入外部资源 构建mysql实例-->
    <context:property-placeholder location="classpath:jdbc.properties" />
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
        <property name="driverClassName" value="${jdbc.driver}" />
    </bean>
</beans>

jdbc.properties

properties 复制代码
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/book?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver

测试

java 复制代码
 @Test
public void testFactorys(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean-import.xml");
    //引入资源
    Factorys foctorys = context.getBean("factorys", Factorys.class);
    System.out.println(foctorys);
    //引入p空间
    Factorys foctorysp = context.getBean("factorysp", Factorys.class);
    System.out.println(foctorysp);
    //引入外部资源
    DruidDataSource dataSource = context.getBean("druidDataSource", DruidDataSource.class);
    System.out.println(dataSource.getUrl());
}

bean生命周期

作用域

在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义参加下表:

作用域 描述
singleton(单例) (默认)为每个 Spring IoC 容器将单个 bean 定义限定为单个对象实例。内存中只有一个对象。
prototype(原型) 将单个 bean 定义限定为任意数量的对象实例。每次都会创建对象,内存中会存在多个对象
request(请求) 将单个 bean 定义限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有其自己的 bean 实例,该实例根据单个 bean 定义创建。仅在 web 感知 Spring ApplicationContext 的上下文中有效。
session(会话) 将单个 bean 定义限定为 HTTP Session 的生命周期。仅在 web 感知 Spring ApplicationContext 的上下文中有效。
application application(应用)
websocket 将单个 bean 定义限定为 WebSocket 的生命周期。仅在 web 感知 Spring ApplicationContext 的上下文中有效。

单例模式示例图

prototype示例图

xml配置

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="factorys" class="com.light.di.Factorys" scope="singleton">
        <!--注入属性-->
        <property name="name" value="面包厂"/>
        <property name="leaderMap" ref="leaders-map" />
        <property name="employsList" ref="emp-list" />
    </bean>
</beans>
生命周期

bean生命周期步骤:

  1. bean对象创建 (调用无参数构造)
  2. 给bean对象设置相关属性 (初始化之前)
  3. bean前置处理器 (调用初始化方法之前执行)
  4. bean对象初始化(调用指定初始化方法)
  5. bean后置处理器(初始化之后执行)
  6. bean对象创建完成了,可以使用了
  7. bean对象销毁(配置指定销毁的方法)
  8. lOC容器关闭了

BeanCycle类

java 复制代码
public class BeanCycle {
    private String name;

    public BeanCycle() {
        System.out.println("1.bean对象创建 (调用无参数构造)");
    }

    public void initMethods(){
        System.out.println("4.bean对象初始化");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("2 给bean对象设置相关属性 (初始化之前)");
    }

    public void destroyedMethod(){
        System.out.println("7.bean对象销毁(配置指定销毁的方法)");
    }

    @Override
    public String toString() {
        return "BeanCycle{" +
                "name='" + name + '\'' +
                '}';
    }
}

MyBeanPost类

java 复制代码
public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("3 bean前置处理器 (调用指定初始化方法之前执行)");
        System.out.println(beanName + "::" + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("5 bean后置处理器 (初始化之后)");
        System.out.println(beanName + "::" + bean);
        return bean;
    }
}

xml配置

必须和BeanCycle类的bean的xml写在一起

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bean-cycle" class="com.light.cycle.BeanCycle" init-method="initMethods" destroy-method="destroyedMethod">
        <property name="name" value="测试数据"/>
    </bean>
    <bean id="my-bean-post" class="com.light.cycle.MyBeanPost"/>
</beans>

FactoryBean

FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObiect()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。

java:

java 复制代码
public class FactoryBeanExp implements FactoryBean<Employs> {

    @Override
    public Employs getObject() throws Exception {
        return new Employs();
    }

    @Override
    public Class<?> getObjectType() {
        return Employs.class;
    }
}

xml配置:

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="factory-bean" class="com.light.cycle.FactoryBeanExp" />
</beans>

测试:

java 复制代码
@Test
public void testFactoryBeanExp(){
    ApplicationContext context = new ClassPathXmlApplicationContext("factory-bean.xml");
    //引入资源
    Employs employs = (Employs) context.getBean("factory-bean");
    System.out.println(employs);
}

自动装配

根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值。本质还是通过setter方法进行注入

cat类

java 复制代码
public class Cat {
    private String name;

    public Cat() {
    }

    public Cat(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

自动装配类:

java 复制代码
public class AutomaticAssembly {
    private Cat cat;
    private Book book;

    public AutomaticAssembly() {
    }

    public Book getBook() {
        return book;
    }

    public void setBook(Book book) {
        this.book = book;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "AutomaticAssembly{" +
                "cat=" + cat +
                ", book=" + book +
                '}';
    }
}

两种自动装配方式:

  • byType: 根据类型匹配IOC容器中的某个兼容类型的bean,为属性自动赋值

    若在IOC中,没有任何一个兼容类型的bean能够为属性赋值,则该属性不装配,即值为默认值null

    若在IOC中,有多个兼容类型的bean能够为属性赋值,则抛出异常NoUniqueBeanDefinitionException

    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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="cat" class="com.light.automatic_assembly.Cat">
            <property name="name" value="大脸猫"/>
        </bean>
        <bean id="book" class="com.light.di.Book"/>
        <bean id="auto" class="com.light.automatic_assembly.AutomaticAssembly" autowire="byType"/>
    </beans>
  • byName:将自动装配的属性的属性名,作为bean的id在IOC容器中匹配相对应的bean进行赋值. 所以属性名(AutomaticAssembly类中属性名cat和book)必须要和bean中的id(cat, book)一致。

    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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="cat" class="com.light.automatic_assembly.Cat">
            <property name="name" value="大脸猫"/>
        </bean>
        <bean id="book" class="com.light.di.Book"/>
        <bean id="auto" class="com.light.automatic_assembly.AutomaticAssembly" autowire="byName	"/>
    </beans>

基于注解管理Bean

在基于XML的Spring设置中,您可以包含以下配置标签以启用与基于注解的配置的混合和匹配:

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: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:annotation-config/>

</beans>

<context:annotation-config/> 元素隐式注册以下后处理器:

  • ConfigurationClassPostProcessor
  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • PersistenceAnnotationBeanPostProcessor
  • EventListenerMethodProcessor

这些PostProcessor是 Spring让 IOC 容器"看懂注解"的桥梁------容器本身只认识 BeanDefinition,每种注解都需要一个对应的处理器来解析生效。

后置处理器 处理的注解 作用
AutowiredAnnotationBeanPostProcessor @Autowired、@Value、@Inject 在属性填充阶段对字段、setter 方法、任意配置方法执行依赖注入
CommonAnnotationBeanPostProcessor @Resource、@PostConstruct、@PreDestroy JSR-250 支持:资源注入 + 初始化/销毁生命周期回调
ConfigurationClassPostProcessor @Configuration、@Bean、@Import、@ComponentScan 等 解析 Java 配置类,把 @Bean 方法等转化为 BeanDefinition 注册进容器
PersistenceAnnotationBeanPostProcessor @PersistenceContext、@PersistenceUnit JPA 支持:注入 EntityManager 等持久化资源
EventListenerMethodProcessor @EventListener 把标注了 @EventListener 的方法注册为应用事件监听器

注解

从Java5开始,Java 增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。

java 复制代码
格式:@注解名称(属性1=值1...)

Spring从 2.5 版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化 Spring 的 XML配置。

Spring 通过注解实现自动装配的步骤如下:

  1. 引入依赖

  2. 开启组件扫描

    Spring默认不使用注解装配 Bean,因此我们需要在 Spring的 XML 配置中,通过 context:component-scan 元素开启 Spring Beans的自动扫描功能。开启此功能后,Spring 会自动从扫描指定的包(base-package 属性设置)及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。

    有三种方式开启:

    • 全部扫描:某个包下都扫描

      xml 复制代码
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/beans/spring-context.xsd
             http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd">
          <!--
      		要使用注解必须加上:
              1.xmlns:context="http://www.springframework.org/schema/context"
              2. xsi:schemaLocation="
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/beans/spring-context.xsd
             http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd"
      	-->
          <!--全部扫描-->
          <context:component-scan base-package="com.light.www" />
      </beans>
    • 排除组件扫描:排除某些注解,其余都扫描

      xml 复制代码
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/beans/spring-context.xsd
             http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd">
          <!--排序组件扫描-->
          <context:component-scan base-package="com.light.www">
              <!--context:exclude-filter标签:指定排除规则-->
              <!--
              type:设置排除或包含的依据
              type="annotation",根据注解排除,expression中设置要排除的注解的全类名
              type="assignable",根据类型排除,expression中设置要排除的类型的全类名
              -->
              <context:exclude-filter type="annotation" expression="com.light.www.controller"/>
          </context:component-scan>
      </beans>
    • 指定组件扫描:只扫描指定的注解

      xml 复制代码
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/beans/spring-context.xsd
             http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd">
          <!--指定组件扫描-->
          <context:component-scan base-package="com.light.www">
              <!--context:include-fi1ter标签:指定在原有扫描规则的基础上追加的规则-->
              <!-- use-default-fi1ters属性: 取值false表示关闲默认扫描规则-->
              <!-- 此时必须设置use-default-fi1ters="false",因为默认规则即扫描指定包下所有类 -->
              <!--
              type:设置排除或包含的依据
              type="annotation",根据注解指定,expression中设置要指定的注解的全类名
              type="assignable",根据类型指定,expression中设置要指定的类型的全类名
              -->
              <context:include-filter type="annotation" expression="com.light.www.controller"/>
      </beans>
  3. 使用注解定义 Bean

  4. 依赖注入

spring中常用的注解

Spring 作用
@Autowired 自动装配注解
@Component 加上此注解,会使类创建的对象加入到容器。
@Scope("singleton") 作用域
@Qualifier 制定具体的注入
@Value 无等效项
java 复制代码
//相当于<bean id="user" value="..."
//首字母小写
@Component(value = "user")
public class User {

}

Autowrited

单独使用@Autowired注解,默认根据类型装配。【默认是byType】

java 复制代码
//表示此注解可以用到的地方:构造器、方法等
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

	/**
	 * 该注解有一个required属性,默认值是true,表示在注入的时候要求被注入的Bean必须是存在的,如果不存在则报错。
	 * 如果required属性设置为false,表示注入的Bean存在或者不存在都没关系,存在的话就注入,不存在的话,也不报错。
	 */
	boolean required() default true;

}

属性注入:在属性上使用@Autowired注入

java 复制代码
@Controller
public class UserController {
    //@Autowired 让 Spring 容器自动把 UserServiceImpl 的 Bean 注入到 userService 字段中
    @Autowired
    private UserServiceImpl userService;

    public void add(){
        System.out.println("controller add...");
        userService.add();
    }
}

注入流程:

在这段代码里,@Autowired 标注在字段 userService 上,作用是告诉 Spring:启动时自动去容器里找类型匹配的 Bean,并赋值给这个字段。这样 add() 方法里调用 userService.add() 时,不需要手动 new UserServiceImpl(),依赖对象的创建和组装全部交给了 Spring 容器(即控制反转 IoC + 依赖注入 DI)。

注入能生效有两个前提:一是 UserServiceImpl 类上通常标注了 @Service(或通过 @Bean、XML 等方式注册进容器),Spring 组件扫描后会把它的实例纳入容器管理;二是 UserController 本身也必须是 Spring Bean------你贴的代码里没看到 @Controller 注解,如果是完整类建议补上,否则 Spring 不会创建它的实例,@Autowired 也就无从注入。

set注入:在setter方法上使用@Autowired注入

注入方式:

Spring 实例化 UserServiceImpl 之后,反射调用带 @Autowired 的 setUserDao() 方法,把容器里的 UserDaoImpl Bean 作为实参传进去。

java 复制代码
@Service
public class UserServiceImpl implements UserService{
    private UserDaoImpl userDao;

    public UserDaoImpl getUserDao() {
        return userDao;
    }

    @Autowired
    public void setUserDao(UserDaoImpl userDao) {
        this.userDao = userDao;
    }
    
    @Override
    public void add() {
        System.out.println("userservice......");
        userDao.add();
    }
}

构造方法注入

只有一个有参数构造方法,可以无注解注入。如果有多个构造方法,必须写@Autowired注入

JAVA 复制代码
@Service
public class UserServiceImpl implements UserService{
    private UserDaoImpl userDao;

    @Autowired
    public UserServiceImpl(UserDaoImpl userDao) {
        this.userDao = userDao;
    }

    @Override
    public void add() {
        System.out.println("userservice......");
        userDao.add();
    }
}

形参上注入

java 复制代码
@Service
public class UserServiceImpl implements UserService{
    private UserDaoImpl userDao;
    
    public UserServiceImpl(@Autowired UserDaoImpl userDao) {
        this.userDao = userDao;
    }

    @Override
    public void add() {
        System.out.println("userservice......");
        userDao.add();
    }
}

@Autowired注解和@Qualifier注解联合使用 Qualifier根据名称进行注入

java 复制代码
@Service
public class UserServiceImpl implements UserService{
    @Autowired
    @Qualifier(value = "userRedisDaoImpl")
    private UserDao userDao;

//    public UserServiceImpl(@Autowired UserDao userDao) {
//        this.userDao = userDao;
//    }

    @Override
    public void add() {
        System.out.println("user service......");
        userDao.add();
    }
}

Resource注入

Resource注解也可以完成属性注入。那它和@Autowired注解有什么区别?

  • Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性(SR-250标准中制定的注解类型。JSR是Java规范提案)
  • Autowired注解是Spring框架自己的。
  • Resource注解默认根据名称装配(byName),未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解一起使用
  • Resource注解用在属性上、setter方法上。Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。

Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。】

value

@vlaue就是个"填空工具":把配置文件里的值自动填到你的 Java 变量里,不用自己写代码读配置

想象你的代码是一道填空题:private String catalog = ____;。这个空总不能写死吧?数据库地址、密钥、开关参数这些东西,得放到配置文件里,换环境时只改文件、不改代码。

它有三种"填法":

写法 人话 例子
${xxx} 去配置文件读 xxx 这一项 @Value("${catalog.name}")
${xxx:默认值} 去读 xxx,读不到就用冒号后面的默认值兜底 @Value("${catalog.name:defaultCatalog}")
#{表达式} 不读文件了,直接执行一段 SpEL 表达式算出结果 @Value("#{systemProperties['user.catalog'] + 'Catalog'}")

示例:

配置文件 application.properties

properties 复制代码
catalog.name=MovieCatalog
max.recommendations=5
movie.genres=Thriller,Comedy,Action

配置类

java 复制代码
@Configuration
@PropertySource("classpath:application.properties")  // 告诉 Spring 去哪读配置
public class AppConfig {
    // 严格模式开关。
    // 默认是"宽松模式":配置项找不到?Spring 不报错,直接把 "${catalog.name}"
    // 这个字符串原样塞进变量里,程序照常跑------这个坑很隐蔽。
    // 配了这个 bean 之后,任何 ${} 解析不了,Spring 启动直接失败,错误提前暴露。
    // 注意:用 JavaConfig 配置时,@Bean 方法必须是 static!
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    // 自定义类型转换器。
    // Spring 自带的转换只够应付 int、String[] 这种简单类型,
    // 想把字符串转成你自己的类(比如 MovieGenre),就得注册自定义转换器。
    @Bean
    public ConversionService conversionService() {
        DefaultFormattingConversionService service = new DefaultFormattingConversionService();
        service.addConverter(new MyCustomConverter());  // 你自己写的"字符串→自定义类型"转换器
        return service;
    }
}

全部注入姿势

java 复制代码
@Component
public class MovieRecommender {
    // 最基础的用法,注入字符串
    // 结果:catalog = "MovieCatalog"
    private final String catalog;

    // 带默认值。配置文件里没有 not.exist 这一项,
    // 结果:backupCatalog = "defaultCatalog"
    private final String backupCatalog;

    // 自动类型转换。配置里是字符串 "5",自动转成 int
    // 结果:maxRecommendations = 5
    private final int maxRecommendations;

    // 逗号分隔的字符串自动转数组,无需额外工作
    // 结果:genres = ["Thriller", "Comedy", "Action"]
    private final String[] genres;

    // SpEL 表达式,运行时动态计算(取系统属性拼接字符串)
    private final String systemCatalog;

    // SpEL 还能直接注入复杂结构,比如一个 Map
    // 结果:{Thriller=100, Comedy=300}
    private final Map<String, Integer> countOfMoviesPerCatalog;

    public MovieRecommender(
            @Value("${catalog.name}") String catalog,
            @Value("${not.exist:defaultCatalog}") String backupCatalog,
            @Value("${max.recommendations}") int maxRecommendations,
            @Value("${movie.genres}") String[] genres,
            @Value("#{systemProperties['user.catalog'] + 'Catalog'}") String systemCatalog,
            @Value("#{{'Thriller': 100, 'Comedy': 300}}") Map<String, Integer> countOfMoviesPerCatalog) {
        this.catalog = catalog;
        this.backupCatalog = backupCatalog;
        this.maxRecommendations = maxRecommendations;
        this.genres = genres;
        this.systemCatalog = systemCatalog;
        this.countOfMoviesPerCatalog = countOfMoviesPerCatalog;
    }
}

全注解开发

全注解开发就是不再使用spring配置文件了,写一个配置类来代替配置文件。

java 复制代码
package com.light.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.light.www")
public class SpringConfig {
}

测试

java 复制代码
@Test
public void allDevelop(){
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserController bean = context.getBean(UserController.class);
    bean.add();
}

IOC基于 Java配置类来管理Bean

spring4之后,大多数都是使用基于java的容器配置,放弃基于xml的配置。

Spring 配置支持的核心构件是 @Configuration 注解的类和 @Bean 注解的方法。

  • @Bean 注解用于指示方法实例化、配置并初始化一个新对象,该对象将由 Spring IoC 容器管理。您可以将 @Bean 注解的方法与任何 Spring @Component 一起使用。然而,它们最常与 @Configuration Bean 一起使用。
  • 用 @Configuration 注解一个类,表示 Bean 定义的来源。

register方法

使用 register() 方法对容器进行额外的类注册

java 复制代码
public static void main(String[] args) {
	AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
	ctx.register(AppConfig.class, OtherConfig.class);
	ctx.register(AdditionalConfig.class);
	ctx.refresh();
	MyService myService = ctx.getBean(MyService.class);
	myService.doStuff();
}

scan方法

AnnotationConfigApplicationContext 暴露了 scan(String...) 方法以允许相同的组件扫描功能

java 复制代码
public static void main(String[] args) {
	AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
	ctx.scan("com.acme");
	ctx.refresh();
	MyService myService = ctx.getBean(MyService.class);
}

@bean

@Bean 是一个方法级别的注解,直接对应 XML 的 <bean/> 元素。该注解支持 <bean/> 提供的一些属性,例如:

  • init-method
  • destroy-method
  • autowiring
  • name

你可以在 @Configuration 注解的类或 @Component 注解的类中使用 @Bean 注解。

java 复制代码
package com.light.config;

import com.light.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//这个也会Spring容器托管,注册到容器中,因为他本来就是一个@Component,和xml作用一样
@Configuration
//自动扫包
@ComponentScan("com.light.pojo")
//导入其他配置类
@Import(xxxConf2.class,xxxConf3.class)
public class BeanConfig {

    //注册一个bean,就相当于我们之前写的一个xml的bean标签
    //这个方法的名字,就相当于bean标签中的id属性
    //这个方法的返回值,就相当于bean标签中的cLass属性
    @Bean
    public User getUser(){
        return new User();
    }
}

也可以用接口(或基类)返回类型声明你的 @Bean 方法,然而,这限制了高级类型预测对指定接口类型的可见性。

一个 @Bean 注解的方法可以有任意数量的参数,这些参数描述了构建该 bean 所需的依赖项。例如,如果我们的 TransferService 需要一个 AccountRepository,我们可以使用方法参数来实现该依赖项

java 复制代码
@Configuration
public class AppConfig {

	@Bean
	public TransferService transferService(AccountRepository accountRepository) {
		return new TransferServiceImpl(accountRepository);
	}
}

bean也支持接收生命周期回调

任何用 @Bean 注解定义的类都支持常规的生命周期回调,并且可以使用 JSR-250 的 @PostConstruct 和 @PreDestroy 注解

常规的 Spring 生命周期回调也完全支持。如果一个 bean 实现了 InitializingBean、DisposableBean 或 Lifecycle,容器会调用它们各自的方法。

标准的一组 *Aware 接口(例如 BeanFactoryAware、BeanNameAware、MessageSourceAware、ApplicationContextAware 等)也完全支持

@Bean 注解支持指定任意初始化和销毁回调方法,就像 Spring XML 中 bean 元素上的 init-method 和 destroy-method 属性一样,如下例所示:

java 复制代码
public class BeanOne {

	public void init() {
		// initialization logic
	}
}

public class BeanTwo {

	public void cleanup() {
		// destruction logic
	}
}

@Configuration
public class AppConfig {

	@Bean(initMethod = "init")
	public BeanOne beanOne() {
		return new BeanOne();
	}

	@Bean(destroyMethod = "cleanup")
	public BeanTwo beanTwo() {
		return new BeanTwo();
	}
}

使用 @Scope 注解, 默认范围是 singleton

java 复制代码
@Configuration
public class MyConfiguration {

	@Bean
	@Scope("prototype")
	public Encryptor encryptor() {
		// ...
	}
}

IOC原理

java 反射机制

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为ava 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。

要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API

(1) java.lang.Class (2) java.lang.reflect ,所以,Class对象是反射的根源。

相关推荐
微微%5 小时前
从零搭建一个java开发环境
java·idea2023·jdk17
choumin5 小时前
C++ 标准容器为什么不能存引用
c++·容器·引用类型
SimonKing5 小时前
又找到一个纯Java的OCR项目,识别的格式还挺多
java·后端·程序员
lengxuenong5 小时前
区间覆盖问题 · 简明学习手册1
java·学习·算法
?abc!5 小时前
(贪心)621. 任务调度器
java·前端·c#
jason成都5 小时前
rtklib_java PPP-RTK 收官测试:修复 BDS-2 宽巷失效与异常卫星死循环 Bug
java·gnss·rtklib·rtklib_java
Flynn Liang5 小时前
Spring AI ChatClient 详解:system/user/assistant 角色、call 与 stream 到底怎么选
java·人工智能·spring·springai·流式输出·chatclient·java接入大模型
Wang's Blog5 小时前
Java 项目实战: 交友项目 - 喜欢与取消喜欢
java·开发语言·交友
摸鱼仙人~6 小时前
校招帮简历字段匹配架构分析报告
java·前端·架构
钛生智能6 小时前
OA 流程定制全参数详解(七):流程的生命周期——启用、停用、导出、导入
java·办公自动化·oa·协同办公·oa办公系统