Spring基础之IoC

创建Maven工程,导入spring依赖

        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-context</artifactid>
            <version>{spring&#x7248;&#x672C;&#x53F7;}</version>
        </dependency>

lombok可以帮助开发者自动生成实体类相关的方法
假设实体类为Student
引入lombok依赖:

        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
            <version>{lombok&#x7248;&#x672C;&#x53F7;}</version>
        </dependency>
import lombok.Data;

@Data
public class Student {
    private Integer id;
    private String name;
    private Integer age;
}

@Data为自动生成实体类方法
也可以使用如下参数:

参数作用@Getter添加get方法@Setter添加set方法@NoArgsConstructor添加无参构造器@AllArgsConstructor添加有参构造器 创建一个对象: 原生Java写法:

public class Test {
    public static void main(String[] args) {
        Student student=new Student();
        System.out.println(student.toString());
    }
}

Spring写法:
在resource下创建一个文件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" xsi:schemalocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="student" class="test.Student">
</bean></beans>

IoC容器通过读取spring.xml配置文件加载bean标签来创建对象

调用API获取IoC中已经存在的对象
主类:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
        Student student= (Student) applicationContext.getBean("student");
        System.out.println(student.toString());
    }
}

IoC容器创建bean的两种方式:

  • 无参构造函数

使用property给成员变量赋值,在spring.xml下:

    <bean id="student" class="test.Student">
        <property name="id" value="1"></property>
        <property name="name" value="test"></property>
        <property name="age" value="22"></property>
    </bean>
  • 有参构造函数

student类中添加了有参构造函数:

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Student {
    private Integer id;
    private String name;
    private Integer age;
}

使用constructor-arg给构造函数赋值

    <bean id="student" class="test.Student">
       <constructor-arg name="id" value="1"></constructor-arg>
        <constructor-arg name="name" value="test"></constructor-arg>
        <constructor-arg name="age" value="22"></constructor-arg>
    </bean>

也可以不写name,构造函数会根据变量次序依次赋值:

    <bean id="student" class="test.Student">
       <constructor-arg value="1"></constructor-arg>
        <constructor-arg value="test"></constructor-arg>
        <constructor-arg value="22"></constructor-arg>
    </bean>

DI指bean之间的依赖注入,设置对象之间的级联关系

用以下方法可以拿出所有bean名称:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-di.xml");
        String[] names= applicationContext.getBeanDefinitionNames();
        for(String name:names)
        {
            System.out.println(name);
        }
    }
}

创建新的spring-di.xml文件,确立新的bean关系:

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

    <!-- Classes-->
    <bean id="classes" class="test.Classes">
        <property name="id" value="1"></property>
        <property name="name" value="t1"></property>
    </bean>
    <bean id="student" class="test.Student">
        <property name="id" value="1"></property>
        <property name="name" value="test"></property>
        <property name="age" value="22"></property>
        <property name="classes" ref="classes"></property>
    </bean>
</beans>

Main方法:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring_di.xml");
        Classes classes=(Classes) applicationContext.getBean("classes");
        Student student=(Student) applicationContext.getBean("student");
        System.out.println(classes.toString());
        System.out.println(student.toString());
    }
}

bean之间的级联需要使用ref属性完成映射,而不能直接使用value,否则会抛出类型转换异常。

换一种关系,班级实体类如下:

import lombok.Data;

import java.util.List;

@Data
public class Classes {
    private Integer id;
    private String name;
    private List<student> studentList;
}
</student>

bean加入新的学生类后将新加的bean装到list中:

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

    <!-- Classes-->
    <bean id="classes" class="test.Classes">
        <property name="id" value="1"></property>
        <property name="name" value="t1"></property>
        <property name="studentList">
            <list>
                <ref bean="student"></ref>
                <ref bean="student2"></ref>
            </list>
        </property>
    </bean>
    <bean id="student" class="test.Student">
        <property name="id" value="1"></property>
        <property name="name" value="test"></property>
        <property name="age" value="22"></property>
    </bean>
    <bean id="student2" class="test.Student">
        <property name="id" value="2"></property>
        <property name="name" value="test2"></property>
        <property name="age" value="23"></property>
    </bean>
</beans>

bean根据scope来表示bean的作用域,有四种类型:

  • singleton,单例,表示通过Spring容器获取的对象是唯一的,默认值
  • prototype,原型,表示通过Spring容器获取的对象是不同的
  • request,请求,表示在一次Http请求内有效
  • session,会话,表示在一个用户会话内有效

request、session适用于web项目

新建一个实体类User

import lombok.Data;

@Data
public class User {
    private Integer id;
    private String name;
}

配bean:

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

<bean id="user" class="test.User">
    <property name="id" value="1"></property>
    <property name="name" value="test"></property>
</bean>
</beans>

执行主程序:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring_scope.xml");
        User user1=(User) applicationContext.getBean("user");
        User user2=(User) applicationContext.getBean("user");
        System.out.println(user1==user2);
    }
}

singleton模式,该模式下只要加载IoC容器,无论是否从IoC中取出bean,配置文件中的bean都会被创建

加作用域:

<bean id="user" class="test.User" scope="prototype">
    <property name="id" value="1"></property>
    <property name="name" value="test"></property>
</bean>

prototype模式,如果不从IoC中取bean,则不创建对象,取一次就会创建一个对象

Spring的继承不同于Java中的继承,区别:Java中的继承是针对于类的,Spring的继承是针对于对象bean的。

Spring的继承中,子bean可以继承父bean中的所有成员变量的值。

创建一个user2继承于user1:

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

    <bean id="user1" class="test.User">
        <property name="id" value="1"></property>
        <property name="name" value="test"></property>
    </bean>
    <bean id="user2" class="test.User" parent="user1"></bean>
</beans>

主类:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring_scope.xml");
        User user1=(User) applicationContext.getBean("user1");
        User user2=(User) applicationContext.getBean("user2");

        System.out.println(user1.toString());
        System.out.println(user2.toString());
    }
}

运行发现user2继承了user1的值

通过设置bean标签的parent属性建立继承关系,同时子bean可以覆盖父bean的值

不同的类的继承:
建一个Account类:

import lombok.Data;

@Data
public class Account {
    private Integer id;
    private String name;
}

bean:

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

    <bean id="user1" class="test.User">
        <property name="id" value="1"></property>
        <property name="name" value="test"></property>
    </bean>
    <bean id="user2" class="test.User" parent="user1"></bean>
    <bean id="account" class="test.Account" parent="user1"></bean>
</beans>

主类:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring_scope.xml");
        User user1=(User) applicationContext.getBean("user1");
        Account account=(Account) applicationContext.getBean("account");
        System.out.println(user1.toString());
        System.out.println(account.toString());

    }
}

Spring的继承是针对对象的,所以子bean和父bean并不需要属于同一个数据类型,只要其成员变量列表一致即可。

用来设置两个bean的创建顺序。

IoC容器默认情况下是通过spring.xml中bean的配置顺序来决定创建顺序的,配置在前面的bean会先创建

在不更改spring.xml配置顺序的前提下通过设置bean之间以来顺序来调整bean的顺序

    <bean id="user" class="test.User" depends-on="account"></bean>
    <bean id="account" class="test.Account"></bean>

上述先创建account,再创建user

实际开发中,数据库配置一般会单独保存起来,保存到后缀为properties的文件中,方便维护和修改,如果使用Spring来加载数据源,就需要再spring.xml中读取properties中的数据,这就是读取外部资源。

需要在xml文件中加入:

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:jdbc.properties"></context:property-placeholder>

必须加入上面几行不然报错!
所以得到下面的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
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!--导入外部资源-->

    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--SpEL-->
    <bean id="datasource" class="test.DataSource">
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
        <property name="url" value="${url}"></property>
        <property name="driverName" value="${driverName}"></property>
    </bean>
</beans>

xml里必须引入:

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

p命名空间可以用来简化bean的配置:
以student和classes为例:

   <bean id="classes" class="test.Classes" p:id="1" p:name="class1">
   <bean id="student" class="test.Student" p:id="1" p:name="test" p:age="22" p:classes-ref="classes">
</bean></bean>

IoC通过工厂模式创建bean有两种方式:

  • 静态工厂方法
  • 实例工厂方法

区别在于静态工厂不需要实例化,实例工厂需要实例化

例:
创建Car类

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Car {
    private Integer num;
    private String brand;
}

创建静态工厂类

import java.util.HashMap;
import java.util.Map;

public class StaticCarFactory {
    private static Map<integer, car> carMap;
    static {
        carMap=new HashMap<>();
        carMap.put(1,new Car(1,"&#x5965;&#x8FEA;"));
        carMap.put(2,new Car(2,"&#x5965;&#x62D3;"));
    }
    public static Car getCar(Integer num){
        return carMap.get(num);
    }
}

</integer,>

配bean

    <bean id="car" class="test.factory.StaticCarFactory" factory-method="getCar">
        <constructor-arg value="1"></constructor-arg>
    </bean>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.Car;

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring_factory.xml");
        Car car=(Car) applicationContext.getBean("car");
        System.out.println(car.toString());
    }
}

factory-method指向静态方法
constructor-arg的value属性是调用静态方法传入的参数

创建实例工厂类

import java.util.HashMap;
import java.util.Map;

public class InstanceCarFactory {
    private static Map<integer, car> carMap;
    public InstanceCarFactory()
    {
        carMap=new HashMap<>();
        carMap.put(1,new Car(1,"&#x5965;&#x8FEA;"));
        carMap.put(2,new Car(2,"&#x5965;&#x62D3;"));
    }
    public Car getCar(Integer num)
    {
        return carMap.get(num);
    }
}
</integer,>

配bean

    <!--实例工厂-->
    <bean id="instanceCarFactory" class="test.factory.InstanceCarFactory"></bean>
    <!--通过实例工厂获取Car-->
    <bean id="car" factory-bean="instanceCarFactory" factory-method="getCar">
        <constructor-arg value="2"></constructor-arg>
    </bean>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.Car;

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring_factory.xml");
        Car car=(Car) applicationContext.getBean("car");
        System.out.println(car.toString());
    }

区别:
静态工厂方法创建Car对象,不需要实例化工厂对象,因为静态工厂方法不需要创建对象即可调用。

实例工厂方法创建Car对象,需要实例化工厂对象,因为getCar方法是非静态的,就必须实例化对象才能调用,所以必须要创建工厂对象,spring.xml中需要配置两个bean,一个是工厂bean,一个是Car bean

spring.xml 是class+factory-method的形式是直接调用类中的方法
spring.xml是factory-bean+factory-method的形式则是调用工厂bean中的工厂方法,必须先创建工厂方法

自动装载是Spring提供的一种更加简便的方式来完成DI,不需要手动配置property,IoC容器会自动选择bean来完成注入
自动装载有两种方式:

  • byName,通过属性名完成自动装载
  • byType,通过属性对应的数据类型完成自动装载

例:
实体类:

import lombok.Data;

@Data
public class Person {
    private Integer id;
    private String name;
    private Car car;
}

在spring.xml中配置Car和Person对应的bean,并且通过自动装载完成依赖注入。

    <bean id="person" class="test.Person" autowire="byName">
        <property name="id" value="1"></property>
        <property name="name" value="test"></property>
    </bean>
    <bean id="car" class="test.Car">
        <constructor-arg name="num" value="1"></constructor-arg>
        <constructor-arg name="brand" value="&#x5965;&#x8FEA;"></constructor-arg>
    </bean>

主类:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import test.Person;

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring_autowire.xml");
        Person person=(Person) applicationContext.getBean("person");
        System.out.println(person.toString());
    }
}

byType进行自动装载时,必须保证IoC中只有一个符合条件的bean,否则会抛出异常。

Spring IoC的作用是帮助开发者创建项目中所需要的bean,同时完成bean之间的依赖注入关系,DI。
实现该功能有两种方式:

  • 基于xml配置
  • 基于注解

基于注解两步操作:

  • 配置自动扫包
  • 添加注解

例:
建一个实体类,加入@Component标签:

import lombok.Data;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
@Data
@Component
public class Repository {
    private DataSource dataSource;
}

将spring.xml加入扫描范围,自动扫包

<context:component-scan base-package="test"></context:component-scan>

主类运行便可注入

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import test.Repository;

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring_annotation.xml");
        Repository repository=(Repository) applicationContext.getBean("repository");
        System.out.println(repository.toString());
    }
}

配置自动扫包添加注解来注入,类首字母改成小写就是默认的id

换id名则在标签里写id名

@Component(value="id&#x540D;")

将DataSource注入给Repository:
给Repository加@Autowired标签,@Qualifier是byName的形式注入,不加默认为byType

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Data
@Component
public class Repository {
   @Autowired
   @Qualifier(value = "ds")
    private DataSource dataSource;
}

类DataSource并赋值:

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Data
@Component(value = "ds")
public class DataSource {
    @Value("root")
    private String user;
    @Value("root")
    private String password;
    @Value("jdbc:mysql://localhost:3306/library")
    private String url;
    @Value("com.mysql.jdbc.Driver")
    private String driverName;
}

表示将IoC中id为ds的bean注入到repository中
实体类中普通的成员变量(String、包装类)可以通过@Value注解进行赋值

等同于spring.xml中的

<bean id="ds" class="test.DataSource">
    <property name="user" value="root"></property>
    <property name="password" value="root"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/library"></property>
    <property name="driverName" value="com.mysql.jdbc.Driver"></property>
</bean>

实际开发中会将程序分为三层:

  • Controller
  • Service
  • Repositroy(DAO)

关系 Controller–>Service–>Repository

如下分别用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" xsi:schemalocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="controller" class="com.test.controller.MyController">
        <property name="myService" ref="service"></property>
    </bean>
    <bean id="service" class="com.test.service.Impl.MyServiceImpl">
        <property name="myRepository" ref="repository"></property>
    </bean>
    <bean id="repository" class="com.test.repository.impl.MyrepositoryImpl"></bean>
</beans>

Controller类:

import com.test.service.MyService;
import lombok.Setter;

@Setter
public class MyController {

    private MyService myService;

    public String service(Double score){
        return myService.doService(score);
    }
}

Service接口及实现类:
接口:

public interface MyService {
    public String doService(Double score);
}

实现类:

import com.test.repository.MyRepository;
import com.test.service.MyService;
import lombok.Setter;

@Setter
public class MyServiceImpl implements MyService {

    private MyRepository myRepository;

    public String doService(Double score) {
        return myRepository.doReopository(score);
    }
}

Repository接口及实现类:
接口:

public interface MyRepository {
    public String doReopository(Double score);
}

实现类:

import com.test.repository.MyRepository;

public class MyrepositoryImpl implements MyRepository {

    public String doReopository(Double score) {
        String result="";
        if(score<60){ result="&#x4E0D;&#x5408;&#x683C;" ; } else if(score>=60&&score<=80) { result="&#x53CA;&#x683C;" ; } else if(score>80)
        {
            result="&#x4F18;&#x79C0;";
        }
        return result;
    }
}
</=80)></60){>

主类:

import com.test.controller.MyController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
        MyController myController=applicationContext.getBean("controller",MyController.class);
        String result=myController.service(new Double(77));
        System.out.println(result);
    }
}

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
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置自动扫包-->
    <context:component-scan base-package="com.test"></context:component-scan>
</beans>

Controller类:

import com.test.service.MyService;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Setter
@Component
public class MyController {
    @Autowired
    private MyService myService;

    public String service(Double score){
        return myService.doService(score);
    }
}

Service接口及实现类:
接口:

public interface MyService {
    public String doService(Double score);
}

实现类:

import com.test.repository.MyRepository;
import com.test.service.MyService;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Setter
@Component
public class MyServiceImpl implements MyService {
    @Autowired
    private MyRepository myRepository;
    @Override
    public String doService(Double score) {
        return myRepository.doReopository(score);
    }
}

Repository接口及实现类:
接口:

public interface MyRepository {
    public String doReopository(Double score);
}

实现类:

import com.test.repository.MyRepository;
import lombok.Setter;
import org.springframework.stereotype.Component;

@Setter
@Component
public class MyrepositoryImpl implements MyRepository {
    @Override
    public String doReopository(Double score) {
        String result="";
        if(score<60){ result="&#x4E0D;&#x5408;&#x683C;" ; } else if(score>=60&&score<=80) { result="&#x53CA;&#x683C;" ; } else if(score>80)
        {
            result="&#x4F18;&#x79C0;";
        }
        return result;
    }
}
</=80)></60){>

主类:

import com.test.controller.MyController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
        MyController myController=applicationContext.getBean(MyController.class);
        String result=myController.service(new Double(77));
        System.out.println(result);
    }
}

@Component也可以在各层分别写成写成:@Controller、@Service、@Repository

核心技术点:XML解析+反射

具体思路:

Original: https://blog.csdn.net/weixin_41489136/article/details/127826381
Author: 盛者无名
Title: Spring基础之IoC

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/661075/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球