目录
IOC接口
- IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
- Spring提供IOC容器实现的两种方式:
- ApplicationContext :BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员使用
- BeanFactory :IOC容器基本实现,是Spring里面内部使用接口,不提供开发人员进行使用
- BeanFactory 在加载配置文件的时候,它不会把里面的对象创建,它只会加载我们的配置文件,当在获取的时候它才会创建对象。
什么时候用的时候才开始创建对象
- ApplicationContext 在加载配置文件的时候,就会把配置文件对象进行创建
- ApplicationContext :BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员使用
ApplicationContext 详解
- FileSystemXmlApplicationContext : 配置文件为系统盘的文件
- ClassPathXmlApplicationContext : 配置为类路径下的文件
IOC操作Bean管理
- Bean管理指的是两个操作
- ① Spring创建对象 :Spring通过Xml解析再通过工厂模式来进行创建对象
- ② Spring 注入属性 :Spring可以使用类似于Set对象的操作对对象或者字注入值
- Bean管理操作有两种方式
- ①基于xml配置文件方式实现
- ②基于注解方式实现
基于xml方式
基于xml方式创建对象
① 在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建
② 在bean标签有很多属性,介绍常用的属性
- id属性 : 获取对象中的唯一的标识
- class属性 :类全路径(包类路径)
- name属性 :name的作用与id一样,name属性可以添加特殊符号
③ 创建对象的时候,默认也是执行无参的构造方法完成对象的创建
基于xml方式注入属性
DI : 依赖注入,就是注入属性
使用set方法进行注入
在spring配置文件配置对象创建,配置属性注入(使用Set方式注入)
通过有参数的构造进行注入
在spring配置文件配置对象创建,配置属性注入(使用有参数构造注入)
p 名称空间注入(了解)
使用 p 名称空间注入,可以简化基于 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 //添加 p 名称空间在配置文件中
http://www.springframework.org/schema/beans/spring-beans.xsd">
//进行属性注入,在 bean 标签里面进行操作
<bean id="book" class="com. atguigu. spring5.Book" p:bname="九阳神功"
p: bauthor="无名氏">×/bean>
</beans>
基于xml方式注入其他类型属性
- 字面量
- null 值
<property name="address">
<null/>
</property>
- 属性值包含特殊符号
<!--属性值包含特殊符号
1 把<>进行转义 < >
2 把带特殊符号内容写到 CDATA
-->
<property name="address">
<value><![CDATA[<<南京>>]]></value>
</property>
- 注入属性-外部 bean (外部调用对象的方法)
- 创建两个类 ,service 类和 dao 类
- 在 service 调用 dao 里面的方法 (对象userService调用userDaoImpl对象的方法)
- 在 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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--1 service和dao对象创建-->
<bean id="userService" class="com.atguigu.spring5.service.UserService">
<!--注入userDao对象
name属性:类里面属性名称
ref属性:创建userDao对象bean标签id值
-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>
</beans>
@Test
public void testBean1() {
//1 加载spring配置文件
ApplicationContext context =
new ClassPathXmlApplicationContext("bean2.xml");
//2 获取配置创建的对象
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
- 注入属性-内部 bean (内部属性包含对象)
① 一对多关系:部门和员工一个部门有多个员工,一个员工属于一个部门部门是一,员工是多
②在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示
部门类
//部门类
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
@Override
public String toString() {
return "Dept{" +
"dname='" + dname + '\'' +
'}';
}
}
员工类
//员工类
public class Emp {
private String ename;
private String gender;
//员工属于某一个部门,使用对象形式表示
private Dept dept;
//生成dept的get方法
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}
public void add() {
System.out.println(ename+"::"+gender+"::"+dept);
}
}
<?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-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!--设置对象类型属性-->
<property name="dept">
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="安保部"></property>
</bean>
</property>
</bean>
</beans>
@Test
public void testBean2() {
//1 加载spring配置文件
ApplicationContext context =
new ClassPathXmlApplicationContext("application.xml");
//2 获取配置创建的对象
Emp emp = context.getBean("emp", Emp.class);
emp.add();
}
- 注入属性-级联赋值 (都可以达到注入的作用)
第一种方法 直接赋值一个对象
<!--级联赋值-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
第二种方法 赋值里面的一个属性值
//生成dept的get方法
public Dept getDept(){
return dept:
}
<!--级联赋值-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property> // <!-- 一定先引用某个bean为属性赋值,才可以使用级联方式更新属性 -->
<property name="dept.dname" value="技术部"></property>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
xml 注入数组类型属性
- 注入数组类型属性
创建类,定义数组、list、map、set 类型属性,生成对应 set 方法
package com.tde.example.Entity;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Student {
//1 数组类型属性
private String[] courses;
//2 list集合类型属性
private List<String> list;
//3 map集合类型属性
private Map<String, String> maps;
//4 set集合类型属性
private Set<String> sets;
public void setSets(Set<String> sets) {
this.sets = sets;
}
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void test() {
System.out.println(Arrays.toString(courses));
System.out.println(list);
System.out.println(maps);
System.out.println(sets);
}
}
<?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">
<!--1 集合类型属性注入-->
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
<!--数组类型属性注入-->
<property name="courses">
<array>
<value>java课程</value>
<value>数据库课程</value>
</array>
</property>
<!--list类型属性注入-->
<property name="list">
<list>
<value>张三</value>
<value>小三</value>
</list>
</property>
<!--map类型属性注入-->
<property name="maps">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<!--set类型属性注入-->
<property name="sets">
<set>
<value>MySQL</value>
<value>Redis</value>
</set>
</property>
</bean>
</beans>
@Test
public void testCollection1() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean5.xml");
Stu stu = context.getBean("stu", Stu.class);
stu.test();
}
2. 在集合里面设置对象类型值
<!--创建多个 course 对象-->
<bean id="course1" class="com.atguigu.spring5.collectiontype.Course">
<property name="cname" value="Spring5 框架"></property>
</bean>
<bean id="course2" class="com.atguigu.spring5.collectiontype.Course">
<property name="cname" value="MyBatis 框架"></property>
</bean>
<!--注入 list 集合类型,值是对象-->
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
//学生所学多门课程
private List<Course> courseList;
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
- 把集合注入部分提取出来
- 在 spring 配置文件中引入名称空间 util
- 使用 util 标签完成 list 集合注入提取
Book类
package com.atguigu.spring5.collectiontype;
import java.util.List;
public class Book {
private List<String> list;
public void setList(List<String> list) {
this.list = list;
}
public void test() {
System.out.println(list);
}
}
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--1 提取list集合类型属性注入-->
<util:list id="bookList">
<value>易筋经</value>
<value>九阴真经</value>
<value>九阳神功</value>
</util:list>
<!--2 提取list集合类型属性注入使用-->
<bean id="book" class="com.atguigu.spring5.collectiontype.Book">
<property name="list" ref="bookList"></property>
</bean>
</beans>
@Test
public void testCollection2() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean6.xml");
Book book1 = context.getBean("book", Book.class);
Book book2 = context.getBean("book", Book.class);
book1.test();
book2.test();
System.out.println(book1);
System.out.println(book2);
}
xml 注入外部属性文件
- resource包下创建application.properties文件 添加外部文件数据库配置
jdbc.user=root
jdbc.password=atguigu
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
- 引入外部属性文件
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
- 配置bean
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
- 测试
@Test
public void testDataSource() throws SQLException {
ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
DataSource dataSource = ac.getBean(DataSource.class);
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
基于bean的作用域
① 在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围
在spring配置文件bean标签里面有属性scope用于设置单实例还是多实例。
所谓单例就是所有的请求都用一个对象来处理,比如我们常用的service和dao层的对象通常都是单例的,而多例则指每个请求用一个新的对象来处理
- singleton单例(默认):
- 概念:在IOC容器中,这个bean的对象始终为单实例
- 创建对象时间范围 :IOC容器初始化时侯创建对象
- prototype多例
- 概念 : 这个bean在IOC容器中有多个实例
- 创建对象时间范围 :获取bean时
② 如何配置bean
<bean class="com.atguigu.bean.User" scope="singleton"></bean> 设置scope值是singleton的时候,加载spring配置文件就会创建单实例对象
<bean class="com.atguigu.bean.User" scope="prototype"></bean> 设置scope值是prototype的时候,在调用getBean方法创建多实例对象
③ bean的生命周期 (从对象创建到对象销毁的过程)
我们都知道spring通过bean工厂+反射的模式来创建我们的对象,程序员就不用再进行手动创建对象了,哪里bean对象的生命周期是什么样的呢?
有七个步骤:
(1)通过构造器创建bean实例(无参构造)
(2)为bean的属性设置值和对其他bean引用(调用set方法)
(3)把bean实例传递bean后置处理器的方法
(4)调用bean的初始化方法(配置初始化方法)
(5)把bean实例传递bean后置处理器的方法
(6)使用bean(对象获取到了)
(7)当容器关闭,调用bean的销毁方法(需要配置销毁的方法)
上述就是bean对象执行过程
bean的后置处理器
bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,有两个默认方法
postProcessBeforeInitialization
postProcessAfterInitialization
package com.example.spring_01.entity;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("在初始化之前执行的方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("在初始化之后执行的方法");
return bean;
}
}
并且配置到IOC容器中
<bean id="myBeanProcessor" class="com.example.spring_01.entity.MyBeanProcessor"/>
需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行,即是每次创建对象都会打印上述日志
System.out.println(“在初始化之前执行的方法”);
System.out.println(“在初始化之后执行的方法”);
基于xml的自动装配
Spring 会在上下文中自动寻找,并自动给 Bean 装配属性(给属性赋值)
自动装配:指 Spring 容器在不使用标签的情况下,可以自动装配(autowire)相互协作的 Bean 之间的关联关系,将一个 Bean 注入其他 Bean 的 Property 中。
bean标签属性autowired,配置自动装配
autowire属性常用的两个值:
-
byName:会自动在容器上下文中查找:和自己对象的 set 方法后的值对应的 bean id;
-
byType:会自动在容器上下文中查找:和自己对象的属性类型相同的 bean。
-
注意:
使用 byName 时,自己对象的 set 方法后的值(首字母大写)必须与对应的 bean id 的值(首字母小写)相同
使用 byType 时,必须保证类型在整个配置文件中是唯一的
<bean id="userServiceImpl" class="com.example.spring_01.entity.UserServiceImpl" autowire="byName">
</bean>
<bean id="userDao" class="com.example.spring_01.entity.UserDaoImpl">
</bean>
比如说上面这段代码,byName就是通过Bean的id或者name,byType就是按Bean的Class的类型。
若autowire="byType"意思是通过 class="com.example.spring_01.entity.UserServiceImpl"来查找UserDaoImpl下所有的对象。
代码autowire="byName"意思是通过id="userDao"来查找Bean中的userDao对象
基于注解方式
和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测
里面程序元在开发中会遇到以下注解
- @Controller
- @Service
- @Repository
package org.springframework.stereotype;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
可以中注解看到这三个注解只是在@Component注解的基础上起了三个新的名字,三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。都是为了引用其他对象的方法
基于注解的自动装配
@Autowired注解
@Autowired中有属性required,默认值为true,因此在自动装配无法找到相应的bean时,会装配失败
可以将属性required的值设置为false,则表示能装就装,装不上就不装,此时自动装配的属性为默认值
但是实际开发时,基本上所有需要装配组件的地方都是必须装配的,用不上这个属性。
@Autowired注解能够标识的位置
- ① 标识在成员变量上,此时不需要设置成员变量的set方法
- ② 标识在set方法上
- ③ 标识在为当前成员变量赋值的有参构造上
Autowired注解的原理
- 默认通过byType的方式,在IOC容器中通过类型匹配某个Bean为属性值赋值
- 若有多个类型匹配的bean,此时会自动转换为 byName的方式实现自动装配的效果,即将要赋值的属性的属性名作为bean的id匹配某个bean为属性赋值
- 若byType和byName的方式都无妨实现自动装配,即IOC容器中有多个类型匹配的bean且这些bean的id和要赋值的属性的属性名都不一致,此时抛异常:NoUniqueBeanDefinitionException,此时可以在要赋值的属性上,添加一个注解Qualifier,通过该注解的value属性值,指定某个bean的id,将这个bean为属性赋值
@Autowired工作流程
99%的情况都是直接使用AutoWired,因为ioc容器中只有一个类型的bean
- 首先根据所需要的组件类型到IOC容器中查找
- 先根据类型去找,能够找到唯一的bean:直接执行装配
- 如果完全找不到匹配这个类型的bean:装配失败
注解实现的必须进行自动装配,否则直接报异常
- 和所需类型匹配的bean不止一个
- 没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为bean的id(**转换为byName方式进行匹配)**进行匹配
- 能够找到:执行装配
- 找不到:装配失败
- 使用@Qualifier注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配
- 能够找到:执行装配
- 找不到:装配失败
@Controller
public class UserController {
@Autowired
@Qualifier("userServiceImpl")
private UserService userService;
public void saveUser(){
userService.saveUser();
}
}
自动装配的注解@AutoWired,@Resource
在使用自动装配的时候,出了可以使用@AutoWired注解之外,还可以使用@Resource注解,大家需要知道这两个注解的区别。
- @AutoWired:是spring中提供的注解,@Resource:是jdk中定义的注解,依靠的是java的标准
- @AutoWired默认是按照类型进行装配,默认情况下要求依赖的对象必须存在,@Resource默认是按照名字进行匹配的,同时可以指定name属性。
- @AutoWired只适合spring框架,而@Resource扩展性更好