Spring的核心思想:对象容器 把对象放在容器中供调用者使用
Spring的核心功能:控制反转(IOC)和面向切面编程(AOP)
Spring是对对象的管理,目的是为了解除代码之间的耦合
Spring是框架的粘合剂,它可以整合其他框架
1.spring的体系
Spring 由 20 多个模块组成,它们可以分为数据访问/集成(Data Access/Integration)、 Web、面向切面编程(AOP, Aspects)、提供JVM的代理(Instrumentation)、消息发送(Messaging)、 核心容器(Core Container)和测试(Test)
2.Spring的核心功能:IOC
IOC:控制反转,以前我们主动创建对象,对象创建的权利由我们自己掌控,这种方式会导致我们代码之间耦合度过高。我们把创建对象的权利交给spring容器,由spring容器创建对象,我们从容器中拿对象,对象创建的权利交给类别人,这叫控制反转
控制反转的作用:解除代码间的耦合
2.1、spring的容器环境
依赖管理
<!--使用spring的环境 去管理对象--> <!--搭建spring的bean容器环境--> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.20</version> </dependency> |
Spring的容器环境:
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"> <!--xml有两种约束 dtd xsd--> <!--xmlns:命名空间 xsi:schemaLocation:约束文件地址 --> <!--这就是spring读取要被容器管理对象的配置信息--> <!--id:该对象在spring容器中的唯一标识 class:类的全限定名 spring会反射得到该类的对象 --> <bean id="u" class="com.powernode.bean.User"/> </beans> |
测试:
@Test public void testGetUser(){
//不在主动创建对象 从spring容器中得到对象 //1、 获取spring容器 ClassPathXmlApplicationContext beans = new ClassPathXmlApplicationContext("spring.xml"); Object obj = beans.getBean("u"); User user = (User)obj; user.setUname("张三"); System.out.println("user = " + user); } |
2.2、Spring读取配置对象的方式
<!--默认调用无参构造创建对象--> <!--<bean id="u" class="com.powernode.bean.User"/>--> <!--2、配置指定有参构造函数--> <bean id="u1" class="com.powernode.bean.User"> <constructor-arg name="uid" value="1001"/> <constructor-arg name="uname" value="李四"/> <constructor-arg name="ucode" value="888123"/> <constructor-arg name="upwd" value=""/> </bean> <!--3、使用无参构造和set方法--> <bean id="u2" class="com.powernode.bean.User"> <property name="uname" value="张三"/> <property name="upwd" value="123"/> </bean> |
2.3、IOC的衍生技术:DI
DI:依赖注入 当A对象依赖B对象时 A对象叫依赖对象 B对象叫被依赖对象
把B对象当做属性放在A对象中 提供set方式 让spring容器管理AB对象 并把B对象通过set方法注入给A对象
依赖注入的前提:依赖类和被依赖类都被spring容器管理
//被依赖类 public class UserDao {
public void addUser(){
System.out.println("这是UserDao中的添加方法"); } } |
//UserService需要UserDao的支持 依赖UserDao //UserService也是一个类 可以让spring容器帮忙 //依赖类 public class UserService {
private UserDao userDao; public void savUserService(){
/*UserDao userDao = new UserDao();//耦合度过高*/
userDao.addUser(); }
public void setUserDao(UserDao userDao) {
this.userDao = userDao; } } |
依赖注入的配置
<!--依赖注入--> <!--被依赖类--> <bean id="ud" class="com.powernode.dao.UserDao"/> <!--依赖类--> <!--ref:引入spring容器手里对象的地址值--> <bean id="us" class="com.powernode.service.UserService"> <property name="userDao" ref="ud"/> </bean> |
测试:
@Test public void testGetUs(){
ClassPathXmlApplicationContext beans = new ClassPathXmlApplicationContext("spring.xml"); UserService userService =(UserService)beans.getBean("us"); userService.savUserService(); } |
2.4、Spring的注解方案
注解是为了简化配置文件的书写
Spring在IOC中提供了两类注解:
- 实体类注解 在实体类上加上注解 让spring容器扫描即可
每个bean注解都有value属性 不写默认是类名的小驼峰式
@Component:普通实体类注解
@Controller:当前类为处理器类
@Service:当前类为业务层
@Repository:当前类为持久层
注意:注解方案使用的是无参构造 保证类有无参函数
@Component("user")//在spring容器中 它的唯一标识是u public class User {
private int uid; private String uname; private String ucode; private String upwd; |
<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 https://www.springframework.org/schema/context/spring-context.xsd"> <!--使用注解来加载对象--> <!--<context:component-scan base-package="com"/>--> <!--注解扫描器的配置 开发中不建议写大范围扫描--> <!--<context:component-scan base-package="com.powernode1.bean"/> <context:component-scan base-package="com.powernode1.dao"/> <context:component-scan base-package="com.powernode1.service"/>--> <!--使用分隔符号 ,或; 在一个扫描器中配置多个包--> <context:component-scan base-package="com.powernode1.bean,com.powernode1.dao;com.powernode1.service"/> </beans> |
测试
@Test public void testGetUser(){
ClassPathXmlApplicationContext beans = new ClassPathXmlApplicationContext("spring1.xml"); User user = (User)beans.getBean("u"); System.out.println("user = " + user); } |
- 依赖注入注解
依赖注入的前提:依赖类和被依赖都要被spring容器管理
@Autowired:依赖注入注解 它是按照类型注入
@Qualifier :是Autowired的辅助注解 标明注入的是那个类
//被依赖类 public interface UserDao {
void addUser(); } |
实现类:ud
//被依赖类 @Repository("ud") public class UserDaoImpl implements UserDao {
public void addUser(){
System.out.println("这是UserDaoImpl中的添加*******方法"); } } |
实现类:userDaoImpl1
//被依赖类 @Repository public class UserDaoImpl1 implements UserDao {
public void addUser(){
System.out.println("这是UserDaoImpl1中的添加&&&&&&&方法"); } } |
依赖类:
//UserService需要UserDao的支持 依赖UserDao //UserService也是一个类 可以让spring容器帮忙 //依赖类 @Component public class UserService {
@Autowired @Qualifier("userDaoImpl1") //按照类型注入 UserDao spring容器会在容器中找给类型的对象 并把地址值注入进来 private UserDao userDao; public void savUserService(){
/*UserDao userDao = new UserDao();//耦合度过高*/
userDao.addUser(); } } |
测试:
@Test public void testGetUS(){
ClassPathXmlApplicationContext beans = new ClassPathXmlApplicationContext("spring1.xml"); UserService userService = (UserService) beans.getBean("userService"); userService.savUserService(); } |
2.5、Spring的配置详解
Spring容器根据读取的配置文件,反射生成对象模型。根据对象模型创建对象
并把对象放入单例池中,我们每次取对象就是从单例池中获取对象
Spring开发中主要是对Bean的配置,Bean的常用配置一览如下:
Scope:属性,当前类的创建方式 是单例还是多例 默认是单例模式
默认情况下,单纯的Spring环境Bean的作用范围有两个:
Singleton和Prototype
singleton:单例,默认值,Spring容器创建的时候,就会进行Bean的实例化,并存储到容器内部的单例池中,每次getBean时都是从单例池中获取相同的Bean实例;
prototype:原型,Spring容器初始化时不会创建Bean实例,当调用getBean时才会实例化Bean,每次getBean都会创建一个新的Bean实例。
当scope设置为singleton时,获得两次对象打印结果是一样的
<!--默认调用无参构造创建对象--> <bean id="u" class="com.powernode.bean.User" scope="prototype"/> |
Lazy-init:属性 懒加载 默认是false 饿汉式
ApplicationContext接口是单例饿汉式 BeanFactory是单例懒汉式
单例对象可以设置懒加载
<!--默认调用无参构造创建对象--> <bean id="u" class="com.powernode.bean.User" scope="singleton" lazy-init="true"/> |
懒加载和多例的注解方案:
@Component("user")//在spring容器中 它的唯一标识是u //@Lazy//当你在这个类上引入lazy注解 就是想让该类懒加载 @Scope("prototype") public class User {
private int uid; private String uname; private String ucode; private String upwd; |
3.Spring的生命周期
Spring Bean的生命周期是从 Bean 实例化之后,即通过反射创建出对象之后,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期。Spring Bean的生命周期大体上分为三个阶段:
1、 Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化;
2、Bean的初始化阶段:Bean创建之后还仅仅是个"半成品",还需要对Bean实例的属性进行填充、执行一些Aware 接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,Spring的注解功能等、spring高频面试题Bean的循环引用问题都是在这个阶段体现的;
3、 Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池 singletonObjects中去了,即完成了Spring Bean的整个生
命周期。
4.Spring的核心功能:AOP
AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。 AOP 底层,就是采用动态代理模式实现的。
采用了两种代理:JDK 的动态代理,与 CGLIB 的动态代理。
面向切面编程本质上还是面向对象编程,只是这里对象的调用使用的动态代理
面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到主业务逻辑中
面向切面编程需要三个要点 :1、切点 2、切面类中的函数 3、通知方式 三者合在一起叫织入
面向切面编程对有什么好处?
1.减少重复;
2.专注业务;
注意:面向切面编程是面向对象编程的一种补充一种升华。
使用 AOP 减少重复代码,专注业务实现:
4.1、搭建spring的aop环境
Aop实在spring容器的基础上建立的