1. Spring
官方文档:https://docs.spring.io/spring-framework/docs/5.2.0.RELEASE/spring-framework-reference/core.html#beans-factory-instantiation
- Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速开发单个微服务
- 约定大于配置
- Spring Cloud
- SpringCloud是基于SpringBoot实现的
2. IoC理论推导(Inversion of control)
控制反转IoC是一种设计思想,DI(依赖注入)是实现IoC的一种方式
-
UserDao 接口
public interface UserDao { void getUser(); }
-
UserDaoImpl 实现类
public class UserDaoImpl implements UserDao{ @Override public void getUser() { System.out.println("默认获取用户数据"); } }
public class UserDaoMySQLImpl implements UserDao{ @Override public void getUser() { System.out.println("MySQL获取用户数据!"); } }
public class UserDaoOracleImpl implements UserDao { @Override public void getUser() { System.out.println("获取Oracle用户数据!!"); } }
-
UserService 业务接口
public interface UserService { void getUser(); }
-
UserServiceImpl 业务实现类
public class UserServiceImpl implements UserService{ /*旧方法*/ // private UserDao userDao = new UserDaoImpl(); // private UserDao userDao = new UserDaoMySQLImpl(); // private UserDao userDao = new UserDaoOracleImpl(); /*新方法*/ private UserDao userDao; //利用set进行动态实现值的注入! public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void getUser() { userDao.getUser(); } }
在旧方法中,用户的需求改变可能会影响原来的代码,需要根据用户需求修改源代码!而修改源代码的成本非常昂贵!!!
而新方法中,通过控制反转可以将修改代码的主动权交给用户直接控制。也不用再去管理对象的创建了。系统的耦合性大大降低,可以更加专注在业务实现上!这就是IOC的原型!!
测试类
public class MyTest {
public static void main(String[] args) {
// 用户实际调用的是业务层,dao层他们不需要接触
UserServiceImpl userSeΩrvice = new UserServiceImpl();
// userService.setUserDao(new UserDaoSQLServerImpl());
// userService.setUserDao(new UserDaoMySQLImpl());
userService.setUserDao(new UserDaoOracleImpl());
userService.getUser();
}
}
3. applicationContext.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">
</beans>
<?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">
<!-- 使用Spring来创建对象,在Spring中这些都成为Bean
类型 变量名 = new 类型();
hello Hello = new hello();
id => 变量名
class => new的对象
property 相当于给对象重的属性设置一个值
-->
<bean id="Hello" class="com.inspur.pojo.hello">
<property name="str" value="Spring"/>
</bean>
<!-- 什么是控制反转???
控制:谁来控制对象的创建,传统应用程序是由程序自身控制对象的创建,使用Spring之后,对象由Spring控制创建。
反转:程序本身不创建对象,而变成被动的接收对象。
依赖注入:就是利用set方法来进行注入的。
IoC是一种编程思想,由主动创建对象变为被动接收对象。
-->
</beans>
4. IoC创建对象方式
实体类
package com.inspur.pojo;
public class User {
private String name;
// public User() {
// System.out.println("User的无参构造!!");
// }
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
-
使用无参构造创建对象,默认实现
<bean id="user" class="com.inspur.pojo.User"> <property name="name" value="spring-03-ioc2"/> </bean>
-
使用有参构造创建对象:三种方式
<!--第一种:下标赋值--> <bean id="user" class="com.inspur.pojo.User"> <constructor-arg index="0" value="下标赋值"/> </bean>
<!--不建议使用:多个参数类型数据--> <bean id="user" class="com.inspur.pojo.User"> <constructor-arg type="java.lang.String" value="类型赋值"/> </bean>
<!--第三种:直接通过参数名传参--> <bean id="user" class="com.inspur.pojo.User"> <constructor-arg name="name" value="liuyike"/> </bean>
当配置文件被加载的时候,容器中管理的对象就已经被初始化了。
5. Spring配置
5.1 别名
<bean id="user" class="com.inspur.pojo.User">
<constructor-arg name="name" value="liuyike"/>
</bean>
<alias name="user" alias="userNew"/>
<bean id="user" class="com.inspur.pojo.User" name="user2,user3;user4 user5">
<constructor-arg name="name" value="liuyike"/>
</bean>
5.2 Bean的配置
<!-- 使用Spring来创建对象,在Spring中这些都成为Bean
类型 变量名 = new 类型();
hello Hello = new hello();
id => 变量名
class => new的对象
property 相当于给对象重的属性设置一个值
-->
<bean id="Hello" class="com.inspur.pojo.hello">
<property name="str" value="Spring"/>
</bean>
5.3 import
可以将多人不同开发的配置文件合并到总配置文件。使用的时候直接使用总配置就好了。
6. 依赖注入
6.1 构造器注入(4.IoC创建对象方式)
6.2 Set方式注入【重点】
-
复杂类型
public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
-
真实测试对象
import java.util.*; public class Student { private String name; private Address address; private String[] books; private List<String> hobbies; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; }
-
applicationContext.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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <bean id="address" class="com.inspur.pojo.Address"> <property name="address" value="北京"/> </bean> <bean id="student" class="com.inspur.pojo.Student"> <!--第一种:普通注入,value--> <property name="name" value="刘依珂"/> <!--第二种:Bean注入,ref--> <property name="address" ref="address"/> <!--第三种:数组注入--> <property name="books"> <array> <value>红楼梦</value> <value>西游记</value> <value>水浒传</value> <value>三国演义</value> </array> </property> <!--第四种:List--> <property name="hobbies"> <list> <value>唱歌</value> <value>看电影</value> <value>敲代码</value> </list> </property> <!--第五种:Map--> <property name="card"> <map> <entry key="身份证" value="123456789012345678"/> <entry key="银行卡" value="0987654321234567890"/> </map> </property> <!--第六种:Set--> <property name="games"> <set> <value>ZXC</value> <value>ASD</value> <value>QWE</value> </set> </property> <!--赋空值--> <property name="wife"> <null/> </property> <property name="info"> <props> <prop key="学号">2019234441</prop> <prop key="性别">男</prop> <prop key="姓名">刘依珂</prop> </props> </property> </bean> </beans>
-
测试类
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = context.getBean("student", Student.class);); System.out.println(student.toString()); } }
6.3 拓展方式(p/c命名空间注入)
导入xml约束
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
配置文件
<!-- p命名空间注入,可以直接注入属性的值:property -->
<bean id="user" class="com.inspur.pojo.User" p:name="孔舒怡" p:age="18"/>
<!-- c命名空间注入,基于构造函数的依赖注入,因此必须要有有参构造函数:construct-args -->
<bean id="user2" class="com.inspur.pojo.User" c:name="liuyike" c:age="21"/>
<?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"
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">
<!--p命名空间注入,可以直接注入属性的值:property-->
<bean id="user" class="com.inspur.pojo.User" p:name="孔舒怡" p:age="18"/>
<!--c命名空间注入,基于构造函数的依赖注入,因此必须要有有参构造函数:construct-args-->
<bean id="user2" class="com.inspur.pojo.User" c:name="liuyike" c:age="21"/>
</beans>
6.4 bean的作用域
1. 单例模式(Spring默认机制)
只有一个单例 bean 的共享实例被管理,并且所有对具有与该 bean 定义匹配的一个或多个 ID 的 bean 的请求都会导致 Spring 容器返回一个特定的 bean 实例。
换句话说,当您定义 bean 定义并将其限定为单例时,Spring IoC 容器会创建该 bean 定义所定义的对象的一个实例。此单个实例存储在此类单例 bean 的缓存中,并且该命名 bean 的所有后续请求和引用都返回缓存的对象。
下图显示了单例作用域的工作原理:
<!--Spring默认为singleton模式,也可显式声明-->
<bean id="user2" class="com.inspur.pojo.User" c:name="liuyike" c:age="21" scope="singleton"/>
// singleton 单例模式测试
@Test
public void test03() {
User user = context.getBean("user2", User.class);
User user2 = context.getBean("user2", User.class);
System.out.println(user2 == user);
System.out.println(user.hashCode());
System.out.println(user2.hashCode());
}
// true
// 1738236591
// 1738236591
2. 原型模式:每次从容器中get的时候,都会产生一个新对象!
bean 部署的非单例原型范围导致每次对特定 bean 发出请求时都会创建一个新的 bean 实例。也就是说,将 bean 注入到另一个 bean 中,或者您通过
getBean()
容器上的方法调用来请求它。通常,您应该对所有有状态 bean 使用原型范围,对无状态 bean 使用单例范围。
<bean id="user2" class="com.inspur.pojo.User" c:name="liuyike" c:age="21" scope="prototype"/>
// singleton 单例模式测试
@Test
public void test03() {
User user = context.getBean("user2", User.class);
User user2 = context.getBean("user2", User.class);
System.out.println(user2 == user);
System.out.println(user.hashCode());
System.out.println(user2.hashCode());
}
// false
// 1558021762
// 225290371
3. 其余的request、session、application这些只能在web开发中使用到!
7. Bean的自动装配
- 自动装配是Spring满足bean依赖的一种方式!
- Spring会在上下文中自动寻找,并自动给bean装配属性
在Spring中有三种自动装配的方式
- 在xml中显式的配置
- 在java中显式的配置
- 隐式的自动装配
7.1 在xml中显式的配置
<bean id="cat" class="com.inspur.pojo.Cat"/>
<bean id="dog" class="com.inspur.pojo.Dog"/>
<!--byName:会自动在容器上下文中查找和自己对象 set 方法参数类型所对应的 bean !!
byType:会自动在容器上下文中查找和自己对象属性类型相同的 bean !!
-->
<bean id="people" class="com.inspur.pojo.People" autowire="byName">
<property name="name" value="liuyike"/>
</bean>
<bean id="cat" class="com.inspur.pojo.Cat"/>
<bean id="dog222" class="com.inspur.pojo.Dog"/>
<!--
byName:会自动在容器上下文中查找和自己对象 set 方法后面的值所对应的 bean !!
byType:会自动在容器上下文中查找和自己对象属性类型相同的 bean,但要保证和自己对象属性类型相同的 bean 唯一!且id可省略。
-->
<bean id="people" class="com.inspur.pojo.People" autowire="byType">
<property name="name" value="liuyike"/>
</bean>
小结:
- byName:需要保证所有的bean唯一,并且这个bean需要和自动注入的对象的set方法的参数类型一致!!
- byType:需要保证所有bean的class唯一,并且这个bean和自动注入的对象的属性类型一致。
测试类
@Test
public void test01() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
People people = context.getBean("people", People.class);
people.getCat().shout();
people.getDog().shout();
}
7.2 使用注解实现自动装配
使用须知:
-
导入约束
-
配置注解的支持:context:annotation-config
<?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/> </beans>
@Autowired注解
直接在属性上使用即可!也可在set方法上使用
使用**@Autowired注解**,我们可以省略set方法的编写,前提是这个自动装配的属性在IoC容器中存在,且符合byName!!
小知识:
@Autowired(required = false) //可以为空
//required 注解底层代码
public @interface Autowired {
boolean required() default true; //默认为真
}
private String name;
// @NUllable 字段标记了这个注解,表示该字段可以为null
public People(@Nullable String name) {
this.name = name;
}
@Qualifier注解
如果@Autowired自动装配环境比较复杂,自动装备无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!!
@Autowired
@Qualifier(value = "cat11") //bean的id
private Cat cat;
@Autowired
private Dog dog;
@Resource注解
@Resource(name = "cat11") //bean的id
private Cat cat;
@Resource
private Dog dog;
<bean id="cat11" class="com.inspur.pojo.Cat"/>
<bean id="dog" class="com.inspur.pojo.Dog"/>
<bean id="cat22" class="com.inspur.pojo.Cat"/>
<bean id="people" class="com.inspur.pojo.People"/>
小结:
@Resource 和 @Autowired 的区别:
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired 通过 byType 的方式实现,而且必须要求这个对象存在!【常用】
- @Resource 默认通过 byName 方式实现,如果找不到名字,则通过byType实现!!【类似于@Autowired集合体】
- 执行顺序不同:@Autowired 通过 byType 实现,@Resource 默认通过 byName 方式实现。
8. 使用注解开发
-
注册bean
@Component public class User { public String name = "刘依珂"; }
-
属性注入
@Component public class User { @Value("liuyike") public String name; }
注解说明:
-
@Autowired:自动装配通过类型。
- @Qualifier:如果属性有多个相同类型的不同名称,使用@Qualifier(value = “xxx”)
-
@Nullable:标记字段可以为空(null)
-
@Resource:自动装配通过名字,类型
-
@Component:组件,放在类上,说明这个类被Spring管理了,等同于在xml文件中注册bean!!!