【本文为bilibili视频雷丰阳的Spring源码解析的完整版总结文章,其中文章前面大部分为他人博文的搬运,后面补充了其未总结的部分】
一、Java的注解
1. 注解的概念
注释:用文字描述程序,给程序员看的;
注解:说明程序的,给计算机看的。
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
概念描述:
- JDK1.5之后的新特性
- 说明程序的
- 使用注解:@注解名称
作用分类:
- 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
/**
1. 注解javadoc演示
2. 3. @version 1.0
4. @since 1.5
*/
public class AnnoDemo1 {
/**
* 计算两数的和
* @param a 整数
* @param b 整数
* @return 两数的和
*/
public int add(int a, int b ){
return a + b;
}
}
- 代码分析:通过代码里表示的注解对代码进行分析【使用反射】
- 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【@Override】
JDK中预定义的一些注解:
@Override
:检测被该注解标注的方法是否是继承自父类(接口)的@Deprecated
:该注解标注的内容,表示已过时@SuppressWarnings
:压制警告。一般传递参数all,@SuppressWarnings("all")
2. 自定义注解
格式:
public @interface 注解名称{
属性列表;
}
本质:注解本质上就是一个接口,该接口默认继承Annotation接口
public interface MyAnno extends java.lang.annotation.Annotation {}
属性:接口中的抽象方法
属性的返回值值类型有如下取值:
- 基本数据类型
- String
- 枚举
- 注解
- 类
- 以上类型数组
public @interface MyAnno {
int value();
Person per();
MyAnno2 anno2();
String[] strs();
String name() default "张三";
}
定义了属性,在使用时需要给属性赋值:
- 如果定义属性时,使用
default
关键字给属性默认初始值,则在使用注解,可以不进行属性的赋值; - 如果只有一个属性需要赋值,并且属性的名称是
value
,则value
可以忽略,直接定义值即可; - 数组赋值时,值使用
{}
包裹。如果数组中只有一个值,则{}
可以省略。
@MyAnno(value=12, per = Person.P1, anno2 = @MyAnno2, strs="bbb")
public class Worker {
}
3. 元注解
元注解: 用于描述注解的注解
-
@Target:描述注解能够给作用的位置。
-
ElementType取值:
- TYPE:可以作用于类上
- METHOD:可以作用于方法上
- FIELD:可以作用于成员变量上
-
@Retention:描述注解被保留的阶段
value 是 java.lang.annotation.RetentionPolicy 枚举类型, RetentionPolicy 有 3 个枚举常量,如下所示:
- SOURCE:在源文件中有效(即源文件保留)
- CLASS:在 class 文件中有效(即 class 保留)
- RUNTIME:在运行时有效(即运行时保留)
@Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到。
-
@Documneted:描述注解是否被抽取到API文档中
-
@Inherited:描述注解是否被子类继承
@Target({
ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnno3 {
}
@MyAnno3
public class Worker {
@MyAnno3
public String name = "aaa";
@MyAnno3
public void show(){
}
}
4. 注解解析
在程序使用(解析)注解:获取注解中定义的属性值。
@Pro(className = "top.tjtulong.annotation.Demo1",methodName = "show")
public class ReflectTest {
public static void main(String[] args) throws Exception {
Pro an = reflectTestClass.getAnnotation(Pro.class);
// 调用注解对象中定义的抽象方法,获取返回值
String className = an.className();
String methodName = an.methodName();
System.out.println(className);
System.out.println(methodName);
}
}
- 获取注解定义的位置上的对象(Class, Method, Field);
- 获取指定的注解:
getAnnotation(Class)
,其实就是在内存中生成了一个该注解接口的子类实现对象。
public class ProImpl implements Pro{
public String className(){
return "top.tjtulong.annotation.Demo1";
}
public String methodName(){
return "show";
}
}
- 调用注解中的抽象方法获取配置的属性值
二、Spring中的注解
1. 组件注册
@Configuration
:指定配置类;
@Component
:用于把当前类对象存入spring容器中;
以下三个注解的作用与@Component完全一样,它们是spring提供的更明确的划分,使三层对象更加清晰:
@Controller
:用于表现层;@Service
:用于业务层;@Repository
:用于持久层;
@Bean
:给容器中注册一个Bean对象,类型为返回值的类型,id默认是用方法名作为id;
@ComponentScan
:指明注解扫描的包,属性包括:
- excludeFilters = Filter[] :指定扫描的时候按照什么规则排除哪些组件,通过
@Filter()
来具体指定 - includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件(同时需要配置
userDefaultFilters=false
,才能生效)-
FilterType.ANNOTATION:按照注解类型过滤
-
FilterType.ASSIGNABLE_TYPE:按照给定的类型过滤
-
FilterType.ASPECTJ:使用ASPECTJ表达式过滤
-
FilterType.REGEX:使用正则指定
-
FilterType.CUSTOM:使用自定义规则过滤
原先使用bean.xml配置时,还需先将默认属性设置成 use-defalut=false ,即禁用默认扫描规则。
-
@ComponentScans
:指明多个@ComponentScan
扫描的包;
@Scope
:调整Bean实例的作用域,共有4种:
- prototype:多实例的,ioc容器启动并不会去调用方法创建对象放在容器中,而是每次获取的时候才会调用方法创建对象;
- singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就是直接从容器中拿(map.get);
- request:同一次请求创建一个实例;
- session:同一个session创建一个实例。
@Lazy
:懒加载,容器启动不创建对象(抑制了singleton原本的加载方式),第一次获取Bean时再创建对象并初始化;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.ComponentScans;
import top.tjtulong.bean.Person;
// 配置类==配置文件
@Configuration // 告诉Spring这是一个配置类
@ComponentScans(
value = {
@ComponentScan(value="top.tjtulong",includeFilters = {
@Filter(type=FilterType.ANNOTATION, classes={
Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE, classes={
BookService.class}),
@Filter(type=FilterType.CUSTOM, classes={
MyTypeFilter.class})
},useDefaultFilters = false)
}
)
public class MainConfig {
//给容器中注册一个Bean,类型为返回值的类型,id默认是用方法名作为id
@Scope("prototype")
@Bean("person")
public Person person01(){
return new Person("lisi", 20);
}
}
public class Person {
private String name;
private Integer age;
//get() set() toString()...
}
自定义的包扫描过滤规则
public class MyTypeFilter implements TypeFilter {
/**
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类信息的
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// TODO Auto-generated method stub
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("--->"+className);
if(className.contains("er")){
return true;
}
return false;
}
}
@Condition
:按照一定的条件进行判断,满足条件给容器中注册bean,可以放在类上,也可以放在方法上;
@Import
:导入组件,id默认是 组件的全类名;
//类中组件统一设置,满足当前条件,这个类中配置的所有bean注册才能生效;
// @Conditional({WindowsCondition.class})
@Configuration
// MyImportSelector:自定义导入逻辑
// @Import({Color.class, Red.class, MyImportSelector.class})
@Import({
Color.class, Red.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig2 {
/**
* 如果系统是windows,给容器中注册("bill")
* 如果是linux系统,给容器中注册("linus")
*/
@Conditional({
WindowsCondition.class})
@Bean("bill")
public Person person01(){
return new Person("Bill Gates",62);
}
@Conditional({
LinuxCondition.class})
@Bean("linus")
public Person person02(){
return new Person("linus", 48);
}
}
Condition 类:
//判断是否linux系统
public class LinuxCondition implements Condition {
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:注释信息
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO是否linux系统
//1、能获取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、获取类加载器
ClassLoader classLoader = context.getClassLoader();
//3、获取当前环境信息
Environment environment = context.getEnvironment();
//4、获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
//可以判断容器中的Bean注册情况,也可以给容器中注册bean
boolean definition = registry.containsBeanDefinition("person");
String property = environment.getProperty("os.name");
if(property.contains("linux")){
return true;
}
return false;
}
}
实现ImportSelector
自定义逻辑导入需要的组件
//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
//返回值,就是要导入到容器中的组件的全类名
//AnnotationMetadata:当前标注 @Import 注解的类的所有注解信息
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//默认不要返回null
//全类名
return new String[]{
"com.atguigu.bean.Blue","com.atguigu.bean.Yellow"};
}
}
实现 ImportBeanDefinitionRegistrar
手动注册bean到容器中:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类;
* 把所有需要添加到容器中的bean;
* 调用BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition("top.tjtulong.bean.Red");
boolean definition2 = registry.containsBeanDefinition("top.tjtulong.bean.Blue");
if(definition && definition2){
//指定Bean定义信息
//RootBeanDefinition 为接口BeanDefintion的实现
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
//注册一个Bean,指定bean名
registry.registerBeanDefinition("rainBow", beanDefinition);
}
}
}
给容器中注册组件方法汇总:
- 包扫描+组件标注注解(
@Controller/@Service/@Repository/@Component
)[自己写的类] @Bean
[导入的第三方包里面的组件]@Import
[快速给容器中导入一个组件]@Import
(要导入到容器中的类);容器中就会自动注册这个组件,id默认是全类名;ImportSelector
:返回需要导入的组件的全类名数组;ImportBeanDefinitionRegistrar
:手动注册bean到容器中;
- 使用Spring提供的
FactoryBean
(工厂Bean);- 默认获取到的是工厂bean调用
getObject()
创建的对象; - 要获取工厂Bean本身,需要给id前面加一个&(
&colorFactoryBean
); 否则,获取的是getObject()方法返回值对象。
- 默认获取到的是工厂bean调用
@Bean
public ColorFactoryBean colorFactoryBean(){
//返回的虽然是colorFactoryBean,但其为com.titjlong.bean.Color类型
//工厂bean的获取最终会调用getObject(),以其返回值类型为准
return new ColorFactoryBean();
}
//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
//返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}
@Override
public Class<Color> getObjectType() {
return Color.class;
}
//是否为单例
@Override
public boolean isSingleton() {
return false;
}
}
2. 生命周期
bean 的生命周期是指:bean的创建 ---> 初始化 ---> 销毁的过程
方法一:通过@Bean
指定初始化和销毁方法
initMethod
:初始化方法;destroyMethod
:销毁方法。
@Component
public class Car {
public Car(){
System.out.