首页 技术 正文
技术 2022年11月8日
0 收藏 688 点赞 1,463 浏览 35068 个字

代码地址:https://github.com/showkawa/spring-annotation/tree/master/src/main/java/com/brian

AnnotationConfigApplicationContext启动原理分析主要分析下面三点

1.@Qualifier与@Primary注解的使用
2.BeanFactory与ApplicationContext区别
3.AnnotationConfigApplicationContext启动原理分析

1.@Qualifier与@Primary注解的使用

下面是 @Qualifier 和 @Primary注解的官方解释

@Qualifier

This annotation may be used on a field or parameter as a qualifier for candidate beans when autowiring. It may also be used to annotate other custom annotations that can then in turn be used as qualifiers.这个注释可用于作为预备bean的字段或参数,也可以用在其他的自定义注释下 @Qualifier(“XXX”) Spring的Bean注入配置注解,该注解指定注入的Bean的名称,Spring框架使用byName方式寻找合格的bean,这样就消除了byType方式产生的歧义。

  qualifer字面意思合格者,一般情况下:代码里面需要注入其他依赖,一般用@Reource( 默认按名称装配,当找不到与名称匹配的bean才会按类型装配和@Autowired(默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用)

@Primary

Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one ‘primary’ bean exists among the candidates, it will be the autowired value. This annotation is semantically equivalent to the {@code} element’s {@code primary} attribute 不难看出如果用primary注解其中一个bean就要优先于其他的Bean,当然这个对于这种三方jar包最好不要添加的,谁知道它会不会后期又出什么幺蛾子,只能改自己的代码了最后的方案是改成@qualifiel(“getMetricRegistry”),选取了其中一个bean注入

primary字面意思最主要者,如果是有配置过多数据源,那一定会熟悉这个注解

1.1 @Qualifier的使用

我们这里定义一个BookService接口

public interface BookService {    void printBook();}

两个实现类BookServiceImpl和OtherBookServiceImpl实现BookService接口的printBook()方法

@Service
public class BookServiceImpl implements BookService {
@Override
public void printBook() {
System.out.println("---BookServiceImpl---: spring5源码分析");
}
}
@Service
public class OtherBookServiceImpl implements BookService {
@Override
public void printBook() {
System.out.println("---OtherBookServiceImpl---: 天道酬勤,一步一个坑");
}
}

我们在BookController注入BooKService

@Controller
public class BookController { @Autowiredprivate BookService bookService; private void printBook() {
bookService.printBook();
}}

附上配置类MainConfig

@Configuration //告诉spring这是一个配置类
/*
* @ComponentScan
* value:只当于扫描的的包
* excludeFilters = 指定扫描的时候按照什么规则排除哪些组件
* includeFilters = 指定扫描的时候只需要包含哪些组件
* Filter.ANNOTATION:按照注解
* Filter.ASSIGNABLE_TYPE: 按照给定的类型
* *///@ComponentScans(value = {
// @ComponentScan(value = "com.brian",includeFilters = {
// @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}),
// @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}),
// @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {BrianTypeFilter.class})
// },useDefaultFilters = false)
//})
@ComponentScan({"com.brian.service", "com.brian.controller"})
//@Import({Brian.class,Alan.class})
@Import({BrianSelector.class})
public class MainConfig { @Bean("person") //给容器中注册一个Bean;类型为返回值的类型;id默认是方法名作为id
public Person person(){
return new Person("Alan",18);
} /*
* @Conditional() 按照条件注册
*
* */
@Conditional({BrianCondition.class})
@Bean("person01")
public Person person01() {
return new Person("Brian",17);
} @Conditional({BrianCondition.class})
@Bean("person02")
public Person person02() {
return new Person("wenTao",19);
} /*
*
*给容器中注册组件
* 1,包扫描+ 组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的方法]
* 2, @Bean [导入的第三方包里面的组件]
* 3,@Import [快速的给容器导入一个组件]
* 1.@Import(要导入的组件class)
* 2.ImportSelector:返回需要导入的组件的全类名数组
* 3.ImportBeanDefinitionRegistrar: 手动注册bean到容器
* 4. 使用Spring提供的FactoryBean
* */
@Bean
public BrianBeanFactory brianBeanFactory() {
return new BrianBeanFactory();
}}

在测试类MainTest类中测试该printBook()方法

public class MainTest {
public static void main(String[] args) {
ApplicationContext mainConfig =
new AnnotationConfigApplicationContext(MainConfig.class); System.out.println("MainConfig容器创建成功"); BookController bookController = mainConfig.getBean(BookController.class);
try {
Method printBook = bookController.getClass().getDeclaredMethod("printBook");
printBook.setAccessible(true);
printBook.invoke(bookController);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
// Alan alan1 = acac.getBean(Alan.class);
// System.out.println("--ALAN--:" + alan1);
// Alan alan2 = acac.getBean(Alan.class);
//System.out.println("比较两个Alan实例: " + (alan1 == alan2));// Person person1 = (Person) acac.getBean("person01");
// System.out.println("---main---test---person1---: " + person1.toString());
// Person person2 = (Person) acac.getBean("person02");
// System.out.println("---main---test---person2---: " + person2.toString()); // MathCalculator mathCalculator = (MathCalculator) acac.getBean("mathCalculator");
// System.out.println("----get--mathCalculator---: " + mathCalculator);// BrianBeanFactory beanFactory = acac.getBean(BrianBeanFactory.class);
// WenTao wentao = null;
// try {
// wentao = beanFactory.getObject();
// } catch (Exception e) {
// e.printStackTrace();
// }
// System.out.println("----get--WenTao---: " + wentao); //关闭ioc容器
((AnnotationConfigApplicationContext) mainConfig).close();
}
}

Spring5深度源码分析(三)之AnnotationConfigApplicationContext启动原理分析

看到有报错No qualifying bean of type ‘com.brian.service.BookService’ available: expected single matching bean but found 2: bookServiceImpl,otherBookServiceImpl,就是因为默认按类型装配

OK,此时我在BookController注入BookSevice的时候加上@Qualifier指定beanName,再次测试

@Controller
public class BookController { @Autowired
@Qualifier("bookServiceImpl")
private BookService bookService; private void printBook() {
bookService.printBook();
}}

Spring5深度源码分析(三)之AnnotationConfigApplicationContext启动原理分析

1.2  @Primary的使用

继续返回到上面的报错时候的场景,因为此时BookService有两个实现类,此时我在OtherBookServiceImpl类上加上@Primary注解,再次测试

@Service
@Primary
public class OtherBookServiceImpl implements BookService {
@Override
public void printBook() {
System.out.println("---OtherBookServiceImpl---: 天道酬勤,一步一个坑");
}
}

Spring5深度源码分析(三)之AnnotationConfigApplicationContext启动原理分析

这里顺便总结下常用的注解:

@Configuration把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。
@Scope注解 作用域
@Lazy(true) 表示延迟初始化
@Service用于标注业务层组件、 
@Controller用于标注控制层组件
@Repository用于标注数据访问组件,即DAO组件。
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
@Scope用于指定scope作用域的(用在类上)
@PostConstruct用于指定初始化方法(用在方法上)
@PreDestory用于指定销毁方法(用在方法上)
@Resource 默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
@DependsOn:定义Bean初始化及销毁时的顺序
@Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
@Autowired 默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用
@Autowired @Qualifier("OtherBookServiceImpl") 存在多个实例配合使用

2.BeanFactory与ApplicationContext区别

之前的源码分析有见过BeanFactory和FactoryBean接口的区别,FactoryBean是创建bean对象,而BeanFactory是获取对象。但ApplicationContext也可以获取对象,我上面的测试类getBean()方法就是的。同样都可以获取bean对象BeanFactory与ApplicationContext有什么区别?

 1. BeanFactory负责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的声明周期。
2. ApplicationContext除了提供上述BeanFactory所能提供的功能之外,还提供了更完整的框架功能:
a. 国际化支持
b. 资源访问:Resource rs = ctx. getResource(“classpath:config.properties”), “file:E:/config.properties”
c. 事件传递:通过实现ApplicationContextAware接口 3. 常用的获取ApplicationContext的方法:
   AnnotationConfigApplicationContext 从@Configuration配置类创建容器
FileSystemXmlApplicationContext:从【文件系统】 或者 【classpath的xml配置文件】 创建,参数为配置文件名或文件名数组
ClassPathXmlApplicationContext:从【classpath的xml配置文件】创建,可以从【jar包】中读取配置文件
WebApplicationContextUtils:从web应用的根目录读取配置文件,需要先在web.xml中配置,可以配置监听器或者servlet来实现

Spring的IoC容器就是一个实现了BeanFactory接口的可实例化类。事实上, Spring提供了两种不同的容器:一种是最基本的BeanFactory,另一种是扩展的ApplicationContext。

BeanFactory 仅提供了最基本的依赖注入支持,而 ApplicationContext 则扩展了BeanFactory ,提供了更多的额外功能。

ApplicationContext的初始化和BeanFactory有一个重大的区别:

BeanFactory在初始化容器时 并未实例化Bean, 直到第一次访问 某个Bean时才实例目标Bean;而ApplicationContext则在初始化应用上下文时就实例化所有的单实例的Bean。
因此ApplicationContext的初始化时间会比BeanFactory稍长一些.

3.AnnotationConfigApplicationContext启动原理分析

Spring5深度源码分析(三)之AnnotationConfigApplicationContext启动原理分析

从AnnotationConfigApplicationContext的类图中可以看出:

AnnotationConfigApplicationContext继承GenericApplicationContext这个通用应用上下文,GenericApplicationContext内部定义了一个DefaultListableBeanFactory实例,
GenericApplicationContext实现了BeanDefinitionRegistry接口,所以可以通过AnnotationConfigApplicationContext实例注册beanDefinition,然后调用refresh()方法来初始化上下文。
AnnotationConfigApplicationContext继承AbstractApplicationContext,AbstractApplicationContext提供了ApplicationContext的抽象实现。

我这边就从MainTest这个测试类开始AnnotationConfigApplicationContext启动原理分析:

MainTest测试类:

public class MainTest {
public static void main(String[] args) {
      // 1.读取配置类创建IOC容器  
ApplicationContext mainConfig =
new AnnotationConfigApplicationContext(MainConfig.class); System.out.println("MainConfig容器创建成功"); BookController bookController = mainConfig.getBean(BookController.class);
try {
Method printBook = bookController.getClass().getDeclaredMethod("printBook");
printBook.setAccessible(true);
printBook.invoke(bookController);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}//关闭ioc容器
((AnnotationConfigApplicationContext) mainConfig).close();
}
}

AnnotationConfigApplicationContext点进去,我这边后面的源码都会附带官方注释的翻译

   /**
    *创建一个新的AnnotationConfigApplicationContext,派生bean定义
    *来自给定的带注释的类并自动刷新上下文。
    * @param annotatedClasses一个或多个带注释的类,
    *例如 {@link Configuration @Configuration}类
    */ 

    public AnnotationConfigApplicationContext(Class<?>… annotatedClasses) {
      //1. 初始化bean读取器和扫描器;
      /** 调用父类GenericApplicationContext无参构造函数,初始化一个BeanFactory: this.beanFactory = new DefaultListableBeanFactory()*/
      this();
      // 2.注册bean配置类
      register(annotatedClasses);
      // 3.刷新上下文
      refresh();  

    }

现在我们点进去this()这个方法;

   /**
      *创建一个需要填充的新AnnotationConfigApplicationContext
      *通过{@link #register}调用,然后手动{@linkplain #refresh刷新}。
     */

    public AnnotationConfigApplicationContext() {
     //在IOC容器中初始化注解bean读取器AnnotatedBeanDefinitionReader
this.reader = new AnnotatedBeanDefinitionReader(this);
     //在IOC容器中初始化按类路径扫描注解bean的扫描器
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

this()方法在调用自身的无参构造方法时会先走父类GenericApplicationContext的无参构造方法

/**
      *创建一个新的GenericApplicationContext。
      * @see #registerBeanDefinition
      * @see #refresh
     */

    public GenericApplicationContext() {
     //初始化一个BeanFactory
this.beanFactory = new DefaultListableBeanFactory();
}

现在我们再点进去看下register(annotatedClasses)这个方法,直接调用AnnotatedBeanDefinitionReader的register方法

/**
*注册一个或多个要处理的注释类。
* <p>对{@code register}的调用是幂等的; 添加相同的
*注释类不止一次没有额外的效果。
* @param annotatedClasses一个或多个带注释的类,
*例如 {@link Configuration @Configuration}类
*/
public void register(Class<?>... annotatedClasses) {
for (Class<?> annotatedClass : annotatedClasses) {
registerBean(annotatedClass);
}
}/**
*从给定的bean类注册bean,从中派生元数据
*类声明的注释。
* @param annotatedClass bean的类
*/
public void registerBean(Class<?> annotatedClass) {
doRegisterBean(annotatedClass, null, null, null);
}/**
*从给定的bean类注册bean,从中派生元数据
*类声明的注释。
* @param annotatedClass bean的类
* @param instanceSupplier用于创建bean实例的回调
*(可能是{@code null})
* @param命名bean的显式名称
* @param限定符特定限定符注释要考虑,如果有的话,
*除了bean类级别的限定符
* @param definition定制一个或多个用于自定义的回调
*工厂的{@link BeanDefinition},例如 设置lazy-init或主标志
* @since 5.0
*/
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
   /** 将Bean配置类信息转成容器中AnnotatedGenericBeanDefinition数据结构,
   * AnnotatedGenericBeanDefinition继承自BeanDefinition作用是定义一个bean的数据结构,
   * 下面的getMetadata可以获取到该bean上的注解信息 */
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); //@Conditional装配条件判断是否需要跳过注册
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
  
   //设置回调 @param instanceSupplier 用于创建bean实例的回调
abd.setInstanceSupplier(instanceSupplier); //解析bean作用域(单例或者原型),如果有@Scope注解,则解析@Scope,没有则默认为singleton 
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); //作用域写回BeanDefinition数据结构, abd中缺损的情况下为空,将默认值singleton重新赋值到abd
abd.setScope(scopeMetadata.getScopeName());   //生成bean配置类beanName
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
   //通用注解解析到abd结构中,主要是处理Lazy, primary DependsOn, Role ,Description这五个注解
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); // @Qualifier特殊限定符处理,@param  qualifiers 除了bean类级别的限定符之外,要考虑的限定符特定限定符注释(如果有)
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
          // 如果配置@Primary注解,则设置当前Bean为自动装配autowire时首选bean
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
          //设置当前bean为延迟加载
abd.setLazyInit(true);
}
else {
          //其他注解,则添加到abd结构中
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}

    /**自定义bean注册,通常用在applicationContext创建后,手动向容器中一lambda表达式的方式注册bean,
     * 比如:applicationContext.registerBean(BookService.class, () -> new BookService()); */

for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
     //自定义bean添加到BeanDefinition
customizer.customize(abd);
}
   //根据beanName和bean定义信息封装一个beanhold,heanhold其实就是一个 beanname和BeanDefinition的映射
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);   //创建代理对象
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);   /** BeanDefinitionReaderUtils.registerBeanDefinition 内部通过
* DefaultListableBeanFactory.registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
   * 按名称将bean定义信息注册到容器中,实际上DefaultListableBeanFactory内部维护一个Map<String, BeanDefinition>类型变量beanDefinitionMap,
* 用于保存注bean定义信息(beanname 和 beandefine映射)*/
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

register方法重点完成了bean配置类本身的解析和注册,处理过程可以分为以下几个步骤:

1.根据bean配置类,使用BeanDefinition解析Bean的定义信息,主要是一些注解信息
2.Bean作用域的处理,没有@Scope注解默认解析成单例
3.借助AnnotationConfigUtils工具类解析通用注解
4.将bean定义信息已beanname,beandifine键值对的形式注册到ioc容器中

然后我们继续点进去refresh()方法

refresh方法在AbstractApplicationContext容器中实现,refresh()方法的作用加载或者刷新当前的配置信息,如果已经存在spring容器,则先销毁之前的容器,重新创建spring容器,载入bean定义,完成容器初始化工作,所以可以看出AnnotationConfigApplicationContext容器是通过调用其父类AbstractApplicationContext的refresh()函数启动整个IoC容器完成对Bean定义的载入。

AbstractApplicationContext类的refresh()方法源代码如下:

@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//1.刷新前的预处理
prepareRefresh(); //2.获取刷新后的内部Bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //3.BeanFactory的预准备工作
prepareBeanFactory(beanFactory); try {
// BeanFactory准备工作完成后,可以做一些后置处理工作
          // 4.空方法,用于在容器的子类中扩展
postProcessBeanFactory(beanFactory); // 5. 执行BeanFactoryPostProcessor的方法,BeanFactory的后置处理器,在BeanFactory标准初始化之后执行的
invokeBeanFactoryPostProcessors(beanFactory); // 6. 注册BeanPostProcessor(Bean的后置处理器),用于拦截bean创建过程,实现对bean的增强
registerBeanPostProcessors(beanFactory); // 7. 初始化MessageSource组件(做国际化功能;消息绑定,消息解析)
initMessageSource(); // 8. 初始化事件派发器
initApplicationEventMulticaster(); // 9.空方法,可以用于子类实现在容器刷新时自定义逻辑
onRefresh(); // 10. 注册事件监听器,将所有项目里面的ApplicationListener注册到容器中来
registerListeners(); // 11. 初始化所有剩下的单实例bean,单例bean在初始化容器时创建,原型bean在获取时(getbean)时创建
finishBeanFactoryInitialization(beanFactory); // 12. 完成BeanFactory的初始化创建工作,IOC容器就创建完成;
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} //销毁已经创建的单例,以避免挂起资源。
destroyBeans(); //重置“活动”标志。
cancelRefresh(ex); //向调用者传播异常。
throw ex;
} finally {
//在Spring的core中重置常见的内省缓存,因为我们可能不再需要单例bean的元数据了...
resetCommonCaches();
}
}
}

接下来我们要分析refresh()里面的这些方法

3.1 prepareRefresh ()刷新预处理方法:

  

  /**
  *准备此上下文以进行刷新,设置其启动日期和
  *活动标志以及执行属性源的任何初始化。
  */

 protected void prepareRefresh() {
//设置容器启动时间
this.startupDate = System.currentTimeMillis();
     //启动标识
this.closed.set(false);
this.active.set(true); if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
} //空方法,用于子容器自定义个性化的属性设置方法
initPropertySources();     //检验属性的合法等
getEnvironment().validateRequiredProperties(); //存储预刷新应用程序监听器…
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
//将本地应用程序侦听器重置为预刷新状态。
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
} //保存容器中的一些早期的事件
this.earlyApplicationEvents = new LinkedHashSet<>();
}

3.2 obtainFreshBeanFactory()方法

该方法获取刷新后的内部Bean工厂,obtainFreshBeanFactory方法为内部bean工厂重新生成id,并返回bean工厂

  /**
  *告诉子类刷新内部bean工厂。
  * @return新鲜的BeanFactory实例
  * @see #refreshBeanFactory()
  * @see #getBeanFactory()
  */ 

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {     

      /**
      *为beanfactory生成唯一序列化id,beanfactory已经在GenericApplicationContext构造函数中初始化了,
      *refreshBeanFactory的逻辑在AbstractApplicationContext的实现类GenericApplicationContext中
      */

        refreshBeanFactory();
     //获取beanfactory
return getBeanFactory();
}

GenericApplicationContext类的refreshBeanFactory()方法

    //---------------------------------------------------------------------
实现AbstractApplicationContext的模板方法
//---------------------------------------------------------------------
/ * *
*什么都不做:我们只有一个内部BeanFactory,并且依赖于调用者
*通过我们的公共方法(或BeanFactory的)注册bean。
* @see # registerBeanDefinition
* /
@Override
protected final void refreshBeanFactory() throws IllegalStateException {
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
}
     //生成一个序列化id
this.beanFactory.setSerializationId(getId());
}

这里使用AbstractApplicationContext.refreshBeanFactory()如果是注解的方式实现,则并没有解析项目包下的注解,而是通过在refresh()方法中执行ConfigurationClassPostProcessor后置处理器完成对bean的加载.

3.3 prepareBeanFactory(beanFactory)方法

prepareBeanFactory主要完成beanFactory的一些属性设置,BeanFactory的预准备工作

/**
*配置工厂的标准上下文特征,
*例如上下文的ClassLoader和后置处理器。
* @param beanFactory要配置的BeanFactory
*/
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//告诉内部bean工厂使用上下文的类加载器等等。
//设置类加载器
     beanFactory.setBeanClassLoader(getClassLoader());
     //bean表达式解析器
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); //使用上下文回调配置bean工厂。
//添加一个BeanPostProcessor实现ApplicationContextAwareProcessor
     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.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this); //将早期后置处理器注册为application监听器,用于检测内部bean。
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); //添加编译时的AspectJ
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
//为匹配的类型设置一个临时类加载器。
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
} // 给beanfactory容器中注册组件ConfigurableEnvironment、systemProperties、systemEnvironment
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());
}
}

3.4 invokeBeanFactoryPostProcessors(beanFactory)方法

invokeBeanFactoryPostProcessors 执行bean工厂后置处理器

/**
*实例化并调用所有已注册的BeanFactoryPostProcessor beans,
*如果给定则遵守显示的顺序。
* <p>必须在单例实例化之前调用。
*/
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}

invokeBeanFactoryPostProcessors方法内部执行实现了BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor这两个接口的Processor,先获取所有BeanDefinitionRegistryPostProcessor的实现,按优先级执行(是否实现PriorityOrdered优先级接口,是否实现Ordered顺序接口);再以相同的策略执行所有BeanFactoryPostProcessor的实现。

PostProcessorRegistrationDelegate. invokeBeanFactoryPostProcessors实现:

public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { //如果有的话,首先调用BeanDefinitionRegistryPostProcessors。
Set<String> processedBeans = new HashSet<>(); if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}  

         //不要在这里初始化FactoryBeans:我们需要保留所有常规bean

         //未初始化的,让bean工厂的后置处理程序负责于它们!

        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();            //首先,调用实现PriorityOrated接口的BeanDefinitionRegistryPostProcessors类的方法。
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear(); //接下来,调用实现Ordered接口的BeanDefinitionRegistryPostProcessors类的方法。
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear(); //最后,调用所有其他BeanDefinitionRegistryPostProcessors,直到没有其他的后置处理器出现。
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
} //现在,调用到目前为止处理完所有处理器的postProcessBeanFactory回调。
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
} else {
//调用在上下文实例中注册的工厂处理器。
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
} //不要在这里初始化FactoryBeans:我们需要保留所有常规bean
//未初始化,以让bean工厂的后值处理器负责于它们!
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
//跳过-已经在上面的第一阶段处理
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
} //首先,调用实现PriorityOrated接口的BeanFactoryPostProcessors类。
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); //接下来,调用实现Ordered接口的BeanFactoryPostProcessors类方法。
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); //最后,调用所有其他BeanFactoryPostProcessors,直到没有其他的后置处理器出现。
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); //清除缓存的合并bean定义,因为后置处理器可能修改了原始元数据,例如替换了值中的占位符……
beanFactory.clearMetadataCache();
}

这里面在处理BeanDefinitionRegistryPostProcessors时有一个非常重要的过程,AnnotationConfigApplicationContext构造函数在初始化reader时为内部beanFactory容器初始化了一个id为org.springframework.context.annotation.internalConfigurationAnnotationProcessor的组件,这是一个ConfigurationClassPostProcessor组件,用来处理添加@Configuration注解的类,并将Bean定义注册到BeanFactory中。

3.5 invokeBeanFactoryPostProcessors(beanFactory)方法

注册后置处理器的大致逻辑是:

  1.获取所有的 BeanPostProcessor

  2.根据处理器实现的接口区分出4中类型:

    2.1.实现PriorityOrdered接口的处理器

    2.2.实现Ordered接口的处理器,

    2.3.实现MergedBeanDefinitionPostProcessor接口的处理器,

    2.4.普通后置处理器

  3.按这个4中类型依次注册到容器中

  4.注册一个特殊的后置处理器ApplicationListenerDetector,ApplicationListenerDetector本身也实现了MergedBeanDefinitionPostProcessor接口

/ * *
*实例化并注册所有BeanPostProcessor bean,如果给定则遵循显式顺序。
* <p>必须在应用程序bean的任何实例化之前调用。
* /
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) { String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false); //注册BeanPostProcessorChecker,当bean在BeanPostProcessor实例化过程中被创建时,即当一个bean没有资格被所有BeanPostProcessor处理时,它记录一条信息到日志。
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount)); //按优先级分类
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
} //首先,调用实现PriorityOrated接口的BeanPostProcessors类。
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); //接下来,调用实现Ordered接口的BeanPostProcessors类方法。
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors); //现在,注册所有常规的BeanPostProcessors。
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); //最后,调用所有其他BeanPostProcessors,直到没有其他的后置处理器出现。
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors); //注册ApplicationListenerDetector,用于Bean创建完时检查是否是ApplicationListener
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

3.6 initMessageSource()方法

初始化MessageSource组件(做国际化功能;消息绑定,消息解析)

/ * *
初始化MessageSource。
*如果在此上下文中没有定义,则使用父类的。
* /
protected void initMessageSource() {
//获取beanFactory
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
//让MessageSource知道父类的MessageSource。
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
//如果没有注册父类的MessageSource,则只将父类的上下文设置为父类的MessageSource。
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using MessageSource [" + this.messageSource + "]");
}
}
else {
//使用空的MessageSource来接受getMessage调用。
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isTraceEnabled()) {
logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
}
}
}

3.7 initApplicationEventMulticaster()方法

初始化事件派发器

/ * *
初始化ApplicationEventMulticaster。
如果上下文中没有定义,则使用SimpleApplicationEventMulticaster。
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
* /
protected void initApplicationEventMulticaster() {
//获取BeanFactory
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//如果有配置beanName为applicationEventMulticaster的事件派发器,则将其赋给容器中的applicationEventMulticaster对象
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
       //不存在,则创建一个SimpleApplicationEventMulticaster事件派发器,并注册到beanfactory中
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}

3.8 registerListeners()方法

registerListeners方法主要实现将事件监听器添加到IOC容器中的事件派发器中,并在最后做了一个事件发布的逻辑(如果之前的步骤有产生事件,则将earlyApplicationEvents中保存的事件逐一发布)

/ * *
添加实现ApplicationListener作为侦听器的bean。
*不影响其他侦听器,可以在不添加bean的情况下添加。
* /
protected void registerListeners() {
//首先注册静态指定的监听器。
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
} //不要在这里初始化factorybean:我们需要不初始化所有常规bean,以便让后置处理器处理它们
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
} //发布早期的application事件…
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}

3.9 finishBeanFactoryInitialization(beanFactory)方法

初始化所有剩下的单实例bean,单例bean在初始化容器时创建,原型bean在获取时(getbean)时创建

/ * *
*完成这个上下文的bean工厂的初始化,
初始化所有剩余的单例bean。
* /
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//初始化此上下文的转换服务。
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
} //如果之前没有注册bean后置处理器(例如PropertyPlaceholderConfigurer bean),则注册一个默认的嵌入式值解析器:此时,主要用于在注释属性值中进行解析。
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
} //提前初始化LoadTimeWeaverAware beans,以便尽早注册它们的转换器。
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
} //停止使用临时类加载器进行类型匹配。
beanFactory.setTempClassLoader(null); //允许缓存所有bean定义元数据,不需要进一步更改。
beanFactory.freezeConfiguration(); //实例化所有剩余的(非懒加载初始化)单例。
beanFactory.preInstantiateSingletons();
}

DefaultListableBeanFactory. preInstantiateSingletons()方法的实现:

@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
} //容器中所有bean名称
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); for (String beanName : beanNames) {
       //获取Bean的定义信息;RootBeanDefinition
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        //非抽象,单例,非延迟加载
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
          //是否是FactoryBean
if (isFactoryBean(beanName)) {
            // 通过"&beanName"获取工厂Bean实例
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
            //不是FactoryBean,则利用getBean(beanName)实例化bean 
getBean(beanName);
}
}
} for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}

3.10 finishRefresh()方法

完成BeanFactory的初始化创建工作,代码走到这一步IOC容器就创建完成

/ * *
*调用LifecycleProcessor,完成此上下文的刷新
方法并发布
* {@link org.springframework.context.event.ContextRefreshedEvent}。
* /
protected void finishRefresh() {
//清除上下文级的资源缓存(例如来自扫描的ASM元数据)。
clearResourceCaches(); //初始化和生命周期有关的后置处理器LifecycleProcessor,默认DefaultLifecycleProcessor
initLifecycleProcessor(); // 回调生命周期处理器
getLifecycleProcessor().onRefresh(); //发布容器刷新完成事件:ContextRefreshedEvent
publishEvent(new ContextRefreshedEvent(this)); LiveBeansView.registerApplicationContext(this);
}

以上基本分析了AnnotationConfigApplicationContext容器的初始化过程, Spring容器在启动过程中,会先保存所有注册进来的Bean的定义信息;Spring容器根据条件创建Bean实例,区分单例,还是原型,后置处理器等(后置处理器会在容器创建过程中通过getBean创建,并执行相应的逻辑);Spring容器在创建bean实例后,会使用多种后置处理器来增加bean的功能,比如处理自动注入,AOP,异步,这种后置处理器机制也丰富了bean的功能。

我这边在AnnotationConfigApplicationContext启动原理分析的代码走读部分有参考博客: https://www.cnblogs.com/ashleyboy/p/9662119.html ,不过有些类的方法会有点不一样,因为我这边时基于Spring5.X的版本分析

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,488
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,903
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,737
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,489
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,128
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,290