【本文为bilibili视频雷丰阳的Spring源码解析的完整版总结文章,其中文章前面大部分为他人博文的搬运,后面补充了其未总结的部分】

一、Java的注解

1. 注解的概念

注释:用文字描述程序,给程序员看的;

注解:说明程序的,给计算机看的。

注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

概念描述:

  • JDK1.5之后的新特性
  • 说明程序的
  • 使用注解:@注解名称

作用分类:

  1. 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
/**1. 注解javadoc演示2.  3. @version 1.04. @since 1.5*/
public class AnnoDemo1 {/*** 计算两数的和* @param a 整数* @param b 整数* @return 两数的和*/public int add(int a, int b ){return a + b;}
}
  1. 代码分析:通过代码里表示的注解对代码进行分析【使用反射】
  2. 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【@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 "张三";
}

定义了属性,在使用时需要给属性赋值:

  1. 如果定义属性时,使用default关键字给属性默认初始值,则在使用注解,可以不进行属性的赋值;
  2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以忽略,直接定义值即可;
  3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略。
@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 {@MyAnno3public String name = "aaa";@MyAnno3public 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);}
}
  1. 获取注解定义的位置上的对象(Class, Method, Field);
  2. 获取指定的注解:getAnnotation(Class),其实就是在内存中生成了一个该注解接口的子类实现对象。
public class ProImpl implements Pro{public String className(){return "top.tjtulong.annotation.Demo1";}public String methodName(){return "show";}
}    
  1. 调用注解中的抽象方法获取配置的属性值

二、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:可以获取到其他任何类信息的*/@Overridepublic 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:注释信息*/@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// TODO是否linux系统//1、能获取到ioc使用的beanfactoryConfigurableListableBeanFactory beanFactory = context.getBeanFactory();//2、获取类加载器ClassLoader classLoader = context.getClassLoader();//3、获取当前环境信息Environment environment = context.getEnvironment();//4、获取到bean定义的注册类BeanDefinitionRegistry registry = context.getRegistry();//可以判断容器中的Bean注册情况,也可以给容器中注册beanboolean 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手工注册进来*/@Overridepublic 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);}}
}

给容器中注册组件方法汇总:

  1. 包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
  2. @Bean[导入的第三方包里面的组件]
  3. @Import[快速给容器中导入一个组件]
    1. @Import(要导入到容器中的类);容器中就会自动注册这个组件,id默认是全类名;
    2. ImportSelector:返回需要导入的组件的全类名数组;
    3. ImportBeanDefinitionRegistrar:手动注册bean到容器中;
  4. 使用Spring提供的FactoryBean(工厂Bean);
    1. 默认获取到的是工厂bean调用getObject()创建的对象;
    2. 要获取工厂Bean本身,需要给id前面加一个&(&colorFactoryBean); 否则,获取的是getObject()方法返回值对象。
@Bean
public ColorFactoryBean colorFactoryBean(){//返回的虽然是colorFactoryBean,但其为com.titjlong.bean.Color类型//工厂bean的获取最终会调用getObject(),以其返回值类型为准return new ColorFactoryBean();
}//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {//返回一个Color对象,这个对象会添加到容器中@Overridepublic Color getObject() throws Exception {System.out.println("ColorFactoryBean...getObject...");return new Color();}@Overridepublic Class<Color> getObjectType() {return Color.class;}//是否为单例@Overridepublic boolean isSingleton() {return false;}
}

2. 生命周期

bean 的生命周期是指:bean的创建 ---> 初始化 ---> 销毁的过程

方法一:通过@Bean指定初始化和销毁方法

initMethod:初始化方法;destroyMethod:销毁方法。

@Component
public class Car {public Car(){System.out.println("car constructor...");}public void init(){System.out.println("car ... init...");}public void detory(){System.out.println("car ... detory...");}
}
@ComponentScan("top.tjtulong.bean")
@Configuration
public class MainConfigOfLifeCycle {//@Scope("prototype")@Bean(initMethod="init", destroyMethod="detory")public Car car(){return new Car();}
}
构造(对象创建):单实例 singleton:在容器启动的时候创建对象多实例 prototype:在每次获取的时候创建对象
初始化:对象创建完成,并赋值好,调用初始化方法。
销毁:单实例 singleton:容器关闭的时候销毁多实例 prototype: 容器不会管理这个bean; 容器不会调用销毁方法。

方法二:通过让Bean实现InitalizingBean接口(定义初始化逻辑)afterPropertiesSet()方法,DisposableBean接口(定义销毁逻辑)destroy() 方法

@Component
public class Cat implements InitializingBean,DisposableBean {public Cat(){System.out.println("cat constructor...");}@Overridepublic void destroy() throws Exception {System.out.println("cat...destroy...");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("cat...afterPropertiesSet...");}
}

方法三:可以使用JSR250

  • @PostConstruct:在bean创建完成并且属性赋值完成,来执行初始化方法;作用于方法层面@Target(METHOD)
  • @PreDestroy:在容器销毁bean之前通知我们进行清理工作;
@Component
public class Dog implements ApplicationContextAware {public Dog(){System.out.println("dog constructor...");}//对象创建并赋值之后调用@PostConstructpublic void init(){System.out.println("Dog....@PostConstruct...");}//容器移除对象之前@PreDestroypublic void detory(){System.out.println("Dog....@PreDestroy...");}
}
以上三种初始化方法的执行顺序为:Constructor > @PostConstruct >InitializingBean接口(afterPropertiesSet) > initMethod三种销毁方法的执行顺序为:@preDestroy >DisposableBean接口 > @Bean(destroyMethod)

方法四:BeanPostProcessor接口(bean的后置处理器)

在bean初始化前后进行一些处理工作

  • postProcessBeforeInitialization:在初始化之前工作,在上面三个方法执行前执行
  • postProcessAfterInitialization:在初始化之后工作,在上面三个方法执行后执行
/*** 后置处理器:初始化前后进行处理工作* 将后置处理器加入到容器中*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {/*** @param bean the new bean instance* @param beanName the name of the bean**/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("postProcessBeforeInitialization..."+beanName+"=>"+bean);return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("postProcessAfterInitialization..."+beanName+"=>"+bean);return bean;}
}

【BeanPostProcessor 的原理】

refresh()方法中的 finishBeanFactoryInitialization(beanFactory) 方法执行时,bean进行初始化。

step1:populateBean(beanName, mbd, instanceWrapper)给bean进行属性赋值
step2:initializeBean()初始化bean对象

  • initializeBean()
     wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);invokeInitMethods(beanName, wrappedBean, mbd); //执行自定义初始化wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

在方法applyBeanPostProcessorBeforeInitialization中:会遍历得到容器中所有的BeanPostProcessor,逐个执行BeanPostProcessor.postProcessorBeforeInitialization()方法,一但返回null,跳出for循环,直接返回,不会执行后面的方法 。

BeanProstProcessor在Spring底层的使用

  • ApplicationContextAware接口获取ioc容器,底层通过ApplicationContextAwareProcessor实现
public class Dog implements ApplicationContextAware {private ApplicationContext applicationContext;public Dog(){System.out.println("dog constructor....");}@PostConstructpublic void init(){System.out.println("Dog...@PostConstruct");}@PreDestroypublic void destory(){System.out.println("Dog...@PreDestory");}public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
}

p.s. bean的赋值、注入其它组件、@Autowired、初始化注解、生命周期注解功能@PreConstruct等、@Async等功能都是通过BeanPostProcessor实现。

3. 属性赋值

使用@Value赋值

  1. 基本数值

  2. 可以写SpEL:#{}

  3. 可以写${},去取配置文件【properties】中的值(在运行环境变量Environment 里面的值)

    #{} 和 ${} 的区别 可以参看

     #{}1. #{}属于SPEL语法。如#{dataSource.userName}给某个属性赋值时,dataSource是程序中已经注入存在的Bean容器, 则可以通过 @value(#{dataSource.userName}) 获取属性的值。2. #{} mybatis中使用它相当于占位符的用法。如#{name} 可以自动进行jdbc类型的属性转换,如果name的值是 mark  则转换之后就是 'mark',它可以防止sql注入。${}3. ${server.port} 取配置文件中的配置值,如 server.port=80 则可以通过 @value(${server.port}) 来获取属性的值。4. ${} mybatis中使用它相当于不做单引号任何处理, 如${name},如果name的值是 create_time,则转换之后就是 order by create_time ,不做处理直接拼接,不能防止sql注入,另外的场景也有模糊查询 like '%${name}%'
    
public class Person {@Value("张三")private String name;@Value("#{20-2}")private Integer age;@Value("${person.nickName}")private String nickName;
}

配置类

// 使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;
// 加载完外部的配置文件以后使用${}取出配置文件的值
@PropertySource(value={"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {@Beanpublic Person person(){return new Person();}
}

针对Property的取值,也可以直接再环境变量Environment中取

 ConfigurableEnvironment environment = applicationContext.getEnvironment();String property = environment.getProperty("person.nickName");System.out.println(property);

4. 自动装配

自动装配:Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值。

  • @Autowired:自动注入,可以放在构造器,参数,方法,属性

    • 默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class),找到就赋值;

    • 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找。

 BookService {@AutowiredBookDao  bookDao;}
  1. 标注在方法位置:
//默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@Component
public class Bosss{private Car car;public Car getCar(){return car;}@Autowired//标注在方法上,Spring容器创建当前对象,就会调用方法,完成赋值//方法使用的参数,自定义类型的值从ioc容器中获取public void setCar(Car car){this.car = car;}
}

@Bean标注的方法创建对象时,方法参数的值从容器中获取,默认不写@Autowired

public class Color{private Car car;//以下省略get/Set方法
}
//方法参数Car,会自动从容器中获取
@Bean
public Color color(Car car){Color color = new Color();color.setCar(car);return new Color();
}
  1. 标注在构造器上(有参构造器):如果组件只有一个有参构造器,则此时@Autowired可以省略。
//默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@Component
public class Bosss{private Car car;//构造器要用的组件,都是从容器中获取@Autowiredpublic Boss(Car car){this.car = car;}public Car getCar(){return car;}public void setCar(Car car){this.car = car;}
}
  1. 标注在参数上
//默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@Component
public class Bosss{private Car car;//放在参数上public Boss(@Autowired Car car){this.car = car;}public void setCar(Car car){this.car = car;}}
  • @Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名;

  • 自动装配默认一定要将属性赋值好,没有就会报错;可以使用@Autowired(required=false)

  • @Primary 用于在有多个同类型对象的实例Bean下,指明首选装配的对象。优先装配标有@Primary的Bean,此时@Qualifier不使用。

Spring还支持使用@Resource(JSR250)和@Inject(JSR330) [java规范的注解]:

  • @Resource:可以和@Autowired一样实现自动装配功能,默认是按照组件名称进行装配的;但没有能支持@Primary功能,也没有支持@Autowired(reqiured=false);

  • @Inject:需要导入javax.inject的依赖包,和@Autowired的功能一样,但没有required=false的功能;

      p.s. @Autowired是Spring定义的;而@Resource、@Inject都是Java的规范。
    

以上注解功能,都是通过AutowiredAnnotationBeanPostProcessor解析完成自动装配功能。


  • 自定义组件想要使用Spring容器底层的一些组件(如:ApplicationContext,BeanFactory,…等)

    • 自定义组件需要实现相应的xxxAware接口:在创建对象的时候,会调用接口规定的方法注入相关的组件。

    • 在Spring中有很多以Aware结尾的接口,如果一个Bean实现了该接口,那么当该Bean被Spring初始化时,Spring会向该Bean注入相关资源(就是会回调接口中的方法)。作用是把Spring底层一些组件注入到自定义的Bean中。

示例:下面的TestService实现了两个接口BeanNameAwareApplicationContextAware接口。当Spring对bean进行初始化时,Spring会调用接口对应得方法。这样就可以获得spring中的资源。

@Component
public class TestService implements BeanNameAware,ApplicationContextAware {private String beanName;private ApplicationContext context;@Override// 获取到bean的名称public void setBeanName(String name) {System.out.println("name = " + name);beanName = name;}@Override// 获取上下文环境ApplicationContextpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("applicationContext = " + applicationContext);context = applicationContext;}
}

Aware子接口及作用: 参考

接口名称 作用
ApplicationContextAware 获取spring 上下文环境的对象
ApplicationEventPublisherAware 事件发布器
BeanClassLoaderAware 加载Spring Bean的类加载器
BeanNameAware 获取该bean在BeanFactory配置中的名字
BeanFactoryAware 创建它的BeanFactory实例
EmbeddedValueResolverAware 手动读取配置参数值
EnvironmentAware 环境变量读取和属性对象获取
ImportAware 处理自定义注解
LoadTimeWeaverAware 加载Spring Bean时植入第三方模块,如AspectJ
MessageSourceAware 国际化
NotificationPublisherWare JMX通知
ResourceLoaderAware 获取ResourceLoader对象,通过它获得各种资源

ApplicationContextAware通过ApplicationContextAwareProcessor实现。

具体实现参考:https://blog.csdn.net/baidu_19473529/article/details/81072524

5. 环境搭建

@Profile:指定组件在哪个环境的情况下才能被注册到容器中,若不指定,任何环境下都能注册这个组件。

便于开发环境、测试环境、生产环境之间的切换

  1. 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境;
  2. 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效;
  3. 没有标注环境标识的bean,在任何环境下都是加载的;
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{@Value("${db.user}")private String user;private StringValueResolver valueResolver;private String driverClass;@Profile("test")@Bean("testDataSource")public DataSource dataSourceTest(@Value("${db.password}")String pwd) throws Exception{ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setUser(user);dataSource.setPassword(pwd);dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");dataSource.setDriverClass(driverClass);return dataSource;}@Profile("dev")@Bean("devDataSource")public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws Exception{ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setUser(user);dataSource.setPassword(pwd);dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud");dataSource.setDriverClass(driverClass);return dataSource;}@Profile("prod")@Bean("prodDataSource")public DataSource dataSourceProd(@Value("${db.password}")String pwd) throws Exception{ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setUser(user);dataSource.setPassword(pwd);dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/scw_0515");   dataSource.setDriverClass(driverClass);return dataSource;}@Override// 处理${}等特殊字符public void setEmbeddedValueResolver(StringValueResolver resolver) {this.valueResolver = resolver;driverClass = valueResolver.resolveStringValue("${db.driverClass}");}
}

设置运行时设置profile的方式:

  1. 使用命令行动态参数: 在虚拟机参数位置加上-Dspring.profiles.active=dev
  2. 代码的方式激活某种环境
//1、使用无参构造创建一个applicationContext
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();//2、设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev");//3、注册主配置类
applicationContext.register(MainConfigOfProfile.class);//4、启动刷新容器
applicationContext.refresh();

三、AOP的原理

AOP动态代理:指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式。

1. AOP的使用

step:

  1. 导入Spring-AOP模块:spring-aspects;

  2. 定义一个业务逻辑类(如:MathCalculator类),在业务逻辑运行的时候将日志进行打印(方法执行之前、方法运行结束、方法出现异常,方法返回前);

  3. 定义一个日志切面类(如LogAspects),切面类里面的方法需要动态感知业务逻辑类运行到哪里然后执行;

  4. 编写通知方法:

     1. 前置通知(@Before):在目标方法运行之前运行;2. 后置通知(@After):在目标方法运行结束之后运行(无论方法正常结束还是异常结束);3. 返回通知(@AfterReturning):在目标方法正常返回之后运行;4. 异常通知(@AfterThrowing):在目标方法出现异常以后运行;5. 环绕通知(@Around):动态代理,手动推进目标方法运行(ProceedingJoinPoint.procced());
    
  5. 给切面类的目标方法标注何时何地运行(通知注解); 将切面类和业务逻辑类(目标方法所在类)都加入到容器中;

  6. 必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect);

  7. 给配置类中加 @EnableAspectJAutoProxy ,开启基于注解的aop模式(很多以Enablexxx开头的注解均是开启对应某项功能)。

@Component("logger")
@Aspect//表示当前类是一个切面类
public class Logger {// 抽取公共切入点表达式, impl下的任意类中、任意方法、任意参数@Pointcut("execution(* io.github.tjtulong.service.impl.*.*(..))")private void pt1(){}/*** 前置通知*///@Before("public int com.atguigu.aop.MathCalculator.div(int,int)")@Before("pt1()")public  void beforePrintLog(JoinPoint joinPoint){Object[] args = joinPoint.getArgs();//打印调用的方法名,以及方法中的参数列表System.out.println("前置通知的"+joinPoint.getSignature.getName()+"方法开始记录日志了。参数列表:{"+Arrays.asList(args)+"}");}/*** 后置通知*/// returning 用于标注接收返回值对象// JoinPoint 要出现在参数的第一位,不能写在后面@AfterReturning(value="pt1()", returning="result")public  void afterReturningPrintLog(JoinPoint joinPoint, Object result){System.out.println("后置通知的"+joinPoint.getSignature.getName()+"方法开始记录日志了。返回结果为:"+result);}/*** 异常通知*/@AfterThrowing(value="pt1()", throwing="exception")public  void afterThrowingPrintLog(Exception exception){System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。异常信息为:{"+exception+"}");}/*** 最终通知*/@After("pt1()")public  void afterPrintLog(){System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。");}
}

阅读Sring源码的核心是:看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么。

2. AOP源码

2.1 @EnableAspectJAutoProxy注解

@EnableAspectJAutoProxy的作用:

  • @Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar
  • 利用AspectJAutoProxyRegistrar自定义给容器中注册bean;
    • 给容器中注册的Bean为 AnnotationAwareAspectJAutoProxyCreator , id为 internalAutoProxyCreator
      (此时保存的是Bean的定义信息BeanDefinetion,具体的对象还未创建)
  • 之后读取注释@EnableAspectJAutoProxy上的额外属性信息,进行必要的额外配置。

@EnableAspectJAutoProxy源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {/*** Indicate whether subclass-based (CGLIB) proxies are to be created as opposed* to standard Java interface-based proxies. The default is {@code false}.*/boolean proxyTargetClass() default false;/*** Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.* Off by default, i.e. no guarantees that {@code AopContext} access will work.* @since 4.3.1*/boolean exposeProxy() default false;}

2.2 AnnotationAwareAspectJAutoProxyCreator 组件

AnnotationAwareAspectJAutoProxyCreator类的层级结构图:

注意:实现了SmartInstantiationAwareBeanPostProcessorBeanFactoryAware接口,说明该类为后置处理器(在bean初始化完成前后做事情)并自动装配了BeanFactory

AbstarctAutoProxyCreator.setBeanFactory();
AbstractAutoProxyCreator.后置处理器的逻辑AbstractAdvisorAutoProxyCreator.setBeanFactory() -> initBeanFactory()AnnotionationAwareAspectJAutoProxyCreator.initBeanFactory();

创建和注册AnnotationAwareAspectJAutoProxyCreator的过程

  • step1:传入配置类,创建ioc容器;

  • step2:注册配置类,调用refresh()刷新容器;

    public AnnotationConfigApplicationContext(Class<?>... annotatedClasses){this();register(annotatedClasses);refresh();
    }
    
  • step3:registerBeanPostProcessors(beanFactory),注册bean的后置处理器来方便拦截bean的创建;

    1. 先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
    2. 给容器中加别的BeanPostProcessor
    3. 优先注册实现了PriorityOrdered接口的BeanPostProcessor
    4. 再给容器中注册实现了Ordered接口的BeanPostProcessor
    5. 注册没实现优先级接口的BeanPostProcessor
    6. 注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
      对于AnnotationAwareAspectJAutoProxyCreator,创建一个id为internalAutoProxyCreatorBeanPostProcessor, 具体流程为:
    1. 通过createBean()创建Bean的实例;
    2. populateBean():给bean的各种属性赋值;
    3. initializeBean():初始化bean;(1) invokeAwareMethods():处理Aware接口的方法回调(赋值)(2) applyBeanPostProcessorsBeforeInitialization():执行所有后置处理器的postProcessBeforeInitialization()(3) invokeInitMethods();执行自定义的初始化方法(4) applyBeanPostProcessorsAfterInitialization():执行所有后置处理器的postProcessAfterInitialization()//设置beanFactory,并initBeanFactory();4. BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;--> aspectJAdvisorsBuilder
    1. 把BeanPostProcessor注册到BeanFactory中:
      beanFactory.addBeanPostProcessor(postProcessor);

以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程


step4:finishBeanFactoryInitialization(beanFactory):完成BeanFactory初始化工作,创建剩下的单实例bean。

  • 遍历获取容器中所有的Bean,依次创建对象getBean(beanName);

  • 创建bean。AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截InstantiationAwareBeanPostProcessor 会调用postProcessBeforeInstantiation()方法,其流程如下:

1. 先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;只要创建好的Bean都会被缓存起来
2. createBean(),创建bean;1) AnnotationAwareAspectJAutoProxyCreator会在任何bean创建之前先尝试返回bean的实例【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】(a) resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续(b)bean = applyBeanPostProcessorsBeforeInstantiation()://拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor,就执行postProcessBeforeInstantiationif (bean != null) {bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);}(b) doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例,与step3中一致

AnnotationAwareAspectJAutoProxyCreatorInstantiationAwareBeanPostProcessor的作用:

  1. 每一个bean创建之前,调用postProcessBeforeInstantiation()

    • 判断当前bean是否在advisedBeans中(保存了所有需要增强的bean)
    • 判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)
  2. 创建对象 ----> postProcessAfterInitialization()

postProcessAfterInitialization()return wrapIfNecessary(bean, beanName, cacheKey); //包装如果需要的情况下1. 获取当前bean的所有增强器(通知方法)  Object[] specificInterceptors(1) 找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的);(2) 获取到能在bean使用的增强器;(3) 给增强器排序.2. 保存当前bean在advisedBeans中;3. 如果当前bean需要增强,创建当前bean的代理对象;(1) 获取所有增强器(通知方法)(2) 保存到proxyFactory(3) 创建代理对象:Spring自动决定JdkDynamicAopProxy(config);jdk动态代理;ObjenesisCglibAopProxy(config);cglib的动态代理;4. 给容器中返回当前组件使用jdk/cglib增强了的代理对象;5. 以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;
  1. 目标方法的执行:
    容器中保存了组件的代理对象,对象里面保存了详细信息(比如增强器,目标对象,xxx)。

    1. CglibAopProxy.intercept():拦截目标方法的执行

    2. 根据ProxyFactory对象获取将要执行的目标方法的拦截器链

 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;Object target = null;TargetSource targetSource = this.advised.getTargetSource();try {  ...  target = targetSource.getTarget();  Class<?> targetClass = (target != null ? target.getClass() : null);  // 重点:将所有的MethodInterceptor串成一个链List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);  Object retVal;//如果没有拦截器,直接执行目标方法if(chain.isEmpty() && Modifier.isPublic(method.getModifiers())){Object[] argsToUse = AopProxyUtils.adaptArgumentsInfNecessary(method, args);retVal = methodProxy.invoke(target, argsToUse);}else{// proxy为动态代理后的bean// target为原bean// proceed方法为处理之前提到的chain上所有methodInterceptor// 最后一个methodinterceptor调用methodProxy的invoke方法// methodProxy.invoke(this.target, this.arguments);retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}retVal = processReturnType(proxy, target, method, retVal);return retVal;}finally {if (target != null && !targetSource.isStatic()) {targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}}

拦截器链的获取:
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {@Overridepublic List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass) {// This is somewhat tricky... We have to process introductions first,// but we need to preserve order in the ultimate list.List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();for (Advisor advisor : config.getAdvisors()) {if (advisor instanceof PointcutAdvisor) {// Add it conditionally.PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {//将所有的增强器,转为Interceptor;MethodInterceptor[] interceptors = registry.getInterceptors(advisor);MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {if (mm.isRuntime()) {// Creating a new object instance in the getInterceptors() method// isn't a problem as we normally cache created chains.for (MethodInterceptor interceptor : interceptors) {interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));}}else {interceptorList.addAll(Arrays.asList(interceptors));}}}}else if (advisor instanceof IntroductionAdvisor) {IntroductionAdvisor ia = (IntroductionAdvisor) advisor;if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {Interceptor[] interceptors = registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}else {Interceptor[] interceptors = registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}return interceptorList;}...
}
@Overridepublic MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);Advice advice = advisor.getAdvice();if (advice instanceof MethodInterceptor) {interceptors.add((MethodInterceptor) advice);}for (AdvisorAdapter adapter : this.adapters) {if (adapter.supportsAdvice(advice)) {interceptors.add(adapter.getInterceptor(advisor));}}if (interceptors.isEmpty()) {throw new UnknownAdviceTypeException(advisor.getAdvice());}return interceptors.toArray(new MethodInterceptor[interceptors.size()]);}
  1. List<Object> interceptorList 保存所有的拦截器

  2. 遍历所有的拦截器,将其转为Interceptor;
    registry.getInterceptors(advisor);

  3. 将增强器转为List<MethodInterceptor>;

    • 如果是MethodInterceptor,直接加入到集合中
    • 如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor
    • 转换完成返回MethodInterceptor数组。
  4. 如果没有拦截器执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器),则执行目标方法;

  5. 链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;拦截器链的机制,保证通知方法与目标方法的执行顺序。

参考:https://www.jianshu.com/p/1b557d22fad3

四、Spring事务控制

1. 声明式事务

@EnableTransactionManagement
@ComponentScan("top.tjtulong.tx")
@Configuration
public class TxConfig {//数据源@Beanpublic DataSource dataSource() throws Exception{ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setUser("root");dataSource.setPassword("123456");dataSource.setDriverClass("com.mysql.jdbc.Driver");dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");return dataSource;}// 创建JdbcTemplate组件@Beanpublic JdbcTemplate jdbcTemplate() throws Exception{//调用dataSource()方法时,Spring对@Configuration类会特殊处理;//给容器中加组件的方法,多次调用都只是从容器中找组件JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());return jdbcTemplate;}//注册事务管理器在容器中@Beanpublic PlatformTransactionManager transactionManager() throws Exception{return new DataSourceTransactionManager(dataSource());}
}
环境搭建步骤:
1. 导入相关依赖:数据源、数据库驱动、Spring-jdbc模块
2. 配置数据源、 JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
3. 给方法上标注@Transactional 表示当前方法是一个事务方法
4. @EnableTransactionManagement 开启基于注解的事务管理功能
5. 配置事务管理器来控制事务 DataSourceTranscationManager()

2. 源码分析

@EnableTransactionManagement

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {/*** Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as* opposed to standard Java interface-based proxies ({@code false}). The default is* {@code false}. <strong>Applicable only if {@link #mode()} is set to* {@link AdviceMode#PROXY}</strong>.* <p>Note that setting this attribute to {@code true} will affect <em>all</em>* Spring-managed beans requiring proxying, not just those marked with* {@code @Transactional}. For example, other beans marked with Spring's* {@code @Async} annotation will be upgraded to subclass proxying at the same* time. This approach has no negative impact in practice unless one is explicitly* expecting one type of proxy vs another, e.g. in tests.*/boolean proxyTargetClass() default false;/*** Indicate how transactional advice should be applied. The default is* {@link AdviceMode#PROXY}.* @see AdviceMode*/AdviceMode mode() default AdviceMode.PROXY;/*** Indicate the ordering of the execution of the transaction advisor* when multiple advices are applied at a specific joinpoint.* The default is {@link Ordered#LOWEST_PRECEDENCE}.*/int order() default Ordered.LOWEST_PRECEDENCE;}

功能于@EnableAspectJAutoProxy类似,利用TransactionManagementConfigurationSelector给容器中导入两个组件:

  1. AutoProxyRegistrar

    • 给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件(也是一个后置处理器)
    • 利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法,利用拦截器链进行调用。

2.ProxyTransactionManagementConfiguration

给容器中注册**事务增强器**;1. 事务增强器要用事务注解的信息,`AnnotationTransactionAttributeSource` 解析事务注解。
2. 事务拦截器:`TransactionInterceptor`。 保存了事务属性信息,事务管理器;(本质上是一个 `MethodInterceptor`)。
在目标方法执行的时候,执行拦截器链,即事务拦截器:1. 先获取事务相关属性,再获取`PlatformTranscationManager`。如果事先没有添加指定任何`transcationManager`,最终会从容器中按类型获取一个`PlatformTranscationManager`。2. 执行目标方法时- 如果异常,获取到事务管理器,利用事务管理回滚操作- 如果正常,利用事务管理器,提交事务。

五、扩展原理

1. BeanFactoryPostProcessor

BeanFactory 的后置处理器。在BeanFactory 标准初始化之后调用,用来定制和修改BeanFactory的内容。

执行时机:所有的bean定义已经保存加载到beanFactory,但是bean的实例还未创建

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("MyBeanFactoryPostProcessor...postProcessBeanFactory...");// 获取容器中组件的数量int count = beanFactory.getBeanDefinitionCount();// 获取容器中全部组件的名称String[] names = beanFactory.getBeanDefinitionNames();System.out.println("当前BeanFactory中有"+ count +" 个Bean");System.out.println(Arrays.asList(names));}
}

原理
ioc容器创建对象 refresh()——> invokeBeanFactoryPostProcessors(beanFactory);

  1. 直接在BeanFactory中找到所有类型是BeanFactoryPostProcessor的组件,并执行他们的方法
  2. 在初始化创建其他组件前面执行。

BeanFactoryPostPorcessor的子接口 BeanDefinitionRegistryPostProcessor

继承于BeanFactoryPostProcessor,实现的方法为postProcessBeanDefinitionRegistry();

执行时机:在所有bean定义信息将要被加载,bean实例还未创建的;

优先于BeanFactoryPostProcessor 执行,利用BeanDefinitionRegistryPostProcessor给容器中再额外添加一些组件;

示例代码

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor{@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("MyBeanDefinitionRegistryPostProcessor...bean的数量:"+beanFactory.getBeanDefinitionCount());}//BeanDefinitionRegistry为Bean定义信息的保存中心,以后BeanFactory就是按照 BeanDefinitionRegistry 里面保存的每一个bean定义信息创建bean实例;@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {System.out.println("postProcessBeanDefinitionRegistry...bean的数量:"+registry.getBeanDefinitionCount());//RootBeanDefinition beanDefinition = new RootBeanDefinition(Blue.class);// 手动添加一个BeanDefinitionAbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Blue.class).getBeanDefinition();registry.registerBeanDefinition("hello", beanDefinition);}}

原理

  1. ioc创建对象
  2. refresh() ——> invokeBeanFactoryPostProcessors(beanFactory);
  3. 从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件;
    1. 依次触发所有的postProcessBeanDefinitionRegistry()方法;
    2. 再来触发postProcessBeanFactory()方法 (BeanFactoryPostProcessor中定义);
  4. 再来从容器中找到BeanFactoryPostProcessor组件;然后依次触发postProcessBeanFactory()方法。

2. ApplicationListener 监听器

监听容器中发布的事件,事件驱动模型开发。
public interface ApplicationListener<E extends ApplicationEvent> 监听ApplicationEvent 及其下面的子事件

@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {//当容器中发布此事件以后,方法触发@Overridepublic void onApplicationEvent(ApplicationEvent event) {System.out.println("收到事件:"+event);}
}

步骤:

  1. 写一个监听器(ApplicationListener 实现类)来监听某个事件( ApplicationEvent 及其子类)也可以使用 @EventListener

  2. 把监听器加入容器中

  3. 只要容器中有相关事件的发布,我们就能监听这个事件

    • ContextRefreshedEvent:容器刷新完成(所有bean都完全创建)会发布这个事件;
    • ContextClosedEvent:关闭容器会发布这个事件;
  4. 也可以自己发布事件:applicationContext.publishEvent(object);

2.1. 监听器的创建过程

refresh() --> registerListeners();

从容器中拿到所有的监听器,把他们注册到applicationEventMulticaster中(注意此时并不初始化):

String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for(String listenerBeanName : listenerBeanNames){getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}

示例:ContextRefreshedEvent 事件,自己发布的事件,ContextClosedEvent 事件:

  1. ContextRefreshedEvent 事件:

    • 容器创建对象:refresh();
    • 容器刷新完成:finishRefresh();
    • finshRefresh() --> publishEvent(new ContextRefreshedEvent(this));
      • 见2.2事件发布流程
  2. 自己发布的事件

  3. 容器关闭事件 ContextClosedEvent

2.2. 事件发布流程

publishEvent(new ContextRefreshedEvent(this));

  1. 获取事件的多播器(派发器):getApplicationEventMulticaster()
  2. multicastEvent() 派发事件;
  3. 获取到所有的 ApplicationListener
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {// 1.如果有Executor,可以支持使用Executor进行异步派发;Executor executor = getTaskExecutor();if (executor != null) {executor.execute(new Runnable() {@Overridepublic void run() {invokeListener(listener, event);}});}else {// 2.否则,同步的方式直接执行listener方法;invokeListener(listener, event); //invokeListener()中将拿到listener并回调onApplicationEvent()方法;}}}

2.3. 事件多播器(派发器)

刷新容器 refresh() ——> initApplicationEventMulticaster(); 初始化ApplicationEventMulticaster

  1. 先去容器中找有没有 id="applicationEventMulticaster"的组件;有则直接获取该组件
  2. 如果没有,可以自动创建一个多播器this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);并且加入到容器中,我们就可以在其他组件要派发事件,自动注入这个applicationEventMulticaster

如何知道容器中有哪些监听器applicationListener

  1. 容器创建对象:refresh();

  2. registerListeners()

protected void registerListeners() {// Register statically specified listeners first.for (ApplicationListener<?> listener : getApplicationListeners()) {getApplicationEventMulticaster().addApplicationListener(listener);}// Do not initialize FactoryBeans here: We need to leave all regular beans// uninitialized to let post-processors apply to them!//从容器中拿到所有的监听器,把它们注册到 applicationEventMulticaster中String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);for (String listenerBeanName : listenerBeanNames) {//将listener注册到ApplicationEventMulticaster中getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);}// Publish early application events now that we finally have a multicaster...Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;this.earlyApplicationEvents = null;if (earlyEventsToProcess != null) {for (ApplicationEvent earlyEvent : earlyEventsToProcess) {getApplicationEventMulticaster().multicastEvent(earlyEvent);}}
}

@EventListener 注解的使用和原理

package com.atguigu.ext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListenr;
import org.springframework.stereotype.Service;@Service
public class UserService{//指定监听什么事件@EventListener(classes={ApplicationEvent.class})public void listen(ApplicationEvent event){System.out.println("UserService..监听到的事件:" + event);}
}

原理:

  1. @EventListener 使用 EventListenerMethodProcessor 处理器来解析方法上的 @EventListener
  2. EventListenerMethodProcessor 方法中实现了 SmarthInitializingSingleton 接口

2.4. SmartInitalizingSingleton 原理

  1. ioc 容器创建对象并 refresh()

  2. finishBeanFactoryInitialization(beanFactory);初始化剩下的单实例bean;

    • 先创建所有单实例bean;通过getBean()方法创建对象。
    • 再获取所有创建好的单实例bean,判断是否是 SmartInitializingSingleton 类型的,如果是就调用 smartSIngleton.afterSingletonsInstantiated()方法;(在所有单实例bean创建完成之后调用)

六、Spring 容器创建

容器初始化过程

(标准初始化工作)
IOC容器中提供的两个接口。BeanFactory和ApplicationContext。
其中,BeanFactory有很多实现类,ApplicationContext是BeanFactory的子接口,其常用实现类是org.springframework.context.support.FileSystemXmlApplicationContextorg.springframework.context.support.ClassXmlAplicationContext

以 ClassXmlApplication为例:

1. new ClassPathXmlApplicationContext(),初始化ClassPathXmlApplicationContext

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)throws BeansException {super(parent);setConfigLocations(configLocations);if (refresh) {refresh();}
}

2. 调用AbstractRefreshableConfigApplicationContextsetConfigLocations(configLocations),设置xml文件路径

public void setConfigLocations(String... locations) {if (locations != null) {Assert.noNullElements(locations, "Config locations must not be null");this.configLocations = new String[locations.length];for (int i = 0; i < locations.length; i++) {this.configLocations[i] = resolvePath(locations[i]).trim();}}else {this.configLocations = null;}
}

3. 调用AbstractApplicationContext的refresh()。 方法内容太多,对于ClassPathXmlApplicationContext加载bean,只需了解它的obtainFreshBeanFactory方法

  • refresh()方法中的:prepareRefresh(): 为刷新准备上下文环境
  • obtainFreshBeanFactory() :让子类刷新内部 bean 工厂。
  • 进入obtainFreshBeanFatory(),关闭前面所有 bean 工厂,为新的上下文环境初始化一个新的 bean 工厂。这里需要子类AbstractRefreshableApplicationContext的 refreshBeanFactory方法来协助完成资源位置定义 ,bean 载入和向 IOC 容器注册的过程

4. obtainFreshBeanFactory方法,获取BeanFactory。
5. 调用AbstractRefreshableApplicationContext的refreshBeanFactory方法
6. 调用AbstractXmlApplicationContext的loadBeanDefinitions方法,装载bean定义

  • 转到定义好的XmlBeanDefinitionReader中loadBeanDefinitions对载入bean信息进行处理
  • Xml Bean读取器(XmlBeanDefinitionReader)调用父类AbstractBeanDefinitionReader的 reader.loadBeanDefinitions方法读取Bean定义资源。
    • 调用资源加载器的获取资源方法resourceLoader.getResource(location),获取到要加载的资源。
    • 其次,真正执行加载功能是其子类XmlBeanDefinitionReader的loadBeanDefinitions方法。

7. 加载bean主要是对配置文件的读取和解析注册,并在DefaultListableBeanFactory中将解析注册后的BeanDefinition保存到的HashMap集合中

Refresh方法

@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}...

1. prepareRefresh() 刷新前的预处理

  • intiPropertySoruces()初始化一些属性设置;主要是提供给子类自定义个性化的属性设置方法。
  • getEnvironment().validateRequiredProperties() :检验属性是否合法。
  • earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>(); 保存容器中的一些早期事件。
protected void prepareRefresh() {this.startupDate = System.currentTimeMillis();this.closed.set(false);this.active.set(true);if (logger.isInfoEnabled()) {logger.info("Refreshing " + this);}// Initialize any placeholder property sources in the context environmentinitPropertySources();// Validate that all properties marked as required are resolvable// see ConfigurablePropertyResolver#setRequiredPropertiesgetEnvironment().validateRequiredProperties();// Allow for the collection of early ApplicationEvents,// to be published once the multicaster is available...this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();}

2. obtainFreshBeanFactory():获取BeanFactory

// 其中只包含默认的属性设置

  1. refreshBeanFactory() :刷新【创建】BeanFactory;
    1. 创建一个 this.beanFactory = new DefultListableBeanFactory();
    2. 设置bean 的 ID
  2. getBeanFactory() 返回刚才GenericApplicationContext创建的BeanFactory。
  3. 将创建的BeanFactory【DefaultListableBeanFactory】返回。

3. prepareBeanFactory : BeanFactory 的预准备工作

(BeanFactory 进行一些设置)

  1. 设置BeanFactory的类加载器,支持的表达式解析器
  2. 添加部分BeanPostProcessor,如【ApplicationContextAwareProceesor
  3. 设置忽略的自动装配的接口:EnvironmentAwareEmbeddedValueResolverAware... ;
  4. 注册可以解析的自动装配;(方便能够直接再任何组件中自动注入)
    • 包括:BeanFactory, ResourceLoader, ApplicationEventPulisher, ApplicationContext.
  5. 添加BeanPostProcessorApplicationListenerDetector
  6. 添加编译时的 AspectJ
  7. 给 BeanFactory 中注册一些能用的组件:
    • environment【ConfigurableEnvironment
    • systemProperties 【Map<String, Object>
    • systemEnviroment 【Map<String, Object>

源码

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {// Tell the internal bean factory to use the context's class loader etc.beanFactory.setBeanClassLoader(getClassLoader());beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));// Configure the bean factory with context callbacks.beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));beanFactory.ignoreDependencyInterface(EnvironmentAware.class);beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);beanFactory.ignoreDependencyInterface(MessageSourceAware.class);beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);// BeanFactory interface not registered as resolvable type in a plain factory.// MessageSource registered (and found for autowiring) as a bean.beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);beanFactory.registerResolvableDependency(ResourceLoader.class, this);beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);beanFactory.registerResolvableDependency(ApplicationContext.class, this);// Register early post-processor for detecting inner beans as ApplicationListeners.beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));// Detect a LoadTimeWeaver and prepare for weaving, if found.if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));// Set a temporary ClassLoader for type matching.beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}// Register default environment beans.if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());}if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());}if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());}}

4. postProcessBeanFactory(beanFactory) :BeanFactory准备工作完成后(标准初始化后)进行的后置处理工作。

子类通过重写这个方法来在BeanFactory创建并预准备完成以后进行进一步的设置。

【以上为BeanFactory的创建及预准备工作(标准初始化工作)】


5. invokeBeanFactoryPostProcessor(beanFactory) :执行BeanFactoryPostProcessor 的方法。

  • BeanFactoryPostProcessor: BeanFactory的后置处理器。在BeanFactory标准初始化之后执行。
  • 实现了两个接口:BeanFactoryPostProcessorBeanDefinitionRegistryPostPorcessor 子接口

执行 BeanFactoryPostProcessor 中的方法:

  • 【需要先执行 BeanDefinitionRegistryPostProcessor 方法】

    1. 获取所有的BeanDefinitionRegistryPostProcessor

    2. 先执行实现了PriorityOrdered 优先级接口的BeanDefinitionRegistryPostProcessor
      postProcessor.postProcessBeanDefinitionRegistry(registry)

    3. 再执行实现了Ordered顺序接口的 BeanDefinitionRegistryPostProcessor:
      postProcessor.postProcessBeanDefinitionRegistry(registry)

    4. 最后执行没有实现任何优先级的 BeanDefinitionRegistryPostProcessor(registry)
      postProcessor.postProcessBeanDefinitionRegistry(registry)

  • 【再执行 BeanFactoryPostProcessor 方法】

    1. 获取所有的BeanFactoryPostPorcessor
    2. 看先执行实现了PriorityOrdered优先级接口的BeanFactoryPostPorcessor;
    3. 再执行实现了Ordered顺序接口的BeanFactoryPostPorcessor;
    4. 最后执行没有实现任何优先级或者是顺序接口的BeanFactoryPostPorcessor()
      postProcessor.postProcessBeanFactory()

6. registerBeanPostPorcessors(beanFactory) : 注册BeanPostPorceesor(Bean 的后置处理器)

作用】:拦截Bean 的创建过程

  • 不同接口类型的BeanPostPorcessor,在Bean创建前后的获取时机不同。

    需要创建的 BeanPostProcessor 包括:

        1. DestructionAwareBeanPostProcessor2. InstantiationAwareBeanPostProcessor3. SmartInstantiationAwarePostPorcessor4. MergeBeanDefinitionPostProcessor
    
  1. 获取所有的BeanPostProcessor;后置处理器默都可以通过PriorityOrdered / Oreder接口来执行优先执行时机。

  2. 先注册PriorityOrdered优先级接口的 BeanPostProcessor;把每一个BeanPostProcessor 添加到BeanFactory中。
    beanFactory.addBeanPostProcessor(postProcessor)

  3. 再注册Ordered接口的;

  4. 最后注册没有实现任何优先级接口的;

  5. 最终注册MergeBeanDefinitionPostProcessor(因为属于internalBeanPostProcessor)

  6. 注册一个 ApplicationListenerDetector ,在Bean创建完成后检查是否是ApplicationListener:如果是,就放在:applicationContext.addApplicationListener((ApplicationLIistener<?>)bean);

7. initMessageSource(); 初始化MessageSource组件

(做国际化功能;消息绑定;消息解析)

  1. 获取BeanFactory;

  2. 看容器中有id为messageSource的,类实现是否MessageSource的组件;

    • 如果有,赋值给 messageSource;如果没有,自己创建一个DelegatingMessageSource
      MessageSource: 去国际化配置文件中某个key的值,能按照区域信息获取
  3. 把创建好的MessageSource注册在容器中,以后获取国际化配置文件中的值的时候,可以自动注入MessageSource;

    beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME,this.messageSource);MessageSource.getMessage(String code, Object[] args, String defaultMessage, Local local);
    

8. initApplicationEventMulticaster() : 初始化事件派法器

  1. 获取 BeanFactory
  2. BeanFactory 中获取 applicationEventMulticasterApplicationEventMulticaster
  3. 如果上一步没有配置好派发器;创建一个 SimpleApplicationEventMulticaster
  4. 将创建的ApplicationEventMulticaster添加到BeanFactory中,以后其他组件直接自动注入。

9. onRefresh() :留给子容器(子类重写onRefresh()方法)

子类重写这个方法,子容器刷新时可以自定义逻辑

10. registerListeners() : 将容器中所有项目里面的ApplicationListener注册进来

  1. 从容器中拿到所有的 ApplicationListener

  2. 将每个监听器添加到事件派法器中
    getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName)

  3. 派发之前步骤产生的事件

11. finishBeanFactoryInitialization(beanFactory); 初始化所有剩下的单实例bean

  1. beanFactory.preInstantiateSingletons(): 初始化后剩下的单实例bean
    a. 获取容器中所有Bean, 依次进行初始化和创建对象
    b. 获取Bean的定义信息:RootBeanDefinition
    c. 如果Bean 不是抽象的,是单例的,不是懒加载的

         1. 判断是否是FactoryBean; 是否是实现FactoryBean接口的Bean2. 不是工厂Bean,利用 getBean(beanName) 来创建Bean对象。
    

11.1. getBean() 方法原理

  1. getBean(beanName)ioc.getBean() 相同.

  2. doGetBean(name, null, null, false);

  3. 先获取缓存中保存的单实例Bean;如果能获取到说明该Bean已被创建过(所有创建过的单实例Bean都会被缓存起来)。

    getSingleton() 从 singletonObjects 中获取。

    private final Map<String, Object> singletonObjects  = new ConcurrentHashMap<String, Object>();
    
  4. 缓存中获取不到,开始Bean的创建

  5. 标记当前bean被创建

  6. 获取bean的定义信息

  7. 获取当前Bean依赖的其他Bean;如果有,按照 getBean() 把依赖的Bean先创建出来

  8. 启动单实例Bean的创建流程

    ○ 所有Bean都利用 getBean() 创建完成以后; 检查所有的Bean是否是 SmartInitializingSingleton 接口的;如果是;就执行 afterSingletonsInstantiated() 方法。

11. 2. 单实例Bean的创建流程

  1. createBean(beanName, mbd, args);

  2. Object bean = resolveBeforeInstantiation(beanName, mbdTouse); 让BeanPostPorcessor先拦截返回代理对象。

    § InstantiationAwareBeanPostProcessor 提前执行;
    § 先触发 postProcessBeforInstantiation();
    § 如果有返回值,触发 postProcessAfterInstantiation() ;

  3. 如果前面的 InstantitationAwareBeanPostProcessor 没有返回代理对象; 调用4

  4. 创建 Bean!Object beanInstance = doCreateBean(beanName, mbdToUse, args);

    1. 【创建Bean实例】:createBeanInstance(beanName, mbdToUse, args);

       利用工厂方法或者对象的构造器来创建Bean实例。
      
    2. applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName)方法】;
      调用 MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(mbd, beanType, beanName);

    3. 为Bean属性赋值populateBean(beanName, mbd, instanceWrapper)

      • ) 拿到InstantiationAwareBeanPostProcessor 后置处理器,调用postProcessAfterInstantiation();

      • ) 拿到 InstantiationAwareBeanPostProcessor后置处理器,调用 postProcessPropertyValues();

    【以上是赋值之前】


  • 应用Bean属性的值,利用Setter方法为对应的属性赋值,applypropertyValues(beanName, mbd, bw,pvs);
  1. Bean初始化initalizeBean(beanName, exposedObject, mbd);

    • ) 【执行Aware接口方法】invokdeAwareMethods(beanName, bean); 执行xxxAware接口方法;
      BeanNameAware \ BeanClassLoaderAware \ BeanFactoryAware 接口

    • )【执行后置处理器初始化之前】 applyBeanPostProcessorsBeforInitialization(wrappedBean, beanName);

      BeanPostPorcessor.postProcessBeforInitialization();
      
    • ) 【执行初始化方法】invokeInitMethods(beanName, wrappedBean, mbd);

      1. 若是InitalizationBean接口的实现, 执行接口规定的初始化
      2. 是否自定义初始化方法
    • ) 【执行后置处理器初始化之后的方法】applyBeanPostProcessorsAfterInitialization

      BeanPostProcessor.postProcessAfterInitialization();
      
    • ) 注册Bean的销毁方法

  2. 将已经创建好的Bean添加到缓存 singletonObjects 中。

    ioc容器就是这些Map组成;Map中保存了单实例Bean,环境信息等。

所有Bean都利用 getBean() 创建完成之后,检查所有的Bean是否是SmartInitializingSingleton接口的;如果是,就执行 afterSingletonsInstantiated()方法。

12. finishRefresh() 完成BeanFactory的初始化创建工作; IOC容器就创建完成

(initLifecycleProcessor() --> getLifecycleProcessor() --> publishEvent() --> LiveBeansView)

  1. initLifecycleProcessor();初始化和生命周期有关的后置处理器;

    • )默认从容器中找是否有lifecycleProcessor的组件LifecycleProcessor,如果没有new DefaultLifecycleProcessor();同时加入到容器中;

    • )其中LifecycleProcessor,运行写一个LifecycleProcessor的实现类,可以在BeanFatory的

        void onRefresh();void onClose();进行回调。
      
  2. getLifecycleProcessor().onRefresh(); 拿到前面定义的生命周期处理器(BeanFactory); 回调onRefresh()方法;

  3. publishEvent( new ContextRefreshedEvent(this));发布容器刷新完成事件;

  4. LiveBeansView.registerApplicationContext(this); ?

总结

  1. Spring容器在启动的时候,先会保存所有注册进来的Bean的定义信息;
    1. xml注册bean;
    2. 使用注解注册Bean;@Service@Component@Bean
  2. Spring 容器会在合适的时机创建这些Bean
    时机:
    1. 用到这个bean的时候,利用getBean()方法创建bean;创建好以后保存在容器中;
    2. 统一创建剩下所有的bean的时候;finishBeanFactoryInitialization();
  3. 后置处理器;BeanPostProcessor
    每一个bean创建完成,都会使用各种后置处理器进行处理;来增强bean的功能;
    如:AutowiredAnnotationBeanPostProcessor: 处理自动注入
    AnnotationAwareAspectJAutoProxyCreator: 来做AOP功能;为bean创建代理对象,通过代理对象增强bean功能
    xxx….
  4. 事件驱动模型;
    1. ApplicationListener:事件监听;
    2. ApplicationEventMulticaster : 事件派发;

Spring源码解析【完整版】--【bilibili地址:https://www.bilibili.com/video/BV1oW41167AV】相关推荐

  1. Spring 源码解析 - Bean创建过程 以及 解决循环依赖

    一.Spring Bean创建过程以及循环依赖 上篇文章对 Spring Bean资源的加载注册过程进行了源码梳理和解析,我们可以得到结论,资源文件中的 bean 定义信息,被组装成了 BeanDef ...

  2. Spring源码解析 -- SpringWeb请求映射Map初始化

    简介 在上篇文章中,大致解析了Spring如何将请求路径与处理方法进行映射,但映射相关的初始化对于我们来说还是一团迷雾 本篇文章就来探索下,请求路径和处理方法的映射,是如何进行初始化的 概览 基于上篇 ...

  3. Spring源码解析-bean实例化

    Spring源码解析-bean实例化 ​ 本文介绍Spring创建 bean 过程中的第一个步骤:实例化 bean. 1. Bean实例化源码 ​ 虽然实例化Bean有多种方式(包括静态工厂和工厂实例 ...

  4. 人人都能看懂的Spring源码解析,Spring如何解决循环依赖

    人人都能看懂的Spring源码解析,Spring如何解决循环依赖 原理解析 什么是循环依赖 循环依赖会有什么问题? 如何解决循环依赖 问题的根本原因 如何解决 为什么需要三级缓存? Spring的三级 ...

  5. 【181126】VC++开发的GIS系统源码无错完整版源代码

    源码下载简介 VC++开发的GIS系统源码无错完整版,陈建春书中的一个源代码,可以顺利编译完成,GIS软件VC版的不太多,这一个也算是一个能为VC朋友提供参考的好实例吧.这套系统可完成VC++使用鼠标 ...

  6. ST-GCN源码运行完整版(含OpenPose编译安装)及常见问题

    ST-GCN源码运行完整版(含OpenPose编译安装) -------本文仅为学习笔记,不做任何商业用途------- 下述相关工具包下载链接ST-GCN所需工具,提取码ze36 一.准备工作 1. ...

  7. php留言板源码免mysql_PHPMYSQL留言板源码(终极完整版).doc

    PHPMYSQL留言板源码(终极完整版).doc 数据库结构?:(库名:lyb) 表一: admin? 字段: id(int11?) name(varch?vr) passw?ord(varch?vr ...

  8. Spring源码解析 - AbstractBeanFactory 实现接口与父类分析

    2019独角兽企业重金招聘Python工程师标准>>> 我们先来看类图吧: 除了BeanFactory这一支的接口,AbstractBeanFactory主要实现了AliasRegi ...

  9. Spring源码解析:自定义标签的解析过程

    2019独角兽企业重金招聘Python工程师标准>>> spring version : 4.3.x Spring 中的标签分为默认标签和自定义标签两类,上一篇我们探究了默认标签的解 ...

最新文章

  1. 深度学习中7种最优化算法的可视化与理解
  2. mysql主主复制、主从复制、半同步的实现
  3. 解决ubuntu中遇到“E:Unable to locate package rar” 的问题
  4. NOI2018 Day1 归程(return)
  5. vlookup 2张表 显示na_Vlookup函数的这7个应用技巧都不掌握,那就真的Out了
  6. VS2005 宽字符 unicode字符集和多字节字符集
  7. 【Python】@property的用法
  8. Drools:基于PHREAK堆栈的评估和向后链接
  9. 蓝桥杯 2017 国赛B组C/C++【对局匹配】
  10. C++ 11右值引用
  11. C++设计模式-代理模式
  12. 微服务中的健康监测以及其在ASP.NET Core服务中实现运行状况检查
  13. C++求解一元二次方程
  14. 用代码关闭冰刃(IceSword)
  15. 基金强力介入移动互联网板块
  16. [FAQ10019]HDMI/MHL如何修改手机默认横竖屏显示方式
  17. 开发工程师必备的一直网站
  18. mysql索引与md5_MySQL理解索引、添加索引的原则
  19. SLIC超像素分割方法
  20. Taro3.2 适配 React Native 之运行时架构详解

热门文章

  1. 数据库的增、删、改、查例子
  2. element-ui message 显示重叠问题
  3. 线性代数学习笔记——第五十三讲——齐次方程组求解实例
  4. HBase数据结构与基本语法详解
  5. 用selenium验证唯品会登录
  6. linux 查看内存 udimm rdimm,服务器UDIMM、LRDIMM、RDIMM三种内存如何区别
  7. Kaldi中如何使用已经训练好的模型进行语音识别ASR呢?
  8. 【媒体管家】媒体邀约以及媒介投放策略
  9. 联想Y430P换屏记
  10. 构建自定义安全令牌服务