1 Spring IOC

【狂神说Java】Spring5最新完整教程IDEA版通俗易懂:https://www.bilibili.com/video/BV1WE411d7Dv/
https://mp.weixin.qq.com/mp/homepage?__biz=Mzg2NTAzMTExNg==&hid=3&sn=456dc4d66f0726730757e319ffdaa23e&scene=1&devicetype=android-33&version=28002259&lang=zh_CN&nettype=WIFI&ascene=7&session_us=gh_1dd456f1d44d
https://www.cnblogs.com/melodyjerry/p/13549091.html

1 Spring

1.1 简介

  • Spring:春天,给软件行业带来了春天
  • 2002年,首次推出了Spring的雏形interface21框架
  • Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布了1.0正式版。
  • Rod Johnson,Spring框架的创始人,同时也是SpringSource的联合创始人。Spring是面向切面编程(AOP,Aspect Oriented Programming)和控制反转(IoC,Inversion of Control)的容器框架。
  • Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!

SSH:Struct2 + Spring + Hibernate
SSM:SpringMVC + Spring + MyBatis
官网:https://spring.io/projects/spring-framework
官网下载地址:http://repo.spring.io/release/org/springframework/spring
GitHub地址:https://github.com/spring-projects/spring-framework

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.23</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.9</version>
</dependency>

1.2 优点

  • Spring是一个开源的免费的框架(容器)
  • Spring是一个轻量级的、非入侵式的框架
  • 控制反转(IOC),面向切面编程(AOP)
  • 支持事务的处理,对框架整合的支持

总结一句话:Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的容器(框架)。

1.3 组成

图片来自网络

1.4 拓展

Build Anything构建一切
Coordinate Anything协调一切
Connect Anything连接一切

  • Spring Boot:
    一个快速开发的脚手架
    基于SpringBoot可以快速的开发单个微服务
    约定大于配置
  • Spring Cloud:
    基于SpringBoot实现的

现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot前提,需要掌握Spring和SpringMVC。
弊端:发展了太久,违背了原来的理念,配置十分繁琐,人称“配置地狱”。

2 IOC理论推导

  1. UserDao接口
  2. UserDaoImpl实现类
  3. UserService业务接口
  4. UserServiceImpl业务接口实现类

在之前的业务中,用户的需求可能会影响原来的代码,我们需要根据用户的需求去修改原代码。
如果程序代码量十分大,修改一次的成本代价十分昂贵。
我们使用一个Set接口实现,已经发生了革命性的变化:

UserServiceImpl.java

package com.liweixiao.service;

import com.liweixiao.dao.UserDao;
import com.liweixiao.dao.UserDaoImpl;
import com.liweixiao.dao.UserDaoMysqlImpl;

/**
 * @author:LiWeixiao
 * @date:2023/4/23
 * @description:
 */
public class UserServiceImpl implements UserService{
    //业务层都会调用dao层,所以引入dao层
    //private UserDao userDao = new UserDaoMysqlImpl();
    private UserDao userDao;

    //利用set进行动态实现值的注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

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

MyTest.java

package com.liweixiao.service;

import com.liweixiao.dao.UserDaoImpl;
import com.liweixiao.dao.UserDaoMysqlImpl;
import org.junit.Test;

/**
 * @author:LiWeixiao
 * @date:2023/4/23
 * @description:
 */
public class MyTest {
    @Test
    public static void main(String[] args) {
        //用户实际调用的是业务层,dao层不需要接触
        UserService userService = new UserServiceImpl();
        //((UserServiceImpl)userService).setUserDao(new UserDaoImpl());
        ((UserServiceImpl)userService).setUserDao(new UserDaoMysqlImpl());
        userService.getUser();
    }
}
  • 之前,程序是主动创建对象,控制器在程序猿手上。
  • 使用了set注入后,程序不再具有主动性,而是变成了被动的接收对象。

这种思想,从本质上解决了问题,我们程序猿不再去管理对象的创建了。系统的耦合性大大降低,可以更加专注的在业务实现上。这是IOC的原型。

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

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

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

3 HelloSpring

Hello.java

public class Hello {
    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

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

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

    <!--  使用Spring来创建对象,在Spring这些都称为Bean
    类型 变量名 = new 类型();
    Hello hello = new Hello();

    id = 变量名
    class = new 的对象;
    property 相当于给对象中的属性设置一个值,name是属性名,value是具体的基本数据类型的值/ref是引用Spring容器中创建的对象即id
      -->
    <bean id="hello" class="com.liweixiao.pojo.Hello">
        <property name="str" value="Spring" />
    </bean>

</beans>

Test.java

public class MyTest {
    public static void main(String[] args) {
        //获取Spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象现在都在Spring中管理了,要使用直接取出来就可以。
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello.toString());
    }
}

控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。
反转:程序本身不创建对象,而变成被动的接收对象。
依赖注入:利用set方法来进行注入的。
IOC是一种编程思想,由主动的编程变成被动的接收。
可以通过new ClassPathXmlApplicationContext去浏览一下底层源码
extends AbstractXmlApplicationContext
extends AbstractRefreshableConfigApplicationContext
extends AbstractRefreshableApplicationContext
extends AbstractApplicationContext
implements ConfigurableApplicationContext
extends ApplicationContext
OK,到了现在,我们彻底不用再去程序中改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的loc一句话搞定:对象由Spring来创建、管理、装配!

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

    <bean id="mysqlImpl" class="com.liweixiao.dao.UserDaoMysqlImpl" />
    <bean id="myoracleImpl" class="com.liweixiao.dao.UserDaoOracleImpl" />
    <bean id="mysqlserverImpl" class="com.liweixiao.dao.UserDaoSqlserverImpl" />

    <bean id="UserServiceImpl" class="com.liweixiao.service.UserServiceImpl">
        <!--
        ref:引用Spring容器中创建的对象
        value:具体的值,基本数据类型
        -->
        <property name="userDao" ref="mysqlserverImpl" />
    </bean>

</beans>

Test.java

public class MyTest {

    public static void main(String[] args) {        
        //获取ApplicationContext:拿到Spring的容器
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //容器在手,天下我有
        UserServiceImpl userServiceImpl = (UserServiceImpl)context.getBean("UserServiceImpl");

        userServiceImpl.getUser();
    }
}

4 IOC创建对象方式

1.使用无参数创建对象,默认。
2.使用有参构造,如下
1)下标赋值
2)类型赋值(不建议使用)
3)直接通过参数名(掌握)

总结:在配置文件加载的时候,容器中管理的对象就已经初始化了。

5 Spring配置

5.1 别名

alias设置别名,为bean设置别名,可以设置多个别名。

<alias name="userT" alias="userNew"/>

5.2 Bean的配置

<!--bean就是java对象,由Spring创建和管理-->

<!--
   id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
   如果配置id,又配置了name,那么name是别名
   name可以设置多个别名,可以用逗号,分号,空格隔开
   如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;

class是bean的全限定名=包名+类名
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.liweixiao.pojo.Hello">
   <property name="name" value="Hello,Spring"/>
</bean>

5.3 import

import一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。
张三(beans.xm1)
李四(beans2.xm1)
王五(beans3.xm1)
applicationContext.xml

<import resource="beans.xm1"/>
<import resource="beans2.xml"/>
<import resource="beans3.xm1"/>

6 DI依赖注入

6.1 构造器注入

前面已经说过了

6.2 Set方式注入【重点】

依赖注入:set注入

  • 依赖:bean对象的创建依赖于容器
  • 注入:bean对象中的所有属性,由容器来注入

【环境搭建】
1.复杂类型
Address类
2.真实测试对象
Student类
3.beans.xml
4.测试
MyTest3

Student.java

@Get
@Set
public class Student {
//别忘了写get和set方法(用lombok注解也行)
    private String name;
    private Address address;
 
    private String[] books;
    private List<String> hobbies;
 
    private Map<String, String> card;
    private Set<String> game;
 
    private Properties infor;
    private String wife;
 
    @Override
    public String toString() {
        return "Student{" +"\n"+
                "name='" + name + '\'' +"\n"+
                ", address=" + address.toString() +"\n"+
                ", books=" + Arrays.toString(books) +"\n"+
                ", hobbies=" + hobbies +"\n"+
                ", card=" + card +"\n"+
                ", game=" + game +"\n"+
                ", infor=" + infor +"\n"+
                ", wife='" + wife + '\'' +"\n"+
                '}';
    }
}

Address.java

public class Address {
 
    private String address;
 
    public String getAddress() {
        return address;
    }
 
    public void setAddress(String address) {
        this.address = address;
    }
 
    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}

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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
 
	<bean id="address" class="pojo.Address">
		<property name="address" value="address你好" />
	</bean>
 
	<bean id="student" class="pojo.Student">
		<!--第一种,普通值注入 -->
		<property name="name" value="name你好" />
		<!--第二种,ref注入 -->
		<property name="address" ref="address" />
 
		<!--数组注入 -->
		<property name="books">
			<array>
				<value>三国</value>
				<value>西游</value>
				<value>水浒</value>
			</array>
		</property>
 
		<!--list列表注入 -->
		<property name="hobbies">
			<list>
				<value></value>
				<value></value>
				<value>rap</value>
				<value>篮球</value>
			</list>
		</property>
 
		<!--map键值对注入 -->
		<property name="card">
			<map>
				<entry key="username" value="root" />
				<entry key="password" value="root" />
			</map>
		</property>
 
		<!--set(可去重)注入 -->
		<property name="game">
			<set>
				<value>wangzhe</value>
				<value>lol</value>
				<value>galname</value>
			</set>
		</property>
 
		<!--空指针null注入 -->
		<property name="wife">
			<null></null>
		</property>
 
		<!--properties常量注入 -->
		<property name="infor">
			<props>
				<prop key="id">20200802</prop>
				<prop key="name">cbh</prop>
			</props>
		</property>
	</bean>
</beans>

6.3 拓展方式注入

官方文档位置:

User.java

public class User {
    private String name;
    private int id;
	public User() {
        
	}
	public User(String name, int id) {
		super();
		this.name = name;
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	@Override
	public String toString() {
		return "User [name=" + name + ", id=" + id + "]";
	}
}

注意: beans 里面加上这下面两行
使用p和c命名空间需要导入xml约束
xmlns:p=“http://www.springframework.org/schema/p”
xmlns:c=“http://www.springframework.org/schema/c”

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"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <!--p命名空间注入/set注入,可以直接注入属性的值-》property-->
    <bean id="user" class="pojo.User" p:name="test" p:id="20" >
    </bean>
 
    <!--c命名空间,通过构造器注入,需要写入有参和无参构造方法-》construct-args-->
    <bean id="user2" class="pojo.User" c:name="hello" c:id="22"></bean>
</beans>

Test.java

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = context.getBean("user",User.class);//确定class对象,就不用再强转了
System.out.println(user.toString());

6.4 Bean的作用域


1.单例模式(默认)

<bean id="user2" class="pojo.User" c:name="cxk" c:age="19" scope="singleton"></bean>

2.原型模式: 每次从容器中get的时候,都产生一个新对象!

<bean id="user2" class="pojo.User" c:name="cxk" c:age="19" scope="prototype"></bean>

3.其余的request、session、application这些只能在web开放中使用!

7 Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式
  • Spring会在上下文自动寻找,并自动给bean装配属性

在Spring中有三种装配的方式
1.在xml中显示配置
2.在java中显示配置
3.隐式的自动装配bean 【重要】

7.1 测试

环境搭建:一个人有两个宠物

7.2 byName自动装配

byName会自动查找,和自己对象set对应的值对应的id
保证所有id唯一,并且和set注入的值一致

7.3 byType自动装配

byType会自动查找,和自己对象set方法参数的类型相同的bean
保证所有的class唯一(类为全局唯一)

<?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="cat" class="com.liweixiao.pojo.Cat"/>
    <bean id="dog" class="com.liweixiao.pojo.Dog"/>
    <!--
    byname会在容器自动查找,和自己对象set方法的set后面的值对应的id
    例如:setDog(),取set后面的字符作为id,则要id = dog 才可以进行自动装配
    byType会在容器自动查找,和自己对象属性相同的bean
    例如,Dog dog; 那么就会查找pojo的Dog类,再进行自动装配;id可以删除
    -->
    <bean id="people" class="com.liweixiao.pojo.People" autowire="byType">
        <property name="name" value="Spring"/>
    </bean>
</beans>

7.4 使用注解实现自动装配

jdk1.5支持的注解,spring2.5支持的注解
The introduction of annotation-based configuration raised the question of whether this approach is “better” than 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
        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/>
    <bean id="cat" class="com.liweixiao.pojo.Cat"/>
    <bean id="dog" class="com.liweixiao.pojo.Dog"/>
    <bean id="people" class="com.liweixiao.pojo.People"/>
</beans>

7.4.1 @Autowired

默认是byType方式,如果匹配不上,就会byName
在属性上个使用,也可以在set上使用
我们可以不用编写set方法了,前提是自动装配的属性在Spring容器里,且要符合ByName 自动装配

public class People {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
}

7.4.2 @Autowired+@Qualifier

@Autowired不能唯一装配时,需要@Autowired+@Qualifier
如果@Autowired自动装配环境比较复杂。自动装配无法通过一个注解完成的时候,可以使用@Qualifier(value = “dog”)去配合使用,指定一个唯一的id对象

public class People {
    @Autowired
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog")
    private Dog dog;
    private String name;
}

7.4.3 @Resource

默认是byName方式,如果匹配不上,就会byType

public class People {
    Resource(name="cat")
    private Cat cat;
    Resource(name="dog")
    private Dog dog;
    private String name;
}

@Resource和@Autowired的区别:

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired通过byType的方式实现,而且必须要求这个对象存在!【常用】
  • @Resource默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】
  • 执行顺序不同:@Autowired通过byType的方式实现。@Resource默认通过byname的方式实现

8 使用注解开发

在spring4之后,使用注解开发,必须要保证aop包的导入

8.1 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"
       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:component-scan base-package="com.liweixiao"/>
    <context:annotation-config/>

</beans>
//@Component 组件
//等价于<bean id="user" classs"com.liweixiao.pojo.User"/> 
@Component
public class User {
    public String name="hello,spring";
}

8.2 属性如何注入

1、可以不用提供set方法,直接在直接名上添加@value("值")

@Component
public class User {
    @Value("Spring")
    // 相当于配置文件中 <property name="name" value="Spring"/>
    public String name;
}

2、如果提供了set方法,在set方法上添加@value("值");

@Component
public class User {
    public String name;

    @Value("Spring1")
    public void setName(String name) {
        this.name = name;
    }
}

8.3 衍生的注解

@Component有几个衍生注解,会按照web开发中,mvc架构中分层。
dao (@Repository)
service(@Service)
controller(@Controller)
这四个注解的功能是一样的,都是代表将某个类注册到容器中

8.4 自动装配置

@Autowired:默认是byType方式,如果匹配不上,就会byName
@Nullable:字段标记了这个注解,说明该字段可以为空
@Resource:默认是byName方式,如果匹配不上,就会byType

8.5 作用域

@scope
singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收

@Component
@Scope("prototype")
public class User {
    //@Value("Spring")
    public String name;

    @Value("Spring1")
    public void setName(String name) {
        this.name = name;
    }
}

8.6 小结

XML与注解比较:

  • XML可以适用任何场景 ,结构清晰,维护方便
  • 注解不是自己提供的类使用不了,开发简单方便
    xml与注解整合开发 :推荐最佳实践
  • xml管理Bean
  • 注解完成属性注入
  • 使用过程中, 可以不用扫描,扫描是为了类上的注解
    context:annotation-config/

作用:

  • 进行注解驱动注册,从而使注解生效
  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
  • 如果不扫描包,就需要手动配置bean
  • 如果不加注解驱动,则注入的值为null!

9 使用Java的方式配置Spring

不使用Spring的xml配置,完全交给java来做!
Spring的一个子项目,在spring4之后,它成为了核心功能

//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中 
@component 
public class User { 
    private String name;
    
    public String getName() { 
    	return name; 
    } 
    //属性注入值
    @value("QINJIANG')  
    public void setName(String name) { 
    	this.name = name; 
    } 
    @Override 
    public String toString() { 
        return "user{" + 
        "name='" + name + '\''+ 
        '}'; 
    } 
}
//这个也会Spring容器托管,注册到容器中,因为他本米就是一个@Component 
// @Configuration表这是一个配置类,就像我们之前看的beans.xml,类似于<beans>标签
@Configuration 
@componentScan("com.Kuang.pojo") //开启扫描
//@Import(KuangConfig2.class)
public class KuangConfig { 
    //注册一个bean , 就相当于我们之前写的一个bean 标签 
    //这个方法的名字,就相当于bean 标签中的 id 属性 ->getUser
    //这个方法的返同值,就相当于bean 标签中的class 属性 ->User
    
    //@Bean 
    public User getUser(){ 
    	return new User(); //就是返回要注入到bean的对象! 
    } 
}
public class MyTest { 
    public static void main(String[ ] args) { 
    //如果完全使用了配置类方式去做,我们就只能通过 Annotationconfig 上下文来获取容器,通过配置类的class对象加载! 
    ApplicationContext context = new AnnotationConfigApplicationContext(KuangConfig.Class); //class对象
    User getUser =(User)context.getBean( "getUser"); //方法名getUser
    System.out.Println(getUser.getName()); 
    } 
}
posted @   LiWeixiao  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示