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的应用过程
- 读取配置信息
- 实例化bean
- 把bean实例放到spring容器中
- 应用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-杨开振