Spring知识点①
Spring重要知识点:
Ioc(Inversion Of Control):控制反转(将对象的实例化权限交给用户去控制,而不是让程序控制)
以前的方式,当我们需要从数据库中查询一条数据的时候:
- 定义连接数据库的接口。
package com.David.SpringDemo; public interface ConnectJDBC { void queryData(); }
- 在实现类中写出具体方法(相当于Dao层):
public class QueryDataImpl implements ConnectJDBC{ @Override public void queryData() { System.out.println("实现数据查询"); } }
- 给出Service接口
public interface UserService { void getUser(); }
- 通过调用Dao层中的方法实现查询
public class UserServiceImpl implements UserService{ ConnectJDBC dao=new QueryDataImpl(); @Override public void getUser() { dao.queryData(); } }
- 用户调用
public class Test { public static void main(String[] args) { UserService userService=new UserServiceImpl(); userService.getUser(); } }
以前查询一条数据的时候是通过此种方法。现在修改一下,当我们在Dao层添加一个新的实现类的时候:public class queryDataByOracle implements ConnectJDBC{ @Override public void queryData() { System.out.println("通过Oracle实现数据查询"); } }
当我们需要使用Oracle去实现数据查询的时候,这时我们就需要将Dao层中具体实现类 UserServiceImpl 进行修改:
public class UserServiceImpl implements UserService{ ConnectJDBC dao=new queryDataByOracle(); @Override public void getUser() { dao.queryData(); } }
假设我们这种修改需求非常大,就需要不停的修改Dao层实现类中的代码,这种设计的耦合性太高了,牵一发而动全身。因此我们需要解决方案:
此时我们应该想在用户调用Service接口时,可不可以将需要使用到的对象传入其中,让其自己控制需要使用哪个对象?public class UserServiceImpl implements UserService{ private ConnectJDBC dao; public void setDao(ConnectJDBC dao) { this.dao = dao; } @Override public void getUser() { dao.queryData(); } }
当用户需要调用时:
public static void main(String[] args) { UserServiceImpl userService=new UserServiceImpl(); userService.setDao(new queryDataByOracle()); userService.getUser(); userService.setDao(new QueryDataImpl()); userService.getUser(); }
以前所有东西都是由程序去进行控制创建 , 而现在是由我们自行控制创建对象 , 把主动权交给了调用者 . 程序不用去管怎么创建,怎么实现了 . 它只负责提供一个接口 .
这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了 , 更多的去关注业务的实现 . 耦合性大大降低 . 这也就是IOC的原型 !
IoC的本质:是一种设计思想,DI(依赖注入)是实现IoC的一种方式。
没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是Spring框架的核心内容,使用多种方式完美实现了IoC,可以使用xml配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
Spring容器在初始化的时候会先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从IoC容器中取出所需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到零配置的目的。
控制反转是一种通过描述(XML或者注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法时依赖注入(Dependency Injection,DI)。
关于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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="com.kuang.pojo.User"> <!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写--> <property name="name" value="ttttt"/> </bean> </beans>
property 给该类对应的属性赋值 name 属性名 当属性为对象的时候,不适用value 使用ref
通过有参构造器来创建:
<!-- 第一种根据index参数下标设置 --> <bean id="userT" class="com.kuang.pojo.UserT"> <!-- index指构造方法 , 下标从0开始 --> <constructor-arg index="0" value="qqqq"/> </bean>
<!-- 第二种根据参数名字设置 --> <bean id="userT" class="com.kuang.pojo.UserT"> <!-- name指参数名 --> <constructor-arg name="name" value="kuangshen2"/> </bean>
<!-- 第三种根据参数类型设置 --> <bean id="userT" class="com.kuang.pojo.UserT"> <constructor-arg type="java.lang.String" value="kuangshen2"/> </bean>
alias 设置别名,为bean设置别名,可以设置多个别名:
<!--设置别名:在获取Bean的时候可以使用别名获取--> <alias name="userT" alias="userNew"/>
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.kuang.pojo.Hello"> <property name="name" value="Spring"/> </bean>
Import 可以导入多个xml配置文件来实现团队合作
<import resource="{path}/beans.xml"/>
依赖注入(DI):
依赖:只Bean对象的创建依赖于容器。Bean对象的依赖资源。
注入:指Bean对象所依赖的资源,由容器来设置和装配。
- 构造器注入(前面已经说过)
- Set注入(重点)
要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is 。
测试pojo类 : Address .java
public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
Student.java
package com.kuang.pojo; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; public void setName(String name) { this.name = name; } public void setAddress(Address address) { this.address = address; } public void setBooks(String[] books) { this.books = books; } public void setHobbys(List<String> hobbys) { this.hobbys = hobbys; } public void setCard(Map<String, String> card) { this.card = card; } public void setGames(Set<String> games) { this.games = games; } public void setWife(String wife) { this.wife = wife; } public void setInfo(Properties info) { this.info = info; } public void show(){ System.out.println("name="+ name + ",address="+ address.getAddress() + ",books=" ); for (String book:books){ System.out.print("<<"+book+">>\t"); } System.out.println("\n爱好:"+hobbys); System.out.println("card:"+card); System.out.println("games:"+games); System.out.println("wife:"+wife); System.out.println("info:"+info); } }
- 常量注入
<bean id="student" class="com.kuang.pojo.Student"> <property name="name" value="小明"/> </bean>
-
Bean注入 注意点:这里的值是一个引用,ref
<bean id="addr" class="com.kuang.pojo.Address"> <property name="address" value="重庆"/> </bean> <bean id="student" class="com.kuang.pojo.Student"> <property name="name" value="小明"/> <property name="address" ref="addr"/> </bean>
-
数组注入
<bean id="student" class="com.kuang.pojo.Student"> <property name="name" value="小明"/> <property name="address" ref="addr"/> <property name="books"> <array> <value>西游记</value> <value>红楼梦</value> <value>水浒传</value> </array> </property> </bean>
-
List注入
<property name="hobbys"> <list> <value>听歌</value> <value>看电影</value> <value>爬山</value> </list> </property>
-
Map注入
<property name="card"> <map> <entry key="中国邮政" value="456456456465456"/> <entry key="建设" value="1456682255511"/> </map> </property>
-
set注入
<property name="games"> <set> <value>LOL</value> <value>BOB</value> <value>COC</value> </set> </property>
-
Null注入
<property name="wife"><null/></property>
-
Properties注入
<property name="info"> <props> <prop key="学号">20190604</prop> <prop key="性别">男</prop> <prop key="姓名">小明</prop> </props> </property>
p命名和c命名注入
- P命名空间注入 : 需要在头文件中加入约束文件
导入约束 : xmlns:p="http://www.springframework.org/schema/p" <!--P(属性: properties)命名空间 , 属性依然要设置set方法--> <bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/>
- c 命名空间注入 : 需要在头文件中加入约束文件
导入约束 : xmlns:c="http://www.springframework.org/schema/c" <!--C(构造: Constructor)命名空间 , 属性依然要设置set方法--> <bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/>
理解:p就是Property属性的注入,c就是构造器注入。
Bean的作用域:
在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由Ioc容器初始化、装配及管理的对象。
类别 | 说明 |
singleton | 在springIoC容器中仅存在一个Bean的实例,Bean以单例的方式存在,默认值 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new xxxBean()对象 |
request |
每次HTTP请求都会创建一个新的Bean,该作用域仅适合用于WebApplicationContext环境 |
session | 同一个Http Session共享一个Bean,不同Session使用不同Bean,仅适用于WebApplicationContext环境 |
几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。
Singleton
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是
Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">
Prototype
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式
调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,
而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成
prototype,可以这样配置:
<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/> 或者 <bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
Request
当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用
域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据
需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean
实例将被销毁。
Session
当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与
request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态
变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
bean的自动装配:
-
自动装配是使用spring满足bean依赖的一种方法
-
spring会在应用上下文中为某个bean寻找其依赖的bean。
Spring中bean有三种装配机制,分别是:
-
在xml中显式配置;
-
在java中显式配置;
-
隐式的bean发现机制和自动装配。
以下为自动化的装配Bean。
Spring的自动装配需要从两个角度来实现,或者说是两个操作:
-
组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
-
自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;
组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。
推荐不使用自动装配xml配置 , 而使用注解 .
- autowire byName (按名称自动装配)
由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。
采用自动装配将避免这些错误,并且使配置简单化。
<bean id="user" class="com.kuang.pojo.User" autowire="byName"> <property name="str" value="qinjiang"/> </bean>
我们将 cat 的bean id修改为 catXXX时候,执行时报空指针java.lang.NullPointerException。因为按byName规则找不对应set方法,真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针错误。
- autowire byType (按类型自动装配)
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。NoUniqueBeanDefinitionException
当一个XML文件中有同一个Bean对象不同的id时就会报此错误。
使用注解:
jdk1.5开始支持注解,spring2.5开始全面支持注解。
准备工作:利用注解的方式注入属性。
1、在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
2、开启属性注解支持!
<context:annotation-config/>
@Autowired
-
@Autowired是按类型自动转配的,不支持id匹配。
-
需要导入 spring-aop的包!
@Qualifier
-
@Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
-
@Qualifier不能单独使用。
@Resource
-
@Resource如有指定的name属性,先按该属性进行byName方式查找装配;
-
其次再进行默认的byName方式进行装配;
-
如果以上都不成功,则按byType的方式自动装配。
-
都不成功,则报异常。
小结
@Autowired与@Resource异同:
1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用
3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
使用注解开发:
在Spring4之后,可以使用注解进行开发,需要引入aop的包
在配置文件中还需要加入一个约束:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> </beans>
注解Bean的实现:
先在xml中配置需要扫描的包:
<!--指定注解扫描包--> <context:component-scan base-package="com.kuang.pojo"/>
再使用@Component注解告诉Spring这是一个需要Spring管理的组件
@Component("user") // 相当于配置文件中 <bean id="user" class="当前注解的类"/> public class User { public String name = "hahah"; }
这个时候我们就可以通过applicationContext.getBean("user")来获取此对象。
属性注入注解实现:
没有set方法时,直接使用@Value注解在该属性上可给属性赋值
有set方法时,可以在set方法上使用@Value给属性赋值
scope作用域注解:
在一个类上使用@Scope注解可以标注该类的作用域。可以添加singleton、prototype等值,跟注解中的作用域意思一样。
小结:
XML与注解比较
-
XML可以适用任何场景 ,结构清晰,维护方便
-
注解不是自己提供的类使用不了,开发简单方便
xml与注解整合开发 :推荐最佳实践
-
xml管理Bean
-
注解完成属性注入
-
使用过程中, 可以不用扫描,扫描是为了类上的注解
需要开启注解的支持:
<context:annotation-config/>
基于Java类进行配置:
新建一个配置类,在该类上使用@Configuration注解,表示该类是一个配置类,里面可以使用@Bean注解来返回指定的类对象。
@Configuration //代表这是一个配置类 public class MyConfig { @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id! public Dog dog(){ return new Dog(); } }
还可以使用@Import导入其它配置类
@Import(MyConfig2.class) //导入合并其他配置类,类似于配置文件中的 inculde 标签
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?