1、spring简介(IOC)
Spring框架是一个开放源代码的J2EE应用程序框架,是针对bean的生命周期进行管理的轻量级容器。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC
1.1、编写第一个spring的案列
- 导入pom 文件
<!-- spring的核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.18</version>
</dependency>
<!--spring应用上下文的包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<!--spring bean管理的包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.18</version>
</dependency>
<!-- 有关java io输入输出的架包-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<!-- 单元测试的架包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
- 编写测试文件
public class TestDemo {
@Test
public void getAnimal() {
ApplicationContext applicationContext = new GenericXmlApplicationContext("./bean.xml");
Animal animal = applicationContext.getBean("animal", Animal.class);
System.out.println(animal);
animal.eat();
}
}
1.2、IOC容器
- 什么是IOC:
- 控制反转,将对象的创建和对象之间调用的过程都交给了spring进行管理
- 之前我们创建对象都是new出来的,使用IOC进行创建的话,我们将对象交给了spring容器进行管理,因此IOC降低了耦合度。
- IOC的底层原理:
- 工厂模式,可以解决耦合度高的问题,类与类之间不直接产生联系,而是通过工厂类的方式创建对象,后面引入话题ioc
- XML的解析、反射 IOC容器原理。
- IOC的过程:
- 创建xml配置文件,配置要创建的对象
- 通过xml解析的方式,加载到类的路径 eg: “com.hema.User”
- 通过反射Class.forName();获取到字节码文件,然后通过class.newInstance()创建对象返回.
- 解耦的原理,将类的路径放到了配置文件中,如果要改变类的话,可以直接进行改变xml配置文件,而不是到处更改类文件
1.3、IOC(接口)
- IOC思想基于IOC容器完成,IOC底层就是对象工厂- Spring提供IOC容器实现的两种方式(两个接口)
- BeanFactory: IOC容器基本的实现,是spring内部进行使用的接口,不提供给开发人员进行使用,在加载配置文件的时候,不会去创建配置文件中对象,只有在获取或者在使用对象的时候才去创建对象,
- ApplicationContext:是BeanFactory接口的子接口,提供更加强大的功能,提供给开发人员进行使用,在加载配置文件的时候就去创建对象,spring主要使用在web中,因此的话,当对象在服务启动的时候去创建,将耗时耗资源的步奏交给服务器进行处理,减少应用层面的压力
- ApplicationContext中主要的实现类
- FileSystemXmlApplicationContext, 使用该实现类的时候,需要将配置文件的绝对路径进行传入.
- ClasspathXmlApplicationContext,使用该实现类的时候,传入配置文件的类路径,相对路径
1.4、IOC的操作Bean管理
- 什么叫做Bean管理,就是两个操作
- Spring创建对象
- Spring注入属性
- Bean的管理操作有两种方式
- 基于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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="animal" class="com.hema.xiaohua.Animal">
</bean>
</beans>
1.4.1、IOC操作Bean管理基于XML方式
-
基于xml方式进行创建对象
- 在spring配置文件中使用bean标签,在标签中添加对应的属性,就可以实现对象的创建,在创建对象的时候是执行类的无参构造方法
- 在bean标签中有很多的属性:
- id属性:就是为要配置的类起别名,也是类的唯一的一个标识
- class属性:要配置类的class路径(包全路径)。
- name:跟id属性的作用差不多,id属性不能加上特殊的符号,而name中可以添加上特殊的符号"/"
- scope属性,可以指定该类创建时单列模式singleton还是多实例模式prototype
-
基于XML方式注入属性
DI:依赖注入,就是注入属性
1.4.1.1、set注入(无参注入)
第一种注入方式:使用set方式注入
1. 创建类,定义属性,以及属性对应的set方法
2. 在spring配置文件中配置对象,创建配置属性注入,使用到无参构造
<bean id="animal" class="com.hema.xiaohua.Animal">
<!-- property标签,表示用来向配置类中注入属性的默认值-->
<property name="id" value="100"></property>
<property name="classId" value="110"></property>
</bean>
1.4.1.2、有参注入
第二种注入方式: 使用有参构造进行注入
1. 创建类,创建类的有参构造
2. 在spring配置文件中配置对象,创建配置属性注入
<bean id="animal" class="com.hema.xiaohua.Animal">
<!-- 使用constructor-arg来指定有参构造属性的注入,可以使用name直接指定属性名字,还可以使用index来直接指定为第几个参数赋值 -->
<!-- <constructor-arg index="1" value="2222"></constructor-arg>-->
<constructor-arg name="id" value="8888"></constructor-arg>
<constructor-arg name="classId" value="9999"></constructor-arg>
</bean>
第三种注入方式:P名称空间注入
使用P名称空间注入,可以简化基于xml配置方式,他是基于set方式进行注入的,在xml的头部添加上 xmlns:p=“http://www.springframework.org/schema/p”
然后就可以使用p标签进行注入属性
<bean id="animal" class="com.hema.xiaohua.Animal" p:id="555" p:classId="666">
</bean>
1.4.1.3、注入空值和特殊符号
字面量: 向类的属性中注入固定的值就叫做字面量
- 向字面量中设置null值
- 向字面量中设置特殊值
<bean id="animal" class="com.hema.xiaohua.Animal" p:id="555" p:classId="666">
<!-- 为字面量,也就是属性值赋空-->
<property name="address">
<null/>
</property>
<!-- 为属性赋特殊的值,可以使用CADTA进行转义-->
<property name="date">
<value><![CDATA[<<1998.12>>]]></value>
</property>
</bean>
1.4.1.4、注入外部的bean
在service和dao中创建两个类,UserDao和UserService
然后再UserService中引用外部的bean userDao
<bean id="userDao" class="com.hema.xiaohua.dao.UserImpl"></bean>
<bean id="userService" class="com.hema.xiaohua.service.UserServiceImpl">
<!-- 使用ref将外部bean进行引用注入,ref的值是配置文件中我们为其配置的别名 -->
<property name="userImpl" ref="userDao"></property>
</bean>
1.4.1.5、注入属性-内部bean
可以与外部bean进行类比,注入外部bean的话,就是将其他类的bean配置单独写在外面,然后使用ref通过别名进行引入,而注入内部bean的话,顾名思义就不用进行引入,直接将该bean的配置写入到当前bean配置的内部。
<bean id="student" class="com.hema.xiaohua.entity.Student">
<property name="id" value="112"></property>
<property name="name" value="xiaohong"></property>
<!-- 内部bean-->
<property name="teacher">
<bean name="teacher" class="com.hema.xiaohua.entity.Teacher">
<property name="id" value="t123"></property>
<property name="name" value="xiaohualaoshi"></property>
</bean>
</property>
</bean>
1.4.1.6、注入属性-级联操作
假如现在有两个类,一个teacher类,一个student类,然后的话,我们将这两个类的bean在配置文件中进行配置,由于student和teacher是多对一的关系,因此的话,在student配置中可以直接配置teacher相关的属性,可以类比外部bean的引用
<!-- 级联赋值第一种写法-->
<bean id="student" class="com.hema.xiaohua.entity.Student">
<property name="id" value="st123"></property>
<property name="name" value="xiaohua"></property>
<property name="teacher" ref="teacher">
</property>
</bean>
<bean id="teacher" class="com.hema.xiaohua.entity.Teacher">
<property name="id" value="teacher123"></property>
<property name="name" value="dss"></property>
</bean>
<!-- 级联赋值第二种写法-->
<bean id="student" class="com.hema.xiaohua.entity.Student">
<property name="id" value="st789"></property>
<property name="name" value="xiaohong"></property>
<property name="teacher" ref="teacher">
</property>
<!-- 使用第二种方法的话需要添加上teacher的get方法-->
<property name="teacher.id" value="hhhhh"></property>
<property name="teacher.name" value="vvvvvvvv"></property>
</bean>
<bean id="teacher" class="com.hema.xiaohua.entity.Teacher">
</bean>
1.4.1.7、注入集合属性
第一种注入普通属性的集合
<bean id="student" class="com.hema.entity.Student">
<!-- 数组属性类型的注入-->
<property name="courses">
<array>
<value>数学</value>
<value>英语</value>
</array>
</property>
<!-- list集合属性的注入-->
<property name="list">
<list>
<value>xiaoqiang</value>
<value>xiaohuahua</value>
</list>
</property>
<!-- map集合的注入-->
<property name="map">
<map>
<entry key="id" value="100"></entry>
<entry key="class" value="一年一班"></entry>
</map>
</property>
<!-- set集合的注入-->
<property name="set">
<set>
<value>xiaohong</value>
<value>xiaohong</value>
<value>xiaohai</value>
</set>
</property>
</bean>
第二种注入包含对象属性的集合:
<bean id="student" class="com.hema.entity.Student">
<!-- 对象集合的注入-->
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
<bean id="course1" class="com.hema.entity.Course">
<property name="name1" value="数学1"></property>
</bean>
<bean id="course2" class="com.hema.entity.Course">
<property name="name1" value="语文1"></property>
</bean>
1.4.1.8、集合抽取成公共部分进行使用
添加上 xmlns:util=“http://www.springframework.org/schema/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: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 id="bookList">
<ref bean="book1"></ref>
<ref bean="book2"></ref>
</util:list>
<bean id="book1" class="com.hema.entity.Book">
<property name="name" value="活着"></property>
</bean>
<bean id="book2" class="com.hema.entity.Book">
<property name="name" value="java核心技术卷"></property>
</bean>
<bean id="course" class="com.hema.entity.Course">
<property name="bookList" ref="bookList"></property>
</bean>
1.4.2、IOC操作Bean管理
1.Spring有两种bean,一种叫做普通的bean,一种叫做工厂bean(FactoryBean)
普通Bean:在配置文件定义的类型就是返回的类型
工厂bean: 在配置文件中定义bean的类型可以和返回类型不一样
1.首先的话写一个类继承工厂类FactoryBean,然后重写里面的方法,新建对象然后返回,工厂类中的泛型写需要返回类
public class MyBean implements FactoryBean<Student> {
@Override
public Student getObject() throws Exception {
Student student = new Student();
String[] arr = {"11","@22"};
student.setCourses(arr);
return student;
}
@Override
public Class<?> getObjectType() {
return null;
}
}
1.4.3、Bean的作用域
- 在scope属性中,默认值是单实例属性singleton,还有第二个值是prototype,表示的是多实例对象
- singleton和prototype的区别:
- singleton单实例,prototype是多实例
- 设置scope值是singleton的时候,加载spring配置文件的时候就会创建单实例对象
- 设置scope属性是prototype的时候,不是加载spring配置文件的时候创建对象,而是在调用getbean方法的时候创建多实例对象
- scope属性还有request和session属性可以进行选择
<bean id="book1" class="com.hema.entity.Book" scope="singleton">
<property name="name" value="活着"></property>
</bean>
1.4.4、Bean的生命周期
- 生命周期
- 从对象的创建到对象销毁的过程
- bean的生命周期
- 通过构造器创建bean实例(无参构造)
- 为bean的属性设置值和其他的bean引用(调用set方法)
- 调用bean的初始化方法(需要进行配置)
- bean的使用
- 当容器关闭的时候,调用bean的销毁的方法,(需要进行配置销毁的方法)
<!-- 可以在bean标签中进行配置init-method,和dstory-method 初始化和销毁要执行的方法 -->
<bean id="book1" class="com.hema.entity.Book" scope="singleton" init-method="" destroy-method="">
<property name="name" value="活着"></property>
</bean>
在没有加上后置处理器的时候,bean的生命周期是5步,加上后置处理器之后,bean的生命周期是7步,后置处理器就是写一个类实现BeanPostProcessor接口,重写里面的postProcessBeforeInitialization和postProcessAfterInitialization接口,通过在配置文件中配置,就会在配置文件所有的bean初始化方法前,和初始化方法后进行执行
- 执行无参方法创建bean实例
- 调用set方法设置属性值
- 在初始化方法之前执行的方法
- 执行初始化方法
- 在初始化方法之后执行的方法
- 使用bean对象
- 执行销毁的方法
1.4.5、IOC容器(自动装配)
1.什么叫做自动装配
根据指定自动装配的规则(属性名称或者是属性类型),spring自动的将匹配的属性值进行注入
bean标签autowire配置自动装配
autowire常用的两个值
byName根据属性名称进行注入,注入bean的id值和类属性名称一样
byType根据属性类型进行注入
<!-- 自动装配-byName,通过bean配置的id别名与定义的属性名字一致进行装配-->
<bean id="student" class="com.hema.entity.Student" autowire="byName"></bean>
<bean id="course" class="com.hema.entity.Course">
<property name="name1" value="123"></property>
</bean>
<bean id="course2" class="com.hema.entity.Course">
<property name="name1" value="123"></property>
</bean>
<!-- 自动装配-bytype,通过bean配置的类型与定义的属性属性的类型一致进行装配,
当配置两个类型一致的bean时候,就需要使用到byName进行自动装配了
-->
<bean id="student" class="com.hema.entity.Student" autowire="byType"></bean>
<bean id="course" class="com.hema.entity.Course">
<property name="name1" value="123"></property>
</bean>
<!-- <bean id="course2" class="com.hema.entity.Course">-->
<!-- <property name="name1" value="123"></property>-->
<!-- </bean>-->
1.4.6、IOC操作(外部属性文件)
案列配置数据库连接池
未使用properties外部文件进行配置
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:33066/demo"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
使用外部properties文件引入数据库连接池
1.要加入context命名空间,通过context标签将properties文件进行引入,进而引用properties中的配置
<?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:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<beans>
<context:property-placeholder location="classpath:./jdbc.properties"/>
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
1.4.7、IOC操作Bean管理(基于注解方式)
-
什么是注解
- 注解是代码中特殊的标记,格式:@注解名称(属性名称=属性值,属性名称=属性值…)
- 使用注解,注解是作用在类上面,方法上面,属性上面
- 使用注解的目的,为了简化xml配置
-
Spring针对Bean管理中创建对象提供注解
- @Component: 中性使用
- @Service: 放到service层上
- @Controller: 放到controller层上
- @Repository: 放到实体类中,dao层上
- 上面四个注解的功能都是一样的,都可以用来创建bean实例
-
基于注解的方式创建对象
- 引用pom坐标:
<!-- 有关spring注解的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.13</version>
</dependency>
<?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:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--使用注解对bean进行管理
1.导入命名空间context xmlns:context="http://www.springframework.org/schema/context"
2.使用context:component-scan 标签开启注解扫描,base-package中可以写要扫描包的路径,可以使用逗号隔开书写多个包路径
3.在书写的配置类上加上@Service/@Component/@Controller/@Repository
4.@Service(value=""),如果value中不写值的话,默认的话使用类名首字母小写
-->
<context:component-scan base-package="com.hema"></context:component-scan>
</beans>
<!-- 在component-sacan中,我们可以配置过滤掉要进行扫描的注解
1.首先将 user-default-filters属性配置为false,不使用spring默认的注解
2.expression配置要过滤的注解内容 下面的是要过滤@Controller的注解
3.还可以使用exclude-filter设置哪些内容不进行扫描,include-filter可以设置哪些内容可以进行扫描
-->
<context:component-scan base-package="com.hema" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
1.4.7.1、基于注解方式实现属性注入
- AutoWired: 根据属性类型进行自动注入(对象)
- @Qualifier: 根据属性的名称进行注入(对象),这个注解的使用要和@Autowired的一起进行使用,用来解决的问题,一个类型可以有多个实现类,因此的话,@Autowired通过类型进行注入的话就会有问题,这个时候就可以使用@Qualifier通过属性名进行注入
- @Resource: 可以根据类型注入,也可以根据名称注入(对象),是java包下面提供的注解,不是spring提供的原生注解
- @Value:根据属性类型进行注入(基本属性)
1.4.7.2、完全使用注解替换掉xml
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration //该注解用来声明是一个配置类,替换掉bean.xml
@ComponentScan("com.hema") //该注解用来属性要扫描bean的包
public class AutoConfig {
}
test:
@Test
public void getUser() {
// ApplicationContext applicationContext = new GenericXmlApplicationContext("./bean.xml");
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AutoConfig.class);
UserService userService = applicationContext.getBean("userService", UserService.class);
System.out.println(userService);
userService.eat();
UserDao userDao = userService.getUserDao();
userDao.eat();
}