Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架

官方文档

Spring官方文档

IOC介绍

什么是IOC(控制反转)

  • 把对象创建和对象之间的调用过程,交给Spring进行管理。
  • 使用IOC目的:为了降低耦合度

IOC本质
控制反转loC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现loC的一种方法,也有人认为DlI只是loC的另一种说法。没有loC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是loC容器,其实现方法是依赖注入(Dependency Injection,Dl)。

IOC底层

xml解析、工厂模式、反射

Spring提供的IOC容器实现的两种方式(两个接口)

  1. BeanFactory接口:IOC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(加载配置文件时候不会创建对象,在获取对象时才会创建对象。)
  2. ApplicationContext接口:BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)推荐使用!

简单IOC例子

没用IOC思想的代码

假如有这些东西,
image.png
部分代码如下

  1. public class UserDao1 implements UserDao {
  2. @Override
  3. public void getUser() {
  4. System.out.println("张三");
  5. }
  6. }
  1. public class UserServiceImpl implements UserService{
  2. UserDao user= new UserDao1();
  3. @Override
  4. public void getUser() {
  5. user.getUser();
  6. }
  7. }

下面属于用户调用了,用户调用不是指客户那种,就好比,我写一个模块,让其他人调用我写的模块。这里的main方法,就是调用的我写的模块。调用的人就相当于用户,因为可以不同的人调用我这个模块,不同的用户。

  1. public class testUser {
  2. public static void main(String[] args) {
  3. UserServiceImpl userService = new UserServiceImpl();
  4. userService.getUser();
  5. }
  6. }

最终会输出UserDao1张三
这时候有个问题,用户想要输出UserDao2怎么办,UserDao3呢?
这不得去改源码吗,UserServiceImpl.java里面UserDao user= new UserDao2();``UserDao user= new UserDao3();
每当调用的人,就是用户,有新需求了,就去改源码,不是一个好程序,需要解耦。这些东西要让用户自定义。

使用IOC思想的代码

我们这样修改源码,多一个set方法。
使用了set注入后,程序不在具有主动性,而是变成了被动的接受对象。

  1. public class UserServiceImpl implements UserService{
  2. UserDao user= null;
  3. public void setUser(UserDao user) {
  4. this.user = user;
  5. }
  6. @Override
  7. public void getUser() {
  8. user.getUser();
  9. }
  10. }

用户调用时,用set方法自己设置一个对象,我要用什么对象,我直接设置,这样就不用去修改源码了,用户用什么,自己传入什么。
main这里是用户调用,不属于源码范围了。我是程序员,我用别人写的Spring,我就是Spring的用户。

  1. public class testUser {
  2. public static void main(String[] args) {
  3. UserServiceImpl userService = new UserServiceImpl();
  4. userService.setUser(new UserDao2());//李四
  5. userService.getUser();
  6. }
  7. }

使用Spring IOC

Maven创建项目后,导入依赖

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-webmvc</artifactId>
  4. <version>5.2.0.RELEASE</version>
  5. </dependency>

这是测试的目录结构
image.png

  1. package com.lyd.pojo;
  2. /**
  3. * @author liyadong
  4. * @create 2022-03-31-14:31
  5. */
  6. public class User {
  7. private String name;
  8. private int age;
  9. public String getName() {
  10. return name;
  11. }
  12. public void setName(String name) {
  13. this.name = name;
  14. }
  15. public int getAge() {
  16. return age;
  17. }
  18. public void setAge(int age) {
  19. this.age = age;
  20. }
  21. @Override
  22. public String toString() {
  23. return "User{" +
  24. "name='" + name + '\'' +
  25. ", age=" + age +
  26. '}';
  27. }
  28. }

Spring配置文件ApplicationContext.xml

官方文档1.2.1

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. https://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <!--
  7. 使用Spring来创建对象,在Spring中这些都称为Bean.
  8. 一个对象就相当于一个bean
  9. id:变量名
  10. class:对象类的全类名
  11. -->
  12. <bean id="user" class="com.lyd.pojo.User">
  13. <property name="name" value="张三"></property>
  14. <property name="age" value="23"></property>
  15. </bean>
  16. </beans>

bean标签(核心)

  1. <!--
  2. 使用Spring来创建对象,在Spring中这些都称为Bean.
  3. 一个对象就相当于一个bean
  4. id:变量名
  5. class:对象类的全类名
  6. name:取多个别名,各种常用的分隔符隔开,可以通过这些别名进行访问对象
  7. -->
  8. <bean id="user" class="com.lyd.pojo.User" name="u u1,u2">
  9. <property name="name" value="张三"></property>
  10. <property name="age" value="23"></property>
  11. </bean>

alias标签

取个别名,可以通过别名获取对象

  • name:被取别名的对象名
  • alias:别名

    1. <alias name="user" alias="u"></alias>

    import标签

    用于团队合作的时候,引入其他的Spring配置文件,就可以用当前的配置文件访问其他文件里面的对象了

    1. <import resource="beans1.xml"></import>
    2. <import resource="beans2.xml"></import>

    依赖注入(DI)

    1. public class Student {
    2. private String name;
    3. private School school;
    4. private String[] books;
    5. private List<String> hobbys;
    6. private Map<String,String> card;
    7. private Set<String> games;
    8. private Properties info;
    9. private String wife;
    10. //......get,set,toString省略
    11. }

    set注入

    对象里面这些参数一定要有set方法才能注入,不然注入不了。 ```xml <?xml version=”1.0” encoding=”UTF-8”?>

    红楼梦 西游记 水浒传 听歌 看电影 学习 王者荣耀 原神 yyyyyyy xxxxxxxx root 123456

  1. <a name="EjZyW"></a>
  2. ### 根据构造器注入
  3. 可以通过注入构造器的参数进行创建对象。**默认是使用无参构造器**<br />当然构造器注入也是可以向set注入那样多种类型注入。
  4. ```xml
  5. <bean id="user" class="com.lyd.pojo.User">
  6. <!--
  7. 1.属性名
  8. -->
  9. <constructor-arg name="age" value="23"/>
  10. <constructor-arg name="name" value="王武"/>
  11. <!--
  12. 2.形参类型
  13. 通过type的方式可以用指定参数类型的方式,没有别名。
  14. 这种方式不推荐,要是多个类型一样的参数呢
  15. -->
  16. <!-- <constructor-arg type="int" value="24"/>-->
  17. <!-- <constructor-arg type="java.lang.String" value="张三"/>-->
  18. <!--
  19. 3.参数索引
  20. -->
  21. <!-- <constructor-arg index="0" value="王二"/>-->
  22. <!-- <constructor-arg index="1" value="25"/>-->
  23. </bean>

p命名和c命名空间注入

需要引入

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

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

image.png
image.png

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:p="http://www.springframework.org/schema/p"
  4. xmlns:c="http://www.springframework.org/schema/c"
  5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. https://www.springframework.org/schema/beans/spring-beans.xsd">
  8. <!--p命名空间注入,可以直接注入属性值:property-->
  9. <bean id="user" class="com.lyd.pojo.User" p:age="23" p:name="张三"/>
  10. <!--c命名空间注入,通过构造器注入:construct-args-->
  11. <bean id="user2" class="com.lyd.pojo.User" c:age="32" c:name="李思"/>
  12. </beans>

使用对象

ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
通过XML获取对象,还可以通过注解,文件等。
执行这句话时,就创建了ApplicationContext.xml里面的全部对象,默认执行的无参构造函数。

把对象都写在程序之外的地方,用户可以直接修改,想要什么对象就自己写什么对象,参数都可以自己配置。使用时只用传一个字符串就得到了。

  1. public class testSpringIOC {
  2. public static void main(String[] args) {
  3. //获取Spring的上下文对象
  4. ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
  5. //全部对象都是spring中管理了,要使用就去Spring中取出来。给用户一个容器,要什么就去取什么
  6. //User user = (User)context.getBean("user");
  7. User user = context.getBean("user",User.class);
  8. System.out.println(user.toString());
  9. //结果:User{name='张三', age=23}
  10. }
  11. }

bean作用域

在bean标签的scope属性里面设置
image.png

  1. 单例模式(Spring默认机制)

只会创建一个实例,翻来覆去用这个实例。

  1. <bean id="user" class="com.lyd.pojo.User" scope="singleton"/>
  1. 原型模式:每次从容器中get的时候,都会产生一个新的对象

    1. <bean id="user" class="com.lyd.pojo.User" scope="prototype"/>
  2. 其余的request,session这些只能在web开发中使用

    bean的自动装配

    使用autoaire属性自动装配

    这是手动装配bean,gei每个set都手动注入值

    1. <bean id="cat" class="com.lyd.pojo.Cat"/>
    2. <bean id="dog" class="com.lyd.pojo.Dog"/>
    3. <bean id="people" class="com.lyd.pojo.People">
    4. <property name="string" value="张三"/>
    5. <property name="cat" ref="cat"/>
    6. <property name="dog" ref="dog"/>
    7. </bean>

    使用自动装配

    1. <bean id="cat" class="com.lyd.pojo.Cat"/>
    2. <bean id="dog" class="com.lyd.pojo.Dog"/>
    3. <!--
    4. autowire属性:可以设置主动装配bean,自动在容器上下文中查找
    5. byName:会自动在容器上下文中查询,和自己对象set方法后面的值对应的beanid
    6. byType:会自动在容器山下文中查找,和自己对象属性类型相同的bean
    7. -->
    8. <bean id="people" class="com.lyd.pojo.People" autowire="byName">
    9. <property name="string" value="张三"/>
    10. </bean>

    小结:

  • byName的时候,需要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set的方法的值一致
  • byType的时候,需要保证所有的bean的class唯一,并且这个bean需要自动注入的属性类型一致

    使用注解自动装配

  • 导入约束

  • 配置注解支持

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:context="http://www.springframework.org/schema/context"
    5. xsi:schemaLocation="http://www.springframework.org/schema/beans
    6. https://www.springframework.org/schema/beans/spring-beans.xsd
    7. http://www.springframework.org/schema/context
    8. https://www.springframework.org/schema/context/spring-context.xsd">
    9. <!--配置注解支持-->
    10. <context:annotation-config/>
    11. <!--xml里面什么都不配置,id和class还是要的-->
    12. <bean id="cat" class="com.lyd.pojo.Cat"/>
    13. <bean id="dog" class="com.lyd.pojo.Dog"/>
    14. <bean id="people" class="com.lyd.pojo.People"/>
    15. </beans>

    @Autowired:

  • 直接放在属性上就可以,也可以放在set方法上

  • 使用Autowired注解就可以不用边写set方法了,注解都是用反射获取值得,提前是自动装配的属性在IOC容器中,且符合规范 ```java package com.lyd.pojo;

import org.springframework.beans.factory.annotation.Autowired;

/**

  • @author liyadong
  • @create 2022-03-31-17:42 */ public class People { @Autowired private Cat cat; //如果显示的定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空 @Autowired(required = false) private Dog dog; private String name;

    public void setName(String name) {

    1. this.name = name;

    }

    public Cat getCat() {

    1. return cat;

    } public Dog getDog() {

    1. return dog;

    } public String getString() {

    1. return name;

    }

    @Override public String toString() {

    1. return "People{" +
    2. "cat=" + cat +
    3. ", dog=" + dog +
    4. ", name='" + name + '\'' +
    5. '}';

    } }

  1. <a name="Lzc9r"></a>
  2. #### @Qualifier
  3. 如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解@Autowired完成的时候、我们可以使用@Qualifier(value="xxx")去配置@Autowired的使用,指定一个唯一的bean对象注入!<br />指定不同的beanid,但是类型不一样是要报错的。
  4. ```xml
  5. <bean id="cat222" class="com.lyd.pojo.Cat"/>
  1. @Autowired
  2. @Qualifier(value = "cat222")
  3. private Cat cat;

@Resource

这是java自带的注解,可以指定name,type进行名字,类型的指定装配

  1. @Resource(name = "cat222",type = Cat.class)
  2. private Cat cat;
  3. @Resource(name="dog",type=Dog.class)
  4. private Dog dog;

小结

@Resource和@Autowired的区别:
都是自动装配的,都可以放在属性字段上
@Autowired默认通过byType的方式实现,然后再去byName
@Resource默认通过byName的方式实现,然后再去byType

使用注解开发

使用注解需要导入aop包
image.png
使用注解需要导入context约束,增加注解的支持

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. https://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context
  8. https://www.springframework.org/schema/context/spring-context.xsd">
  9. <!--
  10. 指定要扫描的包.
  11. 该包下所有带有@Component 注解的类都等价于bean标签,装在容器里面
  12. -->
  13. <context:component-scan base-package="com.lyd.pojo"/>
  14. <!--配置注解支持-->
  15. <context:annotation-config/>
  16. </beans>

@Component和@Value

  • 直接在类上面加上@Component注解,配合配置文件里面的指定扫描包<context:component-scan base-package="com.lyd.pojo"/>,这个类就被装到了IOC容器里面。
  • 在属性或者set方法上面,可以通过@Value注入值 ```java package com.lyd.pojo;

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

//@Component组件,等价于 //名字默认就是这个类的小写 @Component public class User { @Value(“叶凡”)//注入值 private String name;

  1. private int age;
  2. public User() {
  3. }
  4. public User(String name, int age) {
  5. this.name = name;
  6. this.age = age;
  7. }
  8. public String getName() {
  9. return name;
  10. }
  11. public void setName(String name) {
  12. this.name = name;
  13. }
  14. public int getAge() {
  15. return age;
  16. }
  17. @Value("23")
  18. public void setAge(int age) {
  19. this.age = age;
  20. }
  21. @Override
  22. public String toString() {
  23. return "User{" +
  24. "name='" + name + '\'' +
  25. ", age=" + age +
  26. '}';
  27. }

}

  1. 调用
  2. ```java
  3. public class testSpringIOC {
  4. public static void main(String[] args) {
  5. ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
  6. User user = context.getBean("user",User.class);
  7. System.out.println(user.toString());
  8. }
  9. }

衍生注解

@Component有几个衍生注解,根据分层不同,会有不同的注解标记

  • dao层:@Repository
  • service层:@Service
  • controller层:@Controller

这四个注解功能一样,不同层对应不同的注解,都是代表将某个类注册到Spring中,装配Bean
image.png
image.pngimage.pngimage.pngimage.png
需要在配置文件扫描指定包改成base-package="com.lyd"

配置作用域@Scope

  1. @Component
  2. @Scope("singleton")//作用域单例模式
  3. //@Scope("prototype")//原型模式
  4. public class Dog {
  5. }

JavaConfig实现配置

完全用java来配置,就不用xml了。
接着上面的步骤,单独建一个配置类

  1. package com.lyd.config;
  2. import com.lyd.pojo.User;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.ComponentScan;
  5. import org.springframework.context.annotation.Configuration;
  6. import org.springframework.context.annotation.Import;
  7. //这个也会被Spring容器托管,注册到容器中,因为他本来就是一个@Component
  8. //@Component代表这是一个配置类,就是和我们之前的ApplicationContext.xml一样的
  9. @Configuration
  10. //扫描包 <context:component-scan base-package="com.lyd.pojo"/>
  11. @ComponentScan("com.lyd.pojo")
  12. //导入其他的配置类,合并成一个配置类
  13. @Import({MyConfig2.class,MyConfig3.class})
  14. public class MyConfig {
  15. //注册一个bean,就相当于之前写的bean标签
  16. //1.方法的名字,就相当于bean标签的id属性
  17. //2.方法的返回类型,就相当于bean标签中的class属性
  18. //3.返回值就是要注入到bean的对象
  19. @Bean
  20. public User user(){
  21. return new User();
  22. }
  23. }

然后通过注解的方式AnnotationConfigApplicationContext获取上下文对象,来从IOC容器中取出对象

  1. @Test
  2. public void Test01(){
  3. //如果完全使用了配置类方法取做,我们就只能通过AnnotationConfigApplicationContext上下文来获取容器,通过配置类的class对象加载
  4. ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
  5. User user = (User)context.getBean("user");
  6. System.out.println(user.toString());
  7. }