Spring 注解版

本文详细介绍了Spring框架中注解的使用,包括@Bean用于组件注册,@ComponentScan进行包扫描,@Scope设置bean的作用域,@Lazy实现懒加载,以及@Conditional按条件注册等。还涉及了@Autowired、@Qualifier和@Primary的自动装配机制,@Profile环境切换,AOP面向切面编程,以及事务处理的相关注解应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring 注解版

@Bean — 组件注册

对于一个普通的bean: Person

package com.spring.annotation.bean;

public class Person {

    private String name;
    private Integer age;

   getter() setter()...
}
  • 传统方式–配置文件

    applicationContext.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">
    
        <bean id="person" class="com.spring.annotation.bean.Person">
            <property name="name" value="Sairo"></property>
            <property name="age" value="18"></property>
        </bean>
        
    </beans>
    
  • 测试类

    采用配置文件的方式时创建ClassPathXmlApplicationContext对象

    public class XMLTest {
        @Test
        public void test00() {
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
            Person person = (Person) context.getBean("person");
            System.out.println(person);
        }
    }
    

在这里插入图片描述

  • 注解方式–配置类

    当采用注解方式时,需要编写一个用@Configutation标记的类作为配置类,用来取代配置文件

    MainConfig

    /**
     * 配置类代替了配置文件
     * @Configuration: 告诉Spring这是一个配置类
     */
    @Configuration
    public class MainConfig {
    
        /**
         * @Bean: 取代了 <bean><bean/> 标签
         *      给容器中注册一个bean, id默认是方法名
         */
        @Bean
        public Person person() {
            return new Person();
        }
    }
    
  • 测试类

    采用注解方式时,要创建AnnotationConfigApplicationContext实例,传入配置类的class对象

    public class AnnotationTest {
        @Test
        public void test00() {
            ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
            Person person = (Person) context.getBean("person");
            System.out.println(person);
        }
    }
    

    在这里插入图片描述

  • 更改bean在容器中的名字

    1. 修改配置类中的方法的名称

      public Person person01() {
          return new Person("LiSi", 20);
      }
      
    2. 在@Bean注解中通过value属性指定名字

      @Bean(value="person01")
      public Person person() {
          return new Person("LiSi", 20);
      }
      
    3. 测试

      @Test
      public void test01() {
          ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
          Person person = (Person) context.getBean("person");
          System.out.println(person);
          // 查看bean在Spring容器中的名字
          String[] beanNames = context.getBeanNamesForType(Person.class);
          for (String beanName : beanNames) {
              System.out.println(beanName);
          }
      }
      

在这里插入图片描述

@ComponentScan — 包扫描

包扫描是用于扫描指定包(及其子包)下的标注了@Controller, @Service, @Repository, @Component注解的类,并将它们加载到Spring容器中

  • 配置文件方式

    <!-- 包扫描, 用于扫描指定包(及其子包)下的标注了@Controller, @Service, Repository, @Component注解的类, 将其加载到容器中 -->
        <context:component-scan base-package="com.lymboy.spring" ></context:component-scan>
    
  • 注解方式

    标注在MainConfig配置类上(头部)

    @ComponentScan(value = "com.lymboy.spring")
    
  • 测试

    public class IOCTest {
        private ApplicationContext applicationContext;
    
        @Before
        public void init() {
            applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        }
    
        @Test
        public void test01() {
            int count = applicationContext.getBeanDefinitionCount();
            String[] names = applicationContext.getBeanDefinitionNames();
    
            System.out.println(count+": ");
            for (String name : names) {
                System.out.println(name);
            }
        }
    }
    

在这里插入图片描述

在这里插入图片描述

@ComponentScan属性详解

  • value: 用来指定扫描的包

    @ComponentScan(value = "com.lymboy.spring")
    
  • useDefaultFilters: 使用默认扫描规则, 全部扫描

    @ComponentScan(value = "com.lymboy.spring" useDefaultFilters = false) // 关闭
    
  • excludeFilters: 用来排除不需要的类

    Filter[] excludeFilters() default {};

    @ComponentScan(value = "com.lymboy.spring", excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
    })
    

    参数是一个 @Filter数组

    • type: 指定过滤方式 默认是 FilterType.ANNOTATION(注解)方式 (FilterType type() default FilterType.ANNOTATION;), 选择按注解类型过滤时, 在value中指定的包(及其子包)下的所有在classes(下面的这个属性)中指定的注解都不会被扫描
    • classes数组: 指定过滤的类型 . 如果是按注解过滤, 则classes中填注解类型
  • includeFilters: 仅加载指定的类, 首先要禁用默认扫描规则, 使用方法与上面的excludeFilters相同, 作用相反

    @ComponentScan(value = "com.lymboy.spring", useDefaultFilters = false, includeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
    })
    

过滤规则 FilterType

用来指定包扫描的过滤规则

public enum FilterType {
    // 注解方式			常用
	ANNOTATION,
	// 按照给定的类型		常用
	ASSIGNABLE_TYPE,
	// 不常用
	ASPECTJ,
	// 使用正则表达式
	REGEX,
	// 用户自定义
	CUSTOM

}
  • FilterType.ANNOTATION :

  • FilterType.ASSIGNABLE_TYPE : 与 FilterType.ANNOTATION 相似, 前者在classes属性中填 类的类型, 后者填注解的类型

  • FilterType.REGEX : 通过正则表达式过滤

    @ComponentScan(value = "com.lymboy.spring", useDefaultFilters = false, includeFilters = {
            @ComponentScan.Filter(type = FilterType.REGEX, pattern = {".*Service"})
    })
    
  • FilterType.CUSTOM : 通过实现 org.springframework.context.annotation.FilterType 接口, 自定义规则

    MyTypeFilter :自定义的类,实现了 FilterType 接口

    @ComponentScan(value = "com.lymboy.spring", useDefaultFilters = false, includeFilters = {
            @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
    })
    
package com.lymboy.spring.annotation.config;

public class MyTypeFilter implements TypeFilter {

    /**
     * @param metadataReader: 用来读取当前正在扫描的类的信息
     * @param metadataReaderFactory: 用来访问其他类的信息
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

        Resource resource = metadataReader.getResource();
        String filename = resource.getFilename();
        if (filename.contains("Service")) {
            return true;
        }

        return false;
    }
}

在这里插入图片描述

过滤方式表达格式
FilterType.ANNOTATIONclasses 属性中填入需过滤的注解的class对象
FilterType.ASSIGNABLE_TYPEclasses属性中填入需过滤的类的class对象
FilterType.REGEXpattern 属性中填入对应的正则表达式
FilterType.CUSTOM实现 FilterType 接口

@Scope — 作用域

  • @Scope : 用于设定 bean 的作用范围, 即单实例还是多实例

    • @scope 注解添加在@Bean注解添加的地方, 一般是bean上
    @Configuration
    public class MainConfig2 {
        
        /**
         * singleton: 单实例(默认值) 所有的getBean() 返回的都是同一个bean实例
         * prototype: 多实例 每次调用getBean() 都返回一个新的 bean 实例
         * request: web环境下, 每一个请求创建一个request
         * session: web环境下, 每一个请求创建一个session
         */
        @Scope(scopeName = "singleton") // scopeName属性 可以替换为 value, 两个完全一致
        @Bean
        public Person person() {
            return new Person("张三", 25);
        }
    }
    

    测试类

    @Test
    public void test02() {
        applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    
        Person bean1 = applicationContext.getBean(Person.class);
        Person bean2 = applicationContext.getBean(Person.class);
    
        System.out.println(bean1 == bean2);
    
    }
    

    结果

在这里插入图片描述

  • singleton : 只创建一次对应的bean对象, 且默认bean的实例是在 IOC 容器创建完成时就已经创建了, 以后每次调用 getBean() 方法在容器中直接获取

    测试

    // 在配置类中配置
    @Scope(scopeName = "singleton")  // scopeName属性 可以替换为 value, 两个完全一致
    @Bean
    public Person person() {
        System.out.println("Person 创建了...");
        return new Person("张三", 25);
    }
    
    @Test
    public void test03() {
        applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    
        System.out.println("IOC 容器已经创建完了...");
    
        applicationContext.getBean(Person.class);
        applicationContext.getBean(Person.class);
    
    }
    

在这里插入图片描述

可见IOC容器创建完成时bean实例也已经创建完成了, 且每次调用getBean() 方法都是直接在 IOC 容器中获取的,没有调用创建实例方法

如果不想在容器创建的时候创建 bean 实例, 可以使用@Lazy 标记相关的bean为懒加载模式, 即首次调用getBean() 方法时才真正的创建 bean实例

  • prototype : 每次调用 getBean() 方法都创建一个新的 bean实例, 与IOC 容器创建无关

    @Scope(scopeName = "prototype")  // scopeName属性 可以替换为 value, 两个完全一致
        @Bean
        public Person person() {
            System.out.println("Person 创建了...");
            return new Person("张三", 25);
        }
    
    @Test
        public void test03() {
            applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    
            System.out.println("IOC 容器已经创建完了...");
    
            Person bean1 = applicationContext.getBean(Person.class);
            Person bean2 = applicationContext.getBean(Person.class);
    
            System.out.println(bean1 == bean2);
    
        }
    

在这里插入图片描述

@Lazy — 懒加载/延迟加载

懒加载针对单实例 bean

懒加载使得单实例的 bean 在IOC 容器 创建时不自动创建, 而是每次使用到相关的 bean 时容器再去创建.

实例

@Scope(scopeName = "singleton")  // scopeName属性 可以替换为 value, 两个完全一致
    @Bean
    @Lazy
    public Person person() {
        System.out.println("Person 创建了...");
        return new Person("张三", 25);
    }
@Test
    public void test03() {
        applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);

        System.out.println("IOC 容器已经创建完了...");

    }

在这里插入图片描述

可见, 当标注 @Lazy 注解后, 单实例的bean 不再在 IOC容器创建时也一起创建了

@Conditional — 按条件注册

可以标注在类上或方法上

按照一定条件在容器中注册 bean

@Conditional(WindowsCondition.class)

参数是实现了 org.springframework.context.annotation.Condition 接口的类对象

判定是否是 windows 操作系统

public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String osName = environment.getProperty("os.name");
        if (osName.contains("Windows")) {
            return true;
        }
        return false;
    }
}
@Conditional(WindowsCondition.class)
@Bean (value = "bill")
public Person person01() {
    return new Person("Bill Gates", 62);
}
@Test
public void test04() {
    applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);

    String[] names = applicationContext.getBeanNamesForType(Person.class);
    for (String name : names) {
        System.out.println(name);
    }

    Map<String, Person> person = applicationContext.getBeansOfType(Person.class);
    System.out.println(person);

    Environment environment = applicationContext.getEnvironment();
    String os = environment.getProperty("os.name");
    System.out.println(os);
}

在这里插入图片描述

ps: 可以通过修改 vm参数 ‘伪装’ 成 Linux操作系统

-Dos.name=Linux

在这里插入图片描述

如果 @Conditional 注解添加在配置类上, 那么只有当满足@Conditional 的条件时 配置类中的配置才会生效

在这里插入图片描述

在这里插入图片描述

@Import — 组件导入

对于以前的给容器中添加组建的方式有三种

  • 包扫描带有 @Controller, @Service, @Repository, @Component 注解的类, 但是这些类一般是我们自己写的
  • @Bean : 注解一般用于导入第三方的类, 但是美导入一个组件到要在配置类中写一个方法, 显得很繁杂,臃肿
  • @Import : 快速导入一个组件
    • 直接在配置类上标记 @Import 注解, 参数是要导入的组建的class对象的数组
    • 实现 ImportSelector 接口, 返回 需导入的组建的类的全类名数组
    • 实现 ImportBeanDefinitionRegistrar 接口

**第一种方法: **

public class Color {
}

@Test
public void test05() {
    applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    String[] names = applicationContext.getBeanDefinitionNames();
    for (String name : names) {
        System.out.println(name);
    }
}

在这里插入图片描述

在这里插入图片描述

**第二种方法: **
在这里插入图片描述

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.lymboy.spring.annotation.bean.Blue", "com.lymboy.spring.annotation.bean.Red"};
    }
}
 @Test
public void test05() {
    applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    Blue blue = applicationContext.getBean(Blue.class);
    System.out.println(blue);
}

在这里插入图片描述

第三种方法 :

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * @param importingClassMetadata 当前类的注册信息
     * @param registry BeanDefinition注册类,用于对IOC容器中的bean增删改查
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean red = registry.containsBeanDefinition("com.lymboy.spring.annotation.bean.Red");
        boolean color = registry.containsBeanDefinition("com.lymboy.spring.annotation.bean.Color");
        if (red && color) {
            RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
            registry.registerBeanDefinition("rainBow", beanDefinition);
        }
    }
}

在这里插入图片描述

@Test
public void test06() {
    applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    String[] names = applicationContext.getBeanDefinitionNames();

    for (String name : names) {
        System.out.println(name);
    }
}

在这里插入图片描述

FactoryBean — 组件注册

用于给容器中注册bean

需要实现 FactoryBean 接口

public class ColorFactoryBean implements FactoryBean<Color> {
    /**
     * 返回Color对象, 该对象会添加到容器中
     * @return
     * @throws Exception
     */
    @Override
    public Color getObject() throws Exception {
        return new Color();
    }

    @Override
    public Class<?> getObjectType() {
        return Color.class;
    }

    /**
     * 设定是否是单例模式, 是设定, 不是判断
     * true: 单实例
     * false: 多实例
     * @return
     */
    @Override
    public boolean isSingleton() {
        return false;
    }
}
@Bean
public ColorFactoryBean colorFactoryBean() {
    return new ColorFactoryBean();
}
@Test
public void test07() {
    applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    Object color0 = applicationContext.getBean("colorFactoryBean");

    System.out.println(color0);

}

在这里插入图片描述

虽然 getBean 的参数是 colorFactoryBean 但实际上返回的是 Color 对象, 因为容器会自动调用 FactoryBeangetObject 方法, 如果要真的返回 colorFactoryBean 实例, 只要在 getBean 参数前加 ‘&’ 符号就能返回对应的 FactoryBean 实例了 (至于为什么要添加’’&’ 这是因为Spring本身的设置)

// org.springframework.beans.factory.BeanFactory
String FACTORY_BEAN_PREFIX = "&";
@Test
public void test07() {
    applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    Object color0 = applicationContext.getBean("&colorFactoryBean");

    System.out.println(color0);

}

在这里插入图片描述

@Bean — 生命周期

以前都是容器自动管理 bean 的创建与销毁, 但是我们也可以自己管理 bean 的创建与销毁

  • 配置文件方式

    bean 标签中添加 init-methoddestroy-method 属性

    <bean id="person" class="com.lymboy.spring.annotation.bean.Person"
            scope="singleton" init-method="" destroy-method="">
        <property name="name" value="Sairo"></property>
        <property name="age" value="18"></property>
    </bean>
    
  • 注解方式

    @Bean 注解中添加相关属性

    Car

    public class Car {
    
        public void init() {
            System.out.println("Car Created...");
        }
    
        public void destroy() {
            System.out.println("Car has been destroyed...");
        }
    }
    

    配置类

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

    测试

    public class IOCTest_LifeCycle {
        private AnnotationConfigApplicationContext applicationContext;
    
        @Test
        public void test00() {
            applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    
            applicationContext.getBean("car");
    
            applicationContext.close();
        }
    }
    

    结果

在这里插入图片描述

可见, 当在 @Bean 中标注 initMethod 后, 容器创建该 bean 时会主动调用相关的初始化方法

同理, 当容器关闭, 即 bean 要被销毁的时候也会调用相关的销毁方法

**PS: ** 原始的 ApplicationContext 并没有定义 close() 方法, 其具体的实现类才有

注意:

​ 以上的效果只是在单例情况下才有效, 对于多实例 bean, IOC容器在创建完 bean 后就不会管理这个 bean了, 所以当容器关闭时不会调用销毁方法,而且因为是多实例的, 只有当调用 getBean() 方法是才会创建 bean, 即调用 initMethod 标记的方法

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

在这里插入图片描述

InitializingBean, DisposableBean

通过使 bean 实现 InitializingBean, DisposableBean 接口, 并实现相关的方法, 容器会自动调用 初始化和销毁方法, 同样也区分单例和多例

@Component
public class Cat implements InitializingBean, DisposableBean {

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

    /**
     * 用于销毁 bean
     * @throws Exception
     */
    @Override
    public void destroy() throws Exception {
        System.out.println("Cat destroy...");
    }

    /**
     * 当 bean 的所有属性(构造器) 完成后执行此方法, 用于初始化 bean
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Cat afterPropertiesSet...");
    }
}

配置类加包扫描

测试

@Test
public void test01() {
    applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);

    applicationContext.close();
}

在这里插入图片描述

可见, 当使用实现接口这种方法时, 我们就不用再去显示指定初始化和销毁方法了, 容器会自动调用

@PostConstruct, @PreDestroy

直接标注在类的 初始化/销毁 方法上

**@PostConstruct: ** 在对象创建之后,所有属性赋好值后调用, 用于对象的一些初始化操作

@PreDestroy: 容器移除对象之前调用

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

    @PostConstruct
    public void init() {
        System.out.println("Dog created...");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("Dog destroy...");
    }
}

在这里插入图片描述

BeanPostProcessor — 后置处理器

**也是用于对组件的初始化操作, 但是优先级最高, 先于 initMethod/destroyMethod, InitializingBean等方式 **

它是一个接口, 当实现这个接口并将它添加到容器中后, 容器中所有的组件都会被它做一些初始化操作

@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;
    }
}

结果
在这里插入图片描述

@Value — 属性赋值

用于对 bean 的属性填充

相当于配置文件中的 property 标签

<bean id="person" class="com.lymboy.spring.annotation.bean.Person"
        scope="singleton" init-method="" destroy-method="">
    <property name="name" value="Sairo"></property>
    <property name="age" value="18"></property>
</bean>

配置

@Configuration
public class MainConfigOfPropertyValues {

    @Bean
    public Person person() {
        return new Person();
    }
}

测试

public class IOCTest_PropertyValues {

    private AnnotationConfigApplicationContext applicationContext;

    @Test
    public void test00() {
        applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class);
        printNames();

        Person person = (Person) applicationContext.getBean("person");
        System.out.println(person);
    }

    public void printNames() {
        String[] names = applicationContext.getBeanDefinitionNames();

        for (String name : names) {
            System.out.println(name);
        }
    }
}

结果

在这里插入图片描述

可见, bean 的属性值为空, 可以用 @Value 给属性赋值

@Value 的参数

  • 基本数值
  • SpEL 表达式
  • ${} 取出文件中的值

在这里插入图片描述

在这里插入图片描述

@PropertySource — 读取外部文件

@PropertySource 标注在配置类上, 参数是 String 数组, 其值是配置文件路径

然后需要在 bean 的属性上标注 @Value 属性

# person.properties
person.name=altraman
person.age=6

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

@Autowired, @Qualifier, @Primary — 自动装配

@Autowired

按类型装配, @Autowired会在容器中按照对应的类型去查找相关的对象实例, 如果在容器中找到多个类型匹配的实例, 则继续按属性名作为组件 id匹配

用法: 仅有一个参数 required, 意义为是否一定要匹配, 默认为 true, 可以标注在构造器, 方法, 属性, 参数上

对于以前的三层开发模型, Controller–>Service, Service–>Dao 程序员直接在声明一个下层组件的变量并标注 @Autowired 注解, 容器会自动创建相关组件的实例并注入其中.

@Controller
public class BookController {
    @Autowired
    private BookService bookService;
}
@Service
public class BookService {
    @Autowired
    private BookDao bookDao;
}
@Repository
public class BookDao {
}

测试

@Test
public void test01() {
    applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
    BookService bookService = applicationContext.getBean(BookService.class);
    System.out.println(bookService);
}

可见, 我们没有手动创建 BookDao 的实例, 容器就已经帮我们创建并注入到 BookService 中, 这就是控制反转和依赖注入!

如果在容器中找到多个类型匹配的实例, 则继续按属性名作为组件 id匹配

@Bean("bookDao2")
public BookDao bookDao() {
    BookDao dao = new BookDao();
    dao.setLable("2");
    return dao;
}
@Test
public void test01() {
    applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);

    BookService bookService = applicationContext.getBean(BookService.class);
	// 查看依赖注入的 BookDao
    System.out.println(bookService);
	// 另一个 BookDao
    BookDao dao = (BookDao) applicationContext.getBean("bookDao2");

    System.out.println(dao);

    applicationContext.close();
}

结果

在这里插入图片描述

@Qualifier

id 装配当存在多个 相同类型的组件时, 明确指定 id 去匹配

用法:@Autowired 标注在相同的位置, 明确指定要匹配组件的 id

参数: 字符串, 指定的 id, 默认为空

@Service
public class BookService {

    @Qualifier("bookDao2")
    @Autowired()
    private BookDao bookDao;

    @Override
    public String toString() {
        return "BookService{" + "bookDao=" + bookDao + '}';
    }
}
@Test
public void test02() {
    applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
    BookService bookService = applicationContext.getBean(BookService.class);
    System.out.println(bookService);

}

结果:

在这里插入图片描述

@Primary

设定首选的组件, 即在依赖注入式首先注入标记了此注解的组件, 与@Qualifier冲突

用法: 标注在配置类中

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

取消 @Qualifier 注解

在这里插入图片描述

@Resource

Java 本身自带的注解, 也是自动注入

用法: 与@Autowired相似, 参数 [name = id], 当使用name参数时, 其功能与 @Qualifier 相似, 也是按 id 匹配

Aware 注入

自定义组件要想使用容器底层的组件可以通过实现相关的 xxxAware 接口

@Profile — 环境切换

用于切换配置环境

可以标注在方法上也可以标注在配置类上

标注在方法/bean上

参数代表的环境的 id, 当参数为 default 时,默认此配置生效

package com.lymboy.spring.annotation.config;

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

    @Value("${db.user}")
    private String user;

    private String driverClass;

    /**
     * 测试环境
     */
    @Profile("test")
    @Bean("testDataSource")
    public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://locaohost:3306/test");
        dataSource.setDriverClass(driverClass);

        return dataSource;
    }
    
	/**
     * 开发环境
     */
    @Profile("dev")
    @Bean("devDataSource")
    public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://locahost:3306/dev");
        dataSource.setDriverClass(driverClass);

        return dataSource;
    }

    /**
     * 生产环境
     */
    @Profile("pro")
    @Bean("proDataSource")
    public DataSource dataSourcePro(@Value("${db.password}") String pwd) throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/pro");
        dataSource.setDriverClass(driverClass);

        return dataSource;
    }

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


环境切换

方法一: 命令行参数

选择测试环境

在这里插入图片描述

-Dspring.profiles.active=test

测试

package com.lymboy.spring.annotation;

public class IOCTest_Profile {
    private AnnotationConfigApplicationContext applicationContext;

    @Test
    public void test00() {
        applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
        printNames();
        applicationContext.close();
    }

    public void printNames() {
        String[] names = applicationContext.getBeanNamesForType(DataSource.class);
        for (String name : names) {
            System.out.println(name);
        }
    }

}

在这里插入图片描述

方法二: 代码设置

@Test
public void test01() {
    applicationContext = new AnnotationConfigApplicationContext();
    applicationContext.getEnvironment().setActiveProfiles("dev", "test");
    applicationContext.register(MainConfigOfProfile.class);
    applicationContext.refresh();

    printNames();

    applicationContext.close();
}

在这里插入图片描述

标注在配置类上

当标注类上时, 只有当配置类生效时, 此类中的所有的配置才会生效

AOP 面向切面编程

动态代理 : 指在程序运行期间动态地将某段代码切入带指定位置进行运行的编程方式

相关概念

  • Aspects: 切面, 通常是一个类, 即所谓的切面类,切面类中定义了切入点和通知
  • JointPoint: 连接点, 执行的方法(需要通知的方法), 即下面的 ***div()***方法
  • Advice: 通知, 即什么(类型)通知, eg: @Before, @After, @AfterReturning…
  • Pointcut: 切入点, 对连接点进行拦截的定义, 即下面的 logStart(), logEnd() 等等

步骤

  1. 导入 aspects 模块

  2. 定义业务逻辑类, 定义一个实验方法

  3. 定义一个切面类

  4. 定义相关通知和切入点

    通知方法

    • 前置通知: 在目标方法(切点) 运行之前运行
    • 后置通知: 在目标方法(切点) 运行之后运行
    • 返回通知: 在目标方法(切点) 正常返回之后运行
    • 异常通知: 在目标方法(切点) 运行出现异常之后运行
    • 环绕通知: 动态代理, 目标方法运行前后运行
  5. 将切面和业务逻辑类交由容器管理

  6. 给切面类添加 @Aspect 注解, 指明其是切面类

  7. 在配置类中添加 @EnableAspectJAutoProxy 注解, 开启注解版的 AOP 功能

要点

  • 通知(@Before(), @After()) 中填入的是切点表达式: @Before(“execution(public int com.lymboy.spring.annotation.aop.MathCalculator.*(…))”)
  • 为了避免在通知中重复写相同的切点表达式可以使用 @PointCut 注解

假定设计一个业务类 MathCalculator

public class MathCalculator {
    /**
     * 除法操作
     */
    public int div(int a, int b) {
        return a/b;
    }
}

定义一个日志切面类, 切面里的方法动态感知 MathCalculator.div() 运行到哪里了

@Aspect
public class LogAspects {

    @Pointcut("execution(public int com.lymboy.spring.annotation.aop.MathCalculator.*(..))")
    public void pointCut() {

    }

    @Before("execution(public int com.lymboy.spring.annotation.aop.MathCalculator.*(..))")
    public void logStart() {
        System.out.println("除法开始...参数列表是{}");
    }

    @After("com.lymboy.spring.annotation.aop.LogAspects.pointCut()")
    public void logEnd() {
        System.out.println("除法结束...");
    }

    @AfterReturning("com.lymboy.spring.annotation.aop.LogAspects.pointCut()")
    public void logReturn() {
        System.out.println("正常返回...运行结果{}");
    }

    @AfterThrowing("com.lymboy.spring.annotation.aop.LogAspects.pointCut()")
    public void logException() {
        System.out.println("出现异常...异常信息.{}");
    }
}

测试

// 设计一个除 0 异常
@Test
public void test01() {
    applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);

    MathCalculator math = (MathCalculator) applicationContext.getBean("com.lymboy.spring.annotation.aop.MathCalculator");
    int div = math.div(10, 0);
    System.out.println(div);

    applicationContext.close();
}

可见, 在 div() 方法前后相关的通知方法运行了

事务处理

步骤

  • 导入相关模块: 数据源、数据库连接、spring-jdbc模块
  • 配置数据源、JdbcTemplate(Spring内置的简化操作的工具)
  • 书写相关业务逻辑类
  • 给插入方法添加事务注解(service方法)
  • 添加事务管理器组件到容器中
  • 开启事务管理器(@EnableXXX)

配置类

@Configuration
@EnableTransactionManagement	// 必须开启事务管理器
@ComponentScan("com.lymboy.spring.annotation.tx")
public class TXConfig {

    /**
     * 数据源
     */
    @Bean
    public DataSource dataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("123456");
        dataSource.setJdbcUrl("jdbc:mysql://www.lymboy.com:3306/test");
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");

        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate() throws PropertyVetoException {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
        return jdbcTemplate;
    }

    // 事务管理器,实现PlatformTransactionManager接口
    public PlatformTransactionManager platformTransactionManager() throws PropertyVetoException {
        return new DataSourceTransactionManager(dataSource());
    }
}

业务逻辑类

@Repository
public class UserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    public void insert() {
        String sql = "INSERT INTO spring (`name`, `age`) VALUES(?, ?)";
        String name = UUID.randomUUID().toString().substring(0, 5);
        jdbcTemplate.update(sql, name, 19);
    }
}
@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    // 标示注解
    @Transactional
    public void insertUser() {
        userDao.insert();
        System.out.println("插入完成...");
        int i = 10 / 0;
    }
}
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值