IOC容器
1、IOC底层原理
2、IOC接口(BeanFactory)
3、IOC操作Bean管理(基于xml)
4、IOC操作Bean管理(基于注解)
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
1、IOC底层原理
-
IOC概念和原理
1)控制反转,把对象的创建和对象之间的调用过程,交给Spring进行管理。
2)使用IOC目的:为了耦合度降低。 -
IOC底层原理
1)xml解析、工厂模式、反射
在UserService类的excute方法中调用UserDao中调用add方法
原始方式:需要先创建对象,再调用方法实现。耦合度太高
工厂模式:
反射:通过得到类的字节码文件操作类中的所有内容
三种方式获取Class类型的对象:
- 使用类的Class属性获取该类对应的Class对象。如:Student.class
- 调用对象的getClass()方法,返回该对象所属类对应的Class对象
- 该方法是Object类中的方法,所有java对象都可以调用该方法
使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整报名的路径
IOC过程解读:配置xml文件,在工厂类中首先解析xml文件获取class对应的属性值(com.atguigu.UserDao),通过反射的方式使用静态方法forName(classValue)获取对象,通过clazz.newInstan ce()完成对象的创建,在对其进行强转(UserDao)clazz.newInstance()。
2、IOC接口(BeanFactory)
-
IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
-
Spring里面提供了两种IOC容器的实现方式(两个接口)
1)BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用。
加载配置文件的时候,BeanFactory不会创建对象;在获取对象(使用)时才会去创建对象
2)ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
ApplicationContext在加载配置文件的时候就会把在配置文件的对象进行创建 -
ApplicationContext接口有实现类:
两个主要实现类:
FileSystemXmlApplicationContetx
ClassPathXmlApplicationContetx
FileSystem后面需要的是盘符路径
ClassPath后面需要的是类路径
3、IOC操作Bean管理
- Bean管理指的是两个操作
1)Spring创建对象
2)Spring注入属性
- Bean管理操作的两种实现方式
1)基于xml配置文件的方式
2)基于注解@方式实现
4、IOC操作Bean管理(基于xml方式)
1)基于xml方式创建对象
(1)在Spring配置文件中使用bean标签,标签里边添加对应的属性,就可以实现对象的创建。
(2)在bean标签里有很多的属性,介绍最常用的属性:
—id属性:唯一标识
—class属性:要创建对象所在类的全路径(全类属性\包类属性)
(3)创建对象时,默认也是执行无参构造方法,完成对象创建。
2)基于xml方式注入属性
(1)DI:依赖注入,就是注入属性,需要在创建对象的基础之上进行完成,即先创建对象再注入属性
第一中注入方式:使用set构造方法进行注入
第二种注入方式:使用有参构造方法进行注入
public class Book {
private String bname;
public void setBname(String bname) {
this.bname = bname;
}
public static void main(String[] args) {
Book book = new Book();
book.setBname("abc");
}
}
public class Book {
private String bname;
//有参构造注入
public Book(String bname) {
this.bname = bname;
}
public static void main(String[] args) {
Book book = new Book("abc");
}
}
- 使用xml方式注入属性,第一种使用set方法进行注入
(1)创建类,定义属性和对应的set方法
package com.lyy.spring5;
/*
* 演示使用set方法进行注入属性
* */
public class Book {
//创建属性
private String bname;
private String bauthor;
//创建属性对应的Set方法
public void setBname(String bname) {
this.bname = bname;
}
public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
}
(2)在Spring配置文件中配置对象的创建,配置属性注入bean、property标签
<!-- 先创建对象,再set方法注入属性 -->
<bean id="book" class="com.lyy.spring5.Book">
<!--使用property标签完成属性注入
name:类里面属性的名称
value:向属性注入的值
-->
<property name="bname" value="易筋经"></property>
<property name="bauthor" value="达摩老祖"></property>
</bean>
- 使用xml方式注入属性,第二种使用有参构造方法进行注入
(1)创建类,定义属性,创建属性对应有参构造方法
/*
* 使用有参构造注入
* */
public class Order {
//属性
private String oname;
private String address;
//有参构造
public Order(String oname,String address){
this.oname = oname;
this.address = address;
}
}
(2)在Spring配置文件中进行配置,使用bean、constructor-arg标签
<bean id="order" class="com.lyy.spring5.Order">
<constructor-arg name="oname" value="phone"></constructor-arg>
<constructor-arg name="address" value="China"></constructor-arg>
</bean>
总结(两种属性注入的方式):
1.在使用set方法进行属性注入时,在xml中通过bean标签中的id属性和class属性赋值完成创建对象,在通过property标签中name属性和value属性赋值完成注入。
2.在使用有参构造方法属性注入时,在xml中通过bean标签中的id属性和class属性赋值完成创建对象,在通过constructor-arg标签中的name属性和value属性赋值完成注入
5、p名称空间注入(了解一下,实际应用不多)
(1)使用p名称空间注入,可以简化xml配置方式
第一步添加p名称空间在配置文件中
(2)进行属性注入,在bean标签里边进行操作(以前面book类为例,底层还是用的set方法注入)
<!-- 先创建对象,再set方法注入属性 -->
<bean id="book" class="com.lyy.spring5.Book" p:bname="九阳神功" p:bauthor="无名氏">
</bean>
IOC 操作Bean管理(xml注入其他类属性)
1、字面量
(1)null值,不加入属性的value,而是在property标签中添加null标签
<!-- null值-->
<property name="address">
<null/>
</property>
(2)属性值包含特殊符号
<!-- 属性值中包含特殊符号
1、把<>进行转义
2、把带特殊符号内容写到CDATA
-->
<property name="address">
<value><![CDATA[<<南京>>]]></value>
</property>
2、注入属性—外部bean,外部写一个bean,通过ref进行引入
(1)创建两个类service类和dao类
(2)在service调用dao方法
(3)在Spring配置文件中进行配置
import com.dao.UserDao;
import com.dao.UserDaoImpl;
public class UserService {
//创建UserDao类型属性,生成set方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add(){
System.out.println("service add.........");
userDao.update();
}
}
<!--service 和 dao 对象创建-->
<bean id="userService" class="com.service.UserService">
<!-- 注入userDao对象
name属性值:类里面属性名称
ref属性:创建userDao对象bean标签id值
-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.dao.UserDaoImpl"></bean>
3、注入属性—内部bean
(1)一对多关系:部门和员工
一个部门可以有多个员工,而一个员工属于一个部门
(2)在实体类之间表示一对多关系,员工表示所属的部门,使用对象类型属性进行表示
//部门类
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
}
//员工类
public class Emp {
private String ename;
private String gender;
//员工属于某一个部门,使用对象形式表示
private Dept 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;
}
}
(3)在Spring配置文件中进行配置(内部bean)
<!--内部bean-->
<bean id="emp" class="com.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="张三丰"></property>
<property name="gender" value="男"></property>
<property name="dept">
<bean id="dept" class="com.bean.Dept">
<property name="dname" value="保安部"></property>
</bean>
</property>
</bean>
</beans>
4、注入属性—级联赋值(其他代码同3)
<!--级联赋值 第一种写法-->
<bean id="emp" class="com.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="张三丰"></property>
<property name="gender" value="男"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
<!--级联赋值 第二种写法-->
<bean id="emp" class="com.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="张三丰"></property>
<property name="gender" value="男"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="技术部"></property>
</bean>
<bean id="dept" class="com.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
IOC 操作Bean管理(xml注入集合属性)
1.注入数组类型属性--------property、array标签
2.注入List集合类型属性----------property、list标签
3.注入Map集合类型属性--------property、map、entry标签
4.注入Sst集合类型属性--------property、set标签
(1)创建类,定义数组、list、map、set类型属性,生成对应set方法
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Stu {
//数组类型属性
private String[] courses;
//创建List集合类型属性
private List<String> list;
//创建Map集合类型属性
private Map<String,String> map;
//set集合类型属性
private Set<String> set;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setSet(Set<String> set) {
this.set = set;
}
}
(2)在Spring配置文件中进行配置
<bean id="stu" class="com.lyy.spring5.collectiontype.Stu">
<!--数组类型属性注入 -->
<property name="courses">
<array>
<value>java</value>
<value>javaWeb</value>
</array>
</property>
<!--list类型属性注入 -->
<property name="list">
<list>
<value>张三丰</value>
<value>李四</value>
</list>
</property>
<!--map类型属性注入 -->
<property name="map">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<!--set类型属性注入 -->
<property name="set">
<set>
<value>Mysql</value>
<value>Redis</value>
</set>
</property>
</bean>
4、在集合里设置对象类型值
//学生所学的多门课程
private List<Course> courseList;
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
<!--list类型属性注入,值是对象 -->
<bean>
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
<!--创建多个course对象 -->
<bean id="course1" class="com.lyy.spring5.collectiontype.Course">
<property name="cname" value="Spring5框架"></property>
</bean>
<bean id="course2" class="com.lyy.spring5.collectiontype.Course">
<property name="cname" value="Mybatis框架"></property>
</bean>
5、把集合注入部分提取出来
(1)在Spring配置文件中引入名称空间---------------------util
<?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">
(2)使用util标签完成list集合注入提取
IOC 操作Bean管理(FactoryBean)
1、Spring有两种类型Bean,一种普通bean,另一种工厂bean(FactoryBean)
2、普通bean:在Spring配置文件中定义的bean类型就是返回类型
3、工厂bean:在配置文件定义bean类型可以和返回类型不一样
第一步:创建类,让这个类作为工厂bean,实现接口FactoryBean
第二步:实现接口方法,在实现的方法中定义返回的bean类型
import com.lyy.spring5.collectiontype.Course;
import org.springframework.beans.factory.FactoryBean;
public class MyBean implements FactoryBean<Course> {
//定义返回bean
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
@Test
public void testCollection3(){
//加载配置文件创建对象
ApplicationContext context =
new ClassPathXmlApplicationContext("bean6.xml");
Course course = context.getBean("myBean", Course.class);
System.out.println(course);
}
<bean id="myBean" class="com.lyy.spring5.factorybean.MyBean"></bean>
IOC 操作Bean管理(bean的作用域)
1、在Spring里面,可以设置bean实例是单实例还是多实例
2、在Spring里面,在默认情况下,bean是一个单实例对象
3、如何设置bean的单实例和多实例
(1)在Spring配置文件,bean标签里有个属性(scope)用于设置单实例还是多实例
第一个值 默认值,singleton表示单实例对象
<bean id="book" class="com.lyy.spring5.collectiontype.Book" scope="singleton">
<property name="list" ref="bookList"></property>
</bean>
第二个值 prototype,表示是多实例对象
<bean id="book" class="com.lyy.spring5.collectiontype.Book" scope="prototype">
<property name="list" ref="bookList"></property>
</bean>
多实例对象的地址值是不一样的
(2)singleton和prototype的区别
第一 :singleton单实例,prototype多实例
第二 :设置scope值是singleton时,加载spring配置文件时候就会创建单实例对象
设置scope值是prototype时,不是加载spring配置文件时候创建对象,而是在调用getBean方法的时候创建多实例对象
IOC 操作Bean管理(bean的生命周期)
1、生命周期
(1)从对象创建到对象销毁的过程
2、bean的生命周期
(1)通过构造器创建bean实例(执行无参数构造创建对象)
(2)为bean的属性设置值和对其他bean的引用(调用set方法)
(3)调用bean的初始化的方法(需要进行配置初始化的方法)
(4)bean可以使用了(对象获取到了)
(5)当容器关闭时候,调用bean的销毁方法(需要进行配置销毁的方法)
public class Orders {
//无参构造
public Orders(){
System.out.println("第一步执行无参构造创建bean实例");
}
private String oname;
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步调用set方法设置属性值");
}
//创建执行的初始化的方法
public void initMethod(){
System.out.println("第三部 执行初始化的方法");
}
//创建执行的销毁的方法
public void destroyMethod(){
System.out.println("第五步 执行销毁的方法");
}
}
<bean id="orders" class="com.lyy.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="phone"></property>
</bean>
@Test
public void testBean3(){
//加载配置文件创建对象
ApplicationContext context =
new ClassPathXmlApplicationContext("bean7.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四部 获取创建bean实例对象");
System.out.println(orders);
//手动让bean实例销毁
((ClassPathXmlApplicationContext)context).close();
}
}
3、bean的后置处理器,bean生命周期有七步操作
(1)通过构造器创建bean实例(执行无参数构造创建对象)
(2)为bean的属性设置值和对其他bean的引用(调用set方法)
(3)把bean实例传递bean后置处理器的方法
postProcessBeforeInitialization方法
(4)调用bean的初始化的方法(需要进行配置初始化的方法)
(5)把bean实例传递bean后置处理器的方法
postProcessAfterInitialization方法
(6)bean可以使用了(对象获取到了)
(7)当容器关闭时候,调用bean的销毁方法(需要进行配置销毁的方法)
4、演示添加后置处理器
(1)创建类、实现接口BeanPostProcessor,创建后置处理器
public class MybeanPost 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;
}
}
<!--配置后置处理器-->
<bean id="mybeanPost" class="com.lyy.spring5.bean.MybeanPost"></bean>
IOC 操作Bean管理(xml自动装配,在实际中用的很少,实际中用注解的方式较多)
1、自动装配
(1)根据指定装配规则(属性名称或属性类型),Spring自动将匹配的属性值进行注入
2、自动装配过程
(1)根据属性名称自动注入
<!--实现自动装配
bean标签属性autowire,配置自动装配
autowire属性常用两个值:
byName根据属性名称注入 ,注入值bean的id值和类属性名称一样
byType根据属性类型注入
-->
<bean id="emp" class="com.lyy.spring5.autowire.Emp" autowire="byName">
<!-- <property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.lyy.spring5.autowire.Dept"></bean>
(2)根据属性名称自动注入
<!--实现自动装配
bean标签属性autowire,配置自动装配
autowire属性常用两个值:
byName根据属性名称注入 ,注入值bean的id值和类属性名称一样
byType根据属性类型注入
-->
<bean id="emp" class="com.lyy.spring5.autowire.Emp" autowire="byType">
<!-- <property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.lyy.spring5.autowire.Dept"></bean>
IOC 操作Bean管理(外部属性文件)
1、直接配置数据库信息
(1)配置德鲁伊连接池
(2)引入德鲁伊连接池依赖jar包
<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 获取properties文件内容,根据key获取,使用spring表达式获取 -->
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://******:3306/lyy"></property>
<property name="username" value="root"></property>
<property name="password" value="******"></property>
</bean>
2、引入外部属性文件配置数据库连接池
(1)创建外部属性文件,properties格式文件,写数据库信息
(2)把外部properties属性文件引入到Spring配置文件中
通过引入context名称空间把外部properties属性文件引入到Spring配置文件中
(3)在Spring配置文件中使用标签引入外部属性文件
第一步通过context:property-placeholder标签将properties属性文件引入;在value值的获取上使用 **${properties文件中等式左边内容}**这样就可以对外部属性文件中的内容进行注入。
<!--引入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
直接配置连接池
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 获取properties文件内容,根据key获取,使用spring表达式获取 -->
<property name="driverClassName" value="${prop.driverClassName}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.username}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
IOC 操作Bean管理(基于注解方式)
注解的概念:注解是代码里面的特殊标记,格式为:@注解名称(属性名称=属性值,属性名称=属性值)
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
使用注解的目的:简化xml配置
1、Spring针对Bean管理中创建对象提供的注解:
(1)@Component
(2)@Service
(3)@Controller
(4)@Repository
上面的四个注解功能是一个的,都可以用来创建bean实例
3、基于注解方式实现对象创建
第一步 引入依赖
第二步 开启组件扫,(开启之后Spring才能找到注解部分)
引入context名称空间,利用context:component-scan实现对包的扫描
<!-- 开启组件扫描
1、如果要扫描多个包,多个包的路径之间使用逗号隔开 base-package="com.lyy.spring5.dao,com.lyy.spring5.service"
2、写扫描包的上层目录 base-package="com.lyy"
-->
<context:component-scan base-package="com.lyy"></context:component-scan>
第三步 创建类,在类上面添加对象注解
//在注解里面value属性值可以省略不写,
//如果不写就是默认首字母小写的类名称
@Component(value = "userService") //<bean di="userService" class=""/>
public class UserService {
public void add(){
System.out.println("add............");
}
}
4、开启组件扫描中一些细节的配置
<!--示例1
use-default-filters="false"表示现在不使用默认的filter,自己配置filter
context:include-filter 设置要扫描那些内容
-->
<context:component-scan base-package="com.lyy" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<!--到包里面只扫描带Controller注解的类 -->
</context:component-scan>
<!--示例2
下面配置扫描包里所有内容
context:exclude-filter 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.lyy">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<!-- 不扫描Controller注解的类 -->
</context:component-scan>
- 在context:component-scan标签中添加use-default-filters="false"属性,表示不扫描所有内容而是扫描由自己设置的内容;context:include-filter标签表示扫描的内容包含哪些
- context:exclude-filter标签,表示哪些内容不进行扫描
5、基于注解方式实现属性的注入(三种方式)
(1)@Autowired :根据属性类型进行自动注入
第一步 把service和dao对象创建,在service和dao类添加创建对象注解
第二步 在service注入dao对象,在service类添加dao类型属性,在属性上面使用注解
@Service(value = "userService") //<bean di="userService" class=""/>
public class UserService {
//定义dao类型属性
//不需要添加set方法
@Autowired
private UserDao userDao;
public void add(){
System.out.println("add............");
userDao.add();
}
}
(2)@Qualifier :根据属性名称进行注入
这个@Qualifier注解的实验,要和上面@Autowired一起使用
@Service(value = "userService") //<bean di="userService" class=""/>
public class UserService {
//定义dao类型属性
//不需要添加set方法
@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl1")//根据名称进行注入
private UserDao userDao;
public void add(){
System.out.println("add............");
userDao.add();
}
}
(3)@Resource :可以根据类型注入,可以根据名称注入
// @Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
(4)@Value : 注入普通类型属性,替代之前property 操作
6、纯注解开发
(1)创建配置类,用于替代xml配置文件
package com.lyy.spring5.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//添加注解,把当前类作为配置类,用于替代xml配置文件
@Configuration //作为配置类,替代xml配置文件
@ComponentScan(basePackages = {"com.lyy"})
//第二个注解等价于 <context:component-scan base-package="com.lyy"></context:component-scan>,表示需要扫描的包
public class SpringConfig {
}
(2)编写测试类,写法与xml有区别,加载配置类的方式
@Test
public void testService2(){
//通过加载配置文件方式
//ApplicationContext context = new ClassPathXmlApplicationContext("bean9.xml");
//加载配置类,配置类的前提条件是加上@Configuration注解以及其他配置
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
总结:
- 掌握底层原理:工厂模式+反射
- 基于配置文件方式创建对象,注入属性
- 基于注解方式创建对象,注入属性
- 完全注解开发,用一个配置类替代配置文件