Spring源码日常笔记(一)
学习Spring源码太枯燥,看着看着就想睡觉,为了让自己不睡觉,一边学习一边记录下学习的过程,有错误的地方望大佬亲喷.(看的是享学课堂james老师讲的,若有侵权,必删)
初识Spring
Spring是一种开源轻量级框架,是为了解决企业应用程序开发复杂性而创建的,Spring致力于解决JavaEE的各层解决方案,而不仅仅于某一层的方案。
2003年2月Spring框架正式称为一道开源项目,Spring致力于J2EE应用的各种解决方案,而不仅仅专注于某一层解决方案。可以说Spring是企业应用开发的“一站式”选择, Spring贯穿于表现层、业务层、持久层,然而Spring并不想取代那些已经有的框架,而是以高度的开放性,与这些已有的框架进行整合。
Spring的优点
1、让现有的技术更容易使用,
2、促进良好的编程习惯。
Spring是一个全面的解决方案,它坚持一个原则:不从新造轮子。已经有较好解决方案的领域,Spring绝不重复性实现,比如:对象持久化和OR映射,Spring只对现有的JDBC,Hibernate等技术提供支持,使之更容易使用,而不做重复的实现。Spring框架有很多特性,这些特性由7个定义良好的模块构成。
1、 Spring Core:即,Spring核心,它是框架最基础的部分,提供IOC和依赖注入特性
2、 Spring Context:即,Spring上下文容器,它是BeanFactory功能加强的一个子接口
3、 Spring Web:它提供Web应用开发的支持
4、 Spring MVC:它针对Web应用中MVC思想的实现
5、 Spring DAO:提供对JDBC抽象层,简化了JDBC编码,同时,编码更具有健壮性。
6、 Spring ORM:它支持用于流行的ORM框架的整合,比如:Spring + Hibernate、Spring + iBatis、Spring + JDO的整合等等。
7、 Spring AOP:AOP即,面向切面编程,它提供了与AOP联盟兼容的编程实现
Spring的常用组件
Spring的初体验
1 创建maven工程
2 导入spring-context依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
3.1(xml方式)
(1) 创建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="person" class="com.enjoy.cap1.Person">
<property name="name" value="james"></property>
<property name="age" value="19"></property>
</bean>
</beans>
(2) 新建person.java
public class Person {
private String name;
private Integer age;
public Person(){
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
public Integer getAge() {
return age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public void setAge(Integer age) {
this.age = age;
}
}
(3) 测试类
public class MainTest1 {
public static void main(String args[]){
//把beans.xml的类加载到容器
ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
//从容器中获取bean
Person person = (Person) app.getBean("person");
System.out.println(person);
}
}
控制台输出:
Person [name=james, age=19]
3.2(注解方式)
如果我们用注解开发, 很明显是不需要XML的,3.1方法中bean.xml可删除.
(1) 使用@Configuration 和 @Bean注解,@Bean注解可以定义bean的name,如下:
@Configuration
public class MainConfig {
//给容器中注册一个bean, 类型为返回值的类型,
@Bean("abcPerson")
public Person person01(){
return new Person("james",20);
}
}
(2) 测试一下
public class MainTest2 {
public static void main(String args[]){
//把beans.xml的类加载到容器
ApplicationContext app = new AnnotationConfigApplicationContext(MainConfig.class);
String[] namesForBean = app.getBeanNamesForType(Person.class);
for(String name:namesForBean){
System.out.println(name);
}
}
}
控制台输出:
abcPerson
Spring @ComponentScan注解扫描规则
(1) 简单使用:
@Configuration
@ComponentScan(value="com.enjoy.cap2")
public class Cap2MainConfig {
//给容器中注册一个bean, 类型为返回值的类型,
@Bean
public Person person01(){
return new Person("james",20);
}
}
@ComponentScan(value=“com.enjoy.cap2”)表示扫描com.enjoy.cap2下的包
com.enjoy.cap2包下结构如下:
controller,service,dao下都加了@Controller,@service,@Respostry注解,让ComponentScan能扫描到.
测试类:
public class Cap2Test {
public static void main(String[] args) {
ApplicationContext app = new AnnotationConfigApplicationContext(Cap2MainConfig.class);
String[] beanDefinitionNames = app.getBeanDefinitionNames();
for(String s : beanDefinitionNames){
System.out.println(s);
}
}
}
控制台输出:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
cap2MainConfig
orderController
orderDao
orderService
person01
(2) 定制包扫描时的过滤规则
新建dao, service,controller,如下图
在Cap2MainConfig2 @ComponentScan注解中加入配置: @Filter: 扫描规则
@Configuration
//@Controller @Service @Respostry @Component
@ComponentScan(value="com.enjoy.cap2", includeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class})
}, useDefaultFilters=false)//默认是true,扫描所有组件,要改成false,使用自定义扫描范围
public class Cap2MainConfig {
//给容器中注册一个bean, 类型为返回值的类型,
@Bean
public Person person01(){
return new Person("james",20);
}
}
ComponentScan 下属性的意思
@ComponentScan value:指定要扫描的包
excludeFilters = Filter[] 指定扫描的时候按照什么规则排除那些组件
includeFilters = Filter[] 指定扫描的时候只需要包含哪些组件
useDefaultFilters = false 默认是true,扫描所有组件,要改成false
----扫描规则如下
FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;比如按BookService类型
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则,自已写类,实现TypeFilter接口
这里使用FilterType.ANNOTATION的例子遇到一个问题,如下:
配置类:
@Configuration
//@Controller @Service @Respostry @Component
@ComponentScan(value="com.enjoy.cap2", excludeFilters={
@Filter(type=FilterType.ANNOTATION, classes={Controller.class})
}, useDefaultFilters=false)
public class Cap2MainConfig {
//给容器中注册一个bean, 类型为返回值的类型,
@Bean
public Person person01(){
return new Person("james",20);
}
}
com.enjoy.cap2结构如下:
我们使用测试类测试:
public class Cap2Test {
public static void main(String[] args) {
ApplicationContext app = new AnnotationConfigApplicationContext(Cap2MainConfig.class);
String[] beanDefinitionNames = app.getBeanDefinitionNames();
for(String s : beanDefinitionNames){
System.out.println(s);
}
}
}
我们理想输出的结果应该是:
cap2MainConfig
orderDao
orderService
person01
将Controller排除掉
但是现实输出的结果是:
cap2MainConfig
person01
将Controller,Service,Dao都排除掉了
这是为什么呢,好像跟我们想的有点误差:
(1)@componentScan会交给Scanorg.springframework.context.config.ContextNamespaceHandler这个类处理.
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
(2)ComponentScanBeanDefinitionParser会读取配置文件信息并组装成org.springframework.context.annotation.ClassPathBeanDefinitionScanner进行处理。
(3)如果没有配置@ComponentScan的use-default-filters属性,则默认为true,在创建ClassPathBeanDefinitionScanner时会根据use-default-filters.
如果为true,会调用registerDefaultFilters方法.我们这边设置use-default-filters为true,
会调用registerDefaultFilters方法.
具体实现如下:
protected void registerDefaultFilters() {
//Component注解会默认把Controller,Service,Repository注解都扫描进来.
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) cl.loadClass("javax.annotation.ManagedBean")), false));
logger.info("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) cl.loadClass("javax.inject.Named")), false));
logger.info("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
(4)在进行扫描时会通过include-filter/exclude-filter来判断你的Bean类是否是合法的:
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
if (!metadata.isAnnotated(Profile.class.getName())) {
return true;
}
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
return this.environment.acceptsProfiles(profile.getStringArray("value"));
}
}
return false;
}
扫描时首先通过exclude-filter 进行黑名单过滤,然后通过include-filter 进行白名单过滤,否则默认排除。
下面举一个FilterType.CUSTOM的例子(常用)
先新增自定义过滤规则类:(name中包含"order"扫描进来)
public class JamesTypeFilter implements TypeFilter{
private ClassMetadata classMetadata;
/*
* MetadataReader:读取到当前正在扫描类的信息
* MetadataReaderFactory:可以获取到其他任何类信息
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类信息
classMetadata = metadataReader.getClassMetadata();
//获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("----->"+className);
if(className.contains("Order")){//当类包含order字符, 则匹配成功,返回true
return true;
}
return false;
}
}
在Cap2MainConfig申明:
@ComponentScan(value="com.enjoy.cap2",includeFilters={
@Filter(type=FilterType.CUSTOM,classes={JamesTypeFilters.class})
},useDefaultFilters=false)
public class Cap2MainConfig2 {
@Bean
public Person person01(){
return new Person("james",20);
}
}
打印结果:
----->com.enjoy.cap2.Cap2Test
----->com.enjoy.cap2.config.JamesTypeFilter
----->com.enjoy.cap2.controller.OrderController
----->com.enjoy.cap2.dao.OrderDao
----->com.enjoy.cap2.service.OrderService
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
cap2MainConfig
orderController
orderDao
orderService
person01
scope扫描规则
- 新建Cap3MainConfig.java
@Configuration
public class Cap3MainConfig {
//给容器中注册一个bean, 类型为返回值的类型, 默认是单实例
/*
* prototype:多实例: IOC容器启动的时候,IOC容器启动并不会去调用方法创建对象, 而是每次获取的时候才会调用方法创建对象
* singleton:单实例(默认):IOC容器启动的时候会调用方法创建对象并放到IOC容器中,以后每次获取的就是直接从容器中拿(大Map.get)的同一个bean
* request: 主要针对web应用, 递交一次请求创建一个实例
* session:同一个session创建一个实例
*/
@Bean
public Person person(){
//默认为单例
return new Person("james",20);
}
}
//单例只会被实例化一次
多例的情况下添加@Scope注解:
@Configuration
public class Cap3MainConfig {
@Scope("prototype")
@Bean
public Person person(){
return new Person("james",20);
}
}
注:
prototype: 多实例:IOC容器启动并不会去调用方法创建对象放在容器中,而是 每次获取的时候才会调用方法创建对象,见test02
singleton: 单实例(默认):IOC容器启动会调用方法创建对象放到IOC容器中
以后每交获取就是直接从容器(理解成从map.get对象)中拿
request: 主要针对WEB应用,同一次请求创建一个实例
session: 同一个session创建一个实例(后面两个用得不多,了解即可)
lazy懒加载
新建Cap4MainConfig.java:
@Configuration
public class Cap4MainConfig {
//给容器中注册一个bean, 类型为返回值的类型, 默认是单实例
/*
* 懒加载: 主要针对单实例bean:默认在容器启动的时候创建对象
* 懒加载:容器启动时候不创建对象, 仅当第一次使用(获取)bean的时候才创建被初始化
*/
@Lazy
@Bean
public Person person(){
System.out.println("给容器中添加person.......");
return new Person("james",20);
}
}
- 加上@Lazy说明此bean是懒加载的, 只有获取anno.getBean时才会加载到IOC容器中.