IOC底层原理
- IOC概念 :面向对象编程中的一种设计原则,用来降低耦合度
- 通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象引用传递给他。可以说,依赖被注入到对象中。
- 控制反转,把对象创建和对象之间的调用过程,都交给Spring进行管理
- 使用IOC的目的:降低耦合度
- 1214里面的入门案例就是IOC实现
- IOC底层原理
- 主要技术:xml解析、工厂模式、反射
-
上述代码存在问题:耦合度太高class UserDao{ add(){ ...... } } class UserService(){ execute(){ UserDao dao = new UserDao(); dao.add(); } }
- 解决方案:目的:耦合度降低到最低限度
- 工厂模式:
class UserFactory{ public static UserDao getDao() { return new UserDao(); } } class UserService(){ execute(){ UserDao dao = UserFactory.getDao(); dao.add(); } } // 并没有降低到最低限度耦合度
- IOC原理(xml解析、工厂模式、反射)
- 第一步:XML配置文件,配置创建的对象
<bean id ="dao" class = "com.emnets.UserDao"></bean>
- 第二步:有service类和dao类,创建工厂类
class UserFactory{ public static UserDao getDao(){ // 通过xml解析+反射的方式创建对象 String classValue = class属性值; // xml解析 Class clazz = Class.froName(classValue); // 反射创建对象 return (UserDao)clazz.newInstance(); } }
- 进一步降低了耦合度,例如包名改了,这里只需要修改配置文件的路径即可。
- 第一步:XML配置文件,配置创建的对象
- 工厂模式:
IOC接口:BeanFactory和ApplicationContext
- IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
- Spring提供IOC容器实现两种方式(两个接口,用于加载配置文件创建对象):
- BeanFactory:IOC容器基本实现,是Spring内部使用接口,一般不提供给开发人员使用
- ApplicationContext:是BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用
- 二者区别:
- BeanFactory:加载配置文件时不会创建对象,在获取(使用)对象时才去创建对象
- ApplicationContext:加载配置文件时就会把配置文件对象进行创建(服务器启动时就会加载配置文件并创建好对象)
- ApplicationContext的实现类:
- ClassPathXmlApplicationContext:参数写文件在类内src下的路径
- FileSystemXmlApplicationContext:参数写文件在文件系统下的路径
IOC操作:Bean管理
- 什么是Bean管理,指的是两个操作
- Spring创建对象
- Spring注入属性
- Bean管理操作有两种方式:
- 基于XML实现
- 基于注解方式实现
IOC操作:Bean管理(基于XML)
- 基于xml方式创建对象:
- 在apring配置文件中,使用bean标签,添加上对应的属性,就可以实现对象创建
- 在bean标签有很多属性,介绍常用属性
- id:唯一标识
- class:创建对象类的全路径(包类路径)
- name:跟id一样,name可以加特殊符号,id不行;name基本不用
- 创建对象时,默认执行无参数的构造方法,方法里面必须有无参构造函数否则报错
- 基于xml方式注入属性:DI:依赖注入,就是注入属性
- 第一种方式:set方式
- 第一步:创建类,定义属性和对应的set方法
- 第二步: 在spring配置文件配置对象创建,配置属性注入
<bean id="user" class="com.emnets.spring5.testdemo.User"> <!-- set方法注入属性:使用property标签注入属性--> <!-- name:属性名 ; value:属性值--> <property name="username" value="dyg"></property> </bean>
- 注入依赖要在创建对象基础之上完成
- 第二种方式:有参构造函数
- 第一步:定义属性,创建对应有参构造函数
- 第二步:配置文件中进行配置
<bean id="user" class="com.emnets.spring5.testdemo.User"> <!-- set方法注入属性:使用property标签注入属性--> <property name="username" value="dyg"/> <!-- 有参构造注入--> <!-- name:属性名 ;index:属性索引 ; value:属性值--> <constructor-arg name="username" value="zh"/> <constructor-arg index="0" value="zh"/> </bean>
- P.S. 有参构造执行在set之前
- p 名称空间注入(了解)
- 使用 p 名称空间注入,可以简化基于 xml 配置方式 (本质是对set的简化)
- 第一步 添加 p 名称空间在配置文件中
在<beans>标签里面加入:xmlns:p="http://www.springframework.org/schema/p"
- 第二步 进行属性注入,在 bean 标签里面进行操作
<!--2 set 方法注入属性--> <bean id="book" class="com.atguigu.spring5.Book" p:bname="九阳神功"p:bauthor="无名氏"></bean>
- 第一种方式:set方式
- xml注入其他类型属性
- 字面量(设置的固定值)
- null :
<property name="username"> <null></null> </property>
- 属性值保护特殊符号
<!-- 例如包含<>符号--> <!-- 1.可以使用&l和>做转义字符--> <!-- 2.把特殊符号内容写到CDATA--> <property name="username" > <value> <![CDATA[<<dyg>>]]> </value> </property>
- null :
- 注入属性-外部bean
- 创建两个类service和dao类
- service调用dao里面的方法
- 在spring配置文件中进行配置
<bean id = "userService" class="com.emnets.service.UserService"> <!-- 注入userDao对象属性,ref:创建userDao对象bean标签id值 --> <property name="userDao" ref="userDaoImpl"></property> </bean> <bean id = "userDaoImpl" class="com.emnets.dao.userDaoImpl"></bean>
- 注入属性-内部bean
- 一对多关系:部门和员工
- 在实体类之间表示一对多的关系;员工使用一个对象类型属性表示
- 在spring进行相关配置
<!-- 内部bean操作--> <bean id="emp" class="com.emnets.spring5.bean.Emp"> <property name="ename" value="lucy"></property> <property name="gender" value="girl"></property> <!-- 设置对象类型属性,可以按照外部bean方式,建一个对象然后引入--> <property name="dept"> <bean id="dept" class="com.emnets.spring5.bean.Dept"> <property name="dname" value="安保部"></property> </bean> </property> </bean>
- 注入属性-级联赋值
- 写法一:
<!-- 内部bean操作--> <bean id="emp" class="com.emnets.spring5.bean.Emp"> <property name="ename" value="lucy"></property> <property name="gender" value="girl"></property> <!-- 级联赋值 --> <property name="dept" ref="dept"></property> </bean> <bean id="dept" class="com.emnets.spring5.bean.Dept"> <property name="dname" value="finance"></property> </bean>
- 写法二:
<!-- 内部bean操作--> <bean id="emp" class="com.emnets.spring5.bean.Emp"> <property name="ename" value="lucy"></property> <property name="gender" value="girl"></property> <!-- 级联赋值 --> <property name="dept" ref="dept"></property> <property name="dept.dname" value="technological"></property> </bean> <bean id="dept" class="com.emnets.spring5.bean.Dept"> <property name="dname" value="finance"></property> </bean>
- 写法一:
- 注入集合类型属性 数组、list、map、set
<bean id="stu" class="com.emnets.spring5.collectiontype.Stu"> <!-- 数组类型注入 --> <property name="courses"> <array> <value>java课程</value> <value>数据库</value> <value>ai课程</value> </array> </property> <!-- list类型属性注入 --> <property name="list"> <list> <value>张三</value> <value>老三</value> </list> </property> <property name="maps"> <map> <entry key="JAVA" value="java"></entry> <entry key="PHP" value="php"></entry> </map> </property> <property name="sets"> <set> <value>MySQL</value> <value>Redis</value> </set> </property> </bean>
- 在集合里面设置对象类型值
- 创建多个对象
- 在属性值里面用ref引入对象的id值
<bean id="stu" class="com.emnets.spring5.collectiontype.Stu"> <!-- 注入list集合类型,值是对象 --> <property name="courseList"> <list> <ref bean="course1"></ref> <ref bean="course2"></ref> </list> </property> </bean> <!-- 创建多个course对象 --> <bean id="course1" class="com.emnets.spring5.collectiontype.Course"> <property name="cname" value="Spring5"></property> </bean> <bean id="course2" class="com.emnets.spring5.collectiontype.Course"> <property name="cname" value="Mybatis"></property> </bean>
- 把集合注入部分提取出来
- 在spring配置文件中引入名称空间util
<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: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">
- 使用util标签完成list集合注入提取
<!-- 用于提取的bean不同于普通bean的写法 --> <!-- 1.提取list集合类型属性注入--> <util:list id="bookList"> <value>图书1</value> <value>图书2</value> <value>图书3</value> </util:list> <!-- 2.提取list集合类型属性注入使用--> <bean id="book" class="com.emnets.spring5.bean.Book"> <!-- <property name="dept" ref="dept"></property>--> </bean>
- 在spring配置文件中引入名称空间util
- 字面量(设置的固定值)
- FactoryBean
- Spring有两种bean,一种普通bean(用户创建),另一种工厂bean(FactoryBean)
- 普通bean特点:配置文件中定义bean类型就就是返回类型
- 工厂bean特点:配置文件中定义bean类型可以和返回值不一样, 类型取决于实现方法的getObject方法定义的范型
- 第一步:创建类,让这个类作为工厂bean,实现接口FactoryBean
- 第二步:实现接口方法,在实现的方法中定义返回的bean类型
public class MyBean implements FactoryBean<Course> { // 定义返回bean @Override public Course getObject() throws Exception { Course course = new Course(); course.setCname("adv"); return course; } } @Test public void testBean6(){ // 1.加载spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml"); // 2.获取配置文件 Course c = context.getBean("MyBean", Course.class); System.out.println(c); }
- Bean的作用域
- 含义:spring可以设置创建的bean是单实例还是多实例
- 在Spring里面,默认情况下,bean是一个单实例
- 在读取同一个bean的配置文件创建出的对象时,如果地址相同则是单实例,不同则为多实例。
- 设置单实例还是多实例:bean标签里面有相应的属性进行设置:scope
- 默认值:singleton,表示单实例对象
- 第二个值:prototype,表示多实例对象
- 二者区别:设置singleton时,加载配置文件时Spring就会创建单实例对象 ; 设置prototype时,在调用context.getBean方法时才会去创建多实例对象。
- 第三个值:request,此时每次创建对象放到request域对象中
- 第四个值:session,此时每次创建对象都会放到session域对象中
- bean生命周期
- 构造器创建
- 为bean的属性设置值,和对其他bean引用(调用bean对象的set方法)
- 调用bean初始化方法的方法(需要进行配置初始化)
- bean可以使用(对象获取到了)
- 当容器关闭时,调用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.atguigu.spring5.bean.Orders" init- method="initMethod" destroy-method="destroyMethod"> </bean>
@Test public void testBean3() { ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml"); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml"); Orders orders = context.getBean("orders", Orders.class); System.out.println("第四步 获取创建 bean 实例对象"); System.out.println(orders); //手动让 bean 实例销毁 context.close(); }
- bean的后置处理器,bean生命周期有七步,在初始化前后会多两步操作
- 构造器创建
- 为bean的属性设置值,和对其他bean引用(调用bean对象的set方法)
- 把bean实例传递bean后置处理器的方法postProcessBeforeInitialization
- 调用bean初始化方法的方法(需要进行配置初始化)
- 把bean实例传递bean后置处理器的方法postProcessAfterInitialization
- bean可以使用(对象获取到了)
- 当容器关闭时,调用bean销毁方法(需要进行配置销毁)
- 添加后置处理器效果
- 创建类,实现接口BeanPostProcessor,创建后置处理器
- 在所需要添加配置后置处理器的bean对象的配置文件添加,spring会对整个配置文件的bean都添加处理器
- Bean管理xml自动装配
- 什么是自动装配:通过value等属性值,注入属性叫做手动装配;根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入
- 演示自动装配过程
<!--实现自动装配bean 标签属性 autowire,配置自动装配 autowire 属性常用两个值:byName 根据属性名称注入,注入值 bean 的 id 值和类属性名称一样 byType 根据属性类型注入,相同类型的bean只能定义一个 --> <bean id="emp" class="com.emnets.spring5.autowire.Emp" autowire="byName"> <!-- <property name="dept" ref="dept"></property>--> </bean> <bean id="dept" class="com.emnets.spring5.autowire.Dept"></bean>
- 外部属性文件
- 直接配置数据库信息
- 引入外部属性文件配置数据库连接池
- 创建外部属性文件,properties 格式文件,写数据库信息
- 把外部 properties 属性文件引入到 spring 配置文件中 * 引入 context 名称空间
IOC操作:Bean管理(基于注解)
- 注解回顾:
- 注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
- 使用注解,注解作用在类上面,方法上面,属性上面
- 使用注解目的:简化 xml 配置
- Spring针对Bean管理创建对象提供注解
- @Component 创建普通对象
- @Service 一般用在service层
- @Controller 用在web层
- @Repository 用在dao层
- * 上面四个注解功能是一样的,都可以用来创建 bean 实例
- 基于注解方式实现创建
- 第一步:引入依赖,spring-aop-5.2.6.RELEASE.jar
- 第二步:开启组建扫描
<!-- 开启扫描 1 如果扫描多个包,多个包使用逗号隔开 2 扫描包上层目录 --> <context:component-scan base-package="com.emnets.spring5"></context:component-scan>
- 第三步:创建类,在类上添加创建对象的注解
//在注解里面 value 属性值可以省略不写,相当于bean中的id // 默认值是类名称,首字母小写 // UserService -- userService @Component(value = "userService") // 等价于:<bean id ="userService" class = "package path"> public class UserService { public void add(){ System.out.println("Service add!"); } }
- 测试
@Test public void testService(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean01.xml"); UserService userService = context.getBean("userService", UserService.class); System.out.println(userService); userService.add(); }
- 开启组建扫描细节配置
<!-- 以下两个示例看懂即可 --> <!--示例 1 use-default-filters="false" 表示现在不使用默认filter,使用自己配置 filter context:include-filter ,设置扫描哪些内容 这段配置表示:关闭默认filter,自己设置的filter表示:扫描com.atguigu包下全部的controller --> <context:component-scan base-package="com.atguigu" use-default- filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!--示例 2 下面配置扫描包所有内容 context:exclude-filter: 设置哪些内容不进行扫描 --> <context:component-scan base-package="com.atguigu"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
- 属性注入
- @AutoWired 根据属性类型进行自动注入
- 第一步 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解
- 第二步 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解(不需要set方法!)
//在注解里面 value 属性值可以省略不写,相当于bean中的id // 默认值是类名称,首字母小写 // UserService -- userService @Component(value = "userService") // 等价于:<bean id ="userService" class = "package path"> public class UserService { // 定义Dao属性,并且不需要set方法! @Autowired //根据类型进行注入 private UserDao userDao; public void add(){ System.out.println("Service add!"); userDao.add(); } }
- @Qualifier 根据属性名称自动注入
- 这个注解的使用需要和上面的@AutoWired一起使用
- 根据类型名称注入,如果多个实现类继承一个接口,Qualifier的参数value就填对象创建注解里面的id值,例如@Repository (value = ‘userDaoImpl’),如果不填默认类名首字母小写的值
- @Resource 可以根据类型也可以根据名称
- 无参数使用时,默认根据类型进行注入
- 有参数使用时,根据name参数进行名称匹配注入
//@Resource // 根据类型进行注入 @Resource(name = "userDaoImpl1") // 根据名称进行注入 private UserDao userDao;
- Resource是javax包中的,是java的扩展包
- 注:以上但是都是用于对象类型注入,不是普通类型
- @Value:用于注入普通类型 , 进行赋值使用
@Value(value = "value annotation") private String name;
- 回顾:做这些配置的时候,全部用注解完成,仅仅写了一行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" 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"> <!-- 开启扫描 1 如果扫描多个包,多个包使用逗号隔开 2 扫描包上层目录 --> <context:component-scan base-package="com.emnets.spring5"></context:component-scan> </beans>
- @AutoWired 根据属性类型进行自动注入
- 完全注解开发
- 不使用配置文件,将之前那一行配置代码转移到注解中
- 第一步:创建配置类,替代xml配置文件
@Configuration // 作为配置类,替代配置文件 @ComponentScan(basePackages = {"com.emnets.spring5"}) public class SpringConfig { }
- 第二步:写测试类
@Test public void testService2(){ // 加载配置类 ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService", UserService.class); System.out.println(userService); userService.add(); }