写在前面
写在前面:最近在编译最新版spring源码的时候,踩了写小坑。先来看一下最新的官方文档:https://github.com/spring-projects/spring-framework 在build from source里面声明了最新的编译过程:https://github.com/spring-projects/spring-framework/wiki/Build-from-Source 由于最近spring做了一些升级,想要在本地构建spring源码,需要使用jdk17。 此外,最近gradle的语法也因为版本的原因,改动很大。想要在spring源码里面对gradle进行一些定制化配置的时候,可以参照gradle官网。 https://gradle.org/releases/
1.注解
1.1总览
- @Bean 容器中注册组件
- @Primary 同类组件如果有多个,标注主组件
- @DependsOn 组件之间声明依赖关系
- @Lazy 组件懒加载(最后使用的时候才会去创建)
- @Scope 声明组件的作用范围
- @Configuration 声明这是一个配置类,替换xml
- @Component @Controller @Service @Repository
- @Indexed 加速注解,所有标注了@Indexed的组件,直接会启动快速加载
- @Order 数字越小,优先级越高,越先工作
- @ComponentScan 包扫描
- @Conditional 条件注入
- @Import 导入第三方jar包中的组件,或者定制批量导入组件逻辑
- @ImportResource 导入以前的xml配置文件,让其生效
- @Profile 基于多环境激活
- @PropertySource 外部properties配置文件和javaBean进行绑定,结合ConfigurationProperties
- @PropertySources @PropertySource的组合注解
- @Autowired 自动装配
- @Qualifier 精准指定
- @Resource jsr250规范的jdk自带注解
- @Value 取值,计算机环境变量,jvm系统 xxx
- @Lookup 单例组件依赖非单例组件,非单例组件获取需要使用方法
1.2.案例
com.yhd.annotation.AnnoMainTest
2.组件与SPI扩展点机制
2.1总览
- 基础接口
Resource+ResourceLoader 将来自各种不同渠道的配置文件等进行一层抽象封装,让开发人员不必关注于底层的细节实现,通过资源加载器加载资源。
BeanFactory ioc容器顶层接口 ✅
BeanDefinition 从resource解析出bean的定义信息 ✅
BeanDefinitionReader bean定义信息读取器 ,从resource 读取解析 BeanDefinition
BeanDefinitionRegistry bean定义信息的注册中心 解析出来的BeanDefinition信息会被注册到这里
ApplicationContext ioc核心接口 ✅
Aware 实现xxxAware接口是为了能够获取到xxx相关的一些属性 ✅
BeanNameAware
BeanFactoryAware
ApplicationEventPublisherAware
ApplicationContextAware
ApplicationStartupAware
BeanClassLoaderAware
ImportAware
EnvironmentAware - 生命周期-后置处理器
BeanFactoryPostProcessor ✅
BeanDefinitionRegistryPostProcessor ✅
InitializingBean ✅
DisposableBean ✅
BeanPostProcessor ✅
SmartInitializingSingleton ✅ - 监听器
ApplicationListener ✅2.2案例
�com.yhd.annotation.AnnoMainTest
补充:spring源码地址:https://gitee.com/yin_huidong/spring.git
�此源码main分支为手动翻译好的中文注释版spring源码。
一,组件注册
1.Configuration&Bean
@Configuration
注解标识的类标识这是一个Spring的配置类。
@Bean
注解:给容器中注册一个bean,id默认是方法名作为id。
- value:指定id名
- initMethod:指定初始化方法
- destoryMethod:指定销毁方法
1.1 SpringConfig
@Configuration
public class SpringConfig {
@Bean(value = "person",initMethod = "init",destroyMethod = "destroy")
public Person person(){
return new Person(1,"二十");
}
}
1.2 Person
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private Integer id;
private String name ;
public void init(){
System.out.println("init()...");
}
public void destroy(){
System.out.println("destroy()...");
}
}
1.3 Test
public class TestA {
ApplicationContext ioc =
new AnnotationConfigApplicationContext(SpringConfig.class);
@Test
public void testBean(){
Person person = ioc.getBean("person", Person.class);
System.out.println("person = " + person);
}
}
2.RunWith&ContextConfiguration
@RunWith
:Spring整合Junit4。
@ContextConfiguration
:指定配置类。
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {
@Autowired
private Person person;
@Test
public void testBean(){
System.out.println("person = " + person);
}
}
3.ComponentScan
@ComponentScan(“com.yhd”) 组件扫描
- value:指定扫描的包
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)}
只包含哪些包(需要指明:useDefaultFilters = false)
type
type:FilterType.ANNOTATION:按照注解过滤 type=FilterType.ASSIGNABLE_TYPE:按照类型过滤 type=ASPECTJ,切面 type=REGEX,正则 type=CUSTOM,定制(需要实现TypeFilter接口)
excludeFilters:指明排除哪些包不被扫描
3.1 SpringConfig
@Configuration
@ComponentScan(value = "com.yhd",
includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE)
}, excludeFilters = {}, useDefaultFilters = true)
public class SpringConfig {
@Bean(value = "person", initMethod = "init", destroyMethod = "destroy")
public Person person() {
return new Person(1, "二十");
}
}
3.2 Test
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {
@Autowired
private ApplicationContext ioc;
@Test
public void test(){
//查看容器中bean的名字
String[] names = ioc.getBeanDefinitionNames();
for (String name : names) {
System.out.println("name = " + name);
}
}
}
4.Scope
作用:设置组件作用域。
@Scope 默认是单例的
点击进入该注解,在此点击进入文档注释中的ConfigurableBeanFactory
可以看到该注解的几个取值:
- singleton 单例
- prototype 多例
- request 同一次请求
- session 同一个session作用域
在scope=singleton时,对象在容器已创建立即加入容器。
在scope=prototype时,对象每次调用的时候都会添加到容器。
4.1 SpringConfig
@Configuration
public class SpringConfig {
@Bean(value = "person", initMethod = "init", destroyMethod = "destroy")
@Scope("prototype")
public Person person() {
return new Person(1, "二十");
}
}
4.2 Test
@Test
public void test(){
Person p1 = ioc.getBean("person", Person.class);
Person p2 = ioc.getBean("person", Person.class);
System.out.println(p1==p2?"true":"false");//false
}
5.Lazy
@Lazy
懒加载
单实例bean,默认在容器创建时候添加对象。
懒加载:容器启动不创建对象,第一次获取/使用时在创建对象并初始化。
5.1 SpringConfig
@Configuration
public class SpringConfig {
@Lazy
@Bean(value = "person", initMethod = "init", destroyMethod = "destroy")
public Person person() {
System.out.println("person已经加载到容器中!");
return new Person(1, "二十");
}
}
5.2 Test
@Test
public void test(){
Person person = ioc.getBean("person", Person.class);
}
对比加上这个注解前后,容器中单实例bean的变化:
6.Conditional
@Conditional
点击进入Class<? extends Condition>[] value();
发现这个注解里面的value属性需要传入一个Condition数组,
点击进入Conditionboolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
因此,可以实现Condition接口,来使用该注解
当该注解加在方法上,标识满足条件时,该方法会执行
当该注解加在类上时,表示满足条件时,该类里面的所有方法才会执行,否则一个都不执行。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition}s that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
@FunctionalInterface
public interface Condition {
/**
* Determine if the condition matches.
* @param context the condition context
* @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
* or {@link org.springframework.core.type.MethodMetadata method} being checked
* @return {@code true} if the condition matches and the component can be registered,
* or {@code false} to veto the annotated component's registration
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
6.1 案例:根据当前系统环境将对应的bean加入到容器
6.1.1 SpringConfig
@Configuration
public class SpringConfig {
@Bean("person")
@Conditional({Mac.class})
public Person person() {
System.out.println("person已经加载到容器中!");
return new Person(1, "二十");
}
@Bean("person2")
@Conditional({Linux.class})
public Person person2() {
System.out.println("person已经加载到容器中!");
return new Person(1, "二十");
}
}
6.1.2 条件类
public class Mac implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Mac");
}
}
public class Linux implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("linux");
}
}
6.1.3 Test
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {
@Autowired
private ApplicationContext ioc;
@Test
public void test(){
Person person = ioc.getBean("person", Person.class);
Person person2 = ioc.getBean("person2", Person.class);
System.out.println(person);
System.out.println(person2);
}
}
7.Import
用来快速导入一个bean,默认类名是该类的全限定类名,单例。
7.1 SpringConfig
@Configuration
@Import({Dog.class})
public class SpringConfig {
@Bean("person")
@Conditional({Mac.class})
public Person person() {
System.out.println("person已经加载到容器中!");
return new Person(1, "二十");
}
@Bean("person2")
@Conditional({Linux.class})
public Person person2() {
System.out.println("person已经加载到容器中!");
return new Person(1, "二十");
}
}
7.2 Test
@Test
public void test(){
Dog dog = ioc.getBean(Dog.class);
assert dog!=null;
dog.setId(1);
dog.setName("asdfg");
System.out.println("dog = " + dog);
}
8.ImportSelector
public @interface Import {
/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();
}
点击查看@Import
注解可以发现:
里面还可以传入{@link Configuration
}, {@link ImportSelector
}, {@link ImportBeanDefinitionRegistrar
}
importSelector其实就是一个接口,我们需要通过实现它来传入impot注解。
8.1 MyImportSelector
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.yhd.pojo.Person","com.yhd.pojo.Dog"};
}
}
8.2 SpringConfig
@Configuration
@Import({MyImportSelector.class})
public class SpringConfig {
}
8.3 Test
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {
@Autowired
private ApplicationContext ioc;
@Test
public void test(){
String[] names = ioc.getBeanDefinitionNames();
for (String name : names) System.out.println(name);
}
}
9.ImportBeanDefinitionRegistrar
public interface ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
9.1 MyImportBeanDefinitionRegistrar
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
registry.registerBeanDefinition("dog", new RootBeanDefinition("com.yhd.pojo.Dog"));
}
}
9.2 MyImportBeanDefinitionRegistrar
@Configuration
@Import({MyImportBeanDefinitionRegistrar.class})
public class SpringConfig {
}
9.3 Test
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {
@Autowired
private ApplicationContext ioc;
@Test
public void test(){
String[] names = ioc.getBeanDefinitionNames();
for (String name : names) System.out.println(name);
}
}
10.FactoryBean
spring的工厂模式造Bean
- 如果传入id获取到的是工厂造的bean
- 如果传入的是&id获取到的是工厂本身
10.1 PersonFactory
public class PersonFactory implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
return new Person(1,"FactoryBean");
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
10.2 SpringConfig
@Configuration
public class SpringConfig {
@Bean
public PersonFactory personFactory(){
return new PersonFactory();
}
}
10.3 Test
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {
@Autowired
private ApplicationContext ioc;
@Test
public void test(){
PersonFactory personFactory = (PersonFactory) ioc.getBean("&personFactory");
System.out.println("personFactory = " + personFactory);
Person person = (Person) ioc.getBean("personFactory");
System.out.println("person = " + person);
/*personFactory = com.yhd.factory.PersonFactory@6e20b53a
person = Person(id=1, name=FactoryBean)*/
}
}
10.4 原理
public interface BeanFactory {
//more能获取到工厂bean的方法就是在id前加上前缀&
String FACTORY_BEAN_PREFIX = "&";
11.LookUp
如果有一个类C,需要用到类B,如果使用@Autowired注解注入B,那么B每次调用都是同一个对象,即使B不是单例的,现在我希望每次调用B都是不一样的,那么实现方案有2个:
11.1 每次从容器中获取B
@Component
@Scope(scopeName= ConfigurableBeanFactory.SCOPE_PROTOTYPE) //原型 也就是非单例
public class B {
public void sayHi(){
System.out.println("hi");
}
}
@Component
public class C implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void hello(){
B b = (B)applicationContext.getBean("b");
b.sayHi();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
}
11.2 使用@lookup注解
@Component
@Scope(scopeName= ConfigurableBeanFactory.SCOPE_PROTOTYPE) //原型 也就是非单例
public class B {
public void sayHi(){
System.out.println("hi");
}
}
@Component
public abstract class C {
public void hello(){
B b = getB();
b.sayHi();
}
@Lookup
public abstract B getB(); //一般都是抽象方法
}
二,生命周期
1.@Bean指定初始化和销毁方法
bean的生命周期:创建,初始化,使用,销毁。
容器管理bean的生命周期:我们可以自定义初始化和销毁方法,容器在bean进行到当前生命周期的时候,来调用我们自定义的初始化和销毁方法。
单例模式:先是执行对象的无参构造,赋值后,执行初始化方法,在创建容器的时候加入对象,在容器关闭时,执行销毁方法。
多例模式:先创建容器,每次调用对象时,调用无参构造方法,赋值后,执行初始化方法。对象的销毁由java垃圾回收机制回收。
1.1 单例
@Data
@AllArgsConstructor
public class Person {
private Integer id;
private String name ;
public void init(){
System.out.println("init()...");
}
public void destroy(){
System.out.println("destroy()...");
}
public Person(){
System.out.println("constructor()....");
}
}
@Configuration
public class SpringConfig {
@Bean(value = "person",initMethod = "init",destroyMethod = "destroy")
public Person person(){
return new Person();
}
}
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {
@Autowired
private ApplicationContext ioc;
//constructor
@Test
public void test(){
Person person = ioc.getBean("person", Person.class);
System.out.println("person = " + person);
}
}
1.2 多例
@Configuration
public class SpringConfig {
@Scope(value = "prototype")
@Bean(value = "person",initMethod = "init",destroyMethod = "destroy")
public Person person(){
return new Person();
}
}
2.实现接口完成对象的初始化和销毁
InitializingBean, DisposableBean
@Data
@AllArgsConstructor
public class Person implements InitializingBean, DisposableBean {
private Integer id;
private String name ;
public Person(){
System.out.println("constructor()....");
}
@Override
public void destroy() throws Exception {
System.out.println("destroy()....");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet()....");
}
}
@Configuration
public class SpringConfig {
@Bean(value = "person")
public Person person(){
return new Person();
}
}
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {
@Autowired
private ApplicationContext ioc;
//constructor
@Test
public void test(){
System.out.println("容器创建成功!");
Person person = ioc.getBean("person", Person.class);
System.out.println("person = " + person);
}
}
3.BeanPostProcessor
bean的后置处理器,在bean的初始化前后做一些处理工作,需要加入到容器中。
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化前");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化后");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
@Configuration
@Import(MyBeanPostProcessor.class)
public class SpringConfig {
@Bean(value = "person")
public Person person(){
return new Person();
}
}
4.BeanFactoryPostProcessor
bean工厂的后置处理器,可以在spring解析完配置文件,创建对象之前,对bean的定义信息进行修改。
@Component
public class Computer {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("=======BeanFactoryPostProcessor=========");
BeanDefinition person = beanFactory.getBeanDefinition("computer");
person.setScope(BeanDefinition.SCOPE_PROTOTYPE);
System.out.println("=======BeanFactoryPostProcessor=========");
}
}
/**
* 默认Computer对象是单实例的,
* 通过beanFactory的后置处理器处理之后,变成原型的
* <p>
* 执行时机:打断点
*/
private static void testBeanFactoryPostProcessor() {
/*ioc容器创建的12个核心方法里面*/
Computer computer = ioc.getBean(Computer.class);
Computer computer2 = ioc.getBean(Computer.class);
System.out.println(computer == computer2);
}
5.BeanDefinitionRegistryPostProcessor
他是bean工厂后置处理器的子类
插播一条广告:BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor之间的关系?
BeanDefinitionRegistryPostProcessor 是 BeanFactoryPostProcessor的子类和扩展
它里面 搞了一个新的方法 postProcessBeanDefinitionRegistry ,可以往容器中注册更多的bd信息。
扩展点:
①BeanFactoryPostProcessor 对bd信息进行修改
②postProcessBeanDefinitionRegistry 添加更多的bd信息
public class RegistryBean {
}
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
/*这个是她的父类里面的方法 对 bd信息进行修改*/
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
/*这个是给他本身的方法,可以添加额外的 bd 信息*/
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(RegistryBean.class);
//beanDefinition.setScope();
registry.registerBeanDefinition("registryBean",beanDefinition);
}
}
private static void testBeanDefinitionRegistryPostProcessor() {
RegistryBean registryBean = ioc.getBean("registryBean", RegistryBean.class);
System.out.println("registryBean = " + registryBean);
}
6.SmartInitializingSingleton
在所有的单实例bean 通过getBean方法完成初始化之后,就会去查找这个类型的SmartInitializingSingleton 的 组件 ,执行里面的 方法.
@Component
public class MySmartInitializingSingleton implements SmartInitializingSingleton {
@Override
public void afterSingletonsInstantiated() {
System.out.println("断点调试。。。。");
}
}
7.Lifecycle && SmartLifecycle
容器创建完成之后的回调,相当于
传递的参数autoStartUpOnly是干嘛的?
表示只启动SmartLifeCycle生命周期对象,并且启动的对象autoStartUpOnly必须是true,
不会启动普通的生命周期对象,
false的时候,会启动全部的生命周期对象。
public class DemoLifeCycle implements Lifecycle {
private boolean running =false;
@Override
public void start() {
this.running=true;
System.out.println("demo one start!");
}
@Override
public void stop() {
this.running=false;
System.out.println("demo one stop!");
}
@Override
public boolean isRunning() {
return running;
}
}
public class DemoSmartLifeCycle implements SmartLifecycle {
private boolean running = false;
@Override
public void start() {
this.running=true;
System.out.println("demo two start!");
}
@Override
public void stop() {
this.running=false;
System.out.println("demo two stop!");
}
@Override
public boolean isRunning() {
return running;
}
}
三,属性赋值
1.Value
@Value
注解可以给属性赋值,支持SPEL表达式,${},,基本数值。
@Data
@AllArgsConstructor
public class Person {
@Value("1")
private Integer id;
@Value("二十")
private String name ;
public Person(){
System.out.println("constructor()....");
}
}
2.PropertySource
@PropertySource
加载外部属性文件
支持一次写多个,标注在配置类上。
person.name=二十
person.age=20
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
@Value("${person.age}")
private Integer id;
@Value("${person.name}")
private String name ;
}
@Configuration
@PropertySource("classpath:person.properties")
public class SpringConfig {
@Bean(value = "person")
public Person person(){
return new Person();
}
}
四,自动装配
Spring利用依赖注入DI,完成对IOC容器中各个组件的依赖关系赋值。
1.@Autowired && @Qualifier && @Primary
- 默认优先按照类型去容器中找对应的组件,
ioc.getBean(BookDao.class);
- 如果找到多个相同类型的组件,再讲属性的名称作为组件的id去容器中查找
ioc.getBean("bookDao");
@Qualifier("bookDao")
,使用@Qualifier
指定要装配的组件的id,而不是使用属性名- 自动装配默认一定要将属性赋值好,没有就会报错.可以使用
@Autowired(required=false)
@Primary
让Spring进行自动装配的时候,默认使用首选bean,也可以继续使用@Qualifier
指定需要装配的bean的id
2.@Resource && @Inject
spring还支持使用@Resource
(jsr250)和@inject
(jsr330)【java规范的注解】
@Resource
:可以和@Autowired
一样实现自动装配,默认按照组件名称进行装配.不支持@Primary
和required=false
@Inject
:需要导入javax.inject包,没有required=false
3.方法,构造器位置的自动注入
@Autowired
:构造器,参数,方法,属性,都是从容器中获取参数组件的值。
- 标注在方法上:
@Bean
+方法参数,参数从容器中获取,默认不写@Autowired
效果是一样的,都能自动装配 - 标在构造器上:如果组件只有一个构造器,这个有参构造器的
@Autowired
可以省略 - 放在参数位置
3.1 构造器注入
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private Integer id;
private String name ;
}
@Component
@Data
@NoArgsConstructor
public class Dog {
private Person person;
@Autowired
//此处注解可以省略
private Dog(Person person){
this.person=person;
}
}
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestA {
@Autowired
private ApplicationContext ioc;
//constructor
@Test
public void test(){
Dog dog = ioc.getBean("dog", Dog.class);
System.out.println("dog = " + dog);
}
}
3.2 set()注入
@Component
@Data
@NoArgsConstructor
public class Dog {
private Person person;
@Autowired
//此处的注解不能省略
public void setPerson(Person person){
this.person=person;
}
}
3.3 @Bean的方式
@Configuration
public class SpringConfig {
@Bean(value = "person")
public Person person() {
return new Person();
}
@Bean(value = "dog")
@Autowired
//此处的注解可以省略不写
public Dog dog(Person person) {
return new Dog(person);
}
}
4.Aware接口,自定义组件使用Spring底层的组件
自定义接口想要使用Spring
底层的一些组件(ApplicationContext,BeanFactory,xxx)
自定义接口实现xxxAware
,在创建对象的时候,会调用接口规定的方法注入相关组件,Aware吧Spring底层的一些接口注入到自定义的Bean
中
xxxAware
:功能:使用xxxProcessor
ApplicationContextAware-->ApplicationContextAwareProcessor
@Component
public class Dog implements ApplicationContextAware, BeanFactoryAware {
private ApplicationContext ioc;
private BeanFactory beanFactory;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//自此处打断点
this.ioc=applicationContext;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
//此处打断点
this.beanFactory=beanFactory;
}
}
5.@Profile根据环境装配
@Profile
:指定组件在那个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
- 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境
- 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
- 没有标识环境标识的bean在任何环境下都是加载的。
@Configuration
@PropertySource("classpath:jdbc.properties")
public class SpringConfig implements EmbeddedValueResolverAware {
@Value("${jdbc.username}")
private String name;
private StringValueResolver resolver;
private String driver;
@Profile("dev")
@Bean("dev")
public DataSource dataSource1(@Value("${jdbc.password}") String pwd){
DruidDataSource source = new DruidDataSource();
source.setName(name);
source.setPassword(pwd);
source.setDriverClassName(driver);
source.setUrl("jdbc:mysql:///test");
return source;
}
@Profile("pro")
@Bean("pro")
public DataSource dataSource2(@Value("${jdbc.password}") String pwd){
DruidDataSource source = new DruidDataSource();
source.setName(name);
source.setPassword(pwd);
source.setDriverClassName(driver);
source.setUrl("jdbc:mysql:///ssm");
return source;
}
@Profile("test")
@Bean("test")
public DataSource dataSource3(@Value("${jdbc.password}") String pwd){
DruidDataSource source = new DruidDataSource();
source.setName(name);
source.setPassword(pwd);
source.setDriverClassName(driver);
source.setUrl("jdbc:mysql:///zuoye");
return source;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver=resolver;
driver = resolver.resolveStringValue("${jdbc.driverClassName}");
}
}
激活环境:
- 使用命令行动态参数:在虚拟机参数位置加载:
-Dspring.profiles.active=test
- 代码方式激活
@Test
public void test() {
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext();
ioc.getEnvironment().setActiveProfiles("dev");
ioc.register(SpringConfig4.class);
ioc.refresh();
String[] names = ioc.getBeanNamesForType(DataSource.class);
for (String name : names) {
System.out.println(name);
}
}
五,事件驱动
1.ApplicationListener && ApplicationEvent
通过自定义不同类型的事件,使用不同的监听器监听不同类型的事件,做到jvm进程内的消息队列,事件驱动,解耦。
public class MyEvent extends ApplicationEvent {
String message;
public MyEvent(Object source) {
super(source);
}
public MyEvent(Object source,String message) {
super(source);
this.message=message;
}
public void print(){
System.out.println("发布了一个事件:"+message);
}
}
public class MyListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
event.print();
}
}
/**
* 测试spring 的 ioc 容器的事件发布
*/
private static void testPublishEvent() {
ApplicationContext ioc = new ClassPathXmlApplicationContext(CONFIG_LOCATION);
ioc.publishEvent(new MyEvent("", "这是我自定义的一个事件"));
}
2.@EventListener
对上面写法的一个优化,更加简洁,开发量更少,懒人必备神器。
private static void testEventListener() {
ioc.publishEvent(new ApplicationEvent("hello,spring") {
@Override
public Object getSource() {
return super.getSource();
}
});
}
@Component
public class MyEventListener {
@EventListener(classes = ApplicationEvent.class)
public void listener(ApplicationEvent event){
System.out.println("event = " + event);
}
}