Spring source code analysis - The common underlying core notes

A, Spring framework as a whole Features

The Spring Framework is a Java platform that provides comprehensive support infrastructure for the development of Java applications. Spring processing infrastructure, so you can focus on your application.

Spring allows you to build applications from "plain old Java objects" (POJO), and non-invasive manner will apply to business services POJO. This feature is available for Java SE programming model as well as complete and partial Java EE.

As an application developer, you can benefit from the example of Spring platform as follows:

  • Java method executes the transaction in the database, without having to deal with matters API.
  • The native Java methods become remote procedure, without having to deal with remote API.
  • The native Java methods become management operations, without having to deal JMX API.
  • The native Java methods become the message handler, without having to deal JMS API.

Spring Framework functionality modules consists of about 20 composition. These modules are divided into core container, data access / integration, Web, AOP (aspect-oriented programming), the instrument, messaging and test, as shown in FIG.

1. Spring Core Container

Action module: Core Beans module and is a fundamental part of the frame, providing the IoC (rotation speed control) and dependency injection characteristics. The basic concept here is the BeanFactory, which provides for the Classical Factory pattern to eliminate the need for the program 'singletons and allows you really separated dependencies and program logic from the configuration.

1.1 Core

Spring framework mainly contains basic core tools, other components to be used in the Spring class in this package, Core module is the basic core of the other components.

1.2 Beans (BeanFacotry of action)

It includes access to the configuration files, create and manage bean as well as all classes Inversion of Control I Dependency Injection (IoC / DI) related to the operation.

1.3 Context (processing BeanFactory, ApplicationContext or the role of)

Core module is built on the basis of Beans and there is provided a method of accessing an object similar to a Frame JNDI registrar. Context module inherits the properties of Beans, Spring provides a number of extensions to the core, adds support for internationalization (eg resource bundles), event propagation, resource loading and support for Context of transparency created. Key Context module also supports some of the features of J2EE, ApplicationContext interface is the Context module

The essential difference :( BeanFacotry bean is used to delay loading, ApplicationContext loaded non-delay)

1.4 Expression Language

Module provides a powerful expression language for querying and manipulating objects at run time. It is an extension of the JSP 2.1 unifed expression language defined in the specification. Call the language supports with straight / get property values, distribution attributes, methods for accessing the array context (accessiong the context of arrays), the container and indexers, logical and arithmetic operators, named variables and from Spring IoC container retrieving objects by name. It also supports the projector list, selection list and general polymerization

2. Spring Data Access/Integration

2.1 JDBC

Module provides a JDBC abstraction layer, it can eliminate the error code tedious JDBC coding and parsing of database-vendor specific. This module contains all classes Spring data access on JDBC encapsulation

2.2 ORM module for the popular object - relational mapping API

As JPA, JDO, Hibernate, MyBatis etc., to provide an interactive layer. Using the ORM package, it can be mixed with all the characteristics provided by the Spring O / R mapping, such as a simple declarative transaction management.

2.3 OXM module provides an abstraction layer to achieve one pair mapping ObjecνXML

Object / XML mapping implementations include JAXB, Castor, XMLBeans, JiBX and XStrearn

2.4 JMS ( Java Messaging Service )

The main characteristics of the module contains a number of manufacturing and consuming messages.

2.5 Transaction

Support programming and declarative transaction management, these matters must implement a specific interface, and are applicable to all POJO

3. Spring Web

Web module: provides a Web-oriented integration features based. For example, multi-file upload, using servlet listeners initialization

IoC container for a Web application and context. It also contains the Spring remote support relevant section of the Web.

4. Spring Aop

  • Aspects module provides integrated support for AspectJ's.
  • Instrumentation module provides class instrumentation support and classloader achieved, making it possible to use on a specific application server

5. Spring Test

Test modules support the use of JUnit and TestNG test components for Spring

6. Spring inherits FIG container

The control and dependency inversion injection

What is Inversion of Control?

Here it is necessary to first understand the important thought of a software design: Dependency Inversion Principle (Dependency Inversion Principle)

What Dependency Inversion Principle? Suppose we design a car: first design wheels, wheel size and design according to the chassis, and then based on the chassis design of the body, according to last the entire vehicle body design is good. Here there is a "dependent" relationship: car-dependent body, the body relies chassis, chassis dependent on wheels

The figure did not seem anything wrong? But what if the tire size changed, then the need to change the site, the site changed, the body also changed, after the whole automobile construction have changed. Then the car company went out of business ...

Chairman and general manager of money dependent, general manager of the department managers rely on to make money, managers rely on employees to fight, then the employee turnover how to do ???

Conversely ... if the car companies decided to revise the tires we just need to change the wheel design, without moving the chassis, body, car design.

IOC core idea where that resource management could not help using the resources of the parties, but do not use third-party management of resources. This can bring many benefits, as follows:

  • Centralized resource management, resource configurable and manageable
  • 降低了使用资源的双方的依赖程度,也就是耦合度

二、Spring IOC 容器底层注解使用

1、xml配置文件的形式 VS 配置类的形式

1⃣ 基于xml的形式定义Bean的信息

<?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
        http://www.springframework.org/schema/beans/spring-beans.xsd"
//定义一个Bean的信息
<bean id="car" class="com.niuh.compent.Car"></bean>
</beans>

去容器中读取Bean

public static void main( String[] args ) {
   ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
   System.out.println(ctx.getBean("person")); 
}

2⃣ 基于读取配置类的形式定义Bean信息

@Configuration
public class MainConfig {
    @Bean
    public Person person(){
        return new Person();
    }
}

注意: 通过@Bean的形式是使用的话, bean的默认名称是方法名,若@Bean(value=“bean的名称”) 那么bean的名称是指定的

去容器中读取Bean的信息(传入配置类)

public static void main( String[] args ) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
    System.out.println(ctx.getBean("person")); 
}

2、在配置类上写@CompentScan注解来进行包扫描

@Configuration
@ComponentScan(basePackages = {"com.niuh.testcompentscan"}) 
public class MainConfig {
}

1⃣ 排除用法 excludeFilters(排除@Controller注解的和NiuhService的)

@Configuration
@ComponentScan(basePackages = {"com.niuh.testcompentscan"},excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {NiuhService.class}) })
public class MainConfig {
}

2⃣ 包含用法 includeFilters,注意:若使用包含的用法,需要把useDefaultFilters属性设置为false(true表示扫描全部)

@Configuration
@ComponentScan(basePackages = {"com.niuh.testcompentscan"},includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class, Service.class}) },useDefaultFilters = false)
public class MainConfig {
}

3⃣ @ComponentScan.Filter type的类型

*   注解形式的FIlterType.ANNOTATION @Controller @Service @Repository @Compent
*   指定类型的FilterType.ASSIGNABLE_TYPE @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {NiuhService.class}) })
*   aspectj 类型的 FilterType.ASPECTJ (不常用)
*   正则表达式的 FilterType.REGEX (不常用)
*   自定义的 FilterType.CUSTOM
public enum FilterType {
    //注解形式 比如@Controller @Service @Repository @Compent 
    ANNOTATION,
    //指定的类型 
    ASSIGNABLE_TYPE,
    //aspectJ形式的 
    ASPECTJ,
    //正则表达式的 
    REGEX,
   //自定义的 
   CUSTOM
}

FilterType.CUSTOM 自定义类型如何使用

public class NiuhFilterType implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取当前类的注解源信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前类的class的源信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前类的资源信息
        Resource resource = metadataReader.getResource();
       if(classMetadata.getClassName().contains("dao")) { 
            return true;
       }
       return false; 
    }
}

@ComponentScan(basePackages = {"com.niuh.testcompentscan"},includeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM,value = NiuhFilterType.class)},useDefaultFilters = false)
public class MainConfig {
}

3、配置Bean的作用域对象

1⃣ 在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载(启动启动实例就创建好了)

@Bean
public Person person() {
    return new Person(); 
}

2⃣ 指定@Scope为prototype 表示为多实例,而且还是懒汉加载模式(IOC容器启动的时候,并不会创建对象,而是在第一次使用的时候才会创建)

@Bean
@Scope(value = "prototype") 
public Person person() {
    return new Person(); 
}

3⃣ @Scope 指定的作用域方法取值

  • singleton 单实例的(默认)
  • prototype 多实例的
  • request 同一次请求
  • session 同一个会话级别

4、Bean的懒加载 @lazy

主要针对单实例的bean ,容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象

@Bean
@Lazy
public Person person() {
    return new Person(); 
}

5、@Conditional 进行条件判断等

场景,有二个组件NiuhAspect 和 NiuhLog ,我的NiuhLog组件是依赖于NiuhAspect的组件 应用:自己创建一个NiuhCondition的类 实现Condition接口

public class TulingCondition implements Condition { 
    /**
    *
    * @param context
    * @param metadata 
    * @return
    */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //判断容器中是否有tulingAspect的组件 
        if(context.getBeanFactory().containsBean("tulingAspect")) {
            return true; 
        }
        return false; 
    }
}

public class MainConfig {
    @Bean
    public NiuhAspect niuhAspect() {
        return new NiuhAspect();
    }
    //当切 容器中有niuhAspect的组件,那么niuhLog才会被实例化. @Bean
    @Conditional(value = NiuhCondition.class)
    public NiuhLog niuhLog() {
        return new NiuhLog(); 
    }
} 

6、往IOC容器中添加组件的方式

1⃣ 通过@CompentScan + @Controller @Service @Respository @compent

适用场景:针对我们自己写的组件可以通过该方式来进行加载到容器中。

2⃣ 通过@Bean 的方式来导入组件(适用于导入第三方组件的泪)

3⃣ 通过@Import 来导入组建(导入组件的id为全类名路径)

@Configuration
@Import(value = {Person.class, Car.class}) 
public class MainConfig {
}
  • 通过@Import 的ImportSeletor类实现组件的导入 (导入组件的id为全类名路径)
public class NiuhImportSelector implements ImportSelector { 
    //可以获取导入类的注解信息
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.niuh.testimport.compent.Dog"}; 
    }
} 

@Configuration
@Import(value = {Person.class, Car.class, NiuhImportSelector.class}) 
public class MainConfig {
}
  • 通过@Import的 ImportBeanDefinitionRegister导入组件 (可以指定bean的名称)
public class NiuhBeanDefinitionRegister implements ImportBeanDefinitionRegistrar { 
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //创建一个bean定义对象
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Cat.class);
        //把bean定义对象导入到容器中
        registry.registerBeanDefinition("cat",rootBeanDefinition); 
    }
}

@Configuration
//@Import(value = {Person.class, Car.class})
//@Import(value = {Person.class, Car.class, TulingImportSelector.class})
@Import(value = {Person.class, Car.class, NiuhImportSelector.class, NiuhBeanDefinitionRegister.class}) 
public class MainConfig {
}

4⃣ 通过实现FactoryBean 接口来实现注册组建

public class CarFactoryBean implements FactoryBean<Car> {
    //返回bean的对象
    @Override
    public Car getObject() throws Exception {
        return new Car(); 
    }
    //返回bean的类型
    @Override
    public Class<?> getObjectType() {
        return Car.class; 
   }
    //是否为单例
    @Override
    public boolean isSingleton() {
        return true; 
    }
}

7、Bean的初始化方法和销毁方法

1⃣ 什么是Bean 的生命周期?

bean的创建-------> 初始化 ---------> 销毁方法

由容器管理Bean的生命周期,我们可以通过自己指定bean的初始化方法和bean的销毁方法

@Configuration
public class MainConfig {
    //指定了bean的生命周期的初始化方法和销毁方法.
    @Bean(initMethod = "init",destroyMethod = "destroy") 
    public Car car() {
        return new Car(); 
    }
}

针对单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法

针对多实例bean的话,容器启动的时候,bean是不会被创建的而是在获取bean的时候被创建,而且bean的销毁不受 IOC容器的管理.

2⃣ 通过InitializingBean 和 DisposableBean 的2个接口实现bean的初始化以及销毁方法

@Component
public class Person implements InitializingBean,DisposableBean {
    public Person() { 
        System.out.println("Person的构造方法");
    }
    
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean的destroy()方法 "); 
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean的 afterPropertiesSet方法"); 
    }
}

3⃣ 通过JSR250规范提供的注解 @PostConstruct 和 @ProDestory 标注的方法

@Component
public class Book {
    public Book() {
        System.out.println("book 的构造方法");
    }
    
    @PostConstruct 
    public void init() {
        System.out.println("book 的PostConstruct标志的方法"); 
   }
   
    @PreDestroy
    public void destory() {
        System.out.println("book 的PreDestory标注的方法"); 
    }
}

4⃣ 通过Spring的BeanPostProcessor的 bean的后置处理器会拦截所有bean创建过程

postProceessBeforInitialization 在init方法之前调用

postProcessAfterInitialization 在init方法之后调用

@Component
public class NiuhBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("TulingBeanPostProcessor...postProcessBeforeInitialization:"+beanName);
        return bean; 
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("TulingBeanPostProcessor...postProcessAfterInitialization:"+beanName);
        return bean; 
    }
}

BeanPostProcessor 的执行时机

populateBean(beanName, mbd, instanceWrapper) 
initializeBean{
    applyBeanPostProcessorsBeforeInitialization() 
    invokeInitMethods{
        isInitializingBean.afterPropertiesSet
    自定义的init方法 
    }
    applyBeanPostProcessorsAfterInitialization()方法 
}

8、通过@Value + @PropertySource来给组件赋值

public class Person {
    //通过普通的方式 
    @Value("一角钱")
    private String firstName;
    
    //spel方式来赋值 
    @Value("#{28-8}")
    private Integer age; 
    
   通过读取外部配置文件的值 
   @Value("${person.lastName}") 
   private String lastName;
}

@Configuration
@PropertySource(value = {"classpath:person.properties"}) //指定外部文件的位置 
public class MainConfig {
    @Bean
    public Person person() {
        return new Person();
    }
}

9、自动装配

@AutoWired的使用

自动注入:

//一个Dao
@Repository
public class NiuhDao { 
}

@Service
public class NiuhService {
    @Autowired
    private NiuhDao niuhDao;
}

结论:

1⃣ 自动装配首先按照类型进行装配,若在IOC容器中发现了多个相同类型的组件,那么就按照属性名称来进行装配

@Autowired

private NiuhDao niuhDao;

比如,容器中有二个NiuhDao类型的组件,一个叫niuhDao,一个叫niuhDao2

那么我们通过@AutoWired 来修饰的属性名称时niuhDao,那么就加载容器的niuhDao组件,若属性名称为niuhDao2那么就加载niuhDao2组件。

2⃣ 假设我们需要指定特定的组件来进行装配,我们可以通过使用@Qualifilter(“niuhDao”)来指定装配的组件或者在配置类上的@Bean加上@Primary注解

@Autowired 
@Qualifier("niuhDao") 
private NiuhDao niuhDao2;

3⃣ 假设容器中即没有niuhDao 和 niuhDao2,那么在装配的时候就会抛出异常

No qualifying bean of type 'com.niuh.testautowired.NiuhDao' available

若我们想不抛异常 ,我们需要指定 required为false的时候可以了

@Autowired(required = false)
@Qualifier("niuhDao") 
private NiuhDao niuhDao2;

4⃣ @Resource (JSR250规范)

功能和@AutoWired的功能差不多一样,但是不支持@Primary 和@Qualifier的支持

5⃣ @InJect (JSR330规范)

需要导入jar包以来,功能支持@Primary功能 ,但是没有Require=false的功能

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

6⃣ 使用autowired 可以标注在方法上

标注在set方法上

//@Autowired
public void setNiuhLog(NiuhLog niuhLog) {
    this.niuhLog = niuhLog; 
}

标注在构造方法上

@Autowired
public NiuhAspect(NiuhLog niuhLog) {
    this.niuhLog = niuhLog; 
}

标注在配置类上的入参中(可以不写)

@Bean
public NiuhAspect niuhAspect(@Autowired NiuhLog niuhLog) {
    NiuhAspect niuhAspect = new NiuhAspect(niuhLog);
    return niuhAspect; 
}

10、我们自己的组件需要使用spring ioc 的底层组件的时候,比如ApplicationContext等,我们可以通过实现XXXAware接口来实现

@Component
public class NiuhCompent implements ApplicationContextAware,BeanNameAware {
    private ApplicationContext applicationContext;
    
    @Override
    public void setBeanName(String name) {
        System.out.println("current bean name is :【"+name+"】");
    }
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext; 
    }
}

11、通过@Profile 注解来根据环境激活标识不同的Bean

@Profile 标识在类上,那么只有当前环境匹配,整个配置类才会生效

@Profile 标识在Bean上,那么只有当前环境的Bean才会被激活

没有标志为@Profile的bean 不管在什么环境都可以被激活

@Configuration
@PropertySource(value = {"classpath:ds.properties"})
public class MainConfig implements EmbeddedValueResolverAware {
    @Value("${ds.username}")
    private String userName;
     
    @Value("${ds.password}") 
    private String password;
    
   private String jdbcUrl; 
   
   private String classDriver;
   
    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.jdbcUrl = resolver.resolveStringValue("${ds.jdbcUrl}");
        this.classDriver = resolver.resolveStringValue("${ds.classDriver}"); 
    }
    
    //标识为测试环境才会被装配 @Bean
    @Profile(value = "test")
    public DataSource testDs() {
        return buliderDataSource(new DruidDataSource()); 
    }
    
    //标识开发环境才会被激活 @Bean
    @Profile(value = "dev")
    public DataSource devDs() {
        return buliderDataSource(new DruidDataSource());
    }
    
    //标识生产环境才会被激活 @Bean
    @Profile(value = "prod")
    public DataSource prodDs() {
        return buliderDataSource(new DruidDataSource());
    }

    private DataSource buliderDataSource(DruidDataSource dataSource) { 
        dataSource.setUsername(userName); 
        dataSource.setPassword(password); 
        dataSource.setDriverClassName(classDriver); 
        dataSource.setUrl(jdbcUrl);
        return dataSource; 
    }
}

激活切换环境的方法

方法一:通过运行时jvm参数来切换 -Dspring.profiles.active=test|dev|prod

方法二:通过代码方式来激活

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); 
    ctx.getEnvironment().setActiveProfiles("test","dev");
    ctx.register(MainConfig.class); 
    ctx.refresh(); 
    printBeanName(ctx);
}

Guess you like

Origin blog.csdn.net/org_hjh/article/details/94722905