spring源码解析(一)

开始探索spring的奥秘吧:

1.什么是spring

  首先,spring的官网地址:https://spring.io/projects/spring-framework

  官网是这么定义的:

 

The Spring Framework provides a comprehensive programming and configuration model for modern Java-based enterprise applications - on any kind of deployment platform.
A key element of Spring is infrastructural support at the application level: Spring focuses on the "plumbing" of enterprise applications so that teams can focus on 
application-level business logic, without unnecessary ties to specific deployment environments.

翻译:Spring框架可在任何类型的部署平台上为基于Java的现代企业应用程序提供全面的编程和配置模型。

Spring的一个关键元素是在应用程序级别的基础架构支持:Spring专注于企业应用程序的“管道”,以便团队可以专注于应用程序级别的业务逻辑,而不必与特定的部署环境建立不必要的联系。

简单理解:spring一个框架,它的作用是让开发人员专注于业务逻辑的开发,只要把项目配置好之后,就可以简单的进行业务开发,大大减少了开发者的难度与工作量。

2.spring最核心的就是IOC容器:

什么是IOC容器:

官网是这么介绍的:

翻译

 

 简而言之:IOC(控制反转)就是,将创建对象的权限,交给spring去管理。以前我们创建对象时,需要去new一个对象,比如,User user = new Use();

     而现在的创建工作交由spring去帮我们创建,我们只需要在xml文件中去配置即可。

      DI(依赖注入)就是,将对象注入给另一个对象。例如,汽车需要轮胎,我们需要去配置一个汽车对象,和一个轮胎对象。然后把轮胎对象,

      通过构造函数注入或者参数注入(setter)等方法,将轮胎对象,传入给汽车对象。

 

好了。下面就开始用代码说话吧:

 

3.如何去创建一个spring项目呢:

 (1)因为我们是源码分析,所以我们先得把spring源码导入,我这里用idea。如果还未导入的同学,可以参考我的另一篇博客:https://www.cnblogs.com/takeyblogs/p/13476233.html

  (2)导入源码之后:创建一个Pig的pojo类

 

 Pig.java

package bat.ke.qq.com.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


public class Pig {
    private int id;
    private String name;
    private int age;

    private Dog dog;

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Pig{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", dog=" + dog +
                '}';
    }
}

(3)接着我们在resources下创建一个spring.xml文件,将pig类配置在xml文件中:

 

 spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="pig" class="bat.ke.qq.com.bean.Pig"></bean>

</beans>

 (4)我们创建一个测试类,将spring容器启动,并从容器中获取pig对象

public class IOCTest {
@Test
    public void test(){
            ApplicationContext context = new     
            ClassPathXmlApplicationContext("spring.xml");
             System.out.println(context.getBean("pig"));
         
        }
}        

(5)最后我们可以在控制台看见pig对象的输出。

思考:通过上面的demo。spring是怎么帮我们去new这个Pig对象的呢?

上一个简单的流程图:

 

 首先,有一个Resource的类去读取我们的spring.xml文件。再通过ResourceLoader类将我们的配置信息读取加载进内存中,

 并通过BeanDefinitionRegistry注册器将这些配置注册成BeanDeifinition,存入beanDefinitionMap中。

 最后通过反射创建成bean对象,存入单例对象池中,singletonObjects。

这里有一个具体的分析图:https://www.processon.com/view/link/5cd10507e4b085d010929d02

大体流程是这样。我们再通过具体的实例,一点一点的去分析源码。

 

4.IOC的应用

 

 说明,我们不仅可以通过配置xml配置文件去定义bean,还可以通过一系列的注解进行装配bean,在通过beanDefinition去实例化,填充属性,最后初始化bean。

(1)bean的装配方式:

<1>xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user"  class="bat.ke.qq.com.bean.User" />


</beans>
public class IOCTest01 {

    @Test
    public void testxml(){
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

<2>@ImportResource

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user"  class="bat.ke.qq.com.bean.User" />


</beans>
@ImportResource("spring.xml")
public class AppConfig {

}
    @Test
    public void testAnnotation(){
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Object user = context.getBean("user");
        System.out.println(user);
    }

 <3>FactoryBean的使用

先看看什么是FactoryBean

官网是这么介绍的:

 

 翻译:

 

 简而言之:FactoryBean是一个接口,实现此接口,就必须实现它的方法:该接口包含3个方法:

      

T getObject() throws Exception;  获取一个对象。我们可以在该实现方法中去自定义我们想要返回的bean对象。
Class<?> getObjectType();     返回一个对象类型,跟getObject的类型一样
default boolean isSingleton() {  是否是返回单例,默认是返回单例
return true;
}

FactoryBean的使用:
首先先创建一个MyFactoryBean去实现FactoryBean接口,并实现FactoryBean接口的方法:

@Component
public class MyFactoryBean implements FactoryBean {


public Object getObject(){
return new User();
}

public Class getObjectType(){
return User.class;
}
}
    @Test
    public void testAnnotation(){
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Object user = context.getBean("myFactoryBean");
        System.out.println(user);
        Object user2 = context.getBean("&myFactoryBean");
        System.out.println(user2);
    }

 

 

说明:如果我们context.getBean("myFactoryBean"),会返回一个User对象。如果我们context.getBean("&myFactoryBean"),加了一个“&”,会返回FactoryBean自身的对象。

 

思考:那么FactoryBean和BeanFactory的区别是什么?

BeanFactory是spring中的一个基类,它创建和管理了bean。

FactoryBean是一个bean,可以返回我们自定义的bean。

 

<4>@Component+@ComponentScan

@ComponentScan 默认可扫描 @Component, @Repository,@Service, @Controller

 可以扫描到bat.ke.qq.com.bean 包下的, @Component, @Repository,@Service, @Controller这些注解

@Component
public class User{

    private String name;
    private int age;

   //此处省略了getter,setter
}

 

@ComponentScan("bat.ke.qq.com.bean")
public class AppConfig {

}
@Test
    public void testAnnotation(){
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Object user = context.getBean("user");
        System.out.println(user);
        Object user2 = context.getBean("&myFactoryBean");
        System.out.println(user2);
    }

@CompentScan 注解扩展用法:

排除用法 excludeFilters:

@ComponentScan(value = "bat.ke.qq.com.bean",excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {User.class})})
public class AppConfig {
}

包含用法 includeFilters:

public class CustomTypeFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        System.out.println(classMetadata.getClassName());
        if(classMetadata.getClassName().contains("Service")){
            System.out.println("111");
            return true;
        }
        return false;
    }
}
@ComponentScan(value = "bat.ke.qq.com.bean",includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM,value = {CustomTypeFilter.class})},useDefaultFilters = false)
public class AppConfig {

}

<5>@Bean+@Configuration

@Configuration
public class AppConfig {

    @Bean
    public User user(){
        return new User();
    }
}
    @Test
    public void testAnnotation(){
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String s: beanDefinitionNames) {
            System.out.println(s);
        }
    }

思考:配置@Configuration和不配置的区别:

不配置@Configuration: 当内部method bean发生彼此依赖的时候会导致多例

@Configuration的作用:

1.表明当前类是配置类,是方法bean的源。

2.将@Configuration配置的AppConfig的BeanDefinition属性赋值为full型,保证AppConfig类型可以转变为cglib类型。

3.将@Configuration配置的AppConfig由普通类型转变为cglib类型,最后会生成cglib代理对象,通过代理对象的方法拦截器,可以解决AppConfig内部方法bean之间发生依赖调用的时候从容器中去获取,避免了多例的出现。

 

<6>@Import

ImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar2 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(User.class);
        registry.registerBeanDefinition("takey",rootBeanDefinition);
    }
}
@Import(value = MyImportBeanDefinitionRegistrar2.class)
public class AppConfig {
}

导入一个或多个配置类

@Configuration
@Import(AppConfig2.class)
public class AppConfig {

}

@Configuration
public class AppConfig2 {

    @Bean
    public User user2(){
        return new User();
    }
}

ImportSelector:

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"bat.ke.qq.com.bean.Cat"};
    }
}
@Configuration
@Import(value = MyImportSelector.class)
public class AppConfig {
}

应用场景:

中间件底层大量使用,和Spring集成的核心扩展技术

mybatis-spring.jar

@MapperScan

spring boot

@SpringBootApplication XXXAutoConfiguration

spring cloud

@EnableEurekaServer @EnableCircuitBreaker @EnableFeignClients @EnableZuulProxy

 

<7>@Conditional

@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册 bean。

public class MyConditional implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        if(context.getBeanFactory().containsBean("user")){
            return true;
        }
        return false;
    }
}
@Configuration
public class AppConfig {

    //@Bean  //把@Bean注释掉的话,cat无法创建。必须要user先创建
    public User user(){
        return new User();
    }

    @Bean
    @Conditional(value = MyConditional.class)
    public Cat cat(){
        return new Cat();
    }

}

应用场景:

Spring boot 自动配置实现核心技术之一: 条件装配 ,

Spring Boot进行了扩展

@ConditionalOnWebApplication:当前项目是 Web项目的条件下

@ConditionalOnBean:当容器里有指定 Bean 的条件下

@ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下

@ConditionalOnClass:当类路径下有指定类的条件下

@ConditionalOnMissingClass:当类路径下没有指定类的条件下

@ConditionalOnProperty:指定的属性是否有指定的值

 

 

5.Bean的依赖注入

查找方式:

byType

byName

手动装配(手动注入)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user"  class="bat.ke.qq.com.bean.User" >
        <property name="name" value="takey"></property>
        <property name="age" value="25"></property>
    </bean>

    <bean id="userService" class="bat.ke.qq.com.bean.UserService">
        <!--setter-->
        <property name="user" ref="user"></property>
        <!--constructor-arg-->
        <!--<constructor-arg ref="user"></constructor-arg>-->
    </bean>


</beans>

利用autowire属性改为自动装配:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="user"  class="bat.ke.qq.com.bean.User" >
    <property name="name" value="takey"></property>
    <property name="age" value="25"></property>
</bean>

<bean id="userService" class="bat.ke.qq.com.bean.UserService" autowire="constructor">
</bean>


</beans>

 

自动装配(自动注入)注解:

@Autowired

@Autowired是spring自带的注解,通过 AutowiredAnnotationBeanPostProcessor 类实现的依 赖注入;

@Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier;

@Autowired有个属性为required,可以配置为false,如果配置为false之后,当没有找到相应 bean的时候,系统不会抛错;

@Autowired可以作用在变量、setter方法、构造函数上。

@Component
public class UserService {
    @Autowired
    private User user;
@Component
public class UserService {

    private User user;
    @Autowired
    public void setUser(User user) {
        this.user = user;
    }
@Component
public class UserService {

    private User user;

    public void setUser(User user) {
        this.user = user;
    }
    @Autowired
    public UserService(User user){
        this.user=user;
    }

@Resource

@Resource是JSR250规范的实现,需要导入javax.annotation实现注入;

@Resource是根据名称进行自动装配的,一般会指定一个name属性,当找不到与名称匹配的 bean时才按照类型进行装配;

@Resource可以作用在变量、setter方法上。

 

@Inject

@Inject是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject;实现注 入。

@Inject是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named;

@Inject可以作用在变量、setter方法、构造函数上。

posted @ 2020-09-28 09:41  Takey  阅读(1378)  评论(0编辑  收藏  举报