Spring注解开发详细教程
  TEZNKK3IfmPf 2023年11月13日 20 0

Spring注解开发详细教程 (配合雷神的视频)

一、AnnotationConfigApplicationContext

1.配置类

1.1使用传统xml方式

applicationContext.xml:

<bean id="person" class="com.rg.domain.Person" >
    <property name="name" value="张三"/>
    <property name="age" value="19"/>
</bean>

person类:

package com.rg.domain;

public class Person {
     
       
    private String name;
    private Integer age;

    public String getName() {
     
       
        return name;
    }

    public void setName(String name) {
     
       
        this.name = name;
    }

    public Integer getAge() {
     
       
        return age;
    }

    public void setAge(Integer age) {
     
       
        this.age = age;
    }

    public Person(String name, Integer age) {
     
       
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
     
       
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类:

@Test
public void test01(){
     
       
    //加载配置文件
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    Person person = (Person) applicationContext.getBean("person");//通过id获取实体
    System.out.println(person);
}

1.2使用配置类方式:

MainConfig 配置类

//配置类==配置文件
@Configuration //告诉Spring这是一个配置类
public class MainConfig {
     
       
    //给容器注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
    @Bean("person") //修改方法名称
    public Person person(){
     
       
        return new Person("lisi",20);
    }
}

测试类

@Test
public void test02(){
     
       
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    Person person = applicationContext.getBean(Person.class);//通过类名来获取
    System.out.println(person);
}

2.包扫描

2.1传统xml方式:

<!--包扫描,只要标注了@Controller,@service,@Repository,@Component 类对象就会被自动加载到IOC容器中-->
<context:component-scan base-package="com.rg"/>

2.2注解方式:

MainConfig配置类

//配置类==配置文件
@Configuration //告诉Spring这是一个配置类
@ComponentScan("com.rg")
public class MainConfig {
     
       
    //给容器注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
    @Bean("person") //修改方法名称
    public Person person(){
     
       
        return new Person("lisi",20);
    }
}

项目结构:

Spring注解开发详细教程

其中controller,dao,service中的类均使用相对应的注解.

测试类:

@Test
public void test03(){
     
       
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    String[] names = applicationContext.getBeanDefinitionNames();//获取容器中所有的对象名称
    for (String name : names) {
     
       
        System.out.println(name);
    }
}

运行结果:

Spring注解开发详细教程

二、组件添加

1.@ComponentScan

1.1传统xml

<!--包扫描,只要标注了@Controller,@service,@Repository,@Component 类对象就会被自动加载到IOC容器中-->
<context:component-scan base-package="com.rg" use-default-filters="false">
     <!--use-default-filters : 是否使用默认的过滤器,默认值true 扫描包中的全部文件-->
	 <!-- 注意:若使用include-filter去定制扫描内容,要在use-default-filters="false"的情况下,不然会“失效”,被默认的过滤机制所覆盖 -->
    type可为regex (正则),annotation(注解),assignable(接口或类)
    
   <context:exclude-filter type="assignable" expression="com.rg.controller.BookController"/>
        <context:include-filter type="assignable" expression="com.rg.service.BookService"/>
    
</context:component-scan>

1.2使用配置类

1.2.1ComponentScan注解的基本使用
//配置类==配置文件
@Configuration //告诉Spring这是一个配置类

//JDK8之后可以写多个ComponentScan;如果不是该版本则可 使用ComponentScans属性
//注意
@ComponentScan(value="com.rg",includeFilters = {
     
       
        @ComponentScan.Filter(type=FilterType.ANNOTATION,classes={
     
       Controller.class})},
        useDefaultFilters = false)

@ComponentScans(
        value = {
     
       
                @ComponentScan(value="com.rg",includeFilters = {
     
       
                        @ComponentScan.Filter(type=FilterType.ANNOTATION,classes={
     
       Controller.class}),//第一个过滤器,根据注解类型
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {
     
       BookService.class}),//根据给定的类型
                },
                 useDefaultFilters = false),
                @ComponentScan(),
                //...
        }
)
//@ComponentScan value:指定要扫描的包
//excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
//includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照给定的类型;
//FilterType.ASPECTJ:使用ASPECTJ表达式
//FilterType.REGEX:使用正则指定
//FilterType.CUSTOM:使用自定义规则
public class MainConfig {
     
       
    //给容器注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
    @Bean("person") //修改方法名称
    public Person person(){
     
       
        return new Person("lisi",20);
    }
}

测试结果(测试类和上一个相同):

只有Controller包和service包中创建的对象

Spring注解开发详细教程

注意: pom.xml中的

<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>

控制的是编译使用的JDK版本,如果此处使用的是JDK7,则MainConfig上无法添加多个@ComponentScan.

1.2.2自定义过滤器

在config包中创建MyTypeFilter类,并继承TypeFilter接口,写过滤条件.

public class MyTypeFilter implements TypeFilter {
     
       
    /** * * @param metadataReader:读取到的当前正在扫描的类的信息 * @param metadataReaderFactory:可以获取到其他任何类的信息 * @return * @throws IOException */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
     
       
        //获取当前类注解的信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前扫描的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        String className = classMetadata.getClassName();//获取当前的类名
        System.out.println("-->" + className);
        if (className.contains("er")) {
     
       //如果类名包含er
            return true;//被过滤器过滤
        }
        return false;//不被过滤
    }
}

MainConfig中引入:

@ComponentScan(value="com.rg",includeFilters = {
     
       
        // @ComponentScan.Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
        // @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}),
        @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {
     
       MyTypeFilter.class})
        }, useDefaultFilters = false)
@Configuration //告诉Spring这是一个配置类
public class MainConfig {
     
       
    //给容器注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
    @Bean("person") //修改方法名称
    public Person person(){
     
       
        return new Person("lisi",20);
    }
}

测试结果:

第一部分是符合条件的类名称,第二部分是存入到IOC容器中的对象的名称.

Spring注解开发详细教程

2.@Scope

MainConfig:

//默认是单实例的
/** * * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE * * @see ConfigurableBeanFactory#SCOPE_SINGLETON * * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request * * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION session * @return\ * @Scope:调整作用域 * prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。 * 每次获取的时候才会调用方法创建对象; * singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。 * 以后每次获取就是直接从容器(map.get())中拿, * request:同一次请求创建一个实例 * session:同一个session创建一个实例 * * 懒加载: * 单实例bean:默认在容器启动的时候创建对象; * 懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化; * */
@Scope( )
@Configuration
public class MainConfig2 {
     
       
    //给容器注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
    @Bean("person")
    public Person person(){
     
       
        System.out.println("给容器中添加Person...");
        return new Person("lisi",20);
    }
}

测试:

@Test
public void test04(){
     
       

    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    //System.out.println("IOC容器创建完成...");
    //System.out.println("hahha嗷嗷嗷");
    // Person person = (Person) applicationContext.getBean("person");
    // Person person2 = (Person) applicationContext.getBean("person");
    // System.out.println(person == person2); //当为默认时,结果为true,当为prototype结果为false.


}

运行结果:

Spring注解开发详细教程

当修改为@Scope(“propotype”)时,会先创建IOC容器,然后每次获取才调方法,创对象.
Spring注解开发详细教程

:此处会出现@Scope(“propotype”)失效的问题,解决办法还没有找到…

3.@Lazy

单实例bean:默认在容器启动的时候创建对象;

懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化.

MainConfig

@Lazy
@Scope
@Configuration
public class MainConfig2 {
     
       
    //给容器注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
    @Bean("person")
    public Person person(){
     
       
        System.out.println("给容器中添加Person...");
        return new Person("lisi",20);
    }
}

测试:

@Test
public void test04(){
     
       

    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    System.out.println("IOC容器创建完成...");
    //System.out.println("hahha嗷嗷嗷");
       Person person = (Person) applicationContext.getBean("person");
     Person person2 = (Person) applicationContext.getBean("person");
     System.out.println(person == person2);


}

测试结果:

Spring注解开发详细教程

4.@Conditional

按照一定的条件进行判断,满足条件给容器中注册bean;

例:

  • 如果系统是windows,给容器中注册(“bill”)
  • 如果是linux系统,给容器中注册(“linus”)

创建LinuxCondition,WindowsCondition并实现Condition接口.

LinuxCondition

//判断是否是Linux系统
public class LinuxCondition implements Condition {
     
       
    /** * * @param context 判断条件使用的上下文(环境) * @param metadata :注释信息 * @return */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
     
       
        //1.获取IOC使用的BeanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //2.获取类加载器
        ClassLoader classLoader = context.getClassLoader();
        //3.获取当前环境信息
        Environment environment = context.getEnvironment();
        //4.获取Bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();

        String property = environment.getProperty("os.name");

        //可以判断容器中的Bean注册情况,也可以给容器中注册Bean
        if(property.contains("linux")){
     
       
            return true;
        }


        return false;
    }
}

WindowsCondition

//判断是否是windows系统
public class WindowsCondition implements Condition {
     
       
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
     
       
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        if(property.contains("Windows")){
     
       
            return true;
        }
        return false;
    }
}

MainConfig

//类中组件统一设置。满足当前条件,这个类中配置的所有bean注册才能生效;
//@Conditional({WindowsCondition.class})
@Configuration
public class MainConfig3 {
     
       


    /** * @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean * * 如果系统是windows,给容器中注册("bill") * 如果是linux系统,给容器中注册("linus") */
    @Conditional({
     
       WindowsCondition.class})
    @Bean("bill")
    public Person person01(){
     
       
        return new Person("Bill Gates",62);
    }

    @Conditional({
     
       LinuxCondition.class})
    @Bean("linus")
    public Person person02(){
     
       
        return new Person("linus",48);
    }
    
}

测试:

@Test
public void test05() {
     
       
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
    String[] names = applicationContext.getBeanNamesForType(Person.class);
    //获取系统环境配置对象
    ConfigurableEnvironment environment = applicationContext.getEnvironment();
    //动态获取环境变量
    String property = environment.getProperty("os.name");
    System.out.println(property);
    for (String name : names) {
     
       
        System.out.println(name);
    }
    Map <String, Person> persons = applicationContext.getBeansOfType(Person.class);
    System.out.println(persons);

}

运行结果:

Spring注解开发详细教程

当在类上使用时表示:

对类中组件统一设置,满足当前条件,这个类中配置的所有bean注册才能生效;

5.@Import

5.1 @Import(要导入的容器中的组件);

容器中会自动注册这个组件,id默认是全类名.

创建Color,Red类作为导入的组件.

MainConfig

//类中组件统一设置。满足当前条件,这个类中配置的所有bean注册才能生效;
@Conditional({
     
       WindowsCondition.class})
@Configuration
//@Import(value = Color.class)
@Import({
     
       Color.class, Red.class})
public class MainConfig4 {
     
       

    @Bean("person") //修改方法名称
    public Person person(){
     
       
        return new Person("lisi",20);
    }

    @Conditional({
     
       WindowsCondition.class})
    @Bean("bill")
    public Person person01(){
     
       
        return new Person("Bill Gates",62);
    }

    @Conditional({
     
       LinuxCondition.class})
    @Bean("linus")
    public Person person02(){
     
       
        return new Person("linus",48);
    }

}

测试:

@Test
public void test06() {
     
       
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
    String[] names = applicationContext.getBeanDefinitionNames();//获取容器中所有的对象名称
    for (String name : names) {
     
       
        System.out.println(name);
    }

}

运行结果:

Spring注解开发详细教程

5.2 ImportSelector

返回需要导入的组件的全类名数组;

作用:批量注册Bean:

编写MyInportSelector 实现ImportSelector接口

public class MyInportSelector implements ImportSelector {
     
       
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
     
       
        return new String[]{
     
       "com.rg.domain.Blue","com.rg.domain.Yellow"};
    }
}

MainConfig上添加注解:@Import({Color.class, Red.class, MyInportSelector.class})

运行结果:

Spring注解开发详细教程

5.3 MyImportBeanDefinitionRegistrar

手动注册bean到容器中(可以指定Bean的名称)

创建MyImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar接口,编写注册的逻辑.

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
     
       
    /** * AnnotationMetadata:当前类的注解信息 * BeanDefinitionRegistry:BeanDefinition注册类; * 把所有需要添加到容器中的bean;调用 * BeanDefinitionRegistry.registerBeanDefinition手工注册进来,可以指定名称. */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
     
       
        boolean definition = registry.containsBeanDefinition("com.rg.domain.Red");
        boolean definition2 = registry.containsBeanDefinition("com.rg.domain.Blue");
        if(definition && definition2){
     
       
            //指定Bean定义信息;
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(RainBow.class);
            //注册一个Bean,指定Bean名
            registry.registerBeanDefinition("rainBow",rootBeanDefinition);
        }
    }
}

MainConfig上添加注解:@Import({Color.class, Red.class, MyInportSelector.class, MyImportBeanDefinitionRegistrar.class})

运行结果:

Spring注解开发详细教程

6.使用FactoryBean注册组件

使用Spring提供的 FactoryBean(工厂Bean);
1)、默认获取到的是工厂bean调用getObject创建的对象
2)、要获取工厂Bean本身,我们需要给id前面加一个& &colorFactoryBean

创建ColorFactoryBean 实现FactoryBean接口

//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
     
       
    //返回一个Color对象,这个对象会添加到容器中
    @Override
    public Color getObject() throws Exception {
     
       
        System.out.println("ColorFactoryBean...getObject...");
        return new Color();
    }

    //返回对象的类型
    @Override
    public Class <?> getObjectType() {
     
       
        return Color.class;
    }

    //是否单例
    //true:这个bean是单例,在容器中保存一份
    //false:多例,每次获取都会创建一个新的Bean
    @Override
    public boolean isSingleton() {
     
       
        return false;
    }
}

编写MainConfig

//类中组件统一设置。满足当前条件,这个类中配置的所有bean注册才能生效;
@Configuration
public class MainConfig5 {
     
       


    // 使用Spring提供的 FactoryBean(工厂Bean);
    //1)、默认获取到的是工厂bean调用getObject创建的对象
    //2)、要获取工厂Bean本身,我们需要给id前面加一个& &colorFactoryBean

    @Bean
    public ColorFactoryBean colorFactoryBean(){
     
       
        return new ColorFactoryBean();
    }

}

进行测试:

@Test
public void test07() {
     
       
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig5.class);
    //Bean 获取的是调用getObject创建的对象
    Object bean = applicationContext.getBean("colorFactoryBean");
    Object bean2 = applicationContext.getBean("colorFactoryBean");
    System.out.println("bean2 is Type:"+bean2.getClass());
    System.out.println(bean==bean2);

    Object bean3 = applicationContext.getBean("&colorFactoryBean");
    System.out.println(bean3.getClass());
}

运行结果:

Spring注解开发详细教程

&&小结:给容器中注册组件的四种方式

1)、包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]

2)、@Bean[导入的第三方包里面的组件]

3)、@Import[快速给容器中导入一个组件]

  • 1)、@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
  • 2)、ImportSelector:返回需要导入的组件的全类名数组

4)、使用Spring提供的 FactoryBean(工厂Bean);

  •    1)、默认获取到的是工厂bean调用getObject创建的对象
    
  • 2)、要获取工厂Bean本身,我们需要给id前面加一个& &colorFactoryBean

7.生命周期

7.1指定初始化和销毁方法

通过@Bean指定init-method和destroy-method,相当于xml中的

<bean id="person" class="com.rg.domain.Person"  init-method="XXX" destroy-method="XXX" >

构造(对象创建)

  • 单实例:在容器启动的时候创建对象
  • 多实例:在每次获取的时候创建对象

初始化: 对象创建完成并赋值好,调用初始化方法…

销毁:

  • 单实例:容器关闭的时候
  • 多实例:容器不会管理这个Bean,容器不会调用销毁方法.

编写MainConfigOfLifeCycle类

@Configuration
public class MainConfigOfLifeCycle {
     
       
    
    @Scope("prototype")
    @Bean(initMethod="init",destroyMethod = "destroy")
    public Car car(){
     
       
        return new Car();
    }
}

测试:

@Test
public void test01(){
     
       
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    System.out.println("Container creation complete.....");
    applicationContext.getBean("car");
    //关闭容器
    applicationContext.close();
}

当是单实例时:

Spring注解开发详细教程

当是多实例时:

Spring注解开发详细教程

7.2定义初始化逻辑

通过让Bean实现InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑);

创建Cat类并实现InitializingBean,DisposableBean接口.

@Component
public class Cat implements InitializingBean, DisposableBean {
     
       

    public Cat(){
     
       
        System.out.println("Cat constructor...");
    }

    @Override
    public void destroy() throws Exception {
     
       
        System.out.println("cat destroy...");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
     
       
        System.out.println("cat afterPropertiesSet ...");
    }
}

MainConfigOfLifeCycle配置类

@Configuration
@ComponentScan("com.rg")
public class MainConfigOfLifeCycle {
     
       
 	@Bean(initMethod="init",destroyMethod = "destroy")
    public Car car(){
     
       
        return new Car();
    }
}
@Test
public void test01(){
     
       
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    
    //关闭容器
    applicationContext.close();
}

运行结果:

Spring注解开发详细教程

7.3可以使用JSR250

@PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法

@PreDestroy:在容器销毁bean之前通知我们进行清理工作

创建Dog类:

@Component
public class Dog  {
     
       
    public Dog(){
     
       
        System.out.println("Dog constructor...");
    }

    //对象创建并赋值之后调用
    @PostConstruct
    public void init(){
     
       
        System.out.println("Dog PostConstruct...");
    }

    //容器移除对象之前
    @PreDestroy
    public void destroy(){
     
       
        System.out.println("Dog PreDestroy...");
    }
}

测试:

Spring注解开发详细教程

7.4BeanPostProcessor—bean的后置处理器

在bean初始化前后进行一些处理工作;

postProcessBeforeInitialization:在初始化之前工作

postProcessAfterInitialization:在初始化之后工作

创建MyBeanPostProcessor,实现BeanPostProcessor接口

/** * 后置处理器:初始化前后进行处理工作 * 将后置处理器加入到容器中 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor{
     
       


    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
     
       
        System.out.println("postProcessBeforeInitialization..."+beanName+"=>"+bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
     
       
        System.out.println("postProcessAfterInitialization"+beanName+"=>"+bean);
        return bean;
    }


}

运行结果:

Spring注解开发详细教程

BeanPostProcessor原理

populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值
initializeBean
{
     
       
   applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
   applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

Spring底层对 BeanPostProcessor 的使用;

bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx BeanPostProcessor;

8.@Value

作用:为属性赋值

  • 基本数值
  • 可以写SpEL:#{}
  • 可以写${};取出配置文件[properties]中的值(在运行环境变量中的值)

Person类

public class Person {
     
       
    //使用@Value赋值
    //1.基本数值
    //2.可以写SpEL:#{}
    //3.可以写${};取出配置文件[properties]中的值(在运行环境变量中的值)

    @Value("zhangsan")
    private String name;
    @Value("#{20+1}")
    private Integer age;

    @Value("${person.nickName}")
    private String nickName;

    public String getNickName() {
     
       
        return nickName;
    }

    public void setNickName(String nickName) {
     
       
        this.nickName = nickName;
    }

    public String getName() {
     
       
        return name;
    }

    public void setName(String name) {
     
       
        this.name = name;
    }

    public Integer getAge() {
     
       
        return age;
    }

    public void setAge(Integer age) {
     
       
        this.age = age;
    }

    public Person(String name, Integer age) {
     
       
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
     
       
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", nickName='" + nickName + '\'' +
                '}';
    }

    public Person(){
     
       

    }
}

MainConfig配置类:

//使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值
@PropertySource(value = {
     
       "classpath:person.properties"})
//@PropertySources(value = {@PropertySource({}),@PropertySource({})})
@Configuration
public class MainConfigOfPropertyValues {
     
       
    @Bean
    public Person person(){
     
       
        return new Person();
    }
}

测试

@Test
public void test08(){
     
       
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class);
    Person person = (Person) applicationContext.getBean("person");
    System.out.println(person);

    Environment environment = applicationContext.getEnvironment();
    String value = environment.getProperty("person.nickName");//通过environment也可以获取配置文件的属性值.
    System.out.println(value);
    applicationContext.close();
}

person.properties文件

person.nickName=\u5C0F\u674E\u56DB

运行结果:

Spring注解开发详细教程

9.自动装配

Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值;

9.1@Autowired:自动注入

1)、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值

2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找 applicationContext.getBean(“bookDao”)

@Controller
public class BookController {
     
       
    @Autowired
    private BookService bookService;
}

@Service
public class BookService {
     
       
    @Autowired
    private BookDao bookDao;

    @Override
    public String toString() {
     
       
        return "BookService{" +
                "bookDao=" + bookDao +
                '}';
    }
}

// 默认是类名首字母小写
@Repository
public class BookDao {
     
       
    private String Label = "1";

    public String getLabel() {
     
       
        return Label;
    }

    public void setLabel(String label) {
     
       
        Label = label;
    }

    @Override
    public String toString() {
     
       
        return "BookDao{" +
                "Label='" + Label + '\'' +
                '}';
    }
}
@Configuration
@ComponentScan({
     
       "com.rg.service","com.rg.dao","com.rg.controller"})
public class MainConfigOfAutowired {
     
       

    @Bean("bookDao2")
    public BookDao bookDao(){
     
       
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }
}

测试:

@Test
 public void test01(){
     
       
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
    //获取BookService中注入的BookDao
    BookService bookService = applicationContext.getBean(BookService.class);

    //BookDao bean = applicationContext.getBean(BookDao.class); 当IOC容器中有多个对象时不能用类.class进行获取对象.
    //System.out.println(bean);
    System.out.println(bookService);
}

运行结果:

IOC容器中有bookDao和bookDao2,因为@Autowired 下面的需要的名字为bookDao,所以里面注册的就是bookDao对象.如果改为bookDao2则里面注册的就是bookDao2对象.

Spring注解开发详细教程

3)、使用@Qualifier指定需要装配的组件的id,而不是默认使用属性名

BookService{
     
       
	  @Qualifier("bookDao2")	
      @Autowired
      BookDao  bookDao;
}

这样bookDao里面注册的就是bookDao2.

4)、自动装配默认一定要将属性赋值好,没有就会报错;通过使用@Autowired(required=false); 可以当有的时候进行注册,没有的时候为null.

BookService{
     
       
	  @Qualifier("bookDao2")	
      @Autowired(required = false)
      BookDao  bookDao;
}

Spring注解开发详细教程

5)、@Primary:让Spring进行自动装配的时候,默认使用首选的bean;也可以继续使用@Qualifier指定需要装配的bean的名字

BookService{
     
       
	  //@Qualifier("bookDao2") 
      @Autowired(required = false)
      BookDao  bookDao;
}

@Repository
public class BookDao {
     
       
    ......
}


@Configuration
@ComponentScan({
     
       "com.rg.service","com.rg.dao","com.rg.controller"})
public class MainConfigOfAutowired {
     
       

    @Primary
    @Bean("bookDao2")
    public BookDao bookDao(){
     
       
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }
}

当没有Qualifier时, 为BookDao加上@Primary,则其优先权会升高,会优先注册该对象. 但如果此时有Qualifier,则由里面的名称决定.

Spring注解开发详细教程

6 )、扩展:Spring还支持使用@Resource(JSR250)和@Inject(JSR330) [java规范的注解]

@Resource:

  •    可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
    
  • 没有能支持@Primary功能没有支持@Autowired(reqiured=false);
    

@Inject:

  •    需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;支持@Primary
    

@Autowired:Spring定义的规范; @Resource、@Inject都是java规范

小结:

@Autowired 当IOC容器中只有一个时,可以这样使用.此时由 待注册的名称决定.

@Autowired经常和@Qualifier一起使用;此时,可以打破默认,由Qualifier指定的对象注册.

@Autowired还可以和@Primary+其他组件注解(@Bean,@Service,@Controller…),从而提升其优先级,优先被使用.

9.2 扩展:@Autowired使用的位置:

@Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值

1.放在参数位置

1)、放在属性位置或setter方法上

默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作

@Component
public class Car {
     
       
	....
}
@Component
public class Boss {
     
       

    //@Autowired:属性位置.
    private Car car;
    public Car getCar() {
     
       
        return car;
    }

    //标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值
    //方法使用的参数,自定义类型从IOC容器中获取.
    @Autowired//方法上
    public void setCar(Car car) {
     
       
        this.car = car;
    }

}

2 ) 、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取

@Component
public class Car {
     
       
	....
}
@Component
public class Boss {
     
       

    
    private Car car;

    @Autowired//可省略
    public Boss(Car car){
     
       
        this.car = car;
        System.out.println("Boss 有参构造器...");
    }

    public Car getCar() {
     
       
        return car;
    }


  
    public void setCar(Car car) {
     
       
        this.car = car;
    }

    @Override
    public String toString() {
     
       
        return "Boss{" +
                "car=" + car +
                '}';
    }
}


程序中:会自动调用有参构造为Boss中的Car进行赋值.IOC容器中的Car和Boss中的Car是同一个.

3)、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配

@Bean
@Autowired
public Color color(Car car){
     
       
    Color color = new Color();
    color.setCar(car);
    return color;
}

注:参数卸载方法上或者 参数之前,或者省略都是可以的.

9.3@Profile

Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;

如:当开发环境时我们自动在环境中加入开发的相关组件,当是测试环境时,自动添加测试的相关组件,当是生成环境时,自动添加生产的组件.

@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件

@Configuration
@PropertySource("classpath:/jdbc.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
     
       



    private String driverClass;
    @Value("${jdbc.user}") // @value放在属性上
    private String user;

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
     
       
        //this.resolver = resolver;
        driverClass = resolver.resolveStringValue("${jdbc.driver}");
    }

    @Bean
    public Yellow yellow(){
     
       
        return new Yellow();
    }

    @Bean("prodDataSource")
    @Profile("prod")
    //@Profile("default") @Value放在参数上
    public DataSource dataSourceProd(@Value("${jdbc.password}")String pwd) throws Exception {
     
       
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql:///goods");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Profile("test")
    @Bean("testDataSource")
    public DataSource dataSourceTest(@Value("${jdbc.password}")String pwd) throws Exception {
     
       
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql:///test");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Bean("devDataSource")
    @Profile("dev")
    public DataSource dataSourceDev(@Value("${jdbc.password}")String pwd) throws Exception {
     
       
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql:///travel");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

}

补充:

使用配置文件的两种方式:

  1. 使用@PropertySource(“classpath:/文件名称”),搭配@Value(“键”)
  2. 类实现EmbeddedValueResolverAware通过 resolver.resolveStringValue 获取键对应的值.

测试:

	 @Test
    public void test01(){
     
       
        //1.使用命令行动态参数,在Edit Configurations的VM options中加入 -Dspring.profiles.active=test
        //2.使用代码的方式激活某种环境
        //1.创建一个applicationContext
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        //2.设置需要激活的环境
        //AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
       applicationContext.getEnvironment().setActiveProfiles("dev","test");
       //3.注册主配置类
       applicationContext.register(MainConfigOfProfile.class);
       //4.启动刷新容器
        applicationContext.refresh();
        String[] names = applicationContext.getBeanNamesForType(DataSource.class);
        System.out.println(names);
        for (String name : names) {
     
       
            System.out.println(name);
        }
        applicationContext.close();
    }

运行结果:

Spring注解开发详细教程

2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效

如在类上加@Profile(“test”),使用的是开发环境,则里面的所有配置都无效.只有一致时才生效.

3)、没有标注环境标识的bean在,任何环境下都是加载的;

根据尚硅谷 雷神的<<Spring注解开发>> 整理总结

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月13日 0

暂无评论

推荐阅读
  TEZNKK3IfmPf   2024年05月17日   46   0   0 JSpspring
TEZNKK3IfmPf