模拟spring IOC、源码分析
springIOC
what is IOC
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)
Dependency Injection
依赖注入
关于什么是依赖
关于注入和查找以及拖拽
为什么要使用spring IOC
spring体系结构----IOC的位置 自己看官网
在日常程序开发过程当中,我们推荐面向抽象编程,面向抽象编程会产生类的依赖,当然如果你够强大可以自己写一个管理的容器,但是既然spring以及实现了,并且spring如此优秀,我们仅仅需要学习spring框架便可。
当我们有了一个管理对象的容器之后,类的产生过程也交给了容器,至于我们自己的app则可以不需要去关系这些对象的产生了。
spring实现IOC的思路和方法
spring实现IOC的思路是提供一些配置信息用来描述类之间的依赖关系,然后由容器去解析这些配置信息,继而维护好对象之间的依赖关系,前提是对象之间的依赖关系必须在类中定义好,比如A.class中有一个B.class的属性,那么我们可以理解为A依赖了B。既然我们在类中已经定义了他们之间的依赖关系那么为什么还需要在配置文件中去描述和定义呢?
spring实现IOC的思路大致可以拆分成3点
1. 应用程序中提供类,提供依赖关系(属性或者构造方法)
2. 把需要交给容器管理的对象通过配置信息告诉容器(xml、annotation,javaconfig)
3. 把各个类之间的依赖关系通过配置信息告诉容器
配置这些信息的方法有三种分别是xml,annotation和javaconfig
维护的过程称为自动注入,自动注入的方法有两种构造方法和setter
自动注入的值可以是对象,数组,map,list和常量比如字符串整形等
spring编程的风格
schemal-based-------xml
annotation-based-----annotation
java-based----java Configuration
注入的两种方法
spring注入详细配置(字符串、数组等)参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-
reference/core.html#beans-factory-properties-detailed
Constructor-based Dependency Injection
构造方法注入参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-
reference/core.html#beans-constructor-injection
Constructor-based DI is accomplished by the container invoking a constructor with a number of arguments, each representing a dependency. Calling a static
factory method with specific arguments to construct the bean is nearly equivalent, and this discussion treats arguments to a constructor and to a static
factory method similarly. The following example shows a class that can only be dependency-injected with constructor injection:
public class SimpleMovieLister {// the SimpleMovieLister has a dependency on a MovieFinderprivate MovieFinder movieFinder;// a constructor so that the Spring container can inject a MovieFinderpublic SimpleMovieLister(MovieFinder movieFinder) {this.movieFinder = movieFinder;}// business logic that actually uses the injected MovieFinder is omitted...
}
Notice that there is nothing special about this class. It is a POJO that has no dependencies on container specific interfaces, base classes or annotations.
Setter-based Dependency Injection
Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or a no-argument static
factory method to instantiate your bean.
The following example shows a class that can only be dependency-injected by using pure setter injection. This class is conventional Java. It is a POJO that has no dependencies on container specific interfaces, base classes, or annotations.
public class SimpleMovieLister {// the SimpleMovieLister has a dependency on the MovieFinderprivate MovieFinder movieFinder;// a setter method so that the Spring container can inject a MovieFinderpublic void setMovieFinder(MovieFinder movieFinder) {this.movieFinder = movieFinder;}// business logic that actually uses the injected MovieFinder is omitted...
}
自动装配
上面说过,IOC的注入有两个地方需要提供依赖关系,一是类的定义中,二是在spring的配置中需要去描述。自动装配则把第二个取消了,即我们仅仅需要在类中提供依赖,继而把对象交给容器管理即可完成注入。
在实际开发中,描述类之间的依赖关系通常是大篇幅的,如果使用自动装配则省去了很多配置,并且如果对象的依赖发生更新我们可以不需要去更新配置,但是也带来了一定的缺点
自动装配的优点参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-
reference/core.html#beans-factory-autowire
缺点参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-
reference/core.html#beans-autowired-exceptions
作为我来讲,我觉得以上缺点都不是缺点
自动装配的方法
no
byName
byType
constructor
自动装配的方式参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-
reference/core.html#beans-factory-autowire
Mode | Explanation |
---|---|
|
(Default) No autowiring. Bean references must be defined by |
|
Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name and it contains a |
|
Lets a property be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use |
|
Analogous to |
spring懒加载
官网已经解释的非常清楚了:
https://docs.spring.io/spring-framework/docs/current/spring-framework-
reference/core.html#beans-factory-lazy-init
By default, ApplicationContext
implementations eagerly create and configure all singleton beans as part of the initialization process. Generally, this pre-instantiation is desirable, because errors in the configuration or surrounding environment are discovered immediately, as opposed to hours or even days later. When this behavior is not desirable, you can prevent pre-instantiation of a singleton bean by marking the bean definition as being lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.
值得提醒的是,如果你想为所有的对都实现懒加载可以使用官网的配置
In XML, this behavior is controlled by the lazy-init
attribute on the <bean/>
element, as the following example shows:
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
You can also control lazy-initialization at the container level by using the default-lazy-init
attribute on the <beans/>
element, a the following example shows:
<beans default-lazy-init="true"><!-- no beans will be pre-instantiated... -->
</beans>
springbean的作用域
文档参考:
https://docs.spring.io/spring-framework/docs/current/spring-framework-
reference/core.html#beans-factory-scopes
Scope | Description |
---|---|
singleton |
(Default) Scopes a single bean definition to a single object instance for each Spring IoC container. |
prototype |
Scopes a single bean definition to any number of object instances. |
request |
Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring |
session |
Scopes a single bean definition to the lifecycle of an HTTP |
application |
Scopes a single bean definition to the lifecycle of a |
websocket |
Scopes a single bean definition to the lifecycle of a |
xml定义方式
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
annotation的定义方式
Singleton Beans with Prototype-bean Dependencies
意思是在Singleton 当中引用了一个Prototype的bean的时候引发的问题
官网引导我们参考https://docs.spring.io/spring-framework/docs/current/spring-framework-
reference/core.html#beans-factory-method-injection
Method Injection
In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container creates the singleton bean A only once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.
A solution is to forego some inversion of control. You can make bean A aware of the container by implementing the ApplicationContextAware
interface, and by making a getBean("B")
call to the container ask for (a typically new) bean B instance every time bean A needs it. The following example shows this approach:
// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;public class CommandManager implements ApplicationContextAware {private ApplicationContext applicationContext;public Object process(Map commandState) {// grab a new instance of the appropriate CommandCommand command = createCommand();// set the state on the (hopefully brand new) Command instancecommand.setState(commandState);return command.execute();}protected Command createCommand() {// notice the Spring API dependency!return this.applicationContext.getBean("command", Command.class);}public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
}
The preceding is not desirable, because the business code is aware of and coupled to the Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC container, lets you handle this use case cleanly.
Lookup Method Injection
Lookup method injection is the ability of the container to override methods on container-managed beans and return the lookup result for another named bean in the container. The lookup typically involves a prototype bean, as in the scenario described in the preceding section. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to dynamically generate a subclass that overrides the method.
|
In the case of the CommandManager
class in the previous code snippet, the Spring container dynamically overrides the implementation of the createCommand()
method. The CommandManager
class does not have any Spring dependencies, as the reworked example shows:
package fiona.apple;// no more Spring imports!public abstract class CommandManager {public Object process(Object commandState) {// grab a new instance of the appropriate Command interfaceCommand command = createCommand();// set the state on the (hopefully brand new) Command instancecommand.setState(commandState);return command.execute();}// okay... but where is the implementation of this method?protected abstract Command createCommand();
}
In the client class that contains the method to be injected (the CommandManager
in this case), the method to be injected requires a signature of the following form:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
If the method is abstract
, the dynamically-generated subclass implements the method. Otherwise, the dynamically-generated subclass overrides the concrete method defined in the original class. Consider the following example:
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype"><!-- inject dependencies here as required -->
</bean><!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager"><lookup-method name="createCommand" bean="myCommand"/>
</bean>
The bean identified as commandManager
calls its own createCommand()
method whenever it needs a new instance of the myCommand
bean. You must be careful to deploy the myCommand
bean as a prototype if that is actually what is needed. If it is a singleton, the same instance of the myCommand
bean is returned each time.
Alternatively, within the annotation-based component model, you can declare a lookup method through the @Lookup
annotation, as the following example shows:
public abstract class CommandManager {public Object process(Object commandState) {Command command = createCommand();command.setState(commandState);return command.execute();}@Lookup("myCommand")protected abstract Command createCommand();
}
spring声明周期和回调
参考文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-
reference/core.html#beans-factory-lifecycle1、Methods annotated with @PostConstruct
2、afterPropertiesSet() as defined by the InitializingBean callback interface
3、A custom configured init() method
Initialization Callbacks
The org.springframework.beans.factory.InitializingBean
interface lets a bean perform initialization work after the container has set all necessary properties on the bean. The InitializingBean
interface specifies a single method:
void afterPropertiesSet() throws Exception;
We recommend that you do not use the InitializingBean
interface, because it unnecessarily couples the code to Spring. Alternatively, we suggest using the @PostConstruct
annotation or specifying a POJO initialization method. In the case of XML-based configuration metadata, you can use the init-method
attribute to specify the name of the method that has a void no-argument signature. With Java configuration, you can use the initMethod
attribute of @Bean
. See Receiving Lifecycle Callbacks. Consider the following example:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {public void init() {// do some initialization work}
}
The preceding example has almost exactly the same effect as the following example (which consists of two listings):
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {@Overridepublic void afterPropertiesSet() {// do some initialization work}
}
However, the first of the two preceding examples does not couple the code to Spring.
Destruction Callbacks
Implementing the org.springframework.beans.factory.DisposableBean
interface lets a bean get a callback when the container that contains it is destroyed. The DisposableBean
interface specifies a single method:
void destroy() throws Exception;
We recommend that you do not use the DisposableBean
callback interface, because it unnecessarily couples the code to Spring. Alternatively, we suggest using the @PreDestroy
annotation or specifying a generic method that is supported by bean definitions. With XML-based configuration metadata, you can use the destroy-method
attribute on the <bean/>
. With Java configuration, you can use the destroyMethod
attribute of @Bean
. See Receiving Lifecycle Callbacks. Consider the following definition:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {public void cleanup() {// do some destruction work (like releasing pooled connections)}
}
The preceding definition has almost exactly the same effect as the following definition:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {@Overridepublic void destroy() {// do some destruction work (like releasing pooled connections)}
}
However, the first of the two preceding definitions does not couple the code to Spring.
Default Initialization and Destroy Methods
When you write initialization and destroy method callbacks that do not use the Spring-specific InitializingBean
and DisposableBean
callback interfaces, you typically write methods with names such as init()
, initialize()
, dispose()
, and so on. Ideally, the names of such lifecycle callback methods are standardized across a project so that all developers use the same method names and ensure consistency.
You can configure the Spring container to “look” for named initialization and destroy callback method names on every bean. This means that you, as an application developer, can write your application classes and use an initialization callback called init()
, without having to configure an init-method="init"
attribute with each bean definition. The Spring IoC container calls that method when the bean is created (and in accordance with the standard lifecycle callback contract described previously). This feature also enforces a consistent naming convention for initialization and destroy method callbacks.
Suppose that your initialization callback methods are named init()
and your destroy callback methods are named destroy()
. Your class then resembles the class in the following example:
public class DefaultBlogService implements BlogService {private BlogDao blogDao;public void setBlogDao(BlogDao blogDao) {this.blogDao = blogDao;}// this is (unsurprisingly) the initialization callback methodpublic void init() {if (this.blogDao == null) {throw new IllegalStateException("The [blogDao] property must be set.");}}
}
Combining Lifecycle Mechanisms
As of Spring 2.5, you have three options for controlling bean lifecycle behavior:
The
InitializingBean
andDisposableBean
callback interfacesCustom
init()
anddestroy()
methodsThe
@PostConstruct
and@PreDestroy
annotations. You can combine these mechanisms to control a given bean.
Multiple lifecycle mechanisms configured for the same bean, with different initialization methods, are called as follows:
Methods annotated with
@PostConstruct
afterPropertiesSet()
as defined by theInitializingBean
callback interfaceA custom configured
init()
method
Destroy methods are called in the same order:
Methods annotated with
@PreDestroy
destroy()
as defined by theDisposableBean
callback interfaceA custom configured
destroy()
method
Fine-tuning Annotation-based Autowiring with Qualifiers
public class MovieRecommender {@Autowired@Qualifier("main")private MovieCatalog movieCatalog;// ...
}
public class MovieRecommender {private MovieCatalog movieCatalog;private CustomerPreferenceDao customerPreferenceDao;@Autowiredpublic void prepare(@Qualifier("main") MovieCatalog movieCatalog,CustomerPreferenceDao customerPreferenceDao) {this.movieCatalog = movieCatalog;this.customerPreferenceDao = customerPreferenceDao;}// ...
}
The following example shows corresponding bean definitions.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/><bean class="example.SimpleMovieCatalog"><qualifier value="main"/> <!-- inject any dependencies required by this bean --></bean><bean class="example.SimpleMovieCatalog"><qualifier value="action"/> <!-- inject any dependencies required by this bean --></bean><bean id="movieRecommender" class="example.MovieRecommender"/></beans>
Using Generics as Autowiring Qualifiers
@Configuration
public class MyConfiguration {@Beanpublic StringStore stringStore() {return new StringStore();}@Beanpublic IntegerStore integerStore() {return new IntegerStore();}
}
Injection with @Resource
public class SimpleMovieLister {private MovieFinder movieFinder;@Resource(name="myMovieFinder") public void setMovieFinder(MovieFinder movieFinder) {this.movieFinder = movieFinder;}
}
Using @Value
@Component
public class MovieRecommender {private final String catalog;public MovieRecommender(@Value("${catalog.name}") String catalog) {this.catalog = catalog;}
}
With the following configuration:
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }
And the following application.properties
file:
catalog.name=MovieCatalog
Using @PostConstruct
and @PreDestroy
public class CachingMovieLister {@PostConstructpublic void populateMovieCache() {// populates the movie cache upon initialization...}@PreDestroypublic void clearMovieCache() {// clears the movie cache upon destruction...}
}
@Component
and Further Stereotype Annotations
The @Repository
annotation is a marker for any class that fulfills the role or stereotype of a repository (also known as Data Access Object or DAO). Among the uses of this marker is the automatic translation of exceptions, as described in Exception Translation.
Spring provides further stereotype annotations: @Component
, @Service
, and @Controller
. @Component
is a generic stereotype for any Spring-managed component. @Repository
, @Service
, and @Controller
are specializations of @Component
for more specific use cases (in the persistence, service, and presentation layers, respectively). Therefore, you can annotate your component classes with @Component
, but, by annotating them with @Repository
, @Service
, or @Controller
instead, your classes are more properly suited for processing by tools or associating with aspects. For example, these stereotype annotations make ideal targets for pointcuts. @Repository
, @Service
, and @Controller
can also carry additional semantics in future releases of the Spring Framework. Thus, if you are choosing between using @Component
or @Service
for your service layer, @Service
is clearly the better choice. Similarly, as stated earlier, @Repository
is already supported as a marker for automatic exception translation in your persistence layer.
Using Filters to Customize Scanning
The following table describes the filtering options:
Filter Type | Example Expression | Description |
---|---|---|
annotation (default) |
|
An annotation to be present or meta-present at the type level in target components. |
assignable |
|
A class (or interface) that the target components are assignable to (extend or implement). |
aspectj |
|
An AspectJ type expression to be matched by the target components. |
regex |
|
A regex expression to be matched by the target components' class names. |
custom |
|
A custom implementation of the |
The following example shows the configuration ignoring all @Repository
annotations and using “stub” repositories instead:
@Configuration
@ComponentScan(basePackages = "org.example",includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),excludeFilters = @Filter(Repository.class))
public class AppConfig {...
}
The following listing shows the equivalent XML:
<beans><context:component-scan base-package="org.example"><context:include-filter type="regex"expression=".*Stub.*Repository"/><context:exclude-filter type="annotation"expression="org.springframework.stereotype.Repository"/></context:component-scan>
</beans>
Generating an Index of Candidate Components
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context-indexer</artifactId><version>5.2.6.RELEASE</version><optional>true</optional></dependency>
</dependencies>
Simple Construction
In much the same way that Spring XML files are used as input when instantiating a ClassPathXmlApplicationContext
, you can use @Configuration
classes as input when instantiating an AnnotationConfigApplicationContext
. This allows for completely XML-free usage of the Spring container, as the following example shows:
public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);MyService myService = ctx.getBean(MyService.class);myService.doStuff();
}
Using @Profile
The @Profile
annotation lets you indicate that a component is eligible for registration when one or more specified profiles are active. Using our preceding example, we can rewrite the dataSource
configuration as follows:
@Configuration
@Profile("development")
public class StandaloneDataConfig {@Beanpublic DataSource dataSource() {return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).addScript("classpath:com/bank/config/sql/schema.sql").addScript("classpath:com/bank/config/sql/test-data.sql").build();}
}
@Configuration
@Profile("production")
public class JndiDataConfig {@Bean(destroyMethod="")public DataSource dataSource() throws Exception {Context ctx = new InitialContext();return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");}
}
模拟spring IOC、源码分析相关推荐
- Spring Ioc 源码分析(一)--Spring Ioc容器的加载
1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:Intellj idea debug 模式 ...
- Spring Ioc源码分析 之 Bean的加载(6):属性填充(populateBean())
"属性填充",也是在populateBean()方法中. 首先回顾下CreateBean的主流程: 如果是单例模式,从factoryBeanInstanceCache 缓存中获取B ...
- Spring Ioc源码分析 之 Bean的加载(4):实例化Bean(createBeanInstance()方法)
实例化 Bean 在doCreateBean()代码 <2> 处,有一行代码instanceWrapper = createBeanInstance(beanName, mbd, args ...
- java获取当前周一_Java互联网架构-Spring IOC源码分析
欢迎关注头条号:java小马哥 周一至周日下午三点半!精品技术文章准时送上!!! 精品学习资料获取通道,参见文末 源码介绍之前,看几个问题: Bean的承载对象是什么? Bean的定义如何存储的? B ...
- Spring Ioc 源码分析(一)- XML 解析
2019独角兽企业重金招聘Python工程师标准>>> 注 :源码对应版本为 4.2.8.RELEASE 引入Spring依赖的时候,是否发现spring依赖有spring-bean ...
- Spring Ioc源码分析 之 Bean的加载(7):初始化
接着分析doCreateBean()的第6步--初始化 bean 实例对象 首先回顾下CreateBean的主流程: 如果是单例模式,从factoryBeanInstanceCache 缓存中获取Be ...
- Spring Ioc源码分析 之 Bean的加载(5):循环依赖处理(populateBean())
首先回顾下Bean加载的主流程: 1.如果是单例模式,从factoryBeanInstanceCache 缓存中获取BeanWrapper 实例对象并删除缓存 2.调用 createBeanInsta ...
- Spring Ioc源码分析系列--容器实例化Bean的四种方法
- Spring AOP 源码分析 - 拦截器链的执行过程
1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
最新文章
- 自定义控件:旋转菜单
- react16中ref的使用
- matlab单元数组和结构,Matlab使用单元数组和结构数组
- 信安精品课:2020年软考信息安全工程师备考公开课
- java quartz CronScheduleBuilder
- python base64加解密
- 简明firewalld不断的更新中....
- [单片机框架][drivers层][ADC] fuelgauge 软件电量计(二)
- 用python 代码写一个表白I love you
- word页眉页脚修改
- Symantec的SEP服务器(SEPM)从12.1 RU6MP5 升级到14 MP1 操作手册
- 2021.3.23 富途牛牛笔试
- APP中方法超过64K的解决办法
- 《Google Chrome:谷歌浏览器》 --不作恶,但可以恶心你
- 探索一下进制转换 (Python 实现源码)
- 深信服 EDR终端检测响应平台 0day RCE 漏洞
- signature=d208b1bb0cb69ace8714b67c8fb41881,The mechanics of cemented carbonate sands.
- 144显示器只有60_windows10系统中144hz显示器刷新率只显示60hz怎么办
- 2021年PMP®考试改革
- 飞机大战游戏---Pygame
热门文章
- springcloud的config
- 如何更改tomcat7及以上版本内存设置
- 打开AzureRay园子的大门,欢迎大家串门哟~
- 【超详细教程】使用Windows Live Writer 2012和Office Word 2013 发布文章到博客园全面总结...
- 一度的让自己变得那么懒惰,不知道后路还会如何
- C#生成Excel报表 用MyXls组件生成更完美
- mysql中binlog_format模式与配置详解
- 使用pip安装BeautifulSoup4模块
- 表修改语法之列的增删改
- How to log time in Linux? (C Programming)