目录
- 大纲
- 01_尚硅谷_课程简介-注解驱动开发
- 02_尚硅谷_组件注册-@Configuration&@Bean给容器中注册组件
- 03_尚硅谷_组件注册-@ComponentScan-自动扫描组件&指定扫描规则
- 04_尚硅谷_组件注册-自定义TypeFilter指定过滤规则
- 05_尚硅谷_组件注册-@Scope-设置组件作用域
- 06_尚硅谷_组件注册-@Lazy-bean懒加载
- 07_尚硅谷_组件注册-@Conditional-按照条件注册bean
- 08_尚硅谷_组件注册-@Import-给容器中快速导入一个组件
- 09_尚硅谷_组件注册-@Import-使用ImportSelector
- 10_尚硅谷_组件注册-@Import-使用ImportBeanDefinitionRegistrar
- 11.1_尚硅谷_组件注册-使用FactoryBean注册组件
- 11.2_总结上述几种在容器中注册组件的方式
- 12.1_尚硅谷_生命周期-@Bean指定初始化和销毁方法(单例模式)
- 12.2_尚硅谷_生命周期-@Bean指定初始化和销毁方法(原型模式)
- 13.1_尚硅谷_生命周期-InitializingBean和DisposableBean(单例模式)
- 13.2_尚硅谷_生命周期-InitializingBean和DisposableBean(原型模式)
- 14.1_尚硅谷_生命周期-@PostConstruct&@PreDestroy(单例模式)
- 14.2_尚硅谷_生命周期-@PostConstruct&@PreDestroy(原型模式)
- 15.1_尚硅谷_生命周期-BeanPostProcessor-后置处理器(单例模式)
- 15.2_尚硅谷_生命周期-BeanPostProcessor-后置处理器(原型模式)
- 15.3_总结上述几种bean实例初始化方法和销毁方法
- 18_尚硅谷_属性赋值-@Value赋值
- 19_尚硅谷_属性赋值-@PropertySource加载外部配置文件
- 20_尚硅谷_自动装配-@Autowired&@Qualifier&@Primary
- 21_尚硅谷_自动装配-@Resource&@Inject
- 22_尚硅谷_自动装配-方法、构造器位置的自动装配
- 23_尚硅谷_自动装配-Aware注入Spring底层组件&原理
- 24_尚硅谷_自动装配-@Profile环境搭建
- 25_尚硅谷_自动装配-@Profile根据环境注册bean
- 26_尚硅谷_IOC-小结
- 27_尚硅谷_AOP-AOP功能测试
- 36_尚硅谷_声明式事务-环境搭建
- 37_尚硅谷_声明式事务-测试成功
大纲
01_尚硅谷_课程简介-注解驱动开发
大纲介绍,不在记录
02_尚硅谷_组件注册-@Configuration&@Bean给容器中注册组件
(1)准备工作
首先创建一个Maven项目,然后在pom.xml
文件中引入如下两个依赖:
<!--maven会自动引入spring的其他关联依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<!--测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
<!--简化实体类中方法的配置-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
然后创建一个Student
实体类,如下:
package com.atguigu.model;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private Integer age;
}
(2)回顾使用XXX.xml创建bean
在介绍@Configuration、@Bean
之前,我们先来说明一下以前是如何通过xml文件来配置bean的,我们在resources
目录下面创建一个beans.xml
,然后在里面写上如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--id是该bean标签的别名,class是对应类的全限定类名-->
<bean id="studentByXml" class="com.atguigu.model.Student">
<property name="name" value="李华"/>
<property name="age" value="23"/>
</bean>
</beans>
然后就可以创建测试类Test了,如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetBeanByXml() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) applicationContext.getBean("studentByXml");
System.out.println(student);
}
}
测试结果如下:
Student(name=李华, age=23)
(3)使用java配置类创建bean
说完了之前是如何创建bean的,现在说一下如何使用配置类来创建bean,我们来创建一个java配置类,MyConfig如下:
package com.atguigu.config;
@Configuration // 告诉spring这是一个配置类,并且配置类等同于xml配置文件
public class MyConfig {
/**
* 作用:通过@Bean注解的方式注入Bean
* 解释:返回值类型和方法名称说明:返回值类型相当于xml配置文件中bean标签对应的class属性值,方法名相当于xml配置文件中bean标签对应的id属性值
*/
@Bean
public Student studentByAnnotation() {
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetBeanByAnnotation() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Student student = (Student) applicationContext.getBean("studentByAnnotation");
System.out.println(student);
}
}
测试结果如下:
Student(name=李华, age=23)
(4)@Bean注解后面添加name属性
MyConfig如下:
package com.atguigu.config;
@Configuration // 告诉spring这是一个配置类,并且配置类等同于xml配置文件
public class MyConfig {
/**
* 如果更改bean对应的名称,有以下两种办法:
* 方法1:更改方法名称
* 方法2:更改@Bean注解中的name/value属性值
* 比较:方法2的优先级比方法1高,所以如果@Bean注解的name/value属性有值,那么name的值就是bean的id名称,而方法名不是bean的id名称
*/
@Bean(name = {"studentByBeanName"})
public Student studentByAnnotation() {
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetBeanByAnnotation() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Student student = (Student) applicationContext.getBean("studentByBeanName");
System.out.println(student);
}
}
测试结果如下:
Student(name=李华, age=23)
内部代码说明:
1、如果大家按住Ctrl键
点击@Bean注解,你会发现里面的方法名称有value、name等,并且根据@AliasFor("XXX")
注解可以知道value和name属性在@Bean注解中是等价的,另外value属性还可以省略
2、name/value方法的返回值是数组,name里面的值都会有多个,到时候你用哪个名称都可以访问到该bean
,代码如下:
package com.atguigu.config;
@Configuration // 告诉spring这是一个配置类,并且配置类等同于xml配置文件
public class MyConfig {
@Bean(name = {"studentByBeanName1", "studentByBeanName2"})
public Student studentByAnnotation() {
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetBeanName() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Student student1 = (Student) applicationContext.getBean("studentByBeanName1");
Student student2 = (Student) applicationContext.getBean("studentByBeanName2");
System.out.println(student1);
System.out.println(student2);
}
}
测试结果如下:
Student(name=李华, age=23)
Student(name=李华, age=23)
03_尚硅谷_组件注册-@ComponentScan-自动扫描组件&指定扫描规则
(1)@ComponentScan注解的作用
在上面我们用到了MyConfig
主配置类,但是默认只能加载该配置类,我们知道类上面可以添加@Component、@Controller、@Service、@Repository注解,如果我们想让这些类也被spring所管理,那就需要让spring知道这些类的存在,这就需要在MyConfig
主配置类上添加扫描注解,也就是接下来所说的@ComponentScan
注解
(2)basePackages属性
如果想实现@ComponentScan
注解的功能,之前xml配置中我们是这样做的,如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.atguigu"/>
</beans>
然而java配置类中是这样做的,MyConfig如下:
package com.atguigu.config;
@Configuration
@ComponentScan(basePackages = {"com.atguigu"}) // 扫描com.atguigu下面的所有java类
public class MyConfig {
}
下图中用红框圈起来的部分都是注解扫描的范围:
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetComponentScanBasePackages() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
测试结果如下:
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
myConfig
myController
myDao
myService
前面几个比较长的是spring内部bean,后面的myConfig……才是我们自己定义的
(3)excludeFilters属性
如果在扫描注解的范围内,有些地方不想让扫描到,那就可以使用excludeFilters属性来排除这些地方
MyConfig如下:
package com.atguigu.config;
@Configuration
@ComponentScan(basePackages = {"com.atguigu"}, excludeFilters = {
@ComponentScan.Filter(classes = {Controller.class})
}) // 扫描注解
public class MyConfig {
……
}
其中excludeFilters
属性的返回值是一个Filter
注解数组,Filter
注解里面有多个属性,其中type
属性的默认值是代表根据注解进行过滤,当然也可以换成其他的,具体替换内容可以去看源码;然后classes
属性是一个Class数组,如果type是默认值注解,那么Class数组中的值可以是Controller.class
等,所以这就是上面代码的样子
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetComponentScanBasePackages() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
测试结果如下:
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
myConfig
myDao
myService
(4)includeFilters属性
如果在扫描注解的范围内,我们只想让扫描部分地方,其余大部分地方都不想让扫描到,那就可以使用includeFilters属性来包含这些地方
MyConfig如下:
package com.atguigu.config;
@Configuration
@ComponentScan(basePackages = {"com.atguigu"}, includeFilters = {
@ComponentScan.Filter(classes = {Controller.class})
}, useDefaultFilters = false) // 扫描注解
public class MyConfig {
}
对于includeFilters属性,它和上面所说的excludeFilters属性类似,里面都是使用Filter注解数组,需要扫描那些在Filter注解后面的属性中配置即可,属性中不仅可以有classes,还可以有type等
注意我们需要配置:useDefaultFilters = false,意思不用默认的扫描了,而是在basePackages的范围内,只扫描includeFilters指定的地方,其实在xml中也有类似的配置(只是说明,不用在xml中配置),如下:
<context:component-scan base-package="com.atguigu" use-default-filters="false" />
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetComponentScanBasePackages() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
测试结果如下:
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
myConfig
myController
(5)@ComponentScans注解
MyConfig如下:
package com.atguigu.config;
@Configuration
@ComponentScans({
@ComponentScan(basePackages = {"com.atguigu.controller"}),
@ComponentScan(basePackages = {"com.atguigu.service"})
})
public class MyConfig {
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetComponentScanBasePackages() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
测试结果如下:
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
myConfig
myController
myService
(6)java8中复用@ComponentScan注解
MyConfig如下:
package com.atguigu.config;
@Configuration
@ComponentScan(basePackages = {"com.atguigu.controller"})
@ComponentScan(basePackages = {"com.atguigu.service"})
public class MyConfig {
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetComponentScanBasePackages() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
测试结果如下:
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
myConfig
myController
myService
解释说明:
在java8中可以复用@ComponentScan注解,这也是注解上面的@Repeatable
注解限定好的,如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
……
}
04_尚硅谷_组件注册-自定义TypeFilter指定过滤规则
(1)Filter注解中type是FilterType.ASSIGNABLE_TYPE
MyConfig如下:
package com.atguigu.config;
@Configuration
@ComponentScan(basePackages = {"com.atguigu"}, includeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {MyController.class})
}, useDefaultFilters = false)
public class MyConfig {
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetComponentScanBasePackages() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
测试结果如下:
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
myConfig
myController
解释说明:
type默认是FilterType.ANNOTATION
,也就是基于注解,而我们本次使用FilterType.ASSIGNABLE_TYPE
,也就是基于类的类型
(2)Filter注解中type是FilterType.CUSTOM
这是自定义的过滤器,classes中是过滤器类,我们来到找到ComponentScan
中的Filter
注解中的type
属性,我们点击FilterType
,找到CUSTOM
,可以看到上面的注释如下:
/** Filter candidates using a given custom
* {@link org.springframework.core.type.filter.TypeFilter} implementation.
*/
CUSTOM
说明我们定义的过滤器类需要实现TypeFilter
,先定义一个MyTypeFilter
过滤器类,如下:
package com.atguigu.filter;
public class MyTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取类上所有注解的全限定类名,可能有多个,所以是Set集合;例如:org.springframework.stereotype.Controller
// 虽然本次没用到,可能以后会用到呢!
Set<String> annotationTypes = annotationMetadata.getAnnotationTypes();
// 类的全限定类名,例如:com.atguigu.controller.MyController
String className = annotationMetadata.getClassName();
return className.contains("MyController");
}
}
然后MyConfig
主配置类也要重塑,如下:
package com.atguigu.config;
@Configuration
@ComponentScan(basePackages = {"com.atguigu"}, includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
}, useDefaultFilters = false)
public class MyConfig {
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetComponentScanBasePackages() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
测试结果如下:
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
myConfig
myController
解释说明:
在主配置类中Filter注解中type
属性使用的是FilterType.CUSTOM
,classes
用的是过滤器类MyTypeFilter
(3)Filter注解中type是FilterType.ASPECTJ、REGEX
大家自由探索
05_尚硅谷_组件注册-@Scope-设置组件作用域
(1)单例模式
默认就是单例模式,我们进入Scope
注解中可以看到scopeName
默认是""
,然后我们可以看到scopeName属性上的注释中的这一段话,如下:
/**
* Specifies the name of the scope to use for the annotated component/bean.
* <p>Defaults to an empty string ({@code ""}) which implies
* {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
* @since 4.2
* @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
* @see ConfigurableBeanFactory#SCOPE_SINGLETON
* @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
* @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
* ……
*/
说明默认空字符串代表使用的是SCOPE_SINGLETON
单例模式,另外单例模式和原型模式的字符串也是点击ConfigurableBeanFactory#SCOPE_PROTOTYPE
中SCOPE_PROTOTYPE
,以及点击ConfigurableBeanFactory#SCOPE_SINGLETON
中的SCOPE_SINGLETON
所以我们即使在主配置类的方法中添加@Scope注解,那创建的自然也是单例模式,其实单例模式即只会创建一个实例,并且在ApplicationContext容器启动并加载主配置类的时候创建该实例,此后直接从IOC容器中获取就可以了,不用在创建了
MyConfig如下:
package com.atguigu.config;
@Configuration
}, useDefaultFilters = false)
public class MyConfig {
@Bean
public Student student() {
System.out.println("使用单例模式创建student实例");
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetBeanByScope() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("加载主配置类完成……");
Student student1 = (Student) applicationContext.getBean("student");
Student student2 = (Student) applicationContext.getBean("student");
System.out.println(student1 == student2);
}
}
测试结果如下:
使用单例模式创建student实例
加载主配置类完成……
true
解释说明:
使用单例模式创建student实例注释只在主配置类加载完毕之前打印了一次,并且student1和student2两者相等,都验证了单例模式的存在
(2)原型模式
MyConfig如下:
package com.atguigu.config;
@Configuration
}, useDefaultFilters = false)
public class MyConfig {
@Scope("prototype")
@Bean
public Student student() {
System.out.println("使用原型模式创建student实例");
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testGetBeanByScope() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("加载主配置类完成……");
Student student1 = (Student) applicationContext.getBean("student");
Student student2 = (Student) applicationContext.getBean("student");
System.out.println(student1 == student2);
}
}
测试结果如下:
加载主配置类完成……
使用原型模式创建student实例
使用原型模式创建student实例
false
解释说明:
使用原型模式创建student实例注释只在主配置类加载完毕之前没有打印,而从容器中获取了两次bean实例,然后就打印了2次,并且student1和student2两者不相等,所以验证了原型模式是每次获取bean实例都会创建一个新的bean实例
06_尚硅谷_组件注册-@Lazy-bean懒加载
在 05_尚硅谷_组件注册-@Scope-设置组件作用域》(1)单例模式 中可以看到,在加载主配置类MyConfig的时候,就会创建单例模式的bean实例,如果我们想使用懒加载,也就是第一次用到的时候获取该bean实例,我们就应该使用@Lazy注解。
MyConfig如下:
package com.atguigu.config;
@Configuration
}, useDefaultFilters = false)
public class MyConfig {
@Lazy
@Bean
public Student student() {
System.out.println("使用单例模式创建student实例");
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testLazy() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("加载主配置类完成……");
Student student1 = (Student) applicationContext.getBean("student");
Student student2 = (Student) applicationContext.getBean("student");
System.out.println(student1 == student2);
}
}
测试结果如下:
加载主配置类完成……
使用单例模式创建student实例
true
解释说明:
添加懒加载的单例模式,当加载主配置类并且创建IOC容器的时候,不会创建bean实例,只有在第一次获取bean实例的时候才会创建bean实例
07_尚硅谷_组件注册-@Conditional-按照条件注册bean
(1)@Conditional注解加在方法上
该注解的作用是:只有判断通过才会创建Bean实例,该注解中有一个属性,它是一个Class类型,并且要求该类实现Condition,而Condition中有一个matches方法,该方法中有一个属性类型是ConditionContext
,这是上下文对象,我们在Context的实现类中可以通过该属性获取到很多有用信息
MyConfig如下:
package com.atguigu.config;
@Configuration
}, useDefaultFilters = false)
public class MyConfig {
@Conditional({WindowsCondition.class})
@Bean
public Student windows() {
return new Student("windows", 23);
}
@Conditional({LinuxCondition.class})
@Bean
public Student linux() {
return new Student("linux", 23);
}
}
WindowsCondition如下:
package com.atguigu.condition;
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取IOC使用的bean工厂
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 获取类加载器
ClassLoader classLoader = context.getClassLoader();
// 获取bean定义注册类
BeanDefinitionRegistry registry = context.getRegistry();
// 虽然前面几个现在没用到,但是未来可能会用到
// 获取当前环境
Environment environment = context.getEnvironment();
// 获取当前机器名称
String property = environment.getProperty("os.name");
return property.contains("Windows");
}
}
LinuxCondition如下:
package com.atguigu.condition;
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取IOC使用的bean工厂
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 获取类加载器
ClassLoader classLoader = context.getClassLoader();
// 获取bean定义注册类
BeanDefinitionRegistry registry = context.getRegistry();
// 虽然前面几个现在没用到,但是未来可能会用到
// 获取当前环境
Environment environment = context.getEnvironment();
// 获取当前机器名称
String property = environment.getProperty("os.name");
return property.contains("Linux");
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testConditional() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanNamesForType = applicationContext.getBeanNamesForType(Student.class);
for (String s : beanNamesForType) {
System.out.println(s);
}
}
}
测试结果如下:
windows
解释说明:
由于测试环境是windows操作系统,所以environment.getProperty("os.name")
的结果是:Windows 10
,因此bean实例方法名称为windows将会被创建
(2)@Conditional注解加在类上
MyConfig如下:
package com.atguigu.config;
@Configuration
@Conditional({LinuxCondition.class})
@ComponentScan(basePackages = {"com.atguigu"})
public class MyConfig {
@Conditional({WindowsCondition.class})
@Bean
public Student windows() {
return new Student("比尔盖茨", 23);
}
@Conditional({LinuxCondition.class})
@Bean
public Student linux() {
return new Student("雷纳斯", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testConditional() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
测试结果如下:
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
解释说明:
如果@Conditional注解加在类上,那么只要不符合要求,那么该配置类上所有的配置都会失效,相当于这个配置类就没有被加载过一样
08_尚硅谷_组件注册-@Import-给容器中快速导入一个组件
MyConfig如下:
package com.atguigu.config;
@Configuration
@Import({Student.class})
public class MyConfig {
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testImport() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
测试结果如下:
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
myConfig
com.atguigu.model.Student
解释说明:
使用@Import导入的bean实例的id默认是全限定类名,比如上面的com.atguigu.model.Student
09_尚硅谷_组件注册-@Import-使用ImportSelector
上面介绍了@Import注解的属性中可以添加多个类Class,比如Student.class;如果你进入Import注解中,value方法上推荐了一个ImportSelector,作用也是注册组件,如下:
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
……
selectImports方法的返回值是全限定类名组成的数组
MySelector如下:
package com.atguigu.selector;
public class MySelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.atguigu.model.Student"};
}
}
MyConfig如下:
package com.atguigu.config;
@Configuration
@Import({MySelector.class})
public class MyConfig {
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testImportSelector() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
测试结果如下:
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
myConfig
com.atguigu.model.Student
10_尚硅谷_组件注册-@Import-使用ImportBeanDefinitionRegistrar
如果你进入Import注解中,value方法上推荐了一个ImportBeanDefinitionRegistrar,作用也是注册组件,如下:
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
……
我们也可以通过这个接口来做注册bean实例到容器中
MyImportBeanDefinitionRegistrar如下:
package com.atguigu.registrar;
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
// 拓展:可以通过bean名称来获取bean实例,进而判断bean实例是否存在,某些情况下也可以通过该判断来确定是否创建其他bean实例
// BeanDefinition beanDefinition = registry.getBeanDefinition("bean名称");
// RootBeanDefinition类是BeanDefinition接口的实现类
RootBeanDefinition definition = new RootBeanDefinition(Student.class);
// 注册该类,bean的名称是studentByRegistrar
registry.registerBeanDefinition("studentByRegistrar", definition);
}
}
MyConfig如下:
package com.atguigu.config;
@Configuration
@Import({MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testImportBeanDefinitionRegistrar() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
测试结果如下:
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
myConfig
studentByRegistrar
11.1_尚硅谷_组件注册-使用FactoryBean注册组件
MyFactoryBean如下:
public class MyFactoryBean implements FactoryBean<Student> {
@Override
public Student getObject() throws Exception {
// 添加对象到容器中
return new Student();
}
@Override
public Class<?> getObjectType() {
// 对象实例
return Student.class;
}
@Override
public boolean isSingleton() {
// 是否单例
return true;
}
}
MyConfig如下:
package com.atguigu.config;
@Configuration
public class MyConfig {
// 虽然返回值类型是MyFactoryBean,但是在容器中注册的却是Studentl类型
@Bean
public MyFactoryBean studentByFactoryBean() {
return new MyFactoryBean();
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testFactoryBean() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
测试结果如下:
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
myConfig
studentByFactoryBean
11.2_总结上述几种在容器中注册组件的方式
- 包扫描+组件标注注解(@Configuration+@ComponentScan+@Component / @Controller / @Service / @Repository),详情可见目录 03
- @Bean用于导入第三方包中的组件,详情可见目录 02
- @Import快速给容器导入一个组件(1)、@Import注解导入类Class,然后容器将会自动注解Class为组件,bean的id是全限定类名,详情可见目录08(2)、实现ImportSelector接口,返回需要导入组件的全限定类名数组,详情可见目录 09(3)、实现ImportBeanDefinitionRegistrar接口,可以手动注册单个Bean到容器中,详情可见目录 10;@Import中的value属性是一个数组类型,上述三种方式的XXX.class可以一起使用,如下:
package com.atguigu.config;
@Configuration
@Import({Student.class, MySelector.class, MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
……
- 实现FactoryBean接口,默认在容器中注册的是接口实现类中调用getObject方法创建的对象,详情可见目录11.1
12.1_尚硅谷_生命周期-@Bean指定初始化和销毁方法(单例模式)
在xml配置方式中,我们定义初始化方法和销毁方法是这样做的,如下:
<bean id="student" class="com.atguigu.model.Student" init-method="初始化方法名称" destroy-method="销毁方法名称">
……
但是在java配置类中,我们需要在类中先定义初始化方法和销毁方法的具体实现,之后在@Bean后面的initMethod和destroyMethod中分别指定初始化方法和销毁方法名称
Student如下:
package com.atguigu.model;
@Data
@NoArgsConstructor
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("构造方法被调用");
}
public void initMethod() {
System.out.println("初始化方法被调用");
}
public void destroyMethod() {
System.out.println("销毁方法被调用");
}
}
MyConfig如下:
package com.atguigu.config;
@Configuration
public class MyConfig {
@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
public Student student() {
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testInitAndDestroyMethod() {
// 1、创建IOC容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("容器被创建完成");
// 2、获取容器中的对象
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);
// 3、关闭容器
((AnnotationConfigApplicationContext) applicationContext).close();
}
}
测试结果如下:
构造方法被调用
初始化方法被调用
容器被创建完成
Student(name=李华, age=23)
销毁方法被调用
12.2_尚硅谷_生命周期-@Bean指定初始化和销毁方法(原型模式)
在xml配置方式中,我们定义初始化方法和销毁方法是这样做的,如下:
<bean id="student" class="com.atguigu.model.Student" scope="prototype" init-method="初始化方法名称" destroy-method="销毁方法名称">
……
但是在java配置类中,我们需要在类中先定义初始化方法和销毁方法的具体实现,之后在@Bean后面的initMethod和destroyMethod中分别指定初始化方法和销毁方法名称
Student如下:
package com.atguigu.model;
@Data
@NoArgsConstructor
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("构造方法被调用");
}
public void initMethod() {
System.out.println("初始化方法被调用");
}
public void destroyMethod() {
System.out.println("销毁方法被调用");
}
}
MyConfig如下:
package com.atguigu.config;
@Configuration
public class MyConfig {
@Scope("prototype")
@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
public Student student() {
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testInitAndDestroyMethod() {
// 1、创建IOC容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("容器被创建完成");
// 2、获取容器中的对象
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);
// 3、关闭容器
((AnnotationConfigApplicationContext) applicationContext).close();
}
}
测试结果如下:
容器被创建完成
构造方法被调用
初始化方法被调用
Student(name=李华, age=23)
解释说明:
单例模式:在容器创建完成之前完成bean初始化工作,然后在容器销毁之前完成bean销毁工作
原型模式:在容器创建完成也不会进行bean初始化方法,只会在bean被调用的时候才会进行bean初始化工作,并且在容器被销毁的时候也不会进行bean销毁工作
13.1_尚硅谷_生命周期-InitializingBean和DisposableBean(单例模式)
Student如下:
package com.atguigu.model;
@Data
@NoArgsConstructor
public class Student implements InitializingBean, DisposableBean {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("构造方法被调用");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化方法被调用");
}
@Override
public void destroy() throws Exception {
System.out.println("销毁方法被调用");
}
}
MyConfig如下:
package com.atguigu.config;
@Configuration
public class MyConfig {
@Bean
public Student student() {
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testInitializingAndDisposableBean() {
// 1、创建IOC容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("容器被创建完成");
// 2、获取容器中的对象
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);
// 3、关闭容器
((AnnotationConfigApplicationContext) applicationContext).close();
}
}
测试结果如下:
构造方法被调用
初始化方法被调用
容器被创建完成
Student(name=李华, age=23)
销毁方法被调用
13.2_尚硅谷_生命周期-InitializingBean和DisposableBean(原型模式)
Student如下:
package com.atguigu.model;
@Data
@NoArgsConstructor
public class Student implements InitializingBean, DisposableBean {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("构造方法被调用");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化方法被调用");
}
@Override
public void destroy() throws Exception {
System.out.println("销毁方法被调用");
}
}
MyConfig如下:
package com.atguigu.config;
@Configuration
public class MyConfig {
@Scope("prototype")
@Bean
public Student student() {
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testInitializingAndDisposableBean() {
// 1、创建IOC容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("容器被创建完成");
// 2、获取容器中的对象
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);
// 3、关闭容器
((AnnotationConfigApplicationContext) applicationContext).close();
}
}
测试结果如下:
容器被创建完成
构造方法被调用
初始化方法被调用
Student(name=李华, age=23)
解释说明:
本次是在实体类上实现接口完成bean初始化和销毁的;
其中实现InitializingBean接口,并且重写afterPropertiesSet方法来完成bean初始化操作;
然后实现DisposableBean接口,并且重写destroy方法来完成bean销毁操作
14.1_尚硅谷_生命周期-@PostConstruct&@PreDestroy(单例模式)
Student如下:
package com.atguigu.model;
@Data
@NoArgsConstructor
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("构造方法被调用");
}
@PostConstruct
public void init() {
System.out.println("初始化方法被调用");
}
@PreDestroy
public void destroy() {
System.out.println("销毁方法被调用");
}
}
MyConfig如下:
package com.atguigu.config;
@Configuration
public class MyConfig {
@Bean
public Student student() {
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testInitializingAndDisposableBean() {
// 1、创建IOC容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("容器被创建完成");
// 2、获取容器中的对象
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);
// 3、关闭容器
((AnnotationConfigApplicationContext) applicationContext).close();
}
}
测试结果如下:
构造方法被调用
初始化方法被调用
容器被创建完成
Student(name=李华, age=23)
销毁方法被调用
14.2_尚硅谷_生命周期-@PostConstruct&@PreDestroy(原型模式)
Student如下:
package com.atguigu.model;
@Data
@NoArgsConstructor
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("构造方法被调用");
}
@PostConstruct
public void init() {
System.out.println("初始化方法被调用");
}
@PreDestroy
public void destroy() {
System.out.println("销毁方法被调用");
}
}
MyConfig如下:
package com.atguigu.config;
@Configuration
public class MyConfig {
@Scope("prototype")
@Bean
public Student student() {
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testInitializingAndDisposableBean() {
// 1、创建IOC容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("容器被创建完成");
// 2、获取容器中的对象
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);
// 3、关闭容器
((AnnotationConfigApplicationContext) applicationContext).close();
}
}
测试结果如下:
容器被创建完成
构造方法被调用
初始化方法被调用
Student(name=李华, age=23)
15.1_尚硅谷_生命周期-BeanPostProcessor-后置处理器(单例模式)
Student如下:
package com.atguigu.model;
@Data
@NoArgsConstructor
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("构造方法被调用");
}
}
MyBeanPostProcessor如下:
package com.atguigu.processor;
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
// 在所有初始化方法调用之前做操作
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在所有初始化方法之前做操作,bean名称:"+beanName);
return bean; // 可以直接返回bean对象,或者包装之后的bean对象,可以向上看BeanPostProcessor中的postProcessBeforeInitialization方法的返回值描述
}
// 在所有初始化方法调用之后做操作
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在所有初始化方法之后做操作,bean名称:"+beanName);
return bean; // 可以直接返回bean对象,或者包装之后的bean对象,可以向上看BeanPostProcessor中的postProcessAfterInitialization方法的返回值描述
}
}
MyConfig如下:
package com.atguigu.config;
@Configuration
@ComponentScan("com.atguigu.processor")
public class MyConfig {
@Bean
public Student student() {
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testInitializingAndDisposableBean() {
// 1、创建IOC容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("容器被创建完成");
// 2、获取容器中的对象
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);
// 3、关闭容器
((AnnotationConfigApplicationContext) applicationContext).close();
}
}
测试结果如下:
在所有初始化之前做操作,bean名称:myConfig
在所有初始化之后做操作,bean名称:myConfig
构造方法被调用
在所有初始化之前做操作,bean名称:student
在所有初始化之后做操作,bean名称:student
容器被创建完成
Student(name=李华, age=23)
15.2_尚硅谷_生命周期-BeanPostProcessor-后置处理器(原型模式)
Student如下:
package com.atguigu.model;
@Data
@NoArgsConstructor
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("构造方法被调用");
}
}
MyBeanPostProcessor如下:
package com.atguigu.processor;
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
// 在所有初始化方法调用之前做操作
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在所有初始化方法之前做操作,bean名称:"+beanName);
return bean; // 可以直接返回bean对象,或者包装之后的bean对象,可以向上看BeanPostProcessor中的postProcessBeforeInitialization方法的返回值描述
}
// 在所有初始化方法调用之后做操作
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在所有初始化方法之后做操作,bean名称:"+beanName);
return bean; // 可以直接返回bean对象,或者包装之后的bean对象,可以向上看BeanPostProcessor中的postProcessAfterInitialization方法的返回值描述
}
}
MyConfig如下:
package com.atguigu.config;
@Configuration
@ComponentScan("com.atguigu.processor")
public class MyConfig {
@Scope("prototype")
@Bean
public Student student() {
return new Student("李华", 23);
}
}
测试代码如下:
package com.atguigu;
public class Test {
@org.junit.Test
public void testInitializingAndDisposableBean() {
// 1、创建IOC容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("容器被创建完成");
// 2、获取容器中的对象
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);
// 3、关闭容器
((AnnotationConfigApplicationContext) applicationContext).close();
}
}
测试结果如下:
在所有初始化方法之前做操作,bean名称:myConfig
在所有初始化方法之后做操作,bean名称:myConfig
容器被创建完成
构造方法被调用
在所有初始化方法之前做操作,bean名称:student
在所有初始化方法之后做操作,bean名称:student
Student(name=李华, age=23)
解释说明:
在所有初始化方法之前做操作的含义是:在目录中的12、13、14中的那些初始化方法完成之前做操作;
在所有初始化方法之后做操作的含义是:在目录中的12、13、14中的那些初始化方法完成之后做操作;
这种方式将针对所有的bean实例
15.3_总结上述几种bean实例初始化方法和销毁方法
上述几种方式总结:
- 注解属性方式:@Bean指定initMethod属性和destoryMethod属性来指定初始化方法和销毁方法,详情可见目录 12
- 接口方式:实体类实现InitializingBean接口,然后重写两个方法用来定义初始化方法和销毁方法,详情可见13
- 注解方式:实体类中定义初始化方法和销毁方法,然后在方法上面分别添加@PostConstruct和@PreDestroy来实现功能,详情可见目录14
- 实现类方式:定义实现类实现BeanPostProcessor接口,然后扫描该实现类,详情可见目录 15
细节阐述:
- 上述几种方式的区别:序号4中“实现类方式”中重写的postProcessBeforeInitialization方法在上述序号1、2、3初始化方法之前执行,所以说这是最先执行的初始化方法;另外序号4中“实现类方式”中重写的postProcessAfterInitialization方法在上述序号1、2、3初始化方法之后执行,所以说这是最后执行的初始化方法
- 大家可以从目录12、13、14、15中看到在bean可以使用单例模式,也可以是用原型模式,区别是初始化方法执行的位置不同
对于初始化方法:单例模式的初始化方法在容器创建完成之前执行
并且只会执行一次,而原型模式的初始化方法在bean实例被调用的时候执行,并且调用一次执行一次
对于销毁方法:单例模式的销毁方法在容器关闭之前执行,而原型模式的销毁方法不会执行
18_尚硅谷_属性赋值-@Value赋值
Student:
package com.atguigu.model;
@Data
public class Student {
@Value("明快de玄米61")
private String name;
@Value("#{25-2}")
private Integer age;
}
MyConfig:
package com.atguigu.config;
@Configuration
public class MyConfig {
@Bean
public Student student() {
return new Student();
}
}
Test:
package com.atguigu;
public class Test {
@org.junit.Test
public void testValue() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);
}
}
测试结果:
Student(name=明快de玄米61, age=23)
解释说明:
在上述实体类Student中,@Value注解使用了两次,分别是常量值和计算表达式
19_尚硅谷_属性赋值-@PropertySource加载外部配置文件
在xml配置文件中,如果我们向扫描一个配置文件,然后通过${XXX}的方式使用配置文件中的值,我们需要先引入这个配置文件,如下所示:
<context:property-placeholder location="classpath:application.properties" />
application.properties:
student.address=河南省
Student:
package com.atguigu.model;
@Data
public class Student {
@Value("明快de玄米61")
private String name;
@Value("#{25-2}")
private Integer age;
@Value("${student.address}")
private String address;
}
MyConfig:
package com.atguigu.config;
@Configuration
@PropertySource(value = {"classpath:/application.properties"}, encoding = "UTF-8")
public class MyConfig {
@Bean
public Student student() {
return new Student();
}
}
Test:
package com.atguigu;
public class Test {
@org.junit.Test
public void testValue() {
// 1、通过bean获取
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);
// 2、从环境变量中取值
Environment environment = applicationContext.getEnvironment();
String address = environment.getProperty("student.address");
System.out.println("住址:" + address);
}
}
测试结果:
Student(name=明快de玄米61, age=23, address=河南省)
住址:河南省
解释说明:
在上述实体类Student中,@Value注解中通过${XXX}的方式来获取配置文件中的值,注意与#{XXX}的方式可不同;另外在配置类中通过@PropertySource注解来加载配置文件
20_尚硅谷_自动装配-@Autowired&@Qualifier&@Primary
对于@Autowired(Spring注解),本次只讲用法,不讲实际代码,原因是代码过于多,反而影响理解
- @Qualifier:根据@Qualifier(“bean名称”)指定的bean名称来查找对应的bean实例,如果找不到,则会出现异常
- 类型:如果没有@Qualifier注解,那就根据类型来找
- @Primary:如果根据类型可以找到多个bean实例,那就先找添加有@Primary注解的那个,可以在@Service上面添加,也可以在@Bean注解上面添加
- 属性名称:如果根据类型可以找到多个bean实例,并且没有一个添加的有@Primary注解,那就根据属性名称来找对应bean实例
上面说过如果找不到对应的bean实例,将会出现异常,如果不想出现异常,可以允许对象为null,那就需要在@Autowired注解后面添加required属性,值为false,如下:
21_尚硅谷_自动装配-@Resource&@Inject
@Resource(JSR-250,java注解):
- 注解属性name:先看@Resource注解后面的name属性,先根据name属性查找;如果根据name属性找不到,将会报错
- 属性名称:如果没有name属性,将会根据属性名称查找
- 属性类型:如果属性名称找不到,将根据属性类型查找
注意:
@Resource和@primary、required=false无关
@Inject(JSR-330,java注解):
1、添加依赖到pom.xml中
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
2、使用@Inject注解
@Inject注解里面没有属性,比如required=false是不支持的,但是其他用法和@Autowired一致,比如@primary、@Qualifier都是支持的
22_尚硅谷_自动装配-方法、构造器位置的自动装配
构造器:
@Controller
public class MyController {
public MyService myService;
@Autowired
public MyController(MyService myService) {
this.myService = myService;
}
}
如此就可以将myService属性赋值完成了
注意:
如果类中只有一个有参构造方法,可以不添加@Autowired注解,如下:
@Controller
public class MyController {
public MyService myService;
public MyController(MyService myService) {
this.myService = myService;
}
}
set方法:
public class MyController {
public MyService myService;
@Autowired
public void setMyService(MyService myService) {
this.myService = myService;
}
}
这样就可以完成myService的赋值了
@Bean注解:
》》》MyConfig.java:
@Configuration
@ComponentScan({"com.atguigu"})
public class MyConfig {
@Bean
public MyController myController(MyService myService) {
return new MyController();
}
}
》》》MyControll.java:
public class MyController {
public MyService myService;
}
如此便可以将myService注入到MyController类中,当然你也可以在MyService前面添加@Autowired注解,这也是@Autowired注解可以添加到参数前面的证据,如下:
》》》MyConfig.java:
@Configuration
@ComponentScan({"com.atguigu"})
public class MyConfig {
@Bean
public MyController myController(@Autowired MyService myService) {
return new MyController();
}
}
23_尚硅谷_自动装配-Aware注入Spring底层组件&原理
Student:
@Data
public class Student implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
System.out.println("applicationContext:" + applicationContext);
}
@Override
public void setBeanName(String name) {
System.out.println("bean名称:" + name);
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String address = resolver.resolveStringValue("${student.address}");
System.out.println("地址:" + address);
}
}
MyConfig:
@Configuration
@PropertySource(value = {"classpath:/application.properties"}, encoding = "UTF-8")
public class MyConfig {
@Bean
public Student student() {
return new Student();
}
}
Test:
public class Test {
@org.junit.Test
public void testResource() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Student student = (Student) applicationContext.getBean(Student.class);
}
}
测试结果:
bean名称:student
地址:河南省
applicationContext:org.springframework.context.annotation.AnnotationConfigApplicationContext@7cef4e59, started on Sun Nov 14 14:19:41 CST 2021
24_尚硅谷_自动装配-@Profile环境搭建
25_尚硅谷_自动装配-@Profile根据环境注册bean
application.properties:
database.username=root
database.password=123456
database.driverClassName=com.mysql.jdbc.Driver
test.database.url=jdbc:mysql//localhost:3306/test
dev.database.url=jdbc:mysql//localhost:3306/dev
prod.database.url=jdbc:mysql//localhost:3306/prod
MyConfig:
@Configuration
@PropertySource(value = {"classpath:/application.properties"}, encoding = "UTF-8")
public class MyConfig implements EmbeddedValueResolverAware {
// 参数解析器
private StringValueResolver resolver;
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}
// 测试环境
@Profile("test")
@Bean
public DataSource testDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(resolver.resolveStringValue("${database.username}"));
dataSource.setPassword(resolver.resolveStringValue("${database.password}"));
dataSource.setUrl(resolver.resolveStringValue("${test.database.url}"));
dataSource.setDriverClassName(resolver.resolveStringValue("${database.driverClassName}"));
return dataSource;
}
// 开发环境
@Profile("dev")
@Bean
public DataSource devDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(resolver.resolveStringValue("${database.username}"));
dataSource.setPassword(resolver.resolveStringValue("${database.password}"));
dataSource.setUrl(resolver.resolveStringValue("${dev.database.url}"));
dataSource.setDriverClassName(resolver.resolveStringValue("${database.driverClassName}"));
return dataSource;
}
// 生产环境
@Profile("prod")
@Bean
public DataSource prodDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(resolver.resolveStringValue("${database.username}"));
dataSource.setPassword(resolver.resolveStringValue("${database.password}"));
dataSource.setUrl(resolver.resolveStringValue("${prod.database.url}"));
dataSource.setDriverClassName(resolver.resolveStringValue("${database.driverClassName}"));
return dataSource;
}
}
Test:
public class Test {
@org.junit.Test
public void testProfile() {
// 1、创建ApplicationContext
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 2、设置激活环境
applicationContext.getEnvironment().setActiveProfiles("dev");
// 3、注册主配置类
applicationContext.register(MyConfig.class);
// 4、刷新并启动容器
applicationContext.refresh();
}
}
26_尚硅谷_IOC-小结
组件添加:
组件赋值:
组件注入:
27_尚硅谷_AOP-AOP功能测试
添加依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
MyAspect:
@Aspect
public class MyAspect {
@Pointcut("execution(public String com.atguigu.controller.MyController.*(..))")
public void pointCut() {
}
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
System.out.println("执行前置通知方法……");
System.out.println("参数:" + Arrays.asList(joinPoint.getArgs()));
}
@After("pointCut()")
public void after() {
System.out.println("执行后置通知方法……");
}
@AfterReturning(value = "pointCut()", returning = "object")
public void afterReturning(Object object) {
System.out.println("执行返回通知方法……");
System.out.println("返回值:" + object);
}
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void afterThrowing(Exception exception) {
System.out.println("执行异常通知方法……");
System.out.println("异常信息:" + exception.getMessage());
}
}
MyConfig:
@Configuration
@EnableAspectJAutoProxy // 开启aspect注解功能
public class MyConfig {
@Bean
public MyController myController() {
return new MyController();
}
@Bean
public MyAspect myAspect() {
return new MyAspect();
}
}
MyController:
public class MyController {
public String testAspect(String arg) {
System.out.println("方法被调用");
return arg;
}
}
Test:
public class Test {
@org.junit.Test
public void testAspect() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
MyController myController = (MyController) applicationContext.getBean(MyController.class);
myController.testAspect("hello world!");
}
}
测试结果1:
执行前置通知方法……
参数:[hello world!]
方法被调用
执行返回通知方法……
返回值:hello world!
执行后置通知方法……
如果想要测试异常情况,可以改变MyController类,如下:
public class MyController {
public String testAspect(String arg) {
System.out.println("方法被调用");
int a = 10 / 0;
return arg;
}
}
测试结果2:
执行前置通知方法……
参数:[hello world!]
方法被调用
执行异常通知方法……
异常信息:/ by zero
执行后置通知方法……
java.lang.ArithmeticException: / by zero
……
解释说明:
- @Pointcut:使用该注解,我们可以从重复的配置中解脱出来,然后只需要将方法配置在注解后面即可,不然我们每一个注解后面都需要添加切面方法配置
- JoinPoint:可以操控方法以及参数等,必须放在参数中的第一个
- returning:指定接收方法返回值的参数名称
- throwing:指定接收异常的参数名称
- 让切面类起作用的配置:(1)切面类添加@Aspect注解(2)将切面类交给Spring管理,即注册为bean实例(3)在配置类上添加@EnableAspectJAutoProxy注解,该注解相当于xml配置中的
<aop:aspectj-autoproxy/>
36_尚硅谷_声明式事务-环境搭建
37_尚硅谷_声明式事务-测试成功
- 添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
- 开始事务驱动:在主配置类上添加
@EnableTransactionManagement
注解,相当于xml配置文件中的<tx:annotation-driven/>
- 添加事务注解:在方法上添加
@Transactional
注解即可开启事务驱动,不过里面的参数是传播机制、隔离级别、超时时间、超时时间、是否只读等