Spring基础之IoC
创始人
2024-01-20 19:54:57
0

创建Maven工程,导入spring依赖

        org.springframeworkspring-context{spring版本号}

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

        org.projectlomboklombok{lombok版本号}
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:




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下:

    
  • 有参构造函数

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

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

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

    

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

    

IoC DI

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关系:




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 studentList;
}

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




Spring中的bean

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:





执行主程序:

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都会被创建

加作用域:




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

Spring的继承

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

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

创建一个user2继承于user1:




主类:

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:




主类:

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并不需要属于同一个数据类型,只要其成员变量列表一致即可。

Spring的依赖

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

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

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

    

上述先创建account,再创建user

Spring读取外部资源

实际开发中,数据库配置一般会单独保存起来,保存到后缀为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
    

必须加入上面几行不然报错!
所以得到下面的xml:




Spring p命名空间

xml里必须引入:

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

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

   

Spring工厂方法

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 carMap;static {carMap=new HashMap<>();carMap.put(1,new Car(1,"奥迪"));carMap.put(2,new Car(2,"奥拓"));}public static Car getCar(Integer num){return carMap.get(num);}
}

配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 carMap;public InstanceCarFactory(){carMap=new HashMap<>();carMap.put(1,new Car(1,"奥迪"));carMap.put(2,new Car(2,"奥拓"));}public Car getCar(Integer num){return carMap.get(num);}
}

配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 IoC 自动装载 autowire

自动装载是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,并且通过自动装载完成依赖注入。

    

主类:

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基于注解的开发

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加入扫描范围,自动扫包


主类运行便可注入

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名")

将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中的



实际开发的使用

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

  • Controller
  • Service
  • Repositroy(DAO)

关系 Controller–>Service–>Repository

如下分别用xml方法和注解方法来演示该关系:

xml方法

spring.xml:




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 ="不合格";}else if(score>=60&&score<=80){result="及格";}else if(score>80){result="优秀";}return result;}
}

主类:

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:




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 {@Autowiredprivate 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 {@Autowiredprivate MyRepository myRepository;@Overridepublic 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 {@Overridepublic String doReopository(Double score) {String result="";if(score<60){result ="不合格";}else if(score>=60&&score<=80){result="及格";}else if(score>80){result="优秀";}return result;}
}

主类:

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

Spring IoC底层实现

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

具体思路:

  1. 根据需求写XML文件,配置需要创建的bean
  2. 编写程序读取XML文件,获取bean相关信息、类、属性、id
  3. 根据2步获取到的信息,结合反射机制动态创建对象,同时完成属性赋值
  4. 将创建好的bean存入Map集合,设置key-value映射,key就是bean中的id值,value就是bean对象
  5. 提供方法从Map中通过id获取对应的value

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...