三)初始Spring源码以及常用组件
目录
示例源码3.1.1:一般我们在扫描的都是使用 xml 方式 去扫描 整个项目,
示例源码3.1.1:使用@ComponentScan 注解 定义范围扫描
示例源码3.1.2:一般我们在扫描的都是使用 xml 方式 去自动扫描,
示例源码3.1.3:一般我们在扫描的都是使用 xml 方式 去自定义规则扫描,
3.2.1 ,@Scope("prototype")示例: 结果最后 执行的 内存地址不一样,返回==>false
3.2.2,@Scope("singleton")示例: 结果最后 执行的 内存地址不一样,返回 ====>true,
3.2.3 ,@Scope("prototype") XML示例结果最后 执行的 内存地址不一样,返回 ==>false,
三,@Lazy 即:懒加载,延时加载 主要针对单例bean:默认在容器启动的时候不创建对象,仅当第一次使用(获取)bean的时候才创建被初始
一,@ComponentScan 扫描
- 指定扫描范围
- 扫描过滤器
- 自定义扫描规则
示例源码3.1.1:一般我们在扫描的都是使用 xml 方式 去扫描 整个项目,
//base-pacjage 是指扫描范围
<context:component-scan base-package="org.java"></context:component-scan>
示例源码3.1.1:使用@ComponentScan 注解 定义范围扫描
@Configuration
@ComponentScan(value="com.enjoy.cap2")
public class Cap2MainConfig {
//给容器中注册一个bean, 类型为返回值的类型,
@Bean
public Person person01(){
return new Person("james",20);
}
}
@Controller
public class OrderController {
}
@Repository
public class OrderDao {
}
@Service
public class OrderService {
}
public class Cap2Test {
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap2MainConfig.class);
//通过注解拿到bean 的id
String[] names = app.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
}
}
为了测试,我在同一个项目下面 分别写了 @Controller @Repository @Service ,结果意料中的全部被扫描到
示例源码3.1.2:一般我们在扫描的都是使用 xml 方式 去自动扫描,
// includeFilters Filter 后面的意思是只扫描出 Controller
// useDefaultFilters:设置为false 可以扫描全部
@Configuration
@ComponentScan(value="com.enjoy.cap2", includeFilters={
@Filter(type=FilterType.ANNOTATION, classes={Controller.class})
}, useDefaultFilters=false)
public class Cap2MainConfig {
//给容器中注册一个bean, 类型为返回值的类型,
@Bean
public Person person01(){
return new Person("james",20);
}
}
然而没什么用,我们在 xml 可以直接指定
//base-pacjage 是指扫描范围,自定义
<context:component-scan base-package="org.java,Controller"></context:component-scan>
示例源码3.1.3:一般我们在扫描的都是使用 xml 方式 去自定义规则扫描,
@Configuration
@ComponentScan(value="com.enjoy.cap2", includeFilters={
@Filter(type=FilterType.CUSTOM, classes={JamesTypeFilter.class})
}, useDefaultFilters=false)
//@Filter: 扫描规则
//@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,使用自定义扫描范围
//@ComponentScan value:指定要扫描的包
//excludeFilters = Filter[] 指定扫描的时候按照什么规则排除那些组件
//includeFilters = Filter[] 指定扫描的时候只需要包含哪些组件
//useDefaultFilters = false 默认是true,扫描所有组件,要改成false
//----扫描规则如下
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照给定的类型;比如按BookService类型
//FilterType.ASPECTJ:使用ASPECTJ表达式
//FilterType.REGEX:使用正则指定
//FilterType.CUSTOM:使用自定义规则,自已写类,实现TypeFilter接口
public class Cap2MainConfig {
//给容器中注册一个bean, 类型为返回值的类型,
@Bean
public Person person01(){
return new Person("james",20);
}
}
新增自定义过滤规则类: 如果 类名包含有“Order” 返回 ==》true
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")){//当类包含er字符, 则匹配成功,返回true
return true;
}
return false;
}
}
public class Cap2Test {
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap2MainConfig.class);
String[] names = app.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
}
}
扫描,三个类, 三个类名都包含有 Order
二,@Scope :Bean 的生命周期管理
简单来说: 对象在spring容器(IOC容器)中的生命周期,也可以理解为对象在spring容器中的创建方式。
- prototype 即多实例:IOC容器启动的时候,IOC容器启动并不会去调用方法创建对象,而是每次获取的时候才会调用方法创建对象
- singleton 即单例模式:IOC容器启动的时候会调用方法创建对象并放到IOC容器中,以后每次获取的就是直接从容器中拿的同一bean
- request 即主要针对web应用,递交一次请求创建一个实例。Spring 容器 即XmlWebApplicationContext 会为每个HTTP请求创建一个全新的RequestPrecessor对象,当请求结束后,该对象的生命周期即告结束,如同java web中request的生命周期。当同时有10个HTTP请求进来的时候,容器会分别针对这10个请求创建10个全新的RequestPrecessor实例,且他们相互之间互不干扰,简单来讲,request可以看做prototype的一种特例,除了场景更加具体之外,语意上差不多。
<bean id ="requestPrecessor" class="...RequestPrecessor" scope="request" />
- session 同一个session创建一个实例,Spring容器会为每个独立的session创建属于自己的全新的UserPreferences实例,比request scope的bean会存活更长的时间,其他的方面没区别。对于web应用来说,放到session中最普遍的就是用户的登录信息,对于这种放到session中的信息,我们可以使用如下形式的制定scope为session:
<bean id ="userPreferences" class="...UserPreferences" scope="session" />
3.2.1 ,@Scope("prototype")示例: 结果最后 执行的 内存地址不一样,返回==>false
@Configuration
public class Cap3MainConfig {
//给容器中注册一个bean, 类型为返回值的类型, 默认是单实例
/*
* prototype:多实例: IOC容器启动的时候,IOC容器启动并不会去调用方法创建对象, 而是每次获取的时候才会调用方法创建对象
* singleton:单实例(默认):IOC容器启动的时候会调用方法创建对象并放到IOC容器中,以后每次获取的就是直接从容器中拿(大Map.get)的同一个bean
* request: 主要针对web应用, 递交一次请求创建一个实例
* session:同一个session创建一个实例
*/
@Scope("prototype")
@Bean
public Person person(){
return new Person("james",20);
}
}
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap3MainConfig.class);
String[] names = app.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
//从容器中分别取两次person实例, 看是否为同一个bean
Object bean1 = app.getBean("person");
Object bean2 = app.getBean("person");
System.out.println(bean1 == bean2);
//结论:bean1就是bean2,同一个对象
}
3.2.2,@Scope("singleton")示例: 结果最后 执行的 内存地址不一样,返回 ====>true,
@Configuration
public class Cap3MainConfig {
/*
* 这里也可以直接不使用 注解@Scope ,给容器中注册一个bean, 类型为返回值的类型, 默认是单实例
* singleton:单实例(默认):IOC容器启动的时候会调用方法创建对象并放到IOC容器中,以后每次获取的就是直接从容器中拿(大Map.get)的同一个bean
*/
//@Scope("singleton")
@Scope("singleton")
@Bean
public Person person(){
return new Person("james",20);
}
}
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap3MainConfig.class);
String[] names = app.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
//从容器中分别取两次person实例, 看是否为同一个bean
Object bean1 = app.getBean("person");
Object bean2 = app.getBean("person");
System.out.println(bean1 == bean2);
//结论:bean1就是bean2,同一个对象
}
3.2.3 ,@Scope("prototype") XML示例结果最后 执行的 内存地址不一样,返回 ==>false,
将 scope 属性改成 singleton 就是单例
<bean id="prototypeCap3" class="com.enjoy.cap3.config.Cap3MainConfig" scope="prototype"></bean>
public class Cap3MainConfig {
//给容器中注册一个bean, 类型为返回值的类型, 默认是单实例
/*
* prototype:多实例: IOC容器启动的时候,IOC容器启动并不会去调用方法创建对象, 而是每次获取的时候才会调用方法创建对象
* singleton:单实例(默认):IOC容器启动的时候会调用方法创建对象并放到IOC容器中,以后每次获取的就是直接从容器中拿(大Map.get)的同一个bean
* request: 主要针对web应用, 递交一次请求创建一个实例
* session:同一个session创建一个实例
*/
//@Scope("singleton")
//@Bean
public Person person(){
return new Person("james",20);
}
}
public class Cap3Test {
@Test
public void test01(){
//AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap3MainConfig.class);
//把beans.xml的类加载到容器
ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
String[] names = app.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
//从容器中分别取两次person实例, 看是否为同一个bean
Object bean1 = app.getBean("person");
Object bean2 = app.getBean("person");
System.out.println(bean1 == bean2);
//结论:bean1就是bean2,同一个对象
}
}
三,@Lazy 即:懒加载,延时加载 主要针对单例bean:默认在容器启动的时候不创建对象,仅当第一次使用(获取)bean的时候才创建被初始
3.3.1 示例源码:默认在容器启动的时候不创建对象
public class Cap4MainConfig {
//给容器中注册一个bean, 类型为返回值的类型, 默认是单实例
/*
* 懒加载: 主要针对单实例bean:默认在容器启动的时候创建对象
* 懒加载:容器启动时候不创建对象, 仅当第一次使用(获取)bean的时候才创建被初始化
*/
@Lazy
@Bean
public Person person(){
System.out.println("给容器中添加person.......");
return new Person("james",20);
}
}
public class Cap4Test {
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap4MainConfig.class);
System.out.println("IOC容器创建完成........");
app.getBean("person");//执行获取的时候才创建并初始化bean
}
}
执行结果: 在容器创建之后, 并没有去创建bean ,而是在获取bean的时候才去创建并初始化
四,项目 Demo
Spring源码深度解析,(附代码示例 码云地址: https://gitee.com/Crazycw/SpringCode.git)
参考资料: https://docs.spring.io/spring/docs/4.3.18.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/
请看下篇:Spring源码深度解析,Spring源码以及组件(四)(附代码示例:)