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理论推导
- UserDao接口
- UserDaoImpl实现类
- UserService业务接口
- 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());
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术