模拟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-detailedConstructor-based Dependency Injection
構造方法注入參考文檔: https://docs.spring.io/spring-framework/docs/current/spring-framework- reference/core.html#beans-constructor-injectionConstructor-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| no | (Default) No autowiring. Bean references must be defined by?ref?elements. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system. |
| byName | 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?master?property (that is, it has a?setMaster(..)?method), Spring looks for a bean definition named?master?and uses it to set the property. |
| byType | 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?byType?autowiring for that bean. If there are no matching beans, nothing happens (the property is not set). |
| constructor | Analogous to?byType?but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised. |
spring懶加載
官網已經解釋的非常清楚了:
https://docs.spring.io/spring-framework/docs/current/spring-framework- reference/core.html#beans-factory-lazy-initBy 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| 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?ApplicationContext. |
| session | Scopes a single bean definition to the lifecycle of an HTTP?Session. Only valid in the context of a web-aware Spring?ApplicationContext. |
| application | Scopes a single bean definition to the lifecycle of a?ServletContext. Only valid in the context of a web-aware Spring?ApplicationContext. |
| websocket | Scopes a single bean definition to the lifecycle of a?WebSocket. Only valid in the context of a web-aware Spring?ApplicationContext. |
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-injectionMethod 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() methodInitialization 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?and?DisposableBean?callback interfaces
-
Custom?init()?and?destroy()?methods
-
The?@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 the?InitializingBean?callback interface
A custom configured?init()?method
?
Destroy methods are called in the same order:
Methods annotated with?@PreDestroy
destroy()?as defined by the?DisposableBean?callback interface
A 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=MovieCatalogUsing?@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:
| annotation (default) | org.example.SomeAnnotation | An annotation to be?present?or?meta-present?at the type level in target components. |
| assignable | org.example.SomeClass | A class (or interface) that the target components are assignable to (extend or implement). |
| aspectj | org.example..*Service+ | An AspectJ type expression to be matched by the target components. |
| regex | org\.example\.Default.* | A regex expression to be matched by the target components' class names. |
| custom | org.example.MyTypeFilter | A custom implementation of the?org.springframework.core.type.TypeFilter?interface. |
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、源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据验证和JSR303
- 下一篇: AOP各种表达式,aspectj的关系