Spring容器技术内幕 工作机制 spring的AbstractApplicationContext是ApplicationContext的抽象实现类,其中的refresh()方法定义了容器在加载文件后的各项处理过程
//初始化BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //调用工厂后处理器 invokeBeanFactoryPostProcessors(); //注册Bean后处理器 registerBeanPostProcessors(); //初始化消息源 initMessageSource(); //初始化应用上下文事件广播器 initApplicationEventMulticaster(); //初始化其他特殊的Bean:由具体子类实现 onRefresh(); //注册事件监听器 registerListeners(); //初始化所有单例的Bean,使用懒加载模式的Bean除外 finishBeanFactoryInitialization(beanFactory); //完成刷新并发布容器刷新事件 finishRefresh(); Spring组件:
(1)被加工的组件:Resource、BeanDefinition、PropertyEditor和最终的Bean
(2)加工设备:ResourceLoader、BeanDefinitionReader、BeanFactoryPostProcessor、InstanitiationStrategy和BeanWrapper
BeanDefinition BeanDefinition是配置文件< bean >元素标签在容器中的内部表示
< bean >拥有class、scope、lazy-init等配置属性,对应的BeanDefinition提供了beanClass、scope、lazy-Init类属性
父< bean >用RootBeanDefiniton表示,子< bean >用ChildBeanDefiniton表示,而没有父< bean >的< bean >用RootBeanDefiniton表示。
Spring通过BeanDefinition将配置文件中的< bean >配置信息转换为容器的内部表示,并将它们注册到BeanDefinitionRegistry中,便于后续操作读取配置信息。一般在容器启动时加载并解析BeanDefinition。
创建最终BeanDefinition的步骤
首先利用BeanDefinitionReader读取承载配置信息的Resource,通过XML解析器解析配置信息的DOM对象,简单生成对应< bean >的BeanDefinition对象。由于在配置文件中,可能通过占位符变量引用外部属性文件的属性,因此只是半成品
接着利用容器中注册的BeanFactoryPostProcessor对半成品的BeanDefinition进行加工处理,将以占位符表示的配置解析为最终的实际值
InstantiationStrategy 根据BeanDefinition对象创建一个Bean实例
SimpleInstantiationStrategy策略利用Bean实现类的默认构造函数、带参构造函数或工厂方法创建Bean的实例
CglibSubclassingInstantiationStrategy利用CGLib类库为Bean动态生成子类,在子类生成方法注入的逻辑,接着使用这个子类创建Bean实例
InstantiationStrategy仅负责实例化Bean的操作,相当于执行new功能,并不参与Bean属性的设置工作。因此返回的Bean实例时半成品,接下来要BeanWrapper完成属性填充
BeanWrapper Spring委托BeanWrapper完成Bean属性的填充工作
BeanWrapper有两个顶级接口,PropertyAccessor接口定义了各种访问Bean属性的方法,而PropertyEditorRegistry时属性编辑器的注册表...
Spring容器高层视图
Spring容器启动需要三个条件:
Sprng框架的类包放在应用程序的类路径下;
应用程序为Spring提供了完备的Bean配置信息;
Bean的类已经放在应用程序的类路径下
Spring启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,根据这张表实例化Bean,装配好Bean之间的关系,最后把这些准备就绪的Bean放到Bean缓存池中,以供外层的应用程序进行调用
Bean配置信息:
Bean的实现类;
Bean的属性信息,如数据源的连接数、用户名、密码等
Bean的依赖关系,Spring根据依赖关系完成Bean之间的装配
Bean的行为配置,如生命周期范围及生命周期各过程的回调函数等
基于XML的配置
采用Schema格式
命名空间的定义分为两个步骤:
第一步指定命名空间的名称;
第二步指定命名空间的Schema文档格式文件的位置,用空格或回车换行进行分隔
在第一步中,要指定命名空间的缩略名和全名,例如图中的③处
aop为别名,文档后面的元素可通过别名加以区别,例如aop:config/等;而引号内的网址为空间的全限定名,习惯上用文档发布机构的官网作为全限定名,可以很好表示文档所属机构,也可以避免重名。但就语法上说,别名和限定名可任意命名
若别名为空,则表示该命名空间为文档默认命名空间。文档中无前缀的元素都属于默认命名空间,例如属于①处定义的默认命名空间
在第二步中,定义的语法如下:
命名空间使用全限定名,一般使用组织机构提供引用的URL地址,例如④处
Bean基本配置
Bean的定义:
Bean的命名:
配置一个Bean时,需要指定一个唯一的id属性作为Bean的名称,并且需要满足规范:必须以字母开始,不允许逗号、空格这些非完整结束符,如果需要使用特殊符号进行命名,则可以使用的name属性。
name和id都可以指定多个名字,以逗号、分号或空格进行分隔
id可以不同而name可以相同,使用getBean方法时,传入参数若为name,则返回后面声明的Bean(覆盖了前面的name),因此尽量使用id命名
若未指定id和name属性,则Spring自动将全限定类名作为Bean的名称。
属性注入
通过setXxx()方法注入Bean的属性值或依赖对象。
Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值。
在配置文件中进行属性注入
该Bean有三个属性值,每个属性值对应一个标签,name为属性名称,maxSpeed对应setMaxSpeed()方法
Java中xxx属性对应setXxx()方法,属性变量名必须满足:“变量的前两个字母要么全部大写,要么全部小写”
构造函数注入
①按类型匹配入参
Bean提供带参的构造函数,在Spring配置文件使用中的type属性
public Car(String brand, double price) { this.brand=brand; this.price=price; } 的位置不会影响配置效果,不一定要按照参数顺序
②按索引匹配入参
java通过入参的类型及顺序区分不同的重载方法。...
在下载Maven依赖包的时候觉得很慢,搜索引擎找到了国内镜像站,设置setting.xml解决
https://blog.csdn.net/u013058936/article/details/104585376/
Bean的概念:
https://www.liaoxuefeng.com/wiki/1252599548343744/1260474416351680
IoC概念:(Inverse of Control)
控制反转
控制:某一接口具体实现类的选择控制权
反转:将该控制权从调用类中移除,转交给第三方处理
DI:(Dependency Injection)
依赖注入
让调用类对某个接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖。
结合书本的个人理解:
拍电影时,剧本中有角色,而角色的具体动作实现需要由具体某位演员实现,因此剧本、角色和演员有很强的耦合性,在实现剧本中某个情节的时候,需要引入角色接口,并创建具体演员。但这样子在每个剧情都要引入角色接口并创建具体演员,这样加大了剧本的剧情创作难度(因为每个剧情都要引入演员,而不是单单只有角色),因此需要有一个导演,来统筹兼顾,在剧本剧情需要的时候,为其注入一个演员。这里的控制便是剧本中的某个剧情要选择某个角色的具体演员的控制权,而反转便是这个选择权交由导演(也就是第三方)来决定,不由编剧决定了。
那么某一接口实现类的选择控制权便是指定具体演员
调用类便是剧本中的剧情
第三方便是导演
即导演选角,导演起到了一个统筹兼顾的重要角色,大大降低了剧本和角色的耦合性,实现解耦
这样也符合我们普遍认知中的投拍流程。
这篇文章可能有助于理解
https://www.cnblogs.com/youdutec/p/13393023.html
https://www.cnblogs.com/DebugLZQ/archive/2013/06/05/3107957.html(防失效)
IoC的类型:
构造函数注入、属性注入和接口注入
Spring支持构造函数注入和属性注入
1、构造函数注入:
通过调用类的构造函数,将接口实现类通过构造函数变量传入
//剧本 public class MoAttack { private BEN ben; public MoAttack(BEN ben) { this.ben = ben; } //通过构造函数注入BEN的具体饰演者 public void cityGateAsk() { ben.responseAsk("墨者革离"); } } //剧本不关心是谁饰演BEN这个角色,只需要传入演员让其完成具体动作即可 //导演 public class Director { public void direct(){ //指定角色的饰演者 BEN ben = new LiuDeHua(); //注入具体饰演者的剧本中 MoAttack moAttack = new MoAttack(ben); moAttack....
springboot
简化了spring的配置,不再需要编写复杂的XML配置文件
在pom修改MySQL版本
并且在右边的maven中clean和install所需要的依赖包
使用Maven配置:
遇到了报错
Could not transfer artifact org.springframework.boot:spring-boot-starter-parent:pom:1.3.0.M3 from/to local maven
解决方法:
将maven home directory 更改为本地安装的maven
运行spring-boot:run
网页打开localhost:8080
完成项目运行
基于Gradle环境配置:
待填坑
基于Spring Boot CLI 环境配置:
配置CLI:
https://repo.spring.io/release/org/springframework/boot/spring-boot-cli/
一开始按照书本下载了1.3.3版本,结果在运行spring run 命令时报错,无法下载依赖项目,Google后发现可能时版本问题,于是下载了2.1.18版本,成功运行
持久层:
Spring框架提供了几种可选的操作数据库方式
①用Spring内置轻量级的JdbcTemplate
②第三方持久化框架Hibernate或Mybaits
Spring Boot提供了相应的启动器,只需要在pom.xml添加依赖即可
spring-boot-starter-data-jdbc
spring-boot-starter-data-jpa
本项目采用JDBC
在springboot中,通过两种方式配置数据库连接
①通过自定义连接的方式,通过配置spring.datasource.*选项设定数据源的链接地址、连接驱动器、用户名以及密码。
②另外一种通过JNDI方式设置,为spring.datasource.jndi-name属性指定一个JNDI连接名
注意
MySQL8.0版本问题,要在箭头处加上cj.
附上MySQL常见问题:
https://blog.csdn.net/weixin_39723337/article/details/81319083
在userDao中,用@Repository定义一个DAO Bean,用@Autowired将Spring容器中的Bean注入,不同于spring,spring boot将自动装配好DAO,不需要再在Spring容器中装配DAO
业务层:
将持久层的UserDao和LoginLogDao组织起来,实现业务逻辑
编写业务层代码重要步骤:
①:编写正确的业务逻辑
②:对业务事务的管控。
在Spring Boot中,首先在主类Application上标注
$@EnableTransactionManagement$注解(开启事务支持,相当
于XML中的$tx:annotation-driven/$配置方式),然后在
访问Service方法上标注$@Transactional$注解即可。
如果将$@Transactional$注解标注在Service类级别上,那么当前
Service类的所有方法都将被事务增强,一般不建议这么做...
下载安装Maven
创建数据库和表
在创建数据库表时,要将login_log_id设为非主键或设置可以为Null,否则运行时无法写入登陆日志
建立工程,并选定编码格式为UTF-8
使用SpringJDBC作为持久层的实现技术
持久层工作:从数据库表中加载数据并实例化领域对象
领域对象:代表业务状态,贯穿展现、业务和持久层,并最终被持久化到数据库中
领域模型中的实体类分为四种类型:
PO(persistent object):持久化对象,表示持久层的数据结构(如数据库表)
DO(domain object):领域对象,即业务实体对象
DTO(data transfer object):数据传输对象,泛指用于展示层与服务层之间的数据传输对象,可以看作组合版的DO
VO(view object):视图对象,用于展示层试图状态对应的对象。
对应简单模块,PO、DO和VO没有什么区别,可以直接复用PO
采用注解的配置方式 SpringJDBC将传统的JDBC API进行薄层封装,无需执行(获取连接→创建statement→执行数据操作→获取结果→关闭statement→关闭结果集→关闭连接)以及异常处理的操作,通过模板类便可以完成大部分数据库的访问操作。
项目进行的报错及解决方法: 导入项目后在右边的Maven进行clean和install
(注意将工具栏的蓝色⚡选中,以跳过单元测试,提高编译打包效率,而且若测试有问题也不会影响整个项目的构建)
在pom文件中配置对应版本的MySQL和maven-surefire-plugin
关于引入错误
https://blog.csdn.net/ccorg/article/details/89293423
工程重新导入
删除idea
并且将language level改为5以上,以使得@Override可以编译
运行时要修改MySQL时区
https://www.codeprj.com/blog/b9488a1.html
整体流程:
用户访问网页,输入用户名和密码,点击登录
Spring通过调用控制器相应登录请求
控制器通过调用对应方法查询是否存在匹配用户,内部调用持久层对象完成数据库的访问
控制器调用对应方法加载匹配的用户对象,并更新用户最近一次登录时间和ip
控制器调用对应方法进行登录成功的业务处理,更新用户积分并创建一个登陆对象,将其插入数据库的登陆日志中
重定向到欢迎界面,产生响应返回给用户