Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。
优点:
1.Spring是一个开源的免费的框架(容器)
2.Spring是一个轻量级的,非入侵式的框架
3.控制反转(IOC)和面向切面编程(AOP)
4.支持事务的处理,对框架整合的支持
模块
Spring Boot:一个快速开发的脚手架,基于SpringBoot可以快速的开发单个微服务(约定大于配置),学习Spring Boot前提是完全掌握Spring及SpringMVC。
Spring Cloud:是基于Spring Boot实现的
弊端:Spring发展太久之后,违背了原来的理念,配置变得十分繁琐。
IOC理论推导
用户实际调用的是业务层,dao层它们不需要接触。在idea中,用idea中的main方法来模拟用户的需求。
在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原来的代码!如果代码量非常大,修改一次的成本会很十分昂贵。
之前,程序是主动创建对象的,控制权在程序员手里。
UserDao userDao
public class UserServiceImpl implements UserService{
UserDao userDao=new UserDaoOracleImpl;
//UserDao userDao=new UserDaoSqlImpl;
@Override
public void getUser() {
userDao.getUser();
}
}
public class Test {
public static void main(String[] args) {
//main方法代表客户端,模拟的是用户需求,实际调用的是业务层,dao层他们不需要调用
UserService userService=new UserServiceImpl();
userService.getUser();
}
}
当客户需求发生改变时,需要修改业务层的代码(UserDao userDao是死的)。
使用set注入以后,程序不再具有主动性,而是变成了被动的接收对象
public class UserServiceImpl implements UserService{
UserDao userDao;
@Override
public void getUser() {
userDao.getUser();
}
@Override
public void setUserDao(UserDao userDao) {
this.userDao=userDao;
}
}
public class Test {
public static void main(String[] args) {
//main方法代表客户端,模拟的是用户需求,实际调用的是业务层,dao层他们不需要调用
UserService userService=new UserServiceImpl();
userService.setUserDao(new UserDaoImpl());
// 不同的UserDao代表者着不同的需求
// userService.setUserDao(new UserDaoOracleImpl());
// userService.setUserDao(new UserDaoSqlImpl());
userService.getUser();
}
}
当客户需求发生改变时,不需要修改业务层的代码(UserDao userDao由顾客定)。
这种IOC思想从本质上解决了问题,程序员不用再去管理对象的创建。系统的耦合性(块间联系)大大降低,可以更加专注的在业务上的实现。
右图为IOC思想
HelloSpring(初识Spring)
1.配置ApplicationContext.xml文件,将对象放入Spring容器中
<?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-->
<bean id="hello" class="com.kuanghui.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
</beans>
<!--使用Spring来创建对象,在Spring中都被称为Bean-->
<bean id="mysql" class="com.kuanghui.dao.UserDaoSqlImpl"/>
<bean id="oracle" class="com.kuanghui.dao.UserDaoOracleImpl"/>
<bean id="service" class="com.kuanghui.service.UserServiceImpl">
<!--ref: 引用Spring容器中创建好的对象 -->
<property name="userDao" ref="mysql"/>
</bean>
2.通过Spring容器创建对象
public class MyTest {
public static void main(String[] args) {
//获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
//我们的对象都在Spring中管理了,我们要使用,直接去Spring中取出来就可以了
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
hello对象由Spring创建,hello对象的属性由Spring容器设置
控制:传统的应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring创建的。容器就是Beans标签。
反转:程序本身不创建对象,而变成被动的接收对象
依赖注入:利用set方法来进行注入
所谓的IOC,就是对象由Spring来创建,管理,装配!以后要实现不同的操作,只需要在xml配置文件中进行修改即可,彻底不需要去修改程序中的代码。
IOC创建对象的方式
当程序运行时,即使被注册的bean不使用,它也会被创建。
1.使用无参构造创建对象,默认!
<bean id="user" class="com.kuanghui.pojo.User">
<property name="name" value="Shenyycccc"/>
</bean>
2.假如我们使用有参构造创建对象
<!--使用Spring来创建对象,在Spring中都被称为Bean-->
<!--第一种:下标赋值-->
<bean id="user" class="com.kuanghui.pojo.User">
<constructor-arg index="0" value="Shenyccc"/>
</bean>
<!--第二种:类型赋值,若该有参构造器有两个相同的类型,此方法就不行了
所以不推荐使用-->
<bean id="user1" class="com.kuanghui.pojo.User">
<constructor-arg type="java.lang.String" value="Syc"/>
</bean>
<!--第三种:直接通过参数名赋值,推荐-->
<bean id="user2" class="com.kuanghui.pojo.User">
<constructor-arg name="name" value="SSyc"/>
</bean>
总结:在配置文件加载时,Spring容器中管理的对象就已经初始化了.
Spring配置
别名
如果我们添加了别名,也可以通过别名获取到这个对象。
<alias name="user" alias="wok"/>
Bean
id:bean的唯一标识符,相当于Java中的对象名
class:bean对象所对应的全限定名:包名+类型名
name:也是别名,而且name可以同时取多个别名
<bean id="user" class="com.kuanghui.pojo.User">
<property name="name" value="Shenyycccc" name="user2,user3,user4"/>
</bean>
import
这个import一般用于团队开发,它可以将多个配置文件导入合并为一个。
<import resource="beans.xml"/>
DI依赖注入
1.构造器注入
<!--使用Spring来创建对象,在Spring中都被称为Bean-->
<!--第一种:下标赋值-->
<bean id="user" class="com.kuanghui.pojo.User">
<constructor-arg index="0" value="Shenyccc"/>
</bean>
<!--第二种:类型赋值,若该有参构造器有两个相同的类型,此方法就不行了
所以不推荐使用-->
<bean id="user1" class="com.kuanghui.pojo.User">
<constructor-arg type="java.lang.String" value="Syc"/>
</bean>
<!--第三种:直接通过参数名赋值,推荐-->
<bean id="user2" class="com.kuanghui.pojo.User">
<constructor-arg name="name" value="SSyc"/>
</bean>
2.Set方式注入(依赖)
依赖注入:Set注入
依赖:bean对象的创建依赖于Spring容器
注入:bean对象中的所有属性都由容器来注入
public class Student {
String name;
Address address;
String[] books;
List<String> hobbys;
Map<String,String> card;
Set<String> games;
String wife;
Properties properties;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbys() {
return hobbys;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address.getAddress() +
", books=" + Arrays.toString(books) +
", hobbys=" + hobbys +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", properties=" + properties +
'}';
}
}
<bean id="address" class="com.kuanghui.pojo.Address">
<property name="address" value="CJLU"/>
</bean>
<bean id="student" class="com.kuanghui.pojo.Student">
<!-- 普通值注入 -->
<property name="name" value="Shenyc"/>
<!-- bean注入-->
<property name="address" ref="address"/>
<!-- 数组注入-->
<property name="books">
<array>
<value>book1</value>
<value>book2</value>
<value>book3</value>
</array>
</property>
<!-- list注入-->
<property name="hobbys">
<list>
<value>hobby1</value>
<value>hobby2</value>
<value>hobby3</value>
</list>
</property>
<!-- map注入-->
<property name="card">
<map>
<entry key="card1" value="111"/>
<entry key="card2" value="222"/>
<entry key="card3" value="333"/>
</map>
</property>
<!-- set注入-->
<property name="games">
<set>
<value>game1</value>
<value>game2</value>
<value>game3</value>
</set>
</property>
<!-- null,等价于<property name="wife" value="">-->
<property name="wife">
<null></null>
</property>
<!-- Properties
key=value-->
<property name="properties">
<props>
<prop key="学号">xuehao</prop>
<prop key="性别">nan</prop>
</props>
</property>
</bean>
3.拓展
如果想要使用p或者c标签,需要加上下面的语句
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
<!--使用p标签注入,可以直接注入属性的值,相当于property-->
<bean id="user" class="com.kuanghui.pojo.User" p:age="3" p:name="shenyc"/>
<!--使用p标签注入,通过构造器注入属性的值,相当于construction-args -->
<bean id="user1" class="com.kuanghui.pojo.User" c:age="1" c:name="shenyc111"/>
注意点:p命名和c命名不能直接使用,需要导入xml约束。
Bean的作用域
Scope | 说明 |
---|---|
(默认情况下)为每个Spring IoC容器将单个Bean定义的Scope扩大到单个对象实例。 | |
将单个Bean定义的Scope扩大到任何数量的对象实例。 | |
将单个Bean定义的Scope扩大到单个HTTP请求的生命周期。也就是说,每个HTTP请求都有自己的Bean实例,该实例是在单个Bean定义的基础上创建的。只在Web感知的Spring | |
将单个Bean定义的Scope扩大到一个HTTP | |
将单个Bean定义的 Scope 扩大到 | |
将单个Bean定义的 Scope 扩大到 |
1.单例模式(Spring机制默认)
<bean id="user" class="com.kuanghui.pojo.User" p:age="3" p:name="shenyc"
scope="singleton"/>
2.原型模式(每次从容器中get的时候,都会产生一个新的对象)
<bean id="user" class="com.kuanghui.pojo.User" p:age="3" p:name="shenyc"
scope="prototype"/>
3.其余的request,session,application这些只能在web开发中使用
Bean的自动装配
自动装配是Spring满足bean依赖的一种方式!
Spring会在上下文中自动寻找,并自动给Bean装配对象
Spring三种装配方式
1.在xml中显示配置
2.在Java中显示配置
3.隐式的自动装配(重要)
ByName和ByType自动装配
<bean id="cat" class="com.kuanghui.pojo.Cat"/>
<bean id="dog" class="com.kuanghui.pojo.Dog"/>
<!--
byName:会自动在容器上下文中查找,和自己对象属性名对应的beanId
byType:会自动在容器上下文中查找,和自己对象 属性类型相同的bean
-->
<bean id="people" class="com.kuanghui.pojo.People" autowire="byName">
<property name="name" value="小王"/>
</bean>
<bean id="people1" class="com.kuanghui.pojo.People" autowire="byType">
<property name="name" value="小王"/>
</bean>
//等价于
<bean id="people1" class="com.kuanghui.pojo.People" autowire="byType">
<property name="name" value="小王"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
小结:1.byName的时候,需要确保所有bean的id唯一,并且这个bean需要和自动注入的属性名一致。2.byType的时候,需要确保所有bean的class唯一,并且这个bean需要和自动注入的属性类型一致。
Spring所需的jar包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
使用注解开发实现自动装配
jdk1.5开始支持注解,Spring2.5开始支持注解。
要使用注解
1.导入约束
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"
2.配置注解支持
<context:annotation-config></context:annotation-config>
@Autowired
直接在属性上使用,也可以在set方法上使用
使用Autowired我们可以不用再编写Set方法了,前提是你这个装配的属性在IOC(Spring)容器中。
@Autowired先通过类型装配,如果出现多个类型,再通过beanId装配。
@Qualifier
@Qualifier注解的作用是指定需要注入的Bean的名称,在进行自动装配(如@Autowired)的过程中,指定注入哪一个实例,一般跟@Autowired注解配合使用。@Qualifier注解是通过byName进行自动注入的。当在容器中有多个同类型的bean时,使用@Autowired注解默认会按照byType自动注入,即会自动查找与属性类型匹配的bean,但若这些同类型的bean存在一些差异,我们就可以使用@Qualifier注解指定具体的bean的名称,从而避免自动装配时存在的歧义。
@Nullable
字段标记了这个注解,说明这个字段可以为null。
@Resource(java的注解)
@Resource默认按byName自动注入,也提供按照byType 注入
小结:@Resource和@Autowired区别:@Autowired注解来自于Spring Framework,而@Resource注解来自于J2EE规范。@Autowired注解默认按类型(by type)进行匹配注入,如果类型相同,则按名称(by name)进行匹配注入。而@Resource注解默认按名称(by name)进行匹配注入,如果名称不存在,则按类型(by type)进行匹配注入。
public class People {
@Autowired
@Qualifier(value = "dog")
Dog dog;
@Resource
Cat cat;
String name;
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"dog=" + dog +
", cat=" + cat +
", name='" + name + '\'' +
'}';
}
}
使用注解开发
在Spring4之后,要使用注解开发,必须保证aop的包导入了。
使用注解需要导入context约束,增加注解的支持。
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.kuanghui.pojo"/>
<context:annotation-config></context:annotation-config>
Bean注入
@Component
组件,放在类上,说明这个类被Spring管理了
@Component注解通常用于声明一个被Spring容器托管的bean。如果没有为该注解指定bean的名称,则Spring将使用默认的bean名称。默认情况下,bean的名称为类名称的第一个字母小写形式。如果需要为@Component指定一个特定的bean名称,则可以使用value或name属性。
//等价于<bean id="user" class="com.kuanghui.pojo.User"/>
@Component
public class User {
public String name;
}
属性注入
@Value()
@Component
public class User {
//相当于<property name="name" value="Shenyyccc"/>
@Value("Shenyyccc")
public String name;
}
也可以这样实现
@Component
public class User {
public String name;
//也可以这样实现
@Value("Shenyyccc")
public void setName(String name){
this.name=name;
}
}
衍生注解
@Component有几个衍生注解,功能都相同,只不过我们在web开发中,会按照mvc三层架构分层!
dao:@Repository
service:@Service
controller:@Controller
注意:这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配。
@Mapper和@respository区别。
1、使用@mapper后,不需要在spring配置中设置扫描地址,通过mapper.xml里面的namespace属性对应相关的mapper类,spring将动态的生成Bean后注入到ServiceImpl中。
2、@repository则需要在Spring中配置扫描包地址,然后生成dao层的bean,之后被注入到ServiceImpl中
作用域
@scope()
@Component
@Scope("singleton")
public class User {
public String name;
//也可以这样实现
@Value("Shenyyccc")
public void setName(String name){
this.name=name;
}
小结
xml和注解:
1.xml更万能,适用于任何场合,维护简单方便
2.注解 维护相对复杂
3.想让注解生效,必须开启注解的支持。
使用Java的方式配置Spring
我们现在要完全不适用Spring的xml配置了,全权交给Java来做!
JavaConfig是Spring的一个子项目。在Spring4之后,它成为了一个核心功能。
@Component
public class User {
String name;
public String getName() {
return name;
}
@Value(value = "Shenyc")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
//@Configuration注解类似于Spring配置文件ApplicationContext中的beans标签
//这个注解也会被Spring托管,注册到容器中,因为他本身就是一个@Component
//@Comfiguration代表一个配置类,就和我们之前看的beans.xml一样
@Configuration
//@ComponentScan("com.kuanghui.pojo")
//相当于<context:component-scan base-package="com.kuanghui.pojo"/>
@ComponentScan("com.kuanghui.pojo")
//相当于导入了另外一个文件配置类
@Import(SpringConfig1.class)
public class SpringConfig {
//注册一个bean,就相当于我们之前写的一个bean标签,
// 这个方法的名字,就相当于bean标签的id
// 这个方法的返回值,就相当于bean标签中的class属性
@Bean
public User getUser(){
return new User();
}
}
@Test
public void test(){
//如果完全使用了配置类方式去做,我们只能通过AnnotationConfigApplicationContext来获取容器,
// 通过配置类的clss对象加载
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
User user = context.getBean("getUser", User.class);
System.out.println(user.getName());
}
方法一:@ComponentScan("com.kuanghui.pojo")是和pojo中的类@Component搭配使用的,获取对象时使用的是小写类名,请区别@Bean注解。也需要标明@Configuration。
方法二:@Configuration和@Bean搭配使用,@Configuration注解表示这个类是一个配置类,它标识这个类中定义了Bean的配置方法。被标注为@Configuration的类中的方法通常使用@Bean注解来声明Bean。配置类中的@Bean方法会被Spring容器调用,返回的对象会被注册为Spring容器中的Bean。@Configuration注解可以理解为替代了传统的XML配置文件。
这种纯Java的配置方式,在SpringBoot中随处可见!