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 负责创立并初始化所有的对象,即在配置文件中提到的 bean
XmlBeanFactory 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
@Autowired
private 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 创立一个配置类
@Configuration
public 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 定义
@Configuration
public 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- 杨开振