一、Spring框架概述
(1)Spring是轻量级的开源的Java,框架
(2)Spring可以解决企业应用开发的复杂性
(3)Spring有两个核心部分:IOC 和 Aop”
IOC:控制反转,把创建对象过程交给 Spring进行管理
Aop:面向切面,不修改源代码进行功能增强
(4)Spring特点
方便对象管理、解耦,简化开发
Aop编程支持
方便进行事务操作
方便程序测试
方便和其他框架进行整合
降低API开发难度
1. 项目创建
(1)下载:spring下载地址:
(2)创建项目
(3)导入jar包
基本包:(beans、core、context、expression)+ commons-logging日志包
二、IOC容器(反转控制==>依赖注入)
1. IOC介绍
百度百科:通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
作用:
a)把对象创建和对象之间的调用过程,交给Spring进行管理
b)使用IOC目的:为了降低耦合度到最低
2. IOC底层原理
2.1 高耦合度问题(传统):
2.2 工厂模式解耦(工厂):
2.2.1 工厂模式的原理:
xml解析(文件中获取值)+反射(通过得到类的字节码文件来操作类中所有内容)
耦合度还可以再次降低
2.3. IOC 控制反转
1) IOC思想基于IOC容器完成,IOC容器的底层就是对象工厂**
2) Spring提供实现IOC容器的两种方法(BeanFactory、ApplicationContext) 介绍+区别
a)BeanFactory接口:IOC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(原因+区别:加载配置文件时候不会创建对象,在获取对象时才会创建对象。)
b)ApplicationContext接口:BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(区别:加载配置文件时候就会把在配置文件对象进行创建,在Tomcat启动时,耗时的创建过程,进行先初始化是高效的)推荐使用!
FileSystemXmlApplicationContext :盘符路径
ClassPathXmlApplicationContext:src下的相对路径
3). IOC操作Bean管理过程
Bean管理就是两个操作:
(1)Spring创建对象:ApplicationContext加载xml中的bean信息,getBean生成;
(2)Spring注入属性:创建对象过程中,对对象的成员变量设置值。
3. IOC的使用
3.1 基于XML
1 创建对象:
<!--1 配置User对象创建-->
<bean id="user" class="com.atguigu.spring5.User"></bean>
<!-- 2.常用的属性介绍-->
id: 唯一标识
class: 类的全路径 包名.类名
<!-- 3.创建-->
默认生成无参的构造函数。
2 属性 [注入基本数据类型]:
DI(Dependency Injection):依赖注入,注入属性,类成员方法设置值,需要在创建对象的基础上实现。
(1)set方式注入
//(1)传统方式: 创建类,定义属性和对应的set方法
public class Book {
//创建属性
private String bname;
//创建属性对应的set方法
public void setBname(String bname) {
this.bname = bname;
}
}
<!--(2)spring方式: set方法注入属性-->
<bean id="book" class="com.atguigu.spring5.Book">
<!--使用property完成属性注入
name:类里面属性名称
value:向属性注入的值
-->
<property name="bname" value="Hello"></property>
<property name="bauthor" value="World"></property>
</bean>
(2)有参构造函数注入
//(1)传统方式:创建类,构建有参函数
public class Orders {
//属性
private String oname;
private String address;
//有参数构造
public Orders(String oname,String address) {
this.oname = oname;
this.address = address;
}
}
<!--(2)spring方式:有参数构造注入属性-->
<bean id="orders" class="com.atguigu.spring5.Orders">
<constructor-arg name="oname" value="Hello"></constructor-arg>
<constructor-arg name="address" value="China!"></constructor-arg>
</bean>
(3)p名称空间注入 对set注入进行简化(了解即可)
<!--1、添加p名称空间在配置文件头部-->
<?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-->
<!--2、在bean标签进行属性注入(算是set方式注入的简化操作)-->
<bean id="book" class="com.atguigu.spring5.Book" p:bname="very" p:bauthor="good"></bean>
(4) 注入特殊符号属性(了解即可)
字面值: 设置属性为某个值
<bean id="book" class="com.atguigu.spring5.Book">
<!--(1)通过null标签 设置为 null值-->
<property name="address">
<null/><!--属性里边添加一个null标签-->
</property>
<!--(2)设置为 特殊符号赋值-->
<!--属性值包含特殊符 -->
<!--a 把<>进行转义 <(小于<); >(大于>); -->
<property name="address" value="<南京>" />
<!-- b 把带特殊符号内容写到CDATA-->
<property name="address">
<value><![CDATA[<<南京>>]]></value>
</property>
</bean>
3 属性 [注入对象]
注入 外部bean、内部bean、级联bean
(1) 外部bean的注入【ref】
解释:类的成员变量(Set/Constructor)的赋值需要是XML中定义的Bean
例子:service层调用dao层方法
原始方法: 在Service类中 1.创建UserDao userdao = new UserDaoImpl() 2.userdao.调用set方法。
Spring方法:
1)创建两个类service和dao类
public class UserService {//service类
//创建UserDao类型属性,生成set方法 【通过构造函数、set方法获得UserDaoImpl的对象】
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println("service add...............");
userDao.update();//调用dao方法
}
}
public class UserDaoImpl implements UserDao {//dao类
@Override
public void update() {
System.out.println("dao update...........");
}
}
2)在spring配置文件中进行配置(ref)
<!--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>
(2) 内部bean的注入和级联赋值
例子:(1对多的关系 部门-员工)
解释:实际开发中一般使用外部Bean!!! 更为通用。
内部嵌套:当一个成员变量是对象,且可以内部直接生成该对象,则直接在内部生成对象
//部门类
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;
}
}
<!--内部bean-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="Andy"></property>
<property name="gender" value="女"></property>
<!--设置对象类型属性-->
<property name="dept">
<bean id="dept" class="com.atguigu.spring5.bean.Dept"><!--内部bean赋值-->
<property name="dname" value="宣传部门"></property>
</bean>
</property>
</bean>
(3) 级联bean的注入
<!--级联赋值-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="jams"></property>
<property name="gender" value="男"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="技术部门"></property>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
</bean>
dept.dname:要求Emp必须有get(dept)的方法,否则dept.dname拿不到
生成dept的get方法(get方法必须有!!)
public Dept getDept() {
return dept;
}
4 属性 [注入集合类型]
集合类型:注入数组类型属性、List类型属性、Map类型、Set类型集合
<property>里面的内容如下</property>
a) 数组类型
<array>...<value></value>...</array>
<list>...<value></value>...</list>
b) List类型
<list>...<value></value>...</list>
c) Map类型
<map>...<entry key="xxx" value="xxx"></entry>...</map>
d) Set类型
<set><value>xxx</value></set>
e) 在集合里面设置【对象】属性值
放入的是对象,而不是常用的数据类型
1.创建多个对象、将对象ref到
<list><ref bean="xx_id"></ref></list>
具体:
//学生所学多门课程
private List<Course> courseList;//创建集合
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
<!--1.外部 创建多个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>
<!--2. 注入list集合类型,值是对象-->
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
f) 把集合注入部分(相同的)提取出来
<!--第一步:在 spring 配置文件中引入名称空间 util-->
xmlns:util="http://www.springframework.org/schema/util" <!--1. 添加util名称空间(P空间的p改util)-->
xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- 2. 将beans改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"
xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--第二步:使用 util 标签完成 list 集合注入提取-->
<!--把集合注入部分提取出来-->
<!--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>
3.2 基于注解
1、什么是注解
(1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
(2)使用注解,注解作用在类上面,方法上面,属性上面
(3)使用注解目的:简化 xml 配置
2、Spring 针对 Bean 管理中创建对象提供注解
下面四个注解功能是一样的,都可以用来创建 bean 实例
(1)@Component:普通组件,用它都可以创建对象。 (创建)
(2)@Controller:一般用在web层上。(调用service)
(3)@Service:一般用在业务逻辑层或则Service层上。(调用Respository)
(4)@Repository:一般用在Dao层或则。(调用Component)
3、实现:基于注解方式实现对象创建
第一步 引入依赖: (引入spring-aop jar包)
第二步 开启组件扫描:引入context名称空间,告知Spring是扫面哪些包下的注解。
<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 http://www.springframework.org/schema/context/spring-context.xsd"><!--引入context名称空间-->
<!--开启组件扫描
1 如果扫描多个包,多个包使用逗号隔开
<context:component-scan base-package="com.atguigu.xxx, com.atguigu.xxx1"></context:component-scan>
2 扫描包上层目录
<context:component-scan base-package="com.atguigu"></context:component-scan>
-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
第三步 创建类,在类上面添加创建对象注解
// Component:创建对象,bean标签的id值:value = "userService",不写value则默认id="userService(类名首字母小写)"
@Component(value = "userService") //注解等同于XML配置文件:<bean id="userService" class=".."/>
public class UserService {
public void add() {
System.out.println("service add.......");
}
}
第四步 (可选)开启组件扫描细节配置
**filter:**细化设置扫描包中哪些类需要生成对象和依赖注入。
<!--示例 1
use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
context:include-filter ,设置扫描哪些内容
-->
<context:component-scan base-package="com.atguigu" use-defaultfilters="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.atguigu">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/> <!--表示不扫描Controller注解的类-->
</context:component-scan>
4、基于注解方式实现属性注入
(1)@Autowired:
根据属性类型进行自动装配
第一步 把 service 和 dao 对象创建:在 service 和 dao 类添加创建对象注解
第二步 在 service 注入 dao 对象:在 service 类添加 dao 类型属性,在属性上面使用注解autowire
@Service
public class UserService {
//定义 dao 类型属性, 不需要添加 set 方法(自动封装)
@Autowired //添加注入属性注解
private UserDao userDao;
// !!存在问题,autowired是根据类型注入,而接口存在多态问题(一个接口可以有多个实现类),而当接口的多个实现类添加注解@component后,相当于生成多个接口实现类对象,此时无法确定哪个实现类与该接口匹配,则出现报错!!!处理方法见@Qualifier根据名称进行注入,来实现指定实现类对象。
public void add() {
System.out.println("service add.......");
userDao.add();
}
}
//Dao实现类
@Repository
//@Repository(value = "userDaoImpl1")
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("dao add.....");
}
}
(2)@Qualifier (限定修饰):
用于接口有多个实现类时,指定注入哪个实现类
根据名称进行注入,@Qualifier 注解的使用时需要和@Autowired 一起使用
@Autowired
//根据名称进行注入(为该接口指定某一个接口实现类,(否则存在多个实现类,且无法确定哪个而报错))
@Qualifier(value = "userDaoImpl1")
private UserDao userDao;
(3)@Resource:
可以根据类型注入,也可以根据名称注入(它属于javax包下的注解,不属于Spring官方包,不推荐使用!)
//默认根据类型进行注入(可以查看第三章.4 自动装配)
@Resource
private UserDao userDao;
//根据名称进行注入
@Resource(name = "userDaoImpl1")
private UserDao userDao;
(4)@Value:
注入普通类型属性
@Value(value = "abc")
private String name
6、完全注解开发
实际的开发对应的是SpringBoot的开发
(1)创建配置类 config/Springconfig,替代 xml 配置文件
@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.atguigu"})
public class SpringConfig {
}
(2)编写测试类
@Test
public void testService2() {
//1. !!!加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService",UserService.class);
System.out.println(userService);
userService.add();
}
3.3 基于配置类
三、Bean的相关知识
1、 Bean的类型
Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
1、普通 bean:在配置文件中定义 bean 类型就是返回类型
2、工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
(1)普通bean的实现
上诉已经接受。
(2) 实现 工厂 bean
第一步:创建类,该类需要实现接口FactoryBean
第二步:实现接口的getObject方法:定义实际的放回bean类型
public class MyBean implements FactoryBean<Course> {
//定义返回bean
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;
}
}
@Test
public void test3() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean3.xml");
Course course = context.getBean("myBean", Course.class);//返回值类型可以不是定义的bean类型!
System.out.println(course);
}
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean">
</bean>
2、 Bean的作用域
在 Spring 里面,可以设计bean是单实例对象还是多实例对象,默认情况下,bean 是单实例对象。
(1)单实例与多实例的区别
单实例:获取两次bean (book1=context.getBean(“book”, Book.class)、book2=context.getBean(“book”, Book.class)),book1的地址==book2的地址。
多实例:book1的地址≠book2
(2)设置位多实例(scope):
scope =singleton:表示是单实例对象(默认值)
scope =prototype:表示是多实例对象
<bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype"><!--设置为多实例-->
<property name="list" ref="bookList"></property>
</bean>
了解其他属性
require、session
3、Bean的生命周期
(1)5步简单的生命周期
**生命周期:**从对象创建到对象销毁的过程
bean 生命周期:
(1)通过构造器创建 bean 实例(无参数构造)constructor
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)set
(3)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(4)bean 可以使用了(对象获取到了)
(5)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
演示 bean 生命周期 :
public class Orders {
private String oname;
// 1.无参数构造
public Orders() {
System.out.println("第一步 执行无参数构造创建 bean 实例");
}
// 2.属性复制
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步 调用 set 方法设置属性值");
}
//创建执行的初始化的方法--------------
public void initMethod() {
System.out.println("第三步 执行初始化的方法");
}
// <bean ..... init-method="initMethod"></bean>
// ---------------------------------------
//自己调用baen context.getBean(xxx, X.class);
//(1)创建执行的销毁的方法 ----------------
public void destroyMethod() {
System.out.println("第五步 执行销毁的方法");
}
// (2)<bean ..... destroy-method="destroyMethod"></bean>
// (3) 手动销毁 context.close(); 如果context 是ClassPathXmlapplicationContext 则不需要强制转换。
// ----------------------------------
}
(2)7步 bean生命周期扩展
(5)+2=7 后置处理器
初始化之前+1、初始化之后+1
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参数配置-->
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> <!--配置初始化方法和销毁方法-->
<property name="oname" value="手机"></property><!--这里就是通过set方式(注入属性)赋值-->
</bean>
<!--为当前xml的所有bean 都配置后置处理器-->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
bean 的后置处理器,bean 生命周期有七步 (正常生命周期为五步,而配置后置处理器后为七步)
(1)通过构造器创建 bean 实例(无参数构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
(3)把 bean 实例传递 bean 前置处理器的方法 postProcessBeforeInitialization
(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
(6)bean 可以使用了(对象获取到了)
(7)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
4、自动装配
手动装配:为bean的属性设置值,通过value、ref手动指定属性值。
自动配置:根据指定装配规则(属性类型和属性名称),Spring将自动匹配的属性值注入。
autowire=“byName”:进行自动装配的时候,spring容器将会检查需要注入依赖的bean中的依赖名称,当存在依赖的名称时候就将对应得bean注入进去。
autowire=“byType”:spring容器会基于反射查看bean定义的类,然后找到依赖类型相同的bean注入到另外的bean中,这个过程需要setter注入来完成,因此必须存在setter方法,否则就会注入失败。
<!-- autowire的两个值:
byName根据属性名称注入:注入值bean的id和类属性的名称一致。 Dept [dept==emp]
byType根据事先类型注入:类的属性的数据类型要和class="内的一致",[Dept] dept
若相同类型的bean配置多个,则不知道匹配哪个,出现报错。-->
<beans>
<bean id="emp" class="xxx" autowire="byName"></bean>
<bean id="dept" class="xxx" autowire="byType"></bean>
</beans>
第四章、xml引入外部属性文件(数据库配置信息)。
方式一:直接配置数据库信息 :
(1)引入Druid(德鲁伊)连接池依赖 jar 包 druid-1.1.9.jar
(2)配置Druid(德鲁伊)连接池
<!--直接配置连接池-->
<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:3306/userDb"></property> <!--数据库地址-->
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
方式二:引入外部属性文件配置数据库连接池
(1)在src目录下创建外部属性文件(properties 格式文件),写数据库信息(jdbc.properties)
# 文件名:jdbc.properties
# prop.为了不与其他变量冲突
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root
(2)把外部 properties 属性文件引入到 spring 配置文件中 —— 引入 context 名称空间
<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 http://www.springframework.org/schema/context/spring-context.xsd"><!--引入context名称空间-->
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.userName}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
</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 http://www.springframework.org/schema/context/spring-context.xsd"><!--引入context名称空间-->
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.userName}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
</beans>