尚硅谷——Spring注解驱动教程笔记

目录

大纲

在这里插入图片描述

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.CUSTOMclasses 用的是过滤器类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_PROTOTYPESCOPE_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_总结上述几种在容器中注册组件的方式

  1. 包扫描+组件标注注解(@Configuration+@ComponentScan+@Component / @Controller / @Service / @Repository),详情可见目录 03
  2. @Bean用于导入第三方包中的组件,详情可见目录 02
  3. @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 {
	……
  1. 实现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实例初始化方法和销毁方法

上述几种方式总结:

  1. 注解属性方式:@Bean指定initMethod属性和destoryMethod属性来指定初始化方法和销毁方法,详情可见目录 12
  2. 接口方式:实体类实现InitializingBean接口,然后重写两个方法用来定义初始化方法和销毁方法,详情可见13
  3. 注解方式:实体类中定义初始化方法和销毁方法,然后在方法上面分别添加@PostConstruct和@PreDestroy来实现功能,详情可见目录14
  4. 实现类方式:定义实现类实现BeanPostProcessor接口,然后扫描该实现类,详情可见目录 15

细节阐述:

  1. 上述几种方式的区别:序号4中“实现类方式”中重写的postProcessBeforeInitialization方法在上述序号1、2、3初始化方法之前执行,所以说这是最先执行的初始化方法;另外序号4中“实现类方式”中重写的postProcessAfterInitialization方法在上述序号1、2、3初始化方法之后执行,所以说这是最后执行的初始化方法
  2. 大家可以从目录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注解),本次只讲用法,不讲实际代码,原因是代码过于多,反而影响理解

  1. @Qualifier:根据@Qualifier(“bean名称”)指定的bean名称来查找对应的bean实例,如果找不到,则会出现异常
  2. 类型:如果没有@Qualifier注解,那就根据类型来找
  3. @Primary:如果根据类型可以找到多个bean实例,那就先找添加有@Primary注解的那个,可以在@Service上面添加,也可以在@Bean注解上面添加
  4. 属性名称:如果根据类型可以找到多个bean实例,并且没有一个添加的有@Primary注解,那就根据属性名称来找对应bean实例
    在这里插入图片描述

上面说过如果找不到对应的bean实例,将会出现异常,如果不想出现异常,可以允许对象为null,那就需要在@Autowired注解后面添加required属性,值为false,如下:
在这里插入图片描述

21_尚硅谷_自动装配-@Resource&@Inject

@Resource(JSR-250,java注解):

  1. 注解属性name:先看@Resource注解后面的name属性,先根据name属性查找;如果根据name属性找不到,将会报错
  2. 属性名称:如果没有name属性,将会根据属性名称查找
  3. 属性类型:如果属性名称找不到,将根据属性类型查找
    在这里插入图片描述

注意:

@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
……

解释说明:

  1. @Pointcut:使用该注解,我们可以从重复的配置中解脱出来,然后只需要将方法配置在注解后面即可,不然我们每一个注解后面都需要添加切面方法配置
  2. JoinPoint:可以操控方法以及参数等,必须放在参数中的第一个
  3. returning:指定接收方法返回值的参数名称
  4. throwing:指定接收异常的参数名称
  5. 让切面类起作用的配置:(1)切面类添加@Aspect注解(2)将切面类交给Spring管理,即注册为bean实例(3)在配置类上添加@EnableAspectJAutoProxy注解,该注解相当于xml配置中的<aop:aspectj-autoproxy/>

36_尚硅谷_声明式事务-环境搭建

37_尚硅谷_声明式事务-测试成功

  1. 添加依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>
  1. 开始事务驱动:在主配置类上添加@EnableTransactionManagement注解,相当于xml配置文件中的<tx:annotation-driven/>
  2. 添加事务注解:在方法上添加@Transactional注解即可开启事务驱动,不过里面的参数是传播机制、隔离级别、超时时间、超时时间、是否只读等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值