注:此笔记为尚硅谷Spring注解驱动教程(雷丰阳源码级讲解)学习笔记,并同时参考[https://blog.csdn.net/xjhqre/article/details/123264069]博主文章,其中包含个人的笔记和理解,仅做学习笔记之用。
1、实例化一个Bean--也就是我们常说的new;
2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;
3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值
4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);
5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;
注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。
9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;
10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
bean从创建 ----> 初始化 -----> 销毁的过程
容器管理bean的生命周期
我们可以自定义初始化和销毁方法
构造(对象创建)
单实例:在容器启动的时候创建对象
多实例:在每次获取的时候创建对象
初始化
销毁
初始化和销毁方法:
先查看源码,再调用API
1.通过@Bean注解指定init-method和destroy-method
2.实现InitializingBean和DisposableBean接口,重写里面的destroy和afterPropertiesSet方法
3.使用@PostConstruct和@PreDestroy注解
4.BeanPostProcessor:接口,bean后置处理器,在bean初始化前后进行一些处理
初始化和销毁方法:
先查看源码,再调用API
1.通过@Bean注解指定init-method和destroy-method
2.实现InitializingBean和DisposableBean接口,重写里面的destroy和afterPropertiesSet方法
3.使用@PostConstruct和@PreDestroy注解
4.BeanPostProcessor:接口,bean后置处理器,在bean初始化前后进行一些处理
最原始在bean.xml中自定义的bean初始化和销毁方法
通过@Bean注解指定init-method和destroy-method
@Component
public class Car {
public Car(){
System.out.println("car constructor...");
}
public void init(){
System.out.println("car ... init...");
}
public void detory(){
System.out.println("car ... detory...");
}
}
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig5 {
@Bean(initMethod = "init", destroyMethod = "destroy")
public Car car() {
return new Car();
}
}
@Test
public void test6() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig5.class);
applicationContext.close();
}
通过让bean实现InitializingBean
接口来定义初始化逻辑
通过让bean实现DisposableBean
接口来定义销毁逻辑
在InitializingBean
中有一个方法afterPropertiesSet
,该方法在bean创建并赋值后调用
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("创建猫对象");
}
@Override
public void destroy() throws Exception {
System.out.println("销毁猫对象");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化猫对象");
}
}
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre.pojo")
public class MainConfig6 {
@Bean
public Cat cat() {
return new Cat();
}
}
@Test
public void test7() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig6.class);
applicationContext.close();
}
@PostConstruct:在bean创建完成并且属性赋值完成后进行初始化
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}
@PreDestroy:在容器销毁bean之前执行
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}
public class Dog {
public Dog() {
System.out.println("创建狗对象");
}
@PostConstruct
public void init() {
System.out.println("初始化狗对象");
}
@PreDestroy
public void destroy() {
System.out.println("销毁狗对象");
}
}
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre.pojo")
public class MainConfig7 {
@Bean
public Dog dog() {
return new Dog();
}
}
@Test
public void test8() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig7.class);
applicationContext.close();
}
BeanPostProcessor接口中有两个方法:
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* @param bean 刚创建好的实例
* @param beanName 实例的id
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("id为" + beanName + "的bean对象:" + bean + "执行postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("id为" + beanName + "的bean对象:" + bean + "postProcessAfterInitialization");
return bean;
}
}
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre.pojo")
public class MainConfig7 {
@Bean
public Dog dog() {
return new Dog();
}
}
@Test
public void test8() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig7.class);
applicationContext.close();
}
执行populateBean(beanName, mbd, instanceWrapper);
给bean进行属性赋值
开始initializeBean
初始化bean
先执行applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
,遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitialization
然后执行invokeInitMethods(beanName, wrappedBean, mbd);
执行自定义初始化
最后执行applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
使用**@Value赋值**,赋值方法:
public class Person {
@Value("kdz")
private String name;
@Value("#{20-2}")
private Integer age;
@Value("${person.email}")
private String email;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
@PropertySource(value = {"classpath:/person.properties"})
@ComponentScan(value = "com.kdz.pojo")
public class MainConfig9 {
@Bean
public Person person() {
return new Person();
}
}
@Test
public void test9() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig9.class);
Person person = applicationContext.getBean(Person.class);
System.out.println(person);
// 也可以使用环境变量取出
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("person.email");
System.out.println(property);
}
Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值
给属性自动注入值
默认优先按照类型去容器中找对应的组件,找到就赋值。
如果该类型的组件有多个,再将属性名作为组件的id去容器中查找
@AutoWired可以在构造器、参数、方法、属性上标注
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
@AutoWired可以给required属性赋值,类型为boolean,默认为true。表示该组件是否必须。
当required=false时,表示这个组件不是必须的,在调用getBean方法时如果找不到对应的组件时不会直接抛出异常,而是返回一个null
标注在方法上,Spring容器创建当前对象,就会调用方法,完成赋值
方法使用的参数,自定义类型的值从IOC容器中获取
@Autowired
public void setCar(Car car) {
this.car = car;
}
默认加在IOC容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
我们可以在有参构造函数上标注@Autowired,让IOC容器创建组件时调用该类的有参构造方法
如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略
@Autowired
public Boss(Car car) {
this.car = car;
}
public Boss(@Autowired Car car) {
this.car = car;
}
@Bean标注的方法创建对象的时候,方法参数的值从容器中获取
// 参数car会从IOC容器中获取,可以省略Car前的@Autowired
@Bean
public Color color(Car car) {
return new Color();
}
使用@Qualifier指定需要装配的组件的id,而不是用属性名
@Qualifier需要和@AutoWired一起使用
该注解可以让Spring进行自动装配的时候,默认使用首选的bean
也可以继续使用@Qualifier指定需要装配的bean的名字
无论是否有依赖注入,@Primary标注的bean都会被容器创建
/**
* 指示当多个候选者有资格自动装配单值依赖项时,应优先考虑 bean。如果候选中恰好存在一个“主”bean,则它将是自动装配的值。
* 这个注解在语义上等同于 Spring XML 中元素的primary属性。
* 可用于任何直接或间接使用@Component注释的类或使用Bean注释的方法。
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Primary {
}
可以和@Autowired一样实现自动装配功能,但默认是按组件名称进行装配
可以给属性name赋值,自定义组件名称
@Resource(name="bookDao2")
private BookDao bookDao;
但该注解没有支持@Primary、@Autowired(require=false)的功能
需要导入javax.inject依赖
和@Autowired功能一样,但没有required=false的功能
@Inject
private BookDao bookDao;
默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
自定义组件想要使用Spring容器底层的一些组件(ApplicationContext、BeanFactory),需要自定义组件实现xxxAware接口
在创建对象的时候,会调用接口规定的方法,注入相关的组件
Aware的子接口
@Component
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
// 获取IOC容器
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("IOC容器:" + applicationContext);
this .applicationContext = applicationContext;
}
// 获取当前bean对象的名称
@Override
public void setBeanName(String name) {
System.out.println("当前bean的名字:" + name);
}
// 解析String语句中的占位符 $ 或 #
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String s = resolver.resolveStringValue("你好${os.name}, 我是#{20*18}");
System.out.println("解析的字符串:" + s);
}
}
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre.pojo")
public class MainConfig10 {
}
@Test
public void test10() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig9.class);
}
每一个xxxAware都有一个对应的xxxAwareProcessor(后置处理器),用来处理相关逻辑
以ApplicationContextAwareProcessor为例
遍历所有的processor
利用后置处理器processor判断Aware的类型,注入对应的ApplicationContext
@Profile注解是Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能
例子:在不同环境下使用不同的数据源,在开发环境下使用A数据源,在测试环境下使用B数据源,在生产环境下使用C数据源
需要导入c3p0依赖C3p0:JDBC DataSources/Resource Pools和数据库驱动依赖MySQL Connector
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=123456
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定的话在任何环境下都能注册这个组件
加了环境标识的bean,只有在这个环境被激活的时候才能注册到容器中,默认环境为default,即@Profile("default")
@Profile注解写在类上时,只有当指定的环境被激活时,整个类才会被注册
@Configuration // 告诉Spring这是一个配置类
@PropertySource("classpath:/db.properties")
public class MainConfig11 implements EmbeddedValueResolverAware {
@Value("${jdbc.username}")
private String user;
private StringValueResolver valueResolver;
// 测试数据库
@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${jdbc.password}")String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true");
String driverClass = valueResolver.resolveStringValue("${jdbc.driverClassName}");
dataSource.setDriverClass(driverClass);
return dataSource;
}
// 开发数据库
@Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${jdbc.password}")String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/atguigu?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true");
String driverClass = valueResolver.resolveStringValue("${jdbc.driverClassName}");
dataSource.setDriverClass(driverClass);
return dataSource;
}
// 生产数据库
@Profile("prod")
@Bean("prodDataSource")
public DataSource dataSourceProd(@Value("${jdbc.password}")String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true");
String driverClass = valueResolver.resolveStringValue("${jdbc.driverClassName}");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
}
}
激活运行环境方式:
1.命令行方式,在虚拟机选项中输入:-Dspring.profiles.active=test
2.使用代码方式:
1)使用无参构造器创建application,AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
2)设置需要激活的环境:applicationContext.getEnvironment().setActiveProfiles("test", "dev");
3)注册主配置类:applicationContext.register(MainConfig11.class);
4)刷新容器:applicationContext.refresh();
测试中使用代码方式激活环境
@Test
public void test11() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.getEnvironment().setActiveProfiles("test", "dev");
applicationContext.register(MainConfig11.class);
applicationContext.refresh();
String[] dataSources = applicationContext.getBeanNamesForType(DataSource.class);
for (String dataSource : dataSources) {
System.out.println(dataSource);
}
}
只能使用无参构造的方式创建容器的原因
Spring注解驱动开发(二)的学习笔记到此完结,笔者归纳、创作不易,大佬们给个3连再起飞吧