此笔记为尚硅谷Spring注解驱动教程(雷丰阳源码级讲解)学习笔记
根据上面这张脑图,我把整个专栏分成了三个大的部分,分别是:容器、扩展原理以及Web。
容器作为整个专栏的第一大部分,内容包括:
扩展原理作为整个专栏的第二大部分,内容包括:
Web作为整个专栏的第三大部分,内容包括:
这部分,其实就是SpringMVC,在这个部分中,我们会重点来说异步请求。
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.13version>
dependency>
在 resources 目录下创建 application.xml 文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
beans>
public class Person {
private String name;
private Integer age;
// 省略构造方法,get/set方法,toString方法
}
<bean id="person" class="com.xjhqre.pojo.Person">
<property name="name" value="xjhqre"/>
<property name="age" value="18"/>
bean>
public class SpringTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
}
}
测试结果:
Person{name='xjhqre', age=18}
在方法上面加上@Bean注解后,Spring会以方法返回类型作为组件的类型,方法名作为组件的 id
当向@Bean中添加参数时,默认添加的第一个参数是value,强制给组件赋值id
// 配置类等同于xml配置文件
@Configuration // 告诉Spring这是一个配置类
public class MainConfig {
// 给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean("person")
public Person person01(){
return new Person("lisi", 20);
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
测试结果:
Person{name='lisi', age=20}
只要标注了@Controller、@Service、@Repository、@Component的,都会被扫描加入到容器里
**注意:**配置类自身也会被扫描到容器中,如果存在多个配置类,则多个配置类里的所有bean对象都会被扫描进容器中
<context:component-scan base-package="com.xjhqre"/>
在配置类上添加注解@ComponentScan
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig {
// ...
}
Filter[] excludeFilters() default {};
注解排除excludeFilters
的返回类型为Filter[]
Filter 的排除类型 FilterType
有一下几种:
下面以ANNOTATION
按注解排除来演示:
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
})
public class MainConfig {
// ...
}
扫描的时候只需要包含哪些组件,编写方式和excludeFilters
一样
注意要关闭默认的扫描过滤器
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
}, useDefaultFilters = false)
public class MainConfig {
// ...
}
在 jdk8 之后可以写多个@ComponentScan,写法如下:
@Configuration // 告诉Spring这是一个配置类
@ComponentScans(
value = {
@ComponentScan(value = "com.xjhqre", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
}, useDefaultFilters = false)
}
)
public class MainConfig {
// ...
}
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", includeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookController.class})
}, useDefaultFilters = false)
public class MainConfig {
// ...
}
public class MyTypeFilter implements TypeFilter {
/**
* @param metadataReader 读取到当前正在扫描类的信息
* @param metadataReaderFactory 可以获取其他任何类的信息
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当前正在扫描类的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当前类的资源信息(类的路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println(className);
return className.contains("er");
}
}
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
}, useDefaultFilters = false)
public class MainConfig {
// ...
}
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig2 {
@Bean("person")
public Person person() {
return new Person("张三", 25);
}
}
@Test
public void test2() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
Person bean = applicationContext.getBean(Person.class);
Person bean2 = applicationContext.getBean(Person.class);
System.out.println(bean.hashCode());
System.out.println(bean2.hashCode());
}
输出结果:
com.xjhqre.config.MainConfig2
com.xjhqre.config.MyTypeFilter
com.xjhqre.controller.BookController
com.xjhqre.pojo.Person
404214852
404214852
可以看到 bean 和 bean2 是同一个实例
ConfigurableBeanFactory.SCOPE_PROTOTYPE,
ConfigurableBeanFactory.SCOPE_SINGLETON,
org.springframework.web.context.WebApplicationContext.SCOPE_REQUEST,
org.springframework.web.context.WebApplicationContext.SCOPE_SESSION, value
后两个需要导入 Spring web 的jar包,且基本不用,只讨论前两个
在ConfigurableBeanFactory
中有两个常量:
// 单实例,默认值,在IOC容器启动时就会调用方法创建对象,以后每次获取直接从容器中拿(map.get())
String SCOPE_SINGLETON = "singleton";
// 多实例,在使用getBean()获取bean对象时才会创建
String SCOPE_PROTOTYPE = "prototype";
WebApplicationContext
中的两个常量
// 同一次请求创建一个实例
String SCOPE_REQUEST = "request";
// 同一个session创建一个实例
String SCOPE_SESSION = "session";
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig2 {
@Bean("person")
@Scope("prototype")
public Person person() {
return new Person("张三", 25);
}
}
@Test
public void test2() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
Person bean = applicationContext.getBean(Person.class);
Person bean2 = applicationContext.getBean(Person.class);
System.out.println(bean.hashCode());
System.out.println(bean2.hashCode());
}
测试结果:
com.xjhqre.config.MainConfig2
com.xjhqre.config.MyTypeFilter
com.xjhqre.controller.BookController
com.xjhqre.pojo.Person
961712517
1928931046
可以看到两个 bean 的哈希值不同
针对单实例设置,因为单实例是在容器启动时创建对象
懒加载就是不让单实例在容器启动时创建,在第一次获取Bean时创建对象并初始化。
只需要在方法上添加注解@Lazy
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig2 {
@Bean("person")
@Lazy
public Person person() {
return new Person("张三", 25);
}
}
按照一定的条件进行判断,满足条件给容器中注册Bean
@Conditional源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
// 传入一个Condition数组
Class<? extends Condition>[] value();
}
Condition接口源码:
@FunctionalInterface
public interface Condition {
/**
* ConditionContext:判断条件能使用的上下文环境
* AnnotatedTypeMetadata:注释信息
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
如果是linux系统则注册linus,如果是windows系统则注册bill Gates
编写windows条件,需要实现接口Condition,重写matches方法
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 判断是否是Windows系统
// 1. 获取到IOC使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 2. 获取类加载器
ClassLoader classLoader = context.getClassLoader();
// 3. 获取运行环境
Environment environment = context.getEnvironment();
// 4. 获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
// 容器中是否包含person,判断容器中的bean注册情况,也可以给容器中注册bean
boolean person = registry.containsBeanDefinition("person");
// 运行环境是否是Windows
String property = environment.getProperty("os.name");
assert property != null;
return property.contains("Windows");
}
}
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
assert property != null;
return property.contains("linux");
}
}
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig3 {
@Bean("bill")
@Conditional({WindowsCondition.class})
public Person person() {
return new Person("bill gates", 62);
}
@Bean("linus")
@Conditional({LinuxCondition.class})
public Person person2() {
return new Person("linus", 48);
}
}
@Test
public void test3() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
Map<String, Person> beansOfType = applicationContext.getBeansOfType(Person.class);
System.out.println(beansOfType);
}
测试结果:
{bill=Person{name='bill gates', age=62}}
当@Conditional配置在类上时,表示只有满足条件时,这个配置类配置的所有bean才会生效
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
@Import({Color.class, Red.class}) // 导入组件,id默认是组件的全类名
public class MainConfig3 {}
@Test
public void test4() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
测试结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig3
com.xjhqre.pojo.Color
com.xjhqre.pojo.Red
// 自定义逻辑,返回需要导入的组件
public class MyImportSelector implements ImportSelector {
// 返回值就是要导入到容器中的组件的全类名
// AnnotationMetadata:当前标注@Import注解类的所有信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.xjhqre.pojo.Blue", "com.xjhqre.pojo.Yellow"};
}
}
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
@Import({Color.class, Red.class, MyImportSelector.class}) // 导入组件,id默认是组件的全类名
public class MainConfig3 {}
@Test
public void test4() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
测试结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig3
com.xjhqre.pojo.Color
com.xjhqre.pojo.Red
com.xjhqre.pojo.Blue
com.xjhqre.pojo.Yellow
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:注册类
* 把所有需要添加到容器中的bean,调用BeanDefinitionRegistry.registerBeanDefinition手动注册
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 查询容器中是否存在red组件
boolean red = registry.containsBeanDefinition("com.xjhqre.pojo.Red");
if (red) {
// 指定bean的定义信息(bean的类型,bean的scope)
BeanDefinition beanDefinition = new RootBeanDefinition(Purple.class);
// 第一个参数指定bean的id
registry.registerBeanDefinition("purple", beanDefinition);
}
}
}
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig3 {}
@Test
public void test4() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
测试结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig3
com.xjhqre.pojo.Color
com.xjhqre.pojo.Red
com.xjhqre.pojo.Blue
com.xjhqre.pojo.Yellow
purple
FactoryBean是一个接口,我们需要自己实现
public class ColorFactoryBean implements FactoryBean<Color> {
// 返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
// 返回是否为单例,如果为false,则每次创建时调用getObject()方法
@Override
public boolean isSingleton() {
return true;
}
}
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig4 {
@Bean
public ColorFactoryBean colorFactoryBean() {
return new ColorFactoryBean();
}
}
@Test
public void test5() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
// 工厂bean获取的是调用getObject创建的对象
Object colorFactoryBean = applicationContext.getBean("colorFactoryBean");
System.out.println(colorFactoryBean.getClass());
// 如果想要拿到colorFactoryBean对象,则需要在传入的id前加一个&标识
Object colorFactoryBean2 = applicationContext.getBean("&colorFactoryBean");
System.out.println(colorFactoryBean2.getClass());
}
测试结果:
class com.xjhqre.pojo.Color
class com.xjhqre.pojo.ColorFactoryBean
bean从创建 ----> 初始化 -----> 销毁的过程
容器管理bean的生命周期
我们可以自定义初始化和销毁方法
构造(对象创建)
初始化
销毁
初始化和销毁方法:
通过@Bean注解指定init-method和destroy-method
public class Car {
public Car() {
System.out.println("car构造方法");
}
public void init() {
System.out.println("car初始化方法");
}
public void destroy() {
System.out.println("car销毁方法");
}
}
@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();
}
测试结果:
car构造方法
car初始化方法
car销毁方法
通过让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();
}
测试结果:
id为mainConfig7的bean对象:com.xjhqre.config.MainConfig7$$EnhancerBySpringCGLIB$$aef15d18@7c469c48执行postProcessBeforeInitialization
id为mainConfig7的bean对象:com.xjhqre.config.MainConfig7$$EnhancerBySpringCGLIB$$aef15d18@7c469c48postProcessAfterInitialization
创建狗对象
id为dog的bean对象:com.xjhqre.pojo.Dog@1534f01b执行postProcessBeforeInitialization
初始化狗对象
id为dog的bean对象:com.xjhqre.pojo.Dog@1534f01bpostProcessAfterInitialization
销毁狗对象
populateBean(beanName, mbd, instanceWrapper);
给bean进行属性赋值initializeBean
初始化bean
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
,遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitializationinvokeInitMethods(beanName, wrappedBean, mbd);
执行自定义初始化applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
使用@Value赋值,赋值方法:
public class Person {
@Value("xjhqre")
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.xjhqre.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);
}
测试结果:
Person{name='xjhqre', age=18, email='[email protected]'}
xjhqre@qq.com
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;
@Resource和@Inject都是java的规范
自定义组件想要使用Spring容器底层的一些组件(ApplicationContext、BeanFactory),需要自定义组件实现xxxAware接口
在创建对象的时候,会调用接口规定的方法,注入相关的组件
Aware的子接口
下面我们来自定义一个类使用Spring底层的组件
@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);
}
测试结果:
当前bean的名字:red
解析的字符串:你好Windows 10, 我是360
IOC容器:org.springframework.context.annotation.AnnotationConfigApplicationContext@77468bd9, started on Sun Feb 27 18:53:21 CST 2022
每一个xxxAware都有一个对应的xxxAwareProcessor,用来处理相关逻辑
以ApplicationContextAwareProcessor为例
@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;
}
}
激活运行环境方式:
命令行方式,在虚拟机选项中输入:-Dspring.profiles.active=test
使用代码方式:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.getEnvironment().setActiveProfiles("test", "dev");
applicationContext.register(MainConfig11.class);
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);
}
}
测试结果:
testDataSource
devDataSource
AOP:【动态代理】,指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;
AOP使用步骤:
通知方法说明:
*表示所有方法,…表示任意类型的形参
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAop {
//业务逻辑类加入容器中
@Bean
public MathCalculator calculator() {
return new MathCalculator();
}
//切面类加入到容器中
@Bean
public LogAspects logAspects() {
return new LogAspects();
}
}
切面表达式 "execution(public int com.xjhqre.aop.MathCalculator.*(..))"
中 *表示所有方法,…表示任意类型的形参。
JoinPoint joinPoint必须写在形参的第一位。
joinPoint可以获取被增强方法的签名,如方法名、参数列表、方法的返回值、方法抛出的异常等等
@Aspect
public class LogAspects {
//抽取公共的切入点表达式
//1、本类引用
//2、其他的切面引用,引用方法的全类名:"com.atguigu.aop.LogAspects.pointCut()"
@Pointcut("execution(public int com.xjhqre.aop.MathCalculator.*(..))")
public void pointCut() {
}
//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
System.out.println("" + joinPoint.getSignature().getName() + "运行。。。@Before:参数列表是:{" + Arrays.asList(args) + "}");
}
@After("pointCut()")
public void logEnd(JoinPoint joinPoint) {
System.out.println("" + joinPoint.getSignature().getName() + "结束。。。@After");
}
//JoinPoint一定要出现在参数表的第一位
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(JoinPoint joinPoint, Object result) {
System.out.println("" + joinPoint.getSignature().getName() + "正常返回。。。@AfterReturning:运行结果:{" + result + "}");
}
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logException(JoinPoint joinPoint, Exception exception) {
System.out.println("" + joinPoint.getSignature().getName() + "异常。。。异常信息:{" + exception + "}");
}
}
public class MathCalculator {
public int div(int i, int j) {
System.out.println("MathCalculator...div...");
return i / j;
}
}
@Test
public void test12() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAop.class);
MathCalculator bean = applicationContext.getBean(MathCalculator.class);
bean.div(1, 1);
}
测试结果:
div运行。。。@Before:参数列表是:{[1, 1]}
MathCalculator...div...
div正常返回。。。@AfterReturning:运行结果:{1}
div结束。。。@After
AOP使用的三个步骤:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 引入AspectJAutoProxyRegister.class对象
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
// true——使用CGLIB基于类创建代理;false——使用java接口创建代理
boolean proxyTargetClass() default false;
// 是否通过aop框架暴露该代理对象,aopContext能够访问.
boolean exposeProxy() default false;
}
该注解引入了AspectJAutoProxyRegistrar
,AspectJAutoProxyRegister
给容器中导入了给容器中导入了一个AspectJAutoProxyRegistrar
自定义给容器注册bean
在AspectJAutoProxyRegistrar
中有一个registerBeanDefinitions
方法,在该方法中使用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
语句注册了一个名为internalAutoProxyCreator
的组件,该组件的类型为AnnotationAwareAspectJAutoProxyCreator
以下是AnnotationAwareAspectJAutoProxyCreator
的层次结构:
这里主要关注后置处理器和自动装备BeanFactory相关的方法:SmartInstantiationAwareBeanPostProcessor
(后置处理器), BeanFactoryAware
(自动装配BeanFactory).
接下来去AnnotationAwareAspectJAutoProxyCreator
及其父类中寻找有关后置处理器和BeanFactory相关的方法
AbstractAutoProxyCreator:
AbstractAdvisorAutoProxyCreator:
AnnotationAwareAspectJAutoProxyCreator:
断点测试程序执行流程:
注册AnnotationAwareAspectJAutoProxyCreator
的后置处理器的方法registerBeanPostProcessors(beanFactory)
在IOC创建的refresh()
方法中
registerBeanPostProcessors(beanFactory):注册bean的后置处理器来方便拦截bean的创建;
beanFactory.addBeanPostProcessor(postProcessor)
注册AnnotationAwareAspectJAutoProxyCreator的后置处理器流程图:
我们来分析IOC创建方法refresh()
中的finishBeanFactoryInitialization(beanFactory);
方法
finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean
遍历获取容器中所有的Bean,依次创建对象getBean(beanName);getBean->doGetBean()->getSingleton()->
创建bean
先从缓存中获取当前bean:Object sharedInstance = getSingleton(beanName);
,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;只要创建好的Bean都会被缓存起来
createBean();创建bean;
resolveBeforeInstantiation(beanName, mbdToUse);
解析BeforeInstantiation
希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续
后置处理器先尝试返回对象;
bean = applyBeanPostProcessorsBeforeInstantiation();
拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor
,就执行postProcessBeforeInstantiation
如果不是则执行bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
若后置处理器返回对象失败,则会真正的去创建一个bean实例:doCreateBean(beanName, mbdToUse, args);
AnnotationAwareAspectJAutoProxyCreator执行时机流程图:
总结:
AnnotationAwareAspectJAutoProxyCreator
在所有bean创建之前会有一个拦截,它继承了InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()
方法AnnotationAwareAspectJAutoProxyCreator
会在任何bean创建之前先尝试返回bean的实例,BeanPostProcessor
是在Bean对象创建完成初始化前后调用的InstantiationAwareBeanPostProcessor
是在创建Bean实例之前先尝试用后置处理器返回对象的AnnotationAwareAspectJAutoProxyCreator
的类型是InstantiationAwareBeanPostProcessor
postProcessBeforeInstantiation()
advisedBeans
中(里面保存了所有需要增强bean, 如MathCalculator)List candidateAdvisors
集合中,判断每一个增强器是否是 AspectJPointcutAdvisor
类型的,如果是则返回true,但我们的增强器是InstantiationModelAwarePointcutAdvisor
类型,所以返回falsepostProcessAfterInitialization
,该方法返回一个包装bean,return wrapIfNecessary(bean, beanName, cacheKey)
包装如果需要的情况下
getAdvicesAndAdvisorsForBean
方法获取当前bean的所有增强器(通知方法),封装在集合Object[] specificInterceptors
中
createProxy
创建当前bean的代理对象;
proxyFactory.getProxy
创建代理对象:Spring自动决定,创建JdkDynamicAopProxy(config)
jdk动态代理或ObjenesisCglibAopProxy(config)
cglib的动态代理;AnnotationAwareAspectJAutoProxyCreator配置代理流程图:
容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);
CglibAopProxy.intercept();
拦截目标方法的执行List
List
保存所有拦截器 长度为5,里面有一个默认的ExposeInvocationInterceptor
和 4个增强器;registry.getInterceptors(advisor);
List
,如果是MethodInterceptor
,直接加入到集合中;如果不是,使用AdvisorAdapter
将增强器转为MethodInterceptor
;转换完成返回MethodInterceptor
数组;MethodInterceptor
机制)CglibMethodInvocation
对象,并调用 Object retVal = mi.proceed();
执行目标方法到获取拦截器链的流程图:
拦截器执行流程图:
@EnableAspectJAutoProxy
开启AOP功能@EnableAspectJAutoProxy
会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator
是一个后置处理器registerBeanPostProcessors
注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator
对象finishBeanFactoryInitialization
初始化剩下的单实例bean
AnnotationAwareAspectJAutoProxyCreator
拦截组件的创建过程CglibAopProxy.intercept()
DROP TABLE IF EXISTS `tbl_user`;
CREATE TABLE `tbl_user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert() {
String sql = "INSERT INTO `tbl_user`(username,age) VALUES(?,?)";
String username = UUID.randomUUID().toString().substring(0, 5);
jdbcTemplate.update(sql, username, 19);
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void insertUser() {
userDao.insert();
System.out.println("插入完成...");
}
}
@Configuration
@ComponentScan("com.xjhqre.tx")
@PropertySource("classpath:/db.properties")
public class TxConfig implements EmbeddedValueResolverAware {
private StringValueResolver valueResolver;
@Bean
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(valueResolver.resolveStringValue("${jdbc.username}"));
dataSource.setPassword(valueResolver.resolveStringValue("${jdbc.password}"));
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/springtx?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true");
dataSource.setDriverClass(valueResolver.resolveStringValue("${jdbc.driverClassName}"));
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
//Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
}
}
@Test
public void test13() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
userService.insertUser();
}
给方法上标注 @Transactional 表示当前方法是一个事务方法;
并在方法中设置异常
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional
public void insertUser() {
userDao.insert();
int i = 1 / 0;
System.out.println("插入完成...");
}
}
在配置类标注@EnableTransactionManagement 开启基于注解的事务管理功能;
@Configuration // 告诉Spring这是一个配置类
@EnableTransactionManagement
@ComponentScan("com.xjhqre.tx")
@PropertySource("classpath:/db.properties")
public class TxConfig implements EmbeddedValueResolverAware {
// ...
}
在配置类中加入事务管理器bean
//注册事务管理器在容器中
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
@Test
public void test13() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
userService.insertUser();
}
测试结果:
控制台打印除 0 异常,数据库未增加新记录
事务执行原理:
@EnableTransactionManagement
,spring利用TransactionManagementConfigurationSelector
给容器中会导入两个组件:AutoProxyRegistrar
和ProxyTransactionManagementConfiguration
InfrastructureAdvisorAutoProxyCreator
组件;该组件利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;AnnotationTransactionAttributeSource
解析事务注解TransactionInterceptor
;保存了事务属性信息,事务管理器;他是一个 MethodInterceptor
,在目标方法执行的时候执行拦截器链:
PlatformTransactionManager
,如果事先没有添加指定任何transactionmanger
,最终会从容器中按照类型获取一个PlatformTransactionManager
;BeanPostProcessor:bean后置处理器,bean创建对象初始化前后进行拦截工作的
BeanFactoryPostProcessor:beanFactory的后置处理器:
BeanFactoryPostProcessor原理:
invokeBeanFactoryPostProcessors(beanFactory);
public class Dog {
public Dog() {
System.out.println("创建狗对象");
}
@PostConstruct
public void init() {
System.out.println("初始化狗对象");
}
@PreDestroy
public void destroy() {
System.out.println("销毁狗对象");
}
}
编写MyBeanFactoryPostProcessor方法,实现接口BeanFactoryPostProcessor,重写postProcessBeanFactory方法
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("执行postProcessBeanFactory");
int count = beanFactory.getBeanDefinitionCount();
String[] names = beanFactory.getBeanDefinitionNames();
System.out.println("当前BeanFactory中有" + count + " 个Bean");
System.out.println(Arrays.asList(names));
}
}
@ComponentScan("com.xjhqre.ext")
@Configuration
public class ExtConfig {
@Bean
public Dog dog() {
return new Dog();
}
}
@Test
public void test14() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
applicationContext.close();
}
测试结果:
执行postProcessBeanFactory
当前BeanFactory中有8 个Bean
创建狗对象
初始化狗对象
销毁狗对象
BeanDefinitionRegistryPostProcessor
继承自BeanFactoryPostProcessor
其优先于BeanFactoryPostProcessor执行;在所有bean定义信息将要被加载,bean实例还未创建的时候执行
利用BeanDefinitionRegistryPostProcessor给容器中再额外添加一些组件
BeanDefinitionRegistryPostProcessor执行流程:
其他配置类和测试类同上
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("执行MyBeanDefinitionRegistryPostProcessor...bean的数量:" + beanFactory.getBeanDefinitionCount());
}
//BeanDefinitionRegistry Bean定义信息的保存中心,以后BeanFactory就是按照BeanDefinitionRegistry里面保存的每一个bean定义信息创建bean实例;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("执行postProcessBeanDefinitionRegistry...bean的数量:" + registry.getBeanDefinitionCount());
// 手动注册bean对象
// 方法一:RootBeanDefinition beanDefinition = new RootBeanDefinition(Cat.class);
// 方法二:
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Cat.class).getBeanDefinition();
registry.registerBeanDefinition("car", beanDefinition);
}
}
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("初始化猫对象");
}
}
测试结果:
执行postProcessBeanDefinitionRegistry...bean的数量:9
执行MyBeanDefinitionRegistryPostProcessor...bean的数量:10
执行postProcessBeanFactory
当前BeanFactory中有10 个Bean
创建狗对象
初始化狗对象
创建猫对象
初始化猫对象
销毁猫对象
销毁狗对象
结果分析:
MyBeanDefinitionRegistryPostProcessor
里的postProcessBeanDefinitionRegistry
,在输出bean对象的数量后又创建了一个Cat类的beanMyBeanDefinitionRegistryPostProcessor
里的postProcessBeanFactory
方法。输出的bean数量加1MyBeanFactoryPostProcessor
里的postProcessBeanFactory
方法ApplicationListener:监听容器中发布的事件。事件驱动模型开发;
我们需要自己写一个监听器,该监听器必须实现ApplicationListener
接口,用于监听ApplicationEvent
及其下面的子事件;
步骤:
applicationContext.publishEvent()
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
//当容器中发布此事件以后,方法触发
@Override
public void onApplicationEvent(ApplicationEvent event) {
// TODO Auto-generated method stub
System.out.println("收到事件:" + event);
}
}
@Test
public void test14() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
applicationContext.publishEvent(new ApplicationEvent(new String("我发布的事件")) {
});
applicationContext.close();
}
测试结果:
收到事件:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@6bc168e5, started on Wed Mar 02 15:04:16 CST 2022]
收到事件:SpringTest$1[source=我发布的事件]
收到事件:org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@6bc168e5, started on Wed Mar 02 15:04:16 CST 2022]
事件发布顺序:
事件发布流程:
for (final ApplicationListener> listener : getApplicationListeners(event, type))
invokeListener(listener, event);
拿到listener回调onApplicationEvent方法;事件多播器(派发器)创建流程:
initApplicationEventMulticaster();
初始化ApplicationEventMulticaster
;
id=“applicationEventMulticaster”
的组件;this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
并且加入到容器中,我们就可以在其他组件要派发事件,自动注入到派发器applicationEventMulticaster中;监听器创建流程:
registerListeners();
从容器中拿到所有的监听器,把他们注册到applicationEventMulticaster中;String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
通过@EventListener注解,我们也可以用来监听事件
@Service
public class UserService {
@EventListener(classes = {ApplicationEvent.class})
public void listen(ApplicationEvent event) {
System.out.println("UserService。。监听到的事件:" + event);
}
}
@Test
public void test14() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
applicationContext.publishEvent(new ApplicationEvent(new String("我发布的事件")) {
});
applicationContext.close();
}
测试结果:
UserService监听到的事件:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@6bc168e5, started on Wed Mar 02 15:37:36 CST 2022]
UserService监听到的事件:SpringTest$1[source=我发布的事件]
UserService监听到的事件:org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@6bc168e5, started on Wed Mar 02 15:37:36 CST 2022]
spring使用EventListenerMethodProcessor
处理器来解析方法上的@EventListener;其实现了SmartInitializingSingleton
接口
SmartInitializingSingleton原理:
finishBeanFactoryInitialization(beanFactory);
初始化剩下的单实例bean;
getBean();
SmartInitializingSingleton
类型的;如果是就调用afterSingletonsInstantiated();
Spring容器的refresh()
创建刷新:
prepareRefresh()
:刷新前的预处理;
initPropertySources()
初始化一些属性设置;子类自定义个性化的属性设置方法;getEnvironment().validateRequiredProperties();
检验属性的合法等earlyApplicationEvents= new LinkedHashSet();
保存容器中的一些早期的事件;obtainFreshBeanFactory();
获取BeanFactory;
refreshBeanFactory();
刷新创建BeanFactory,设置序列化ID;getBeanFactory();
返回刚才GenericApplicationContext
创建的BeanFactory
对象;BeanFactory(DefaultListableBeanFactory)
返回;prepareBeanFactory(beanFactory);
BeanFactory的预准备工作,对BeanFactory进行一些设置;
BeanPostProcessor【ApplicationContextAwareProcessor】
EnvironmentAware
、EmbeddedValueResolverAware
…BeanFactory
、ResourceLoader
、ApplicationEventPublisher
、ApplicationContext
BeanPostProcessor【ApplicationListenerDetector】
environment【ConfigurableEnvironment】
、systemProperties【Map】
、systemEnvironment【Map】
postProcessBeanFactory(beanFactory);
BeanFactory准备工作完成后进行的后置处理工作;
BeanFactory的创建及预准备工作流程图:
invokeBeanFactoryPostProcessors(beanFactory);
执行BeanFactoryPostProcessor;BeanFactoryPostProcessor
:BeanFactory的后置处理器。在BeanFactory标准初始化之后执行的;
BeanDefinitionRegistryPostProcessor
PriorityOrdered
优先级接口的BeanDefinitionRegistryPostProcessor
,将其存入currentRegistryProcessors数组中sortPostProcessors(currentRegistryProcessors, beanFactory);
对所有BeanDefinitionRegistryPostProcessor
进行排序registryProcessors.addAll(currentRegistryProcessors);
注册所有BeanDefinitionRegistryPostProcessor
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
执行所有BeanDefinitionRegistryPostProcessor
Ordered
优先级接口的BeanDefinitionRegistryPostProcessor
,之后的步骤同上面的 3 ~ 5BeanDefinitionRegistryPostProcessor
,之后的步骤同上面的 3 ~ 5BeanDefinitionRegistryPostProcessor
之后才会处理BeanFactoryPostProcessor
,处理过程和BeanDefinitionRegistryPostProcessor
相同registerBeanPostProcessors(beanFactory);
注册BeanPostProcessor,Bean的后置处理器beanFactory.addBeanPostProcessor(postProcessor);
MergedBeanDefinitionPostProcessor
;ApplicationListenerDetector
;来在Bean创建完成后检查是否是ApplicationListener,如果是则调用applicationContext.addApplicationListener((ApplicationListener>) bean);
方法将其添加进容器中initMessageSource();
初始化MessageSource组件(做国际化功能;消息绑定,消息解析)
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
MessageSource.getMessage(String code, Object[] args, String defaultMessage, Locale locale);
initApplicationEventMulticaster();
初始化事件派发器;
ApplicationEventMulticaster
的applicationEventMulticaster
;SimpleApplicationEventMulticaster
ApplicationEventMulticaster
添加到BeanFactory中,以后其他组件直接自动注入onRefresh();
留给子容器(子类)
registerListeners();
给容器中将所有项目里面的ApplicationListener注册进来;
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
finishBeanFactoryInitialization(beanFactory);
初始化所有剩下的单实例bean;
beanFactory.preInstantiateSingletons();
初始化后剩下的单实例bean
getBean(beanName);
创建对象private final Map singletonObjects = new ConcurrentHashMap(256);
获取的createBean(beanName, mbd, args);
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
让BeanPostProcessor先拦截返回代理对象;InstantiationAwareBeanPostProcessor
没有返回代理对象;调用4)Object beanInstance = doCreateBean(beanName, mbdToUse, args);
创建Bean
createBeanInstance(beanName, mbd, args);
利用工厂方法或者对象的构造器创建出Bean实例applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
调用MergedBeanDefinitionPostProcessor
的postProcessMergedBeanDefinition(mbd, beanType, beanName);
populateBean(beanName, mbd, instanceWrapper);
InstantiationAwareBeanPostProcessor
后置处理器:postProcessAfterInstantiation();
InstantiationAwareBeanPostProcessor
后置处理器:postProcessPropertyValues()
initializeBean(beanName, exposedObject, mbd);
invokeAwareMethods(beanName, bean);
执行xxxAware接口的方法applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd);
applyBeanPostProcessorsAfterInitialization
finishRefresh();
完成BeanFactory的初始化创建工作;IOC容器就创建完成;
initLifecycleProcessor();
初始化和生命周期有关的后置处理器;默认从容器中找是否有lifecycleProcessor的组件【LifecycleProcessor】;如果没有new DefaultLifecycleProcessor();
加入到容器;getLifecycleProcessor().onRefresh();
拿到前面定义的生命周期处理器(BeanFactory),回调onRefresh()方法;publishEvent(new ContextRefreshedEvent(this));
发布最终事件liveBeansView.registerApplicationContext(this);
finishBeanFactoryInitialization();
jsp页面:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<h1><%= "Hello World!" %>
</h1>
<br/>
<a href="HelloServlet">Hello Servlet</a>
</body>
</html>
Servlet:
@WebServlet(name = "HelloServlet", value = "/HelloServlet")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("hello xjhqre");
}
}
Shared libraries(共享库) / runtimes pluggability(运行时插件能力)
Servlet容器启动会扫描,当前应用里面每一个jar包的ServletContainerInitializer的实现
使用方法:
代码演示:
MyServletContainerInitializer:
//容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来;
@HandlesTypes(value={HashMap.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
/**
* 应用启动的时候,会运行onStartup方法;
*
* Set> arg0:感兴趣的类型的所有子类型;
* ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用对应一个ServletContext;
*/
@Override
public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
// TODO Auto-generated method stub
System.out.println("感兴趣的类型:");
for (Class<?> claz : arg0) {
System.out.println(claz);
}
}
}
创建javax.servlet.ServletContainerInitializer文件,
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("userServlet响应");
}
}
public class MyListener implements ServletContextListener {
// 监听ServletContext销毁
@Override
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("监听ServletContext销毁");
}
// 监听ServletContext启动初始化
@Override
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("监听ServletContext启动初始化");
}
}
public class MyFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
// 过滤请求
System.out.println("过滤请求");
// 放行
arg2.doFilter(arg0, arg1);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
public class MyServletContainerInitializer implements ServletContainerInitializer {
/**
* 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
* 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
* 必须在项目启动的时候来添加;
* 1)、ServletContainerInitializer得到的ServletContext;
* 2)、ServletContextListener得到的ServletContext;
*/
@Override
public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
// 注册组件 ServletRegistration
ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new MyServlet());
// 配置servlet的映射信息
servlet.addMapping("/user");
// 注册Listener
sc.addListener(MyListener.class);
// 注册Filter FilterRegistration
FilterRegistration.Dynamic filter = sc.addFilter("userFilter", MyFilter.class);
// 配置Filter的映射信息,过滤所有请求
filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
}
}
META-INF/services/javax.servlet.ServletContainerInitializer
SpringServletContainerInitializer
WebApplicationInitializer
接口的下的所有组件;createRootApplicationContext()
方法创建根容器;createServletApplicationContext()
方法创建一个web的ioc容器;DispatcherServlet
;createRootApplicationContext()
创建根容器,调用getRootConfigClasses();
传入一个配置类createServletApplicationContext();
创建web的ioc容器: 调用getServletConfigClasses();
获取配置类;**结论:**以注解方式来启动SpringMVC的步骤:
AbstractAnnotationConfigDispatcherServletInitializer
;<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.14version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>servlet-apiartifactId>
<version>3.0-alpha-1version>
<scope>providedscope>
dependency>
@Service
public class HelloService {
public String sayHello(String name) {
return "Hello " + name;
}
}
@Controller
public class HelloController {
@Autowired
HelloService helloService;
@ResponseBody
@RequestMapping("/hello")
public String hello() {
String hello = helloService.sayHello("tomcat..");
return hello;
}
}
//Spring的容器不扫描controller;父容器
@ComponentScan(value = "com.example.SpringMVC_Annotation", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
})
public class RootConfig {
}
//SpringMVC只扫描Controller;子容器
//useDefaultFilters=false 禁用默认的过滤规则;
@ComponentScan(value="com.example.SpringMVC_Annotation",includeFilters={
@ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class})
},useDefaultFilters=false)
@EnableWebMvc
public class AppConfig {
}
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//获取根容器的配置类(Spring的配置文件)父容器;
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{RootConfig.class};
}
//获取web容器的配置类(SpringMVC配置文件)子容器;
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{AppConfig.class};
}
//获取DispatcherServlet的映射信息
// /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp;
// /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的;
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
通过继承WebMvcConfigurerAdapter
类,重写里面的方法来配置springMVC。
例如:配置视图解析器、静态资源访问、拦截器等等
在WEB-INF目录下新建Views目录,在Views目录下创建success.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
success
public class MyInterceptor implements HandlerInterceptor {
// 目标方法运行之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("目标方法运行之前执行");
return true;
}
// 目标方法执行正确以后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
System.out.println("目标方法执行正确以后执行");
}
// 页面响应以后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
System.out.println("页面响应以后执行");
}
}
@Controller
public class HelloController {
@Autowired
HelloService helloService;
@ResponseBody
@RequestMapping("/hello")
public String hello() {
String hello = helloService.sayHello("xjhqre");
return hello;
}
// /WEB-INF/views/success.jsp
@RequestMapping("/suc")
public String success(){
return "success";
}
}
//SpringMVC只扫描Controller;子容器
//useDefaultFilters=false 禁用默认的过滤规则;
@ComponentScan(value="com.example.SpringMVC_Annotation",includeFilters={
@ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class})
},useDefaultFilters=false)
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter{
//视图解析器
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
//默认所有的页面都从 /WEB-INF/ xxx .jsp
//registry.jsp();
registry.jsp("/WEB-INF/views/", ".jsp");
}
//静态资源访问
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//super.addInterceptors(registry);
// "/**":任意多的路径
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
}
}
异步请求模型:
编写HelloAsyncServlet
@WebServlet(value="/async",asyncSupported=true)
public class HelloAsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、支持异步处理asyncSupported=true
//2、开启异步模式
System.out.println("主线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
AsyncContext startAsync = req.startAsync();
//3、业务逻辑进行异步处理;开始异步处理
startAsync.start(new Runnable() {
@Override
public void run() {
try {
System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
sayHello();
startAsync.complete();
//4、获取响应
ServletResponse response = startAsync.getResponse();
response.getWriter().write("hello async...");
System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
} catch (Exception e) {
}
}
});
System.out.println("主线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
}
public void sayHello() throws Exception{
System.out.println(Thread.currentThread()+" processing...");
Thread.sleep(3000);
}
}
测试结果:
主线程开始。。。Thread[http-nio-8080-exec-9,5,main]==>1646310054633
主线程结束。。。Thread[http-nio-8080-exec-9,5,main]==>1646310054633
副线程开始。。。Thread[http-nio-8080-exec-5,5,main]==>1646310054633
Thread[http-nio-8080-exec-5,5,main] processing...
副线程结束。。。Thread[http-nio-8080-exec-5,5,main]==>1646310057634
步骤:
编写AsyncController:
@Controller
public class AsyncController {
/**
* 1、控制器返回Callable
* 2、Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
* 3、DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
* 4、Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
* 5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
*/
@ResponseBody
@RequestMapping("/async01")
public Callable<String> async01() {
System.out.println("主线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("副线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("副线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
return "Callable async01()" ;
}
};
System.out.println("主线程结束..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
return callable;
}
}
测试结果:
目标方法运行之前执行
主线程开始...Thread[http-nio-8080-exec-8,5,main]==>1646311553329
主线程结束...Thread[http-nio-8080-exec-8,5,main]==>1646311553329
副线程开始...Thread[MvcAsync2,5,main]==>1646311553329
副线程开始...Thread[MvcAsync2,5,main]==>1646311555330
目标方法运行之前执行
目标方法执行正确以后执行
页面响应以后执行
请求模型:
执行步骤:
编写DeferredResultQueue:
public class DeferredResultQueue {
private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedQueue<DeferredResult<Object>>();
public static void save(DeferredResult<Object> deferredResult) {
System.out.println("加入队列");
queue.add(deferredResult);
}
public static DeferredResult<Object> get() {
System.out.println("从队列中取出");
return queue.poll();
}
}
编写AsyncController:
@Controller
public class AsyncController {
@ResponseBody
@RequestMapping("/createOrder")
public DeferredResult<Object> createOrder() {
// 设置超时时间
DeferredResult<Object> deferredResult = new DeferredResult<>((long) 3000, "创建订单失败");
DeferredResultQueue.save(deferredResult);
return deferredResult;
}
@ResponseBody
@RequestMapping("/create")
public String create() {
//创建订单
String order = UUID.randomUUID().toString();
DeferredResult<Object> deferredResult = DeferredResultQueue.get();
deferredResult.setResult(order);
return "success ===> " + order;
}
}
回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
5. 根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
编写AsyncController:
@Controller
public class AsyncController {
/**
* 1、控制器返回Callable
* 2、Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
* 3、DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
* 4、Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
* 5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
*/
@ResponseBody
@RequestMapping("/async01")
public Callable<String> async01() {
System.out.println("主线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("副线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("副线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
return "Callable async01()" ;
}
};
System.out.println("主线程结束..." + Thread.currentThread() + "==>" + System.currentTimeMillis());
return callable;
}
}
测试结果:
目标方法运行之前执行
主线程开始...Thread[http-nio-8080-exec-8,5,main]==>1646311553329
主线程结束...Thread[http-nio-8080-exec-8,5,main]==>1646311553329
副线程开始...Thread[MvcAsync2,5,main]==>1646311553329
副线程开始...Thread[MvcAsync2,5,main]==>1646311555330
目标方法运行之前执行
目标方法执行正确以后执行
页面响应以后执行
请求模型:
[外链图片转存中…(img-faiKtIM8-1646313066352)]
执行步骤:
编写DeferredResultQueue:
public class DeferredResultQueue {
private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedQueue<DeferredResult<Object>>();
public static void save(DeferredResult<Object> deferredResult) {
System.out.println("加入队列");
queue.add(deferredResult);
}
public static DeferredResult<Object> get() {
System.out.println("从队列中取出");
return queue.poll();
}
}
编写AsyncController:
@Controller
public class AsyncController {
@ResponseBody
@RequestMapping("/createOrder")
public DeferredResult<Object> createOrder() {
// 设置超时时间
DeferredResult<Object> deferredResult = new DeferredResult<>((long) 3000, "创建订单失败");
DeferredResultQueue.save(deferredResult);
return deferredResult;
}
@ResponseBody
@RequestMapping("/create")
public String create() {
//创建订单
String order = UUID.randomUUID().toString();
DeferredResult<Object> deferredResult = DeferredResultQueue.get();
deferredResult.setResult(order);
return "success ===> " + order;
}
}