Spring

What is Spring ?

Spring是一个以IoC(Inversion of Control,管制反转)和AOP(Aspect OrientedProgramming)为内核的框架。IoC是Spring的根底。IoC实现的是一种管制,简略地说,就是以前调用new构造方法来创建对象,当初变成应用Spring来创建对象。

spring 容器

  • Spring 容器是 Spring 框架的外围。容器将创建对象,把它们连贯在一起,配置它们,

并治理他们的整个生命周期从创立到销毁。Spring 容器应用依赖注入(DI)来治理组成一个应用程序的组件。这些对象被称为 Spring Beans

  • Spring IoC 容器利用 Java 的 POJO 类和配置元数据来生成齐全配置和可执行的零碎或应用程序
  • 通过浏览配置元数据提供的指令,容器晓得对哪些对象进行实例化,配置和组装。配置元数据能够通过 XML,Java 正文或 Java 代码来示意
  • 通常new一个类的实例,控制权由码农管制,而"管制反转"是指new实例工作不禁码农来做而是交给Spring容器来做。
  • Spring中IOC容器的 BeanFactory (简略实现) 容器和有ApplicationContext(蕴含了BeanFactory的性能) 容器
  • Spring IoC容器是一个治理Bean的容器,在Spring的定义中,它要求所有的IoC容器都须要实现接口BeanFactory,它是一个顶级容器接口,下边是源码,因为BeanFactory的性能还不够弱小,因而在BeanFactory的根底上,还设计了一个更为高级的接口ApplicationContext
  • ApplicationContext接口通过继承下级接口,进而继承BeanFactory接口,然而在BeanFactory的根底上,扩大了音讯国际化接口(MessageSource)、环境可配置接口(EnvironmentCapable)、利用事件公布接口(ApplicationEventPublisher)和资源模式解析接口(ResourcePatternResolver),所以它的性能会更为弱小
package org.springframework.beans.factory;import org.springframework.beans.BeansException;import org.springframework.core.ResolvableType;import org.springframework.lang.Nullable;public interface BeanFactory {  // 前缀  String FACTORY_BEAN_PREFIX = "&";  // 多个getbean办法  Object getBean(String var1) throws BeansException;  <T> T getBean(String var1, Class<T> var2) throws BeansException;  Object getBean(String var1, Object... var2) throws BeansException;  <T> T getBean(Class<T> var1) throws BeansException;  <T> T getBean(Class<T> var1, Object... var2) throws BeansException;  <T> ObjectProvider<T> getBeanProvider(Class<T> var1);  <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);  // 是否蕴含bean  boolean containsBean(String var1);  // bean是否是单例  boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;  // bean是否原型  boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;  // 是否类型匹配   boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;  boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;  // 获取bean的类型  @Nullable  Class<?> getType(String var1) throws NoSuchBeanDefinitionException;  @Nullable  Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;  // 获取bean的别名  String[] getAliases(String var1);}

管制反转

  • IoC实践上是借助于“第三方”实现具备依赖关系对象之间的解耦
  • 即把各个对象类封装之后,通过IoC容器来关联这些对象类。这样对象之间就通过IoC容器进行分割,而对象之间没有间接分割

依赖注入

  • DI是Dependency Inject的缩写,译为“依赖注入”。
  • 所谓依赖注入,就是由IoC容器在运行期间动静地将某种依赖关系注入对象之中
// TextEditor 类依赖 SpellChecker 类,并实例化 SpellChecker 类public class TextEditor {   private SpellChecker spellChecker;     public TextEditor() {      spellChecker = new SpellChecker();   }}// 在spring中,可能是这样,构造函数注入// TextEditor 不实例化 SpellChecker类,控制权交给Spring public class TextEditor {   private SpellChecker spellChecker;   public TextEditor(SpellChecker spellChecker) {      this.spellChecker = spellChecker;   }}

spring bean

  • Spring如同一个工厂,用于生产和治理Spring容器中的Bean。要应用这个工厂,须要开发者对Spring的配置文件进行配置
  • bean 是一个被实例化,组装,并通过 Spring IoC 容器所治理的对象
  • bean是由用容器提供的配置元数据创立
<bean id="helloWorld"      class="com.huahuadavids.wehcat.pojo.HelloWorld"      lazy-init="true"         // lazy-init="true" 提早初始化的 bean 通知 IoC 容器在它第一次被申请时,而不是在启动时去创立一个 bean 实例      init-method="initData"   // 在 bean 的所有必须的属性被容器设置之后,调用回调办法      destroy-method="destroy" // 当蕴含该 bean 的容器被销毁时,应用回调办法>    <property name="message" value="Hello huahuadavids!"/></bean>// java 源文件// 第一步利用框架提供的 XmlBeanFactory() API 去生成工厂 bean 以及利用 ClassPathResource() API 去加载在门路 CLASSPATH 下可用的 bean 配置文件。// XmlBeanFactory() API 负责创立并初始化所有的对象,即在配置文件中提到的 beanXmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));// 第二步利用第一步生成的 bean 工厂对象的 getBean() 办法失去所须要的 bean。// 这个办法通过配置文件中的 bean ID 来返回一个真正的对象,该对象最初能够用于理论的对象HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");obj.getMessage();// FileSystemXmlApplicationContext 是从文件系统找 bean的配置// ApplicationContext context = new FileSystemXmlApplicationContext("/Users/davidzhang/Desktop/spring-starter/src/main/resources/beans.xml");ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");HelloWorld obj1 = (HelloWorld) context.getBean("helloWorld");obj1.getMessage();

spring的单元测试 更不便的测试bean

这里须要一个包 spring test, spring boot 默认有了这个包

import org.junit.runner.RunWith;import org.junit.Test;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;// 在测试文件中 这么应用 @RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:beans.xml") 指定配置文件 public class AOPTest {  @Resource(name="productDao")  private ProductDao productDao;  @Test  public void demo1(){    productDao.save();    System.out.println("???");  }}

spring bean的应用过程

  1. 读取配置信息
  2. 实例化bean
  3. 把bean实例放到spring容器中
  4. 应用bean,从bean缓存池中取

spring bean的scope

  • 对有状态的bean应该应用prototype作用域,而对无状态的bean则应该应用singleton作用域
  • singleton 在spring IoC容器仅存在一个Bean实例,Bean以单例形式存在,默认值

    // 两次输入的信息一样,证实 obj1 和 obj2是一样的,单例模式 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");HelloWorld obj1 = (HelloWorld) context.getBean("helloWorld");obj1.setMessage("huhu");obj1.getMessage();HelloWorld obj2 = (HelloWorld) context.getBean("helloWorld");obj2.getMessage();
  • prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()

bean的生命周期

  • 就是 Bean的定义——Bean的初始化——Bean的应用——Bean的销毁

bean初始化拦截器

// 1. 实现BeanPostProcessor 接口 public class MyBeanPostProcessor implements BeanPostProcessor {  @Override  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {    Class beanClass = bean.getClass();    if (beanClass == HelloWorld.class) {      System.out.println("HelloWorld bean 对象初始化之前······");    }    return bean;  }  @Override  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {    System.out.println("HelloWorld bean 对象初始化之后······");    return bean;  }}// 2. xml中配置 这示意利用于所有的bean <bean class="com.huahuadavids.wehcat.pojo.MyBeanPostProcessor" />

bean的定义继承

  • 就是继承一个bean的一些配置信息,而不是java的继承
// 1 两个类 public class HelloWorld implements InitializingBean {  private String message1;  public void  getMessage2() {    System.out.println("Hello World Message2 : " + message2);  }  public void setMessage2(String message2) {    this.message2 = message2;  }  private String message2;  public void setMessage1(String message1) {    this.message1 = message1;  }    public void getMessage() {    System.out.println("Hello World Message : " + message1);  }}public class HelloIndia {  private String message1;  private String message2;  private String message3;  public void setMessage1(String message){    this.message1  = message;  }  public void setMessage2(String message){    this.message2  = message;  }  public void setMessage3(String message){    this.message3  = message;  }  public void getMessage1(){    System.out.println("India Message1 : " + message1);  }  public void getMessage2(){    System.out.println("India Message2 : " + message2);  }  public void getMessage3(){    System.out.println("India Message3 : " + message3);  }} // 2 xml的bean配置 <bean id="helloWorld" class="com.huahuadavids.wehcat.pojo.HelloWorld">        <property name="message1" value="world-message1"/>        <property name="message2" value="world-message2"/>    </bean>    <bean id="helloIndia" class="com.huahuadavids.wehcat.pojo.HelloIndia" parent="beanTeamplate">        <property name="message1" value="HelloIndia-message1"/>        <property name="message3" value="HelloIndia-message3"/>    </bean>    <bean class="com.huahuadavids.wehcat.pojo.MyBeanPostProcessor" />
  • 继承模板
<bean id="beanTeamplate" abstract="true">    <property name="message1" value="Hello bbbb!"/>    <property name="message2" value="Hello aaaa World!"/>    <property name="message3" value="Namaste India!"/></bean><bean id="helloIndia" class="com.demo.HelloIndia" parent="beanTeamplate">  <property name="message1" value="Hello India!"/>  <property name="message3" value="Namaste India!"/></bean>

Spring 基于构造函数的依赖注入

// 1 两个相互依赖的类public class TextEditor {   private SpellChecker spellChecker;   public TextEditor(SpellChecker spellChecker) {      System.out.println("Inside TextEditor constructor." );      this.spellChecker = spellChecker;   }   public void spellCheck() {      spellChecker.checkSpelling();   }}public class SpellChecker {  public SpellChecker(){    System.out.println("SpellChecker constructor" );  }  public void checkSpelling() {    System.out.println("SpellChecker checkSpelling..." );  }}// 2. bean的xml配置<!-- Definition for textEditor bean --><bean id="textEditor" class="com.demo.TextEditor">   <constructor-arg ref="spellChecker"/></bean><!-- Definition for spellChecker bean --><bean id="spellChecker" class="com.demo.SpellChecker"></bean>// 3. 应用AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");TextEditor te = (TextEditor) context.getBean("textEditor");te.spellCheck();// 4 如果构造函数有多个参数,程序问题,就bean中程序一样即可 ,最好的形式是指定 index<bean id="textEditor" class="com.demo.TextEditor">   <constructor-arg ref="spellChecker1" index="0"/>   <constructor-arg ref="spellChecker2" index="1"/></bean>// 5. bean 配置能够指定参数的类型<bean id="exampleBean" class="examples.ExampleBean">   <constructor-arg type="int" value="2001"/>   <constructor-arg type="java.lang.String" value="Zara"/></bean>

Spring 基于设值函数的依赖注入

当容器调用一个无参的构造函数或一个无参的动态 factory 办法来初始化你的 bean 后,通过容器在你的 bean 上调用设值函数

// 1 批改 TextEditor 类public class TextEditor {  public SpellChecker getSpellChecker() {    return spellChecker;  }  public void setSpellChecker(SpellChecker spellChecker) {    System.out.println("SpellChecker setter");    this.spellChecker = spellChecker;  }  private SpellChecker spellChecker;  public void spellCheck() {    spellChecker.checkSpelling();  }}// 2 批改bean xml的配置 关联类作为一个属性传入<bean id="textEditor" class="com.starbugs.wehcat.pojo.TextEditor"> <property name="spellChecker" ref="spellChecker"/></bean>

p-namespace 配置XML

// 退出xml的命名空间xmlns:p="http://www.springframework.org/schema/p"       // 上述配置能够写成  <bean id="textEditor" class="com.starbugs.wehcat.pojo.TextEditor" p:spellChecker-ref="spellChecker" />    

外部Bean bean外部配置 Bean

<bean id="textEditor" class="com.starbugs.wehcat.pojo.TextEditor">   <property name="spellChecker">       <bean id="spellChecker" class="com.starbugs.wehcat.pojo.SpellChecker" />   </property></bean>

注入汇合
汇合中还能够注入类

// 1 定义pojo public class JavaBeanCollection {  private List addressList;  private Set  addressSet;  private Map  addressMap;  private Properties addressProp;    public void setAddressList(List addressList) {    this.addressList = addressList;  }    public List getAddressList() {    System.out.println("List Elements :"  + addressList);    return addressList;  }    public void setAddressSet(Set addressSet) {    this.addressSet = addressSet;  }    public Set getAddressSet() {    System.out.println("Set Elements :"  + addressSet);    return addressSet;  }  public void setAddressMap(Map addressMap) {    this.addressMap = addressMap;  }    public Map getAddressMap() {    System.out.println("Map Elements :"  + addressMap);    return addressMap;  }    public void setAddressProp(Properties addressProp) {    this.addressProp = addressProp;  }    public Properties getAddressProp() {    System.out.println("Property Elements :"  + addressProp);    return addressProp;  }}// 2 bean的xml配置 <bean id="javaBeanCollection" class="com.starbugs.wehcat.pojo.JavaBeanCollection">        <property name="addressList">            <list>                <value>list1</value>                <value>list2</value>                <value>list3</value>            </list>        </property>        <property name="addressSet">            <set>               <ref bean="address2"/>                <value>set1</value>                <value>set2</value>                <value>set3</value>            </set>        </property>        <property name="addressMap">            <map>                <entry key="11" value="value-11" />                <entry key="22" value="value-22" />                <entry key ="three" value-ref="address2"/>            </map>        </property>        <property name="addressProp">            <props>                <prop key="one">INDIA</prop>                <prop key="two">Pakistan</prop>                <prop key="three">USA</prop>                <prop key="four">USA</prop>            </props>        </property>    </bean>

spring 主动拆卸

byName 、 byType

// 1 定义类 应用setter注入 public class TextEditor {  private SpellChecker spellChecker;  private String name;  public void setSpellChecker( SpellChecker spellChecker ){    System.out.println("TextEditor setSpellChecker");    this.spellChecker = spellChecker;  }  public SpellChecker getSpellChecker() {    return spellChecker;  }  public void setName(String name) {    System.out.println("TextEditor setName");    this.name = name;  }  public String getName() {    return name;  }  public void spellCheck() {    spellChecker.checkSpelling();  }}// 2 xml 的Bean 配置<bean id="textEditor" class="com.starbugs.wehcat.pojo.TextEditor" autowire="byName">    <property name="name" value="huahua" /></bean> <bean id="spellChecker" class="com.starbugs.wehcat.pojo.SpellChecker" />

由构造函数主动拆卸

// 1 类批改为构造方法注入public TextEditor( SpellChecker spellChecker, String name ) {  this.spellChecker = spellChecker;  this.name = name;}// 2 批改xml配置<bean id="textEditor" class="com.starbugs.wehcat.pojo.TextEditor" autowire="constructor">    <constructor-arg ref="spellChecker"/>    <constructor-arg value="huhuhu"/></bean>

基于注解的配置

注解来配置依赖注入,会被XML注入所笼罩。

@Required
正文利用于 bean 属性的 setter 办法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中

public class Student {  private Integer age;  private String name;  @Required  public void setAge(Integer age) {    this.age = age;  }  public Integer getAge() {    return age;  }  @Required  public void setName(String name) {    this.name = name;  }  public String getName() {    return name;  }}// 标注了 @required的,必须要呈现在xml的property 配置中<bean id="student" class="com.starbugs.wehcat.pojo.Student">   <property name="name" value="huahua"/>   <property name="age" value="22"/></bean>

@Autowired

// @Autowired 润饰 setter ,相当于 配置了 byType 形式主动连贯 public class TextEditor {   private SpellChecker spellChecker;   @Autowired   public void setSpellChecker( SpellChecker spellChecker ){      this.spellChecker = spellChecker;   }   public SpellChecker getSpellChecker( ) {      return spellChecker;   }   public void spellCheck() {      spellChecker.checkSpelling();   }}// 2 @Autowired 润饰属性,打消了setter,然而并不是为类主动产生了一个 setter @Autowiredprivate SpellChecker spellChecker; // 3 @Autowired默认必须有Integer 这个bean,否则就报错,加上required = false就不会报错了 @Autowired(required = false)public void setAge(Integer age) {  this.age = age;}

@Qualifier
当你创立多个具备雷同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行拆卸

// 1 xml配置 <bean id="student" class="com.starbugs.wehcat.pojo.Student">     <property name="name" value="huahua"/>     <property name="age" value="22"/> </bean> <bean id="student1" class="com.starbugs.wehcat.pojo.Student">     <property name="name" value="name1"/>     <property name="age" value="111"/> </bean>// 2 应用类public class Profile {  @Autowired  @Qualifier("student1")  private Student student;  public Profile(){    System.out.println("Inside Profile constructor." );  }  public void printAge() {    System.out.println("Age : " + student.getAge() );  }  public void printName() {    System.out.println("Name : " + student.getName() );  }}

@Component
标注一个类,示意申明为一个spring配置的bean

// 1 xml的bean配置 开启组件扫描 <context:component-scan base-package="com.starbugs.wehcat.pojo" />// 2 pojo类中申明bean的id @Component("profile")// 3 具体应用AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");Profile profile = (Profile) context.getBean("profile");profile.printAge();profile.printName();

@ComponentScan 组件扫描注解
spring boot的SpringBootApplication注解,默认集成了这个注解

@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Documented// 一个类中可反复定义@Repeatable(ComponentScans.class)public @interface ComponentScan {  // 定义扫描的包  @AliasFor("basePackages")  String[] value() default {};  // 定义扫描的包  @AliasFor("value")  String[] basePackages() default {};    // 定义扫描的类  Class<?>[] basePackageClasses() default {};   // bean name 生成器  Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;  // 作用域解析器  Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;  // 作用域代理模式   ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;  // 资源匹配模式  String resourcePattern() default "**/*.class";  // 是否应用默认过滤器   boolean useDefaultFilters() default true;  // 当满足过滤器条件是扫描  ComponentScan.Filter[] includeFilters() default {};    // 当不满足过滤器条件是扫描  ComponentScan.Filter[] excludeFilters() default {};  // 是否提早初始化  boolean lazyInit() default false;  // 定义过滤器   @Retention(RetentionPolicy.RUNTIME)  @Target({})  public @interface Filter {    FilterType type() default FilterType.ANNOTATION;    @AliasFor("classes")    Class<?>[] value() default {};    @AliasFor("value")    Class<?>[] classes() default {};    String[] pattern() default {};  }}

@PostConstruct 、@PreDestroy

@PostConstruct ===  init-method@PreDestroy === destroy-method

@Resources
提供一个bean的name字段注入,不能够用在构造函数上

 @Resource(name = "spellChecker") private SpellChecker spellChecker;

基于java的配置

  • 应用这种配置,能够不必xml,应用一个类,作为bean的起源
  • 带有 @Configuration 的注解类示意这个类能够应用 Spring IoC 容器作为 bean 定义的起源。
  • @Bean 注解通知 Spring,一个带有 @Bean 的注解办法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean
  • @Bean注解也能够配置初始化办法和销毁办法
// 1 创立一个配置类 @Configurationpublic class HelloWorldConfig {  @Bean(initMethod = "init", destroyMethod = "cleanup" )  public HelloWorld helloWorld(){    return new HelloWorld();  }}// 2 应用public static void main(String[] args) {   ApplicationContext ctx =    new AnnotationConfigApplicationContext(HelloWorldConfig.class);    HelloWorld helloWorld = ctx.getBean(HelloWorld.class);   helloWorld.setMessage("Hello World!");   helloWorld.getMessage();}// 3 还能够加载 refresh 多个配置类 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();ctx.register(HelloWorldConfig.class);ctx.refresh();HelloWorld helloWorld = ctx.getBean(HelloWorld.class);helloWorld.setMessage("Hello World!");helloWorld.getMessage();

@Import 注解:
@import 注解容许从另一个配置类中加载 @Bean 定义

@Configurationpublic class ConfigA {   @Bean   public A a() {      return new A();    }}// 这个配置能够应用上一个配置的信息 @Configuration@Import(ConfigA.class)public class ConfigB {   @Bean   public B a() {      return new A();    }}

spring事件处理

通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被公布到 ApplicationContext 上,那个 bean 会被告诉

// 1 实现 ApplicationListener 接口 public class CStartEventHandler implements ApplicationListener<ContextStartedEvent> {  @Override  public void onApplicationEvent(ContextStartedEvent event) {    System.out.println("ContextStartedEvent Received");  }}// 2 xml中配置 <bean id="cStartEventHandler" class="com.starbugs.wehcat.demos.CStartEventHandler" /><bean id="cStopEventHandler" class="com.starbugs.wehcat.demos.CStopEventHandler" />

自定义事件

// 1 新建 事件类public class CustomEvent extends ApplicationEvent {  public CustomEvent(Object source) {    super(source);  }  @Override  public String toString() {    return "CustomEvent{}";  }}// 2 新建 handler类public class CustomEventHandler implements ApplicationListener<CustomEvent> {  @Override  public void onApplicationEvent(CustomEvent event) {    System.out.println(event.toString());  }}// 3 新建publisher公布类public class CustomEventPublisher implements ApplicationEventPublisherAware {    private ApplicationEventPublisher publisher;    @Override    public void setApplicationEventPublisher(ApplicationEventPublisher publisher){      this.publisher = publisher;    }    public void publish() {      CustomEvent ce = new CustomEvent(this);      publisher.publishEvent(ce);    }}// 4 配置类到xml里// 5. 应用 CustomEventPublisher cvp = (CustomEventPublisher) context.getBean("customEventPublisher");cvp.publish();cvp.publish();

AOP

传送门

Spring mvc

传送门

参考资料
  • Spring+Spring MVC+MyBatis从零开始学(吴为胜 杨章伟)
  • 深入浅出Spring Boot 2.x-杨开振