【实战版】Spring IOC 控制反转详解

目录

一、含义

[二、基于 XML 管理 Bean](#二、基于 XML 管理 Bean)

1、准备

[2、配置 Bean](#2、配置 Bean)

[3、获取 Bean](#3、获取 Bean)

[4、Spring 管理数据源](#4、Spring 管理数据源)

5、自动装配

[三、基于注解管理 Bean](#三、基于注解管理 Bean)

1、准备

2、标记

3、扫描

[4、获取 Bean](#4、获取 Bean)

5、自动装配

四、其他知识

[1、Bean 的作用域](#1、Bean 的作用域)

[2、Bean 的生命周期](#2、Bean 的生命周期)

[3、Bean 的后置处理器](#3、Bean 的后置处理器)

4、FactoryBean


一、含义

把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。

二、基于 XML 管理 Bean

1、准备

创建 Maven 工程,pom.xml 添加依赖。

XML 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.1</version>
    </dependency>
</dependencies>

com.example.pojo 包下,创建实体类 Teacher、Clazz、Student。

java 复制代码
public class Teacher { //Alt+Insert,一键生成构造器、Get、Set、toString 方法...

    private Integer tid;
    private String tname;

    public Teacher() {
    }
    public Teacher(Integer tid, String tname) {
        this.tid = tid;
        this.tname = tname;
    }

    public void setTid(Integer tid) {
        this.tid = tid;
    }
    public void setTname(String tname) {
        this.tname = tname;
    }
    public Integer getTid() {
        return tid;
    }
    public String getTname() {
        return tname;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "tid=" + tid +
                ", tname='" + tname + '\'' +
                '}';
    }

}


public class Clazz {

    private Integer cid;
    private String cname;
    private List<Student> students;

    public Clazz() {
    }
    public Clazz(Integer cid, String cname) {
        this.cid = cid;
        this.cname = cname;
    }

    public Integer getCid() {
        return cid;
    }
    public String getCname() {
        return cname;
    }
    public List<Student> getStudents() {
        return students;
    }
    public void setCid(Integer cid) {
        this.cid = cid;
    }
    public void setCname(String cname) {
        this.cname = cname;
    }
    public void setStudents(List<Student> students) {
        this.students = students;
    }

    @Override
    public String toString() {
        return "Clazz{" +
                "cid=" + cid +
                ", cname='" + cname + '\'' +
                ", students=" + students +
                '}';
    }

}


public class Student {

    private Integer sid;
    private String sname;
    private Integer age;
    private String gender;
    private Clazz clazz;
    private String[] hobby;
    private Map<String,Teacher> teacherMap;

    public Student() {
    }
    public Student(Integer sid, String sname, Integer age, String gender) {
        this.sid = sid;
        this.sname = sname;
        this.age = age;
        this.gender = gender;
    }

    public Integer getSid() {
        return sid;
    }
    public String getSname() {
        return sname;
    }
    public Integer getAge() {
        return age;
    }
    public String getGender() {
        return gender;
    }
    public Clazz getClazz() {
        return clazz;
    }
    public String[] getHobby() {
        return hobby;
    }
    public Map<String, Teacher> getTeacherMap() {
        return teacherMap;
    }
    public void setSid(Integer sid) {
        this.sid = sid;
    }
    public void setSname(String sname) {
        this.sname = sname;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public void setClazz(Clazz clazz) {
        this.clazz = clazz;
    }
    public void setHobby(String[] hobby) {
        this.hobby = hobby;
    }
    public void setTeacherMap(Map<String, Teacher> teacherMap) {
        this.teacherMap = teacherMap;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", clazz=" + clazz +
                ", hobby=" + Arrays.toString(hobby) +
                ", teacherMap=" + teacherMap +
                '}';
    }

}

2、配置 Bean

右击目录 resources,依次选择 New -> XML Configuration File -> Spring Config,创建 Spring 配置文件 applicationContext.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: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/util
       https://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">

    <!--
        bean 标签:用于配置 bean,将对象交给 IOC 容器管理,其中
        id 用于设置 bean 的唯一标识;class 用于设置 bean 对应类型的全类名。
    -->
    <bean id="student" class="com.example.pojo.Student">
        <!--
            property 标签:用于给对象的属性赋值,其中 name 用于设置属性名;value 用于设置属性值;
            ref 用于设置要引用的 IOC 容器中 bean 的 id,从而将 bean 对应对象赋值给当前属性。
            此时,Spring 使用 bean 对应类型的全类名,通过反射调用无参构造创建对象,再通过 setXxx() 为对象的属性赋值。
        -->
        <property name="sid" value="1001"></property>
        <property name="sname" value="张三"></property>
        <!-- 为属性赋 null 值 -->
        <property name="age">
            <null />
        </property>
        <!-- 为属性赋含有 >、<等符号 的值,使用 CDATA 节将内容原样解析 -->
        <property name="gender">
            <value><![CDATA[<男>]]></value>
        </property>
        <!-- 通过引用外部已声明的 bean,为属性赋 类类型 的值 -->
        <!--<property name="clazz" ref="clazz"></property>-->
        <!-- 通过内部 bean,为属性赋 类类型 的值 -->
        <property name="clazz">
            <!-- 内部 bean 仅用于给属性赋值,不能在外部通过 IOC 容器获取,因此可以省略 id 属性 -->
            <bean id="clazzInner" class="com.example.pojo.Clazz">
                <property name="cid" value="3002"></property>
                <property name="cname" value="飞船班"></property>
            </bean>
        </property>
        <!-- 为属性赋 数组类型 的值 -->
        <property name="hobby">
            <array>
                <value>抽烟</value>
                <value>喝酒</value>
                <value>烫头</value>
            </array>
        </property>
        <!-- 通过引用外部已声明的集合类型的 bean,为属性赋 Map 集合类型 的值 -->
        <!--<property name="teacherMap" ref="teacherMap"></property>-->
        <!-- 通过 map 标签,为属性赋 Map 集合类型 的值 -->
        <property name="teacherMap">
            <map>
                <entry key="2001" value-ref="teacherOne"></entry>
                <entry key="2002" value-ref="teacherTwo"></entry>
            </map>
        </property>
    </bean>

    <bean id="clazz" class="com.example.pojo.Clazz">
        <property name="cid" value="3001"></property>
        <property name="cname" value="火箭班"></property>
        <!-- 通过引用外部已声明的集合类型的 bean,为属性赋 List 集合类型 的值 -->
        <!--<property name="students" ref="studentList"></property>-->
        <!-- 通过 list 标签,为属性赋 List 集合类型 的值 -->
        <property name="students">
            <list>
                <ref bean="student"></ref>
            </list>
        </property>
    </bean>

    <!-- 通过 util 约束,配置集合类型的 bean -->
    <util:list id="studentList">
        <ref bean="student"></ref>
    </util:list>

    <!-- 通过 util 约束,配置集合类型的 bean -->
    <util:map id="teacherMap">
        <entry key="2001" value-ref="teacherOne"></entry>
        <entry key="2002" value-ref="teacherTwo"></entry>
    </util:map>

    <bean id="teacherOne" class="com.example.pojo.Teacher">
        <!--
            constructor-arg 标签:用于指定有参构造,其中 name 用于设置参数名;value 用于设置参数值。
            此时,Spring 使用 bean 对应类型的全类名,通过反射调用与 constructor-arg 标签参数匹配的有参构造创建对象。
        -->
        <constructor-arg name="tid" value="2001"></constructor-arg>
        <constructor-arg name="tname" value="李白"></constructor-arg>
    </bean>

    <bean id="teacherTwo" class="com.example.pojo.Teacher">
        <property name="tid" value="2002"></property>
        <property name="tname" value="陶渊明"></property>
    </bean>

</beans>

要点精炼:基于 XML 配置 Bean,依赖注入之 setter 注入、依赖注入之构造器注入,为属性赋字面量类型的值、null 值、含有特殊符号的值、类类型的值、数组类型的值、List 集合类型的值、Map 集合类型的值。

3、获取 Bean

根据 id、类型、id & 类型,从 IOC 容器中获取 Bean 对象。

java 复制代码
//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//(1)根据 id,从 IOC 容器中获取 bean 对象
Student studentOne = (Student) ioc.getBean("student");
/*
    (2)根据类型,从 IOC 容器中获取 bean 对象
    注意:要求容器中有且仅有一个类型匹配的 bean,否则报错 NoSuchBeanDefinitionException/NoUniqueBeanDefinitionException;
         满足 bean 唯一性的前提下,通过 bean 的类型、bean 所继承的类的类型、bean 所实现的接口的类型均可获取 bean 对象。
*/
Student studentTwo = ioc.getBean(Student.class);
//(3)根据类型 & id,从 IOC 容器中获取 bean 对象
Teacher teacher = ioc.getBean("teacherOne", Teacher.class);

4、Spring 管理数据源

pom.xml 添加依赖。

XML 复制代码
<!-- MySQL驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.16</version>
</dependency>
<!-- 数据源 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.31</version>
</dependency>

右击目录 resources,依次选择 New -> Resource Bundle,创建属性配置文件 jdbc.properties。

java 复制代码
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2b8
jdbc.username=root
jdbc.password=root
jdbc.initialSize=10
jdbc.maxActive=20
jdbc.maxWait=5000

Spring 配置文件 applicationContext.xml,配置数据源。

XML 复制代码
<!-- 配置 Alibaba Druid 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
	<property name="driverClassName" value="${jdbc.driver}"></property>
	<property name="url" value="${jdbc.url}"></property>
	<property name="username" value="${jdbc.username}"></property>
	<property name="password" value="${jdbc.password}"></property>
	<!-- 数据库连接池,初始化连接数,默认为 0 -->
	<property name="initialSize" value="${jdbc.initialSize}"></property>
	<!-- 数据库连接池,最大连接数,默认为 8 -->
	<property name="maxActive" value="${jdbc.maxActive}"></property>
	<!-- 从数据库连接池获取连接的最大等待时间,默认为 -1(无限等待) -->
	<property name="maxWait" value="${jdbc.maxWait}"></property>
</bean>

<!-- 引入外部属性文件 -->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>

根据类型,从 IOC 容器中获取数据源。

java 复制代码
//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//根据类型,从 IOC 容器中获取 Druid 数据库连接池对象
DruidDataSource dataSource = ioc.getBean(DruidDataSource.class);
//从数据库连接池,获取数据库连接对象
System.out.println(dataSource.getConnection());

5、自动装配

IOC 容器按照既定规则,匹配某个 bean,为指定 bean 依赖的类类型或接口类型属性赋值的过程。

通过 bean 标签的属性 autowire 设置自动装配的规则,值 no、default 表不装配,属性使用默认值;byType 表根据 setter 参数类型匹配 bean,为属性赋值,无匹配则不装配,多个匹配则抛 NoUniqueBeanDefinitionException;byName 表截取 setter 名称作为 bean 的 id 匹配 bean,为属性赋值,无匹配则不装配。

值得注意的是,基于 XML 配置 Bean,设置 autowire 为 byType / byName,Spring 会先调用无参构造创建对象,再 set 注入,为对象类类型或接口类型的属性赋值;字符串、数字等字面量类型的属性,跳过不赋值,需手动 property 赋值。

三、基于注解管理 Bean

1、准备

创建 Maven 工程,pom.xml 添加依赖。

XML 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.1</version>
    </dependency>
</dependencies>

2、标记

com.example.controller 包下,创建类 UserController,添加注解 @Controller 将其标识为控制层组件。

java 复制代码
@Controller
public class UserController {

    @Autowired
    private UserService userService;

    //添加用户信息
    public void saveUser(){
        userService.saveUser();
    }

}

com.example.service 包下,创建接口 UserService;com.example.service.impl 包下,创建实现类 UserServiceImpl,添加注解 @Service 将其标识为业务层组件。

java 复制代码
public interface UserService {
    //保存用户信息
    void saveUser();
}

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public void saveUser() {
        userDao.saveUser();
    }

}

com.example.dao 包下,创建接口 UserDao;com.example.dao.impl 包下,创建实现类 UserDaoImpl,添加注解 @Repository 将其标识为持久层组件。

java 复制代码
public interface UserDao {
    //保存用户信息
    void saveUser();
}

@Repository
public class UserDaoImpl implements UserDao {

    @Override
    public void saveUser() {
        System.out.println("保存成功!");
    }

}

此外,注解 @Component 用于将类标识为普通组件,@Controller、@Service、@Repository 都是其派生注解,基本功能一致,仅语义分层区别,方便开发者区分代码层次;注解只是做标记,为了让 Spring 知道开发者在哪些地方标记了什么注解,还需要配置组件扫描。

3、扫描

右击目录 resources,依次选择 New -> XML Configuration File -> Spring Config,创建 Spring 配置文件 applicationContext.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: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
       https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 扫描组件 -->
    <context:component-scan base-package="com.example">
        <!--
            不扫描谁
            type 用于设置排除依据,值 annotation 表根据注解排除,此时 expression 用于设置要排除的注解的全类名;
                               值 assignable 表根据类型排除,此时 expression 为要排除的类型的全类名;
                               SSM 整合时,springMVC 只扫描控制层,spring 扫描其他层。
        -->
        <!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
        <!--<context:exclude-filter type="assignable" expression="com.example.controller.UserController"/>-->

        <!--
            只扫描谁,此时需要在 context:component-scan 标签中设置属性 use-default-filters 的值为 false
            use-default-filters 属性默认值为 true,表所设置包下的所有类都需要扫描,此时可以使用排除扫描
            use-default-filters 属性设置值为 false,表所设置包下的所有类都不需要扫描,此时可以使用包含扫描
        -->
        <!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
        <!--<context:include-filter type="assignable" expression="com.example.controller.UserController"/>-->
    </context:component-scan>

</beans>

通过 注解 & 扫描 配置的 bean,id 默认为类的小驼峰(如:userController),可通过标识类的注解的 value 属性值设置 bean 的自定义 id(如:@Controller("controller"));此时,Spring 通过反射调用默认无参构造创建对象。

4、获取 Bean

根据 id、类型、id & 类型,从 IOC 容器中获取 Bean 对象。

java 复制代码
//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//(1)根据 id,从 IOC 容器中获取 bean 对象
UserController userController = (UserController) ioc.getBean("userController");
//(2)根据类型,从 IOC 容器中获取 bean 对象
UserService userService = ioc.getBean(UserService.class);
//(3)根据类型 & id,从 IOC 容器中获取 bean 对象
UserDao userDao = ioc.getBean("userDaoImpl", UserDao.class);

5、自动装配

成员变量添加 @Autowired 注解,即可完成自动装配。

Spring 默认根据成员变量的类型,在 IOC 容器中匹配某个 bean,为成员变量赋值。若有多个类型匹配的 bean,则将成员变量的变量名作为 bean 的 id,在 IOC 容器中匹配某个 bean,为成员变量赋值;若有多个类型匹配的 bean,但这些 bean 的 id 均与要赋值成员变量的变量名不一致,则抛 NoUniqueBeanDefinitionException,此时,可以在要赋值成员变量的变量名上添加注解 @Qualifier,通过该注解 value 属性值指定某个 bean 的 id,为成员变量赋值。若没有任何一个类型匹配的 bean,则抛 NoSuchBeanDefinitionException,因为 @Autowired 注解中属性 required 默认值为 true,要求必须完成自动装配,可以将属性 required 设置为 false,此时能装配则装配,无法装配则使用成员变量默认值。

值得注意的是,基于注解配置 Bean,成员变量添加 @Autowired 注解,Spring 会先调用默认无参构造创建对象,再利用反射直接给成员变量赋值,不属于 setter 注入、构造器注入;而 @Autowired 注解也可标记在 set 方法、构造器上,此处不做探讨。

四、其他知识

1、Bean 的作用域

规定 Spring IOC 容器,何时创建对象、创建多少个对象,以及对象的生效范围。基于 XML 管理 Bean,通过 bean 标签的属性 scope 设置 bean 的作用域,值 singleton 表单例(默认)、prototype 表多例(每次获取均为新对象);基于注解管理 Bean,通过注解 @Scope 设置 bean 的作用域,值 ConfigurableBeanFactory.SCOPE_SINGLETON 表单例(默认)、ConfigurableBeanFactory.SCOPE_PROTOTYPE 表多例(每次获取均为新对象)。

2、Bean 的生命周期

bean 为单例,获取 IOC 容器时,创建对象、依赖注入、执行后置处理器的 postProcessBeforeInitialization() 方法、执行初始化方法、执行后置处理器的 postProcessAfterInitialization() 方法;IOC 容器关闭时,执行销毁方法,代码中没有地方持有这个 bean 的引用后,等待 JVM 发生 GC 回收对象。

bean 为多例,获取 bean 时,创建对象、依赖注入、执行后置处理器的 postProcessBeforeInitialization() 方法、执行初始化方法、执行后置处理器的 postProcessAfterInitialization() 方法;不会执行销毁方法,代码中没有地方持有这个 bean 的引用后,等待 JVM 发生 GC 回收对象。

基于 XML 管理 Bean,通过 bean 标签的属性 init-method 设置 bean 的初始化方法,destroy-method 设置 bean 的销毁方法;基于注解管理 Bean,通过注解 @PostConstruct 设置 bean 的初始化方法,@PreDestroy 设置 bean 的销毁方法。

3、Bean 的后置处理器

用于在 Bean 生命周期初始化前后,添加额外操作。创建类 MyBeanPostProcessor 实现 BeanPostProcessor 接口,重写 postProcessBeforeInitialization()、postProcessAfterInitialization() 方法;

java 复制代码
public class MyBeanPostProcessor implements BeanPostProcessor { //Ctrl+O,选择需要实现的方法

    //在 bean 生命周期初始化之前执行,参数 bean 为当前 IOC 容器管理的 bean、beanName 为 bean 的 id
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    //在 bean 生命周期初始化之后执行,参数 bean 为当前 IOC 容器管理的 bean、beanName 为 bean 的 id
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}

Spring 配置文件 applicationContext.xml,配置后置处理器。此处以 XML 方式进行配置,也可使用注解方式配置 bean。

XML 复制代码
<bean class="com.example.process.MyBeanPostProcessor"></bean>

值得注意的是,Bean 的后置处理器不是单独针对某一个 bean 生效,而是针对 IOC 容器中所有 bean 都会执行。

4、FactoryBean

Spring 提供的工厂 Bean 接口,用于封装复杂对象的创建逻辑。com.example.pojo 包下,创建实体类 User。

java 复制代码
public class User {

    private Integer id;
    private String username;
    private String password;
    private Integer age;

    public User() {
    }
    public User(Integer id, String username, String password, Integer age) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }
    public String getUsername() {
        return username;
    }
    public String getPassword() {
        return password;
    }
    public Integer getAge() {
        return age;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                '}';
    }

}

创建类 UserFactoryBean 实现 FactoryBean 接口,泛型配置为最终交给 IOC 容器管理的对象类型,重写 getObject()、getObjectType()、isSingleton() 方法;

java 复制代码
public class UserFactoryBean implements FactoryBean<User> { //指定泛型为最终交给 IOC 容器管理的对象类型

    @Override
    public User getObject() throws Exception {
        //在这里手写复杂创建逻辑!
        return new User(); //返回最终交给 IOC 容器管理的对象
    }

    @Override
    public Class<?> getObjectType() {
        return User.class; //返回最终交给 IOC 容器管理的对象的类型
    }

    @Override
    public boolean isSingleton() {
        return true; //设置最终交给 IOC 容器管理的对象是否单例,true表单例(默认)、false表多例
    }

}

Spring 配置文件 applicationContext.xml,配置 UserFactoryBean。此处以 XML 方式进行配置,也可使用注解方式配置 bean。

XML 复制代码
<bean id="userBean" class="com.example.factory.UserFactoryBean"></bean>

根据 id,从 IOC 容器中获取 Bean 对象。

java 复制代码
//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//根据 id,从 IOC 容器中获取 bean 对象
User user = (User) ioc.getBean("userBean");
//加 & 符号,从 IOC 容器中获取工厂本身 UserFactoryBean 对象
UserFactoryBean userFactoryBean = (UserFactoryBean) ioc.getBean("&userBean");

值得注意的是,BeanFactory 是 IOC 容器最顶层根接口,是 Bean 的工厂,负责创建、获取、管理所有 Bean 对象;而 FactoryBean 是 Spring 提供的工厂 Bean 接口,用于封装复杂对象的创建逻辑,Spring 先实例化 FactoryBean 工厂,再调用 getObject() 获取目标对象放入容器,对外提供使用,工厂本身也可通过 id 加 & 前缀,从 IOC 容器中获取。

本文相关项目,请关注 程序员钟霖 ,回复 CSDN获取。

友情链接

【附安装包】Java/JDK介绍、下载、安装、配置与使用(保姆级教程)_java jdk-CSDN博客

【附安装包】Maven下载、安装、配置与使用(保姆级教程)-CSDN博客

【附安装包】IDEA下载、安装、配置与使用(保姆级教程)_idea下载安装配置教程-CSDN博客

相关推荐
元界metalite1 小时前
Spring-AOP切面越写越多怎么办-8类场景的一条有序处理器链
后端
有米9791 小时前
Logstash的安装和Elasticsearch的整合
后端
newerp1 小时前
reflect.Value 与动态值操作
后端
jinzhe1 小时前
终端bash&zsh中实现基于前缀的历史命令搜索
后端
用户8181870627461 小时前
二、多线程并发篇
后端·程序员
Zane19941 小时前
ThreadLocal 与 BlockingQueue:线程本地变量的内存泄漏原理,以及阻塞队列怎么选?
java·后端
用户3126874877201 小时前
Spring Bean 生命周期到底经历了什么?从实例化到销毁的全链路拆解
java·spring boot
橘色的喵1 小时前
一块 RISC-V MCU 是怎么启动的: 内存保护、中断与从 Flash 直接执行
后端
长栎2 小时前
JDBC 用了 30 年的 Bridge 模式,多数人以为它只是策略模式换了层皮
后端
Flittly2 小时前
【我的手搓轮子日记】(2)handmade-ioc 手搓 IOC 容器
java·spring boot·spring