# Spring
# 摘要
简介spring中应该要了解的内容, 早期只会加入一些正在学习的内容.
# spring的事件机制
# 参考地址
spring @EventListener 事件与监听 (opens new window) spring事件通知机制详解 (opens new window) spring event的事件驱动模型的最佳实践@EventListener (opens new window)
# 作用
解耦业务
# spring组件扫描/组件加载
@Component注解的类被扫描时会加载成组件bean
a. 但是需要显式配置
组件扫描范围 b.组件扫描可以使用xml或者java config配置
- 组件扫描的方式
a. 使用
@ComponentScan注解配置类, 可以让spring扫描此配置类同包或者子包中所有的组件. b. 使用xml配置的spring context命名空间(<context:component-scan base-package="cn.jessex.xxx.xxxx">)组件扫描.
- 组件扫描注解
@ComponentScan的几种使用
a. 直接传值(value). b. 使用
@ComponentScan(basePackages = "xx.xxx"), 可以接收单个String或者String[]数组, 但此时是类型不安全的. c. 使用@ComponentScan(basePackageClasses = my.class), 可以接收class或class[]数组, 但重构代码同样会引发问题.只有以上三种方式可以配置此组件扫描注解, 但是仍然会存在一些问题, 因此, 可以写一些空标记接口(marker interface)用来进行扫描, 这样能保持代码重构友好.
- 组件(bean)的ID
a. spring为bean设置默认ID, 为类名
首字母小写. b. 给@ComponentScan传值设置ID c. 使用Java DI规范的@Named设置ID(Spring支持此注解替换@ComponentScan)
# Spring创建bean
使用Xml创建bean
过去Spring只提供了xml配置的方式, 但现在已经不建议使用此方式了, 因为对于维护没有自动化配置和JavaConfig方便.
此时bena类中不需要使用任何注册bean的注解,完全由spring读取xml然后去配置。
<!--使用xml的bean标签创建&装配bean--> <bean id="student001" class="org.example.Student"> <property name="id" value="1" /> <property name="name" value="jesse001" /> </bean> <bean id="student002" class="org.example.Student"> <property name="id" value="2" /> <property name="name" value="jesse002" /> </bean> <bean id="class1" class="org.example.Klass"> <property name="students"> <list> <ref bean="student001" /> <ref bean="student002" /> </list> </property> </bean> <bean id="school" class="org.example.School"> <property name="klass" ref="class1" /> <property name="student" ref="student002" /> </bean>验证类:
/** * 使用xml加载bean */ @Test public void test1() { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student001 = (Student) ctx.getBean("student001"); System.out.println("student001:" + student001); Student student002 = (Student) ctx.getBean("student002"); System.out.println("student002:" + student002); }xml配置自动scan,扫描注解创建bean
spring提供了多种支持注解创建bean的方式。
xml配置
<context:component-scan base-package="org.example" />,然后在需要创建的bean类中,使用注解即可创建bean当前bean。测试类:
/** * xml中配置自动创建 */ @Test public void test2() { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); School school = (School) ctx.getBean("school"); System.out.println("school:" + school); }类中使用@ComponentScan指定扫描bean的相关注解(功能同xml配置scan)
与之前不同的是,这里不能使用xml的applicationContext来加载了,因为需要加载java类的配置,因此使用
new AnnotationConfigApplicationContext(Config.class)。@ComponentScan默认会扫描该配置类所在包及其子包下的所有bean。Java配置类:
@ComponentScan public class BeanConfig { }测试类:
/** * 类中使用@ComponentScan指定扫描bean的相关注解(功能同xml配置scan) */ @Test public void test3() { ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanConfig.class); School school = (School) ctx.getBean("school"); System.out.println("school:" + school); }使用@Bean在Java类中显示配置
注解使用在Method上,通过方法的返回值创建bean,方法的入参会在spring容器中寻找存在的bean进行注入,方法名是bean的名字。
Java bean配置类:
@Configuration public class BeanConfig2 { @Bean public Student student() { Student s = new Student(); s.setId(999); s.setName("@beanConfig jesse999"); return s; } @Bean public Klass klass(Student student) { Klass klass = new Klass(); klass.setStudents(Collections.singletonList(student)); return klass; } @Bean public School school(Klass klass, Student student) { School school = new School(); school.setKlass(klass); school.setStudent(student); return school; } }测试类:
@Test public void test4() { ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanConfig2.class); School school = (School) ctx.getBean("school"); System.out.println("school:" + school); }使用@Import
@Import指定要导入的组件类,在spring容器中的id为全类名。
Java配置类:
@Import(Student.class) public class BeanConfig { }测试类:
@Test public void test5() { ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanConfig.class); System.out.println(" context.getBeanDefinitionNames() ===>> " + String.join(",", ctx.getBeanDefinitionNames())); Student student = (Student) ctx.getBean("org.example.Student"); System.out.println("student:" + student); }使用ImportSelector配合@Import实现
Java配置类:
public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"org.example.Student"}; } }测试类:
@Test public void test6() { ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanConfig.class); System.out.println(" context.getBeanDefinitionNames() ===>> " + String.join(",", ctx.getBeanDefinitionNames())); Student student = (Student) ctx.getBean("org.example.Student"); System.out.println("student:" + student); }ImportBeanDefinitionRegistrar接口,配合@Import实现
Java配置类:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry); // 指定Bean类 RootBeanDefinition beanDefinition = new RootBeanDefinition(Student.class); // 注册一个Bean,并指定Bean名 registry.registerBeanDefinition("s001", beanDefinition); } }测试类:
@Test public void test7() { ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanConfig.class); System.out.println(" context.getBeanDefinitionNames() ===>> " + String.join(",", ctx.getBeanDefinitionNames())); Student student = (Student) ctx.getBean("s001"); System.out.println("s001:" + student); }手动注入Bean容器,有些场景下需要代码动态注入,以上方式都不适用。这时就需要创建 对象手动注入。通过DefaultListableBeanFactory注入。
# Spring的装配bean
使用Xml装配bean
请看Spring创建bean的xml配置。
使用
@Autowired自动装配a. 此注解可以用在类的任何地方, 都能帮助对应参数进行装配. b.
@Autowired(required = false)可以设置为false, 装配时没有发现对应bean不会抛出异常, 但需要程序检查NPException c. 当有多个bean满足装配时(产生歧义时), 会抛出异常. d. 可以使用Java规范提供的@Inject替换.使用
@Bean创建beana.
@Bean创建bean只受限于Java.
# 自动装配时引发歧义
@Primary@Qualifier
# @Autowired, @Qualifier,@Resource三者的区别与关系
@Autowired//默认按type注入 @Qualifier("cusInfoService")//一般作为@Autowired()的修饰用 @Resource(name="cusInfoService")//默认按name注入,可以通过name和type属性进行选择性注入
一般@Autowired和@Qualifier一起用,@Resource单独用。 当然没有冲突的话@Autowired也可以单独用
混合配置时的引用问题
- JavaConfig配置的引用方式
使用
@Importa. 一个config引入另一个时使用 b. 创建一个上级config, 同时引入多个子config(可以传入数组class[])
- JavaConfig中引入Xml配置
使用
@ImportResource
- Xml配置引用方式
使用
<import>标签导入
- Xml中引入JavaConfig
使用
<bean>导入JavaConfig配置类
# 给bean设置作用域
@Scopexml配置;的
<bean/>中的属性scope
# profile
使用
@profile注解定义profile使用xml定义profile
profile重构后也使用了
@conditional
# @ContextConfiguration加载配置作为上下文
# lazy-init延迟加载
spring默认在应用启动的时候就直接实例化对象(所有singleton对象)放到容器中,还是等到getBean的时候再去实例化。
使用方式:xml的bean声明加上lazy-init="true"属性(xml支持当前xml全局配置);注解使用@Lazy。
使用场景:
- 一定程度提高容器启动的性能(对于现在性能过剩的情况,一般不会因为这个)
- 对于不常用的bean设置延迟加载,偶尔使用到的时候再加载,节省了资源占用
# 原理剖析
# FactoryBean、BeanFactory剖析
spring有两种bean,一个是普通bean,一个是工厂bean(FactoryBean)。
FactoryBean可以生成某一个类型的bean实例,可以借助它完成自动bean的创建过程。
# FactoryBean剖析
代码:
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
# Spring Bean的生命周期
# 源码剖析
# PropertyPlaceholderConfigurer
# BeanFactory
- BeanFactory是Spring框架中IOC容器的顶层接口
- 用来定义基础功能和规范