Spring相关注解

news2024/12/24 10:07:08

文章目录

  • Spring注解
  • @Bean
    • 1、@Bean 概述
    • 2、Bean 的声明
      • 1)搭配 @Configuration
      • 2)搭配 @Component
      • 3)搭配 ApplicationContext
    • 3、Bean 的注入
      • 1)NO(主要关注这个)
        • 【1】同一配置类
        • 【2】不同配置类
      • 2)BY_NAME(了解)
      • 3)BY_TYPE(了解)
    • 4、基于@Bean的扩展注解
  • @Autowired
    • 1、基本使用
    • 2、搭配 @Qualifier
    • 2、三种注入方式
      • 1)Filed变量注入
      • 2)Setter方法注入
      • 3)构造器注入(推荐)
    • 4、构造器注入 案例分析
      • 1)单个构造
      • 2)空参构造 + 有参构造
      • 3)多个有参构造
      • 4)多个 @Autowired(false)
    • 5、注意事项
  • @Resource
    • 1、基本使用
    • 2、注意事项
  • @Configuration
    • 1、proxyBeanMethods = true
    • 2、proxyBeanMethods = false
  • @Primary
  • @Lazy
    • 1、加在类上
    • 2、加在配置类上
    • 3、加在字段上
    • 4、加在构造方法上
    • 5、加在方法上
    • 6、加在参数前
  • @Import
  • @Profile
    • 1、配置profile
    • 2、激活profile
  • @Lookup
    • 1、抽象类注入
    • 2、单例bean中使用多例bean
  • @Conditional
    • 1、相关类
    • 2、基本使用
  • @Value
    • 1、字符串
    • 2、${} - 属性占位符
    • 3、#{} - SpEL表达式
    • 4、@Value的扩展注解
  • AOP 相关的注解

Spring注解

@Bean

1、@Bean 概述

@Configuration
public class BeanAttribute {

    /**
     * 通过@Bean注解声明bean
     */
    @Bean
    public Animal cat() {
        return new Cat();
    }

    /**
     * name属性:设置bean的name/id(默认是带有@Bean注解的方法名)
     */
    @Bean(name = "husky")
    public Animal dog() {
        return new Dog();
    }

    /**
     * autowireCandidate属性:是否作为自动装配的候选项。(默认为true)
     *
     * autowireCandidate = false  ->  Spring IoC 容器将不会自动将其注入到其他 bean 中。
     *
     * 解决自动装配歧义性:
     * 当存在多个候选项符合自动装配的条件时,会抛出 NoUniqueBeanDefinitionException 异常
     * 这个时候可以将 autowireCandidate 属性设置为 false,以解决自动装配的歧义性。
     * 
     * 注意事项:这个属性只影响byType,不影响byName.
     * 1)不影响 getBeanByName --> 根据name:dog2,可以获取type=Dog.class的bean
     * 2)会影响 getBeanByType --> 根据type:Dog.class,无法获取name=dog2的bean
     * 3)不影响 byName的注入 --> @Resource会注入dog2
     * 4)会影响 byType的注入 --> @Autowired只会注入husky,不会注入dog2(如果设置为true,通过@Autowired注入会报错)
     */
    @Bean(autowireCandidate = false)
    public Animal dog2() {
        return new Dog();
    }

    /**
     * autowire属性:这个bean自动注入的方式(默认为NO),已被标记为@Deprecated
     *
     * 1)autowire = Autowire.NO         不自动注入,使用@Autowired/@Resource注入
     * 2)autowire = Autowire.BY_TYPE    根据名称,调用set方法注入
     * 3)autowire = Autowire.BY_NAME    根据类型,调用set方法注入
     */
    @Bean(autowire = Autowire.BY_NAME)
    public Animal pig() {
        return new Pig();
    }
}

2、Bean 的声明

1)搭配 @Configuration

@Configuration
public class Config {
    @Bean
    public User user() {
        return new User();
    }
}

2)搭配 @Component

@Component
public class Config {
    @Bean
    public User user() {
        return new User();
    }
}

3)搭配 ApplicationContext

public class Config {
    @Bean
    public User user() {
        return new User();
    }
}

AnnotationConfigApplicationContext 读取 Config 对象,作用和 @Configuration 一致。

public class ApplicationContextTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        User user = (User) context.getBean("user");
    }
}

3、Bean 的注入

可以看到,@Beanautowire属性已经被打上了@Deprecated,这里重点关注default Autowire.NO,其他的做一个了解。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @Deprecated
	Autowire autowire() default Autowire.NO;
}
public enum Autowire {
	/**
	 * Constant that indicates no autowiring at all.
	 */
	NO(AutowireCapableBeanFactory.AUTOWIRE_NO),

	/**
	 * Constant that indicates autowiring bean properties by name.
	 */
	BY_NAME(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME),

	/**
	 * Constant that indicates autowiring bean properties by type.
	 */
	BY_TYPE(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);

    // ...
}

1)NO(主要关注这个)

autowire 这个属性已经过期了,所以我们主要关注 default Autowire.NO(也就是不指定该属性),其余两个了解一下即可。

【1】同一配置类

同一配置类中,可以直接引用依赖对象的@Bean方法进行注入(必须在同一配置类中)

@Configuration
public class Config {
    
    /**
     * orderService()方法上添加了@Bean注解,Spring将会拦截所有对它的调用,
     * 确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。
     */
    @Bean
    public OrderService orderService() {
        return new OrderService();
    }

    @Bean
    public UserService userService() {
        return new UserService(orderService());
    }

}

注意:看起来 UserService 对应的bean 是通过调用 orderService() 得到的,但情况并非完全如此。

默认情况下,@Configuration标记的Config是代理对象,因此调用的 orderService() 是代理类调用的

  • 如果 OrderService 对应的bean不存在,会创建对应的bean
  • 如果 OrderService 对应的bean已存在,会直接返回已存在的bean

也就是说,每次调用 orderService() 方法,得到的都是同一个bean

【2】不同配置类

不同配置类中,可以通过@Bean修饰方法的参数进行注入(不需要在统一配置类也可以)

@Configuration
public class Config1 {
    @Bean
    public OrderService orderService() {
        return new OrderService();
    }
}
@Configuration
public class Config2 {
    // 创建UserService的bean时,会自动装配一个OrderService
    @Bean
    public UserService userService(OrderService orderService) {
        return new UserService(orderService);
    }
}

这样在构建UserService的bean时,Spring会找到OrderService的bean,并传入@Bean方法构建UserService的bean。

2)BY_NAME(了解)

@Configuration
public class BeanInjectConfig {

    /**
     * autowire = Autowire.BY_NAME    根据set方法注入(name 根据 setXxx 的 Xxx)
     * 1、没有set方法 -> 注入失败(属性为null,不报错)
     * 2、根据name没找到 -> 注入失败(属性为null,不报错)
     * 3、根据name找到了 -> 注入成功
     */
    @Bean(autowire = Autowire.BY_NAME)
    public BeanInjectService beanInjectService() {
        return new BeanInjectService();
    }

}
public class BeanInjectService {

    private OneBeanObject oneBeanObject;
    private NoBeanObject noBeanObject;
    private Color color;

    /**
     * 根据name找到了 -> 注入成功
     */
    public void setOneBeanObject(OneBeanObject oneBeanObject) {
        this.oneBeanObject = oneBeanObject;
    }

    /**
     * 根据name没找到 -> 注入失败(属性为null,不报错)---> 存在bean,但是set后面的name不匹配
     */
    //    public void setOneBeanObject123(OneBeanObject oneBeanObject) {
    //        this.oneBeanObject = oneBeanObject;
    //    }

    /**
     * 根据name没找到 -> 注入失败(属性为null,不报错)---> 本来就没有bean
     */
    public void setNoBeanObject(NoBeanObject noBeanObject) {
        this.noBeanObject = noBeanObject;
    }

    /**
     * 根据name没找到 -> 注入失败(属性为null,不报错)---> 存在bean,但是set后面的name不匹配
     */
    public void setColor(Color color) {
        this.color = color;
    }

    /**
     * 根据name找到了 -> 注入成功
     */
//    public void setBlue(Color color) {
//        this.color = color;
//    }

}

3)BY_TYPE(了解)

@Configuration
public class BeanInjectConfig {

    /**
     * autowire = Autowire.BY_TYPE    根据方法的参数类型注入(必须是set开头的方法)
     * 1、没有set开头的方法 -> 注入失败(属性为null,不报错)
     * 2、根据type没找到 -> 注入失败(属性为null,不报错)
     * 3、根据type找到一个 -> 注入成功
     * 4、根据type找到多个 -> 报错 NoUniqueBeanDefinitionException
     */
    @Bean(autowire = Autowire.BY_TYPE)
    public BeanInjectService beanInjectService() {
        return new BeanInjectService();
    }

}
public class BeanInjectService {

    private OneBeanObject oneBeanObject;
    private NoBeanObject noBeanObject;
    private Color color;

    /**
     * 根据type找到一个 -> 注入成功(必须是set开头的方法)
     */
    public void set123(OneBeanObject oneBeanObject) {
        this.oneBeanObject = oneBeanObject;
    }

    /**
     * 根据type没找到 -> 注入失败(属性为null,不报错)---> 本来就没有bean
     */
    public void setNoBeanObject(NoBeanObject noBeanObject) {
        this.noBeanObject = noBeanObject;
    }

    /**
     * 根据type找到一个 -> 注入成功(必须是set开头的方法)
     */
    public void setBlue(Blue color) {
        this.color = color;
    }

    /**
     * 根据type找到多个 -> 报错 NoUniqueBeanDefinitionException
     */
//    public void setColor(Color color) {
//        this.color = color;
//    }

}

4、基于@Bean的扩展注解

@Configuration
public class BeanConfig {
    
    @Bean
    @Scope("prototype")   
    public OrderService orderService() {
        return new OrderService();
    }
    
}

如果有很多地方都要注册为多例bean,可以基于这两个注解做扩展

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Bean
@Scope("prototype")
public @interface PrototypeBean {

}
@Configuration
public class BeanConfig {
    
    @PrototypeBean  
    public OrderService orderService() {
        return new OrderService();
    }
    
}

@Autowired

1、基本使用

/**
 * 依赖注入之 @Autowired
 *  先byType
 *      1)根据type有一个 -> 直接注入
 *      2)根据type有多个 -> 再根据name筛选
 *  再byName
 *      1)根据name有一个 -> 直接注入
 *      2)根据name没找到 -> 项目启动就会报错 NoUniqueBeanDefinitionException(type有多个,name不存在)
 *      3)根据name有多个 -> 项目启动就会报错 ConflictingBeanDefinitionException
 *
 *  required属性(默认为true)
 *      1)required=false -> 允许找不到bean注入,值为null
 *      2)required=true  -> 必须找到bean注入
 */
@Component
public class InjectByAutowired {

    /**
     * 先根据type -> 找到多个 -> 再根据name -> 找到一个 -> 注入blue
     */
    @Autowired
    private Color blue;

    /**
     * 启动就报错 NoUniqueBeanDefinitionException(和上面的区别是,有blue,但是没有green)
     */
//    @Autowired
//    private Color green;

    /**
     * 先根据type
     *   1)required = true  --> 抛错
     *   2)required = false --> 不抛错,noBeanObject = null
     */
    @Autowired(required = false)
    private NoBeanObject noBeanObject;

    public void testInject() {
        blue.printName();
        System.out.println("noBeanObject = " + noBeanObject);	// null
    }

}

2、搭配 @Qualifier

@Qualifier 注解用于限定注入的是哪个bean。

使用@Autowired时,「type有多个,name不存在」会导致启动报错,通过 @Qualifier 可以解决

@Component
public class InjectWithQualifier {

    /**
     * 不加 @Qualifier -> 启动就报错 NoUniqueBeanDefinitionException(type有多个,name不存在)
     * 加了 @Qualifier -> 从多个Color中选择red注入
     */
    @Autowired
    @Qualifier("red")
    private Color green;

    public void testInject() {
        green.printName();
    }

}

2、三种注入方式

1)Filed变量注入

@Component
public class OrderService {
    @Autowired
    private DependencyA dependencyA;

    @Autowired
    private DependencyB dependencyB;

    @Autowired
    private DependencyC dependencyC;
}

2)Setter方法注入

@Component
public class OrderService {
    // 待注入的属性
    private DependencyA dependencyA;
    private DependencyB dependencyB;
    private DependencyC dependencyC;

    // 参数是 要注入的对象
    @Autowired
    public void setDependencyA(DependencyA dependencyA) {
        this.dependencyA = dependencyA;
    }

    @Autowired
    public void setDependencyB(DependencyB dependencyB) {
        this.dependencyB = dependencyB;
    }

    @Autowired
    public void setDependencyC(DependencyC dependencyC) {
        this.dependencyC = dependencyC;
    }
}

3)构造器注入(推荐)

@Component
public class OrderService {
    // 待注入的属性
    private DependencyA dependencyA;
    private DependencyB dependencyB;
    private DependencyC dependencyC;

    // 参数是 要注入的对象
    @Autowired
    public OrderService(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC) {
        this.dependencyA = dependencyA;
        this.dependencyB = dependencyB;
        this.dependencyC = dependencyC;
    }
}

在 Spring4.3 之后,如果我们的类中只有单个构造函数,那么Spring就会实现一个隐式的自动注入

@Component
public class OrderService {
    // 待注入的属性
    private DependencyA dependencyA;
    private DependencyB dependencyB;
    private DependencyC dependencyC;

    // 省略了@Autowired
    public OrderService(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC) {
        this.dependencyA = dependencyA;
        this.dependencyB = dependencyB;
        this.dependencyC = dependencyC;
    }
}

如果有多个构造方法,请看后面的「构造器注入 案例分析」

4、构造器注入 案例分析

1)单个构造

在 Spring4.3 之后,如果我们的类中只有单个构造函数,那么Spring就会实现一个隐式的自动注入(可省略@Autowired)

/**
 * 使用空参构造实例化
 */
@Component
public class UserService {
    
    private User user;
    
    public UserService() {
    }

}
/**
 * 使用有参构造实例化
 */
@Component
public class UserService {
    
    private User user;

    public UserService(User user) {
        this.user = user;
    }
    
}

2)空参构造 + 有参构造

/**
 * 空参构造 + 有参构造
 * ① 没有@Autowired --> 默认使用空参构造(不会注入InjectService)
 * ② 一个@Autowired --> 使用@Autowired标记的构造方法
 * ③ 多个@Autowired --> 报错:Invalid autowire-marked constructor ... 
 *							 Found constructor with 'required' Autowired annotation already
 */
@Component
public class ManyConstruction1 {

    public InjectService injectService;

    public ManyConstruction1() {
    }

    public ManyConstruction1(InjectService injectService) {
        this.injectService = injectService;
    }

}

3)多个有参构造

/**
 * 多个有参构造
 * ① 没有@Autowired --> 报错:No default constructor found; 
 *							 nested exception is java.lang.NoSuchMethodException: 
 *							 com.demo.spring.ioc.inject.construct.ManyConstruction2.<init>()
 * ② 一个@Autowired --> 使用@Autowired标记的构造方法
 * ③ 多个@Autowired --> 报错:Invalid autowire-marked constructor ... 
 *							 Found constructor with 'required' Autowired annotation already
 */
@Component
public class ManyConstruction2 {

    public InjectService injectService;
    public InjectService2 injectService2;

    @Autowired
    public ManyConstruction2(InjectService injectService) {
        this.injectService = injectService;
    }

    public ManyConstruction2(InjectService injectService1, InjectService2 injectService2) {
        this.injectService = injectService1;
        this.injectService2 = injectService2;
    }

}

4)多个 @Autowired(false)

/**
 * 如果多个@Autowired都声明了required = false,不会报错
 * ① 优先使用参数最多的构造方法
 * ② 如果参数多的构造方法的参数bean不存在/找不到 --> 使用参数第二多的构造方法(以此类推)
 * ③ 如果存在参数一样多的构造方法 --> 使用声明在上面的构造方法
 */
@Component
public class ManyConstruction3 {

    public InjectService injectService;
    public InjectService2 injectService2;
    public NoBeanObject noBeanObject;

    public ManyConstruction3() {
    }

    @Autowired(required = false)
    public ManyConstruction3(InjectService injectService) {
        System.out.println("使用了1个参数的构造方法1");
        this.injectService = injectService;
    }

    @Autowired(required = false)
    public ManyConstruction3(InjectService2 injectService2) {
        System.out.println("使用了1个参数的构造方法2");
        this.injectService2 = injectService2;
    }

    @Autowired(required = false)
    public ManyConstruction3(InjectService injectService, InjectService2 injectService2) {
        System.out.println("使用了2个参数的构造方法");
        this.injectService = injectService;
        this.injectService2 = injectService2;
    }

    @Autowired(required = false)
    public ManyConstruction3(InjectService injectService, InjectService2 injectService2, NoBeanObject noBeanObject) {
        System.out.println("使用了3个参数的构造方法");
        this.injectService = injectService;
        this.injectService2 = injectService2;
        this.noBeanObject = noBeanObject;
    }

}

5、注意事项

父类中 @Autowired 修饰的会被注入

public class BaseService {

    // 是否会被注入??
    @Autowired
    public UserService userService;

}

@Component
public class OrderService extends BaseService {

    private void test() {
        System.out.println(userService); // 会被注入!
    }
    
}

static修饰的属性不会被注入

@Component
public class OrderService {

    // 是否会被注入??
    @Autowired
    private static UserService userService;
    
    private void test() {
        System.out.println(userService); // null 不会被注入
    }

}

@Resource

1、基本使用

/**
 * 依赖注入之 @Resource
 *  1. 指定name(根据name)
 *      1)根据name有一个 -> 直接注入
 *      2)根据name没找到 -> 项目启动就会报错 NoSuchBeanDefinitionException
 *      3)根据name有多个 -> 项目启动就会报错 ConflictingBeanDefinitionException
 *
 *  2. 指定type(先根据type,再根据name)
 *      1)根据type有一个 -> 直接注入
 *      2)根据type没找到 -> 抛错
 *      3)根据type有多个 -> 再根据name找
 *
 *  3. 都不指定(先根据name,再根据type)
 *      1)根据name有一个 -> 直接注入
 *      2)根据name没找到 -> 再根据type找 
 *						-> 根据type有一个 -> 直接注入
 *						-> 根据type有多个 -> 抛错 NoUniqueBeanDefinitionException
 *      3)根据name有多个 -> 项目启动就会报错 ConflictingBeanDefinitionException
 */
@Component
public class InjectByResource {

    /**
     * 先根据name -> 注入的是Red
     */
    @Resource(name = "red")
    private Color blue;

    /**
     * 先根据type -> 找到多个 -> 再根据name -> 注入white
     */
    @Resource(type = Color.class)
    private Color white;

    /**
     * 默认先根据name -> 找不到 -> 再根据type -> 找到多个 -> 抛错 NoUniqueBeanDefinitionException
     */
//    @Resource
//    private Color green;

    /**
     * 默认先根据name -> 找不到 -> 再根据type -> 找到一个 -> 注入oneBeanObject
     */
    @Resource
    private OneBeanObject oneBeanObj;

    public void testInject() {
        blue.printName();
        white.printName();
    }

}

2、注意事项

@Resource 注解不是Spring提供的,是由JDK提供的(javax.annotation.Resource),但是解析是由Spring负责的。

@Configuration

@Configuration修饰的类,首先是一个bean,其次是一个配置bean

对于配置bean,不光会放到Spring容器中,Spring还回去解析配置bean(例如为配置bean中的@Bean注解生成对应的bean)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    /**
     * 配置bean的beanName
     */
    @AliasFor(annotation = Component.class)
    String value() default "";
    
    /**
     * proxyBeanMethods = true		配置bean对应的是配置类的「代理对象」
     * proxyBeanMethods = false		配置bean对应的是配置类的「普通对象」
     */
    boolean proxyBeanMethods() default true;
}

1、proxyBeanMethods = true

/**
 * proxyBeanMethods = true   --->  Full配置bean(代理对象)
 */
@Configuration(proxyBeanMethods = true)
public class ConfigurationWithProxy {

    @Bean
    public ConfigurationTestBeanA configurationTestBeanA() {
        return new ConfigurationTestBeanA();
    }

    /**
     * 此时@Configuration标记的是代理对象,调用的也是代理类的 configurationTestBeanA()
     * 1)如果 ConfigurationTestBeanA 对应的bean 不存在,会创建对应的bean
     * 2)如果 ConfigurationTestBeanA 对应的bean 已存在,会直接返回已存在的bean
     * 也就是说,每次调用 configurationTestBeanA() 方法,得到的都是同一个bean
     */
    public void test() {
        // 返回的都是同一个对象
        System.out.println(configurationTestBeanA());
        System.out.println(configurationTestBeanA());
        System.out.println(configurationTestBeanA());
    }

}

2、proxyBeanMethods = false

/**
 * proxyBeanMethods = false  --->  Lite配置bean(普通对象)
 */
@Configuration(proxyBeanMethods = false)
public class ConfigurationWithoutProxy {

    @Bean
    public ConfigurationTestBeanB configurationTestBeanB() {
        return new ConfigurationTestBeanB();
    }

    /**
     * 此时@Configuration标记的是普通对象
     * 每次调用 configurationTestBeanB 方法,都会执行 configurationTestBeanB 方法的逻辑(新new一个对象)
     */
    public void test() {
        // 返回的都是不同的对象
        System.out.println(configurationTestBeanB());
        System.out.println(configurationTestBeanB());
        System.out.println(configurationTestBeanB());
    }
}

@Primary

自动装配出现多个Bean候选者时,会抛出异常 NoUniqueBeanDefinitionException

@Primary注解:自动装配出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者

  • 注意:@Primary注解只能使用一次,不能使用@Primary标记多个相同类型的bean,否则会报错
@Configuration
public class PrimaryConfig {

    @Bean("primaryTestBean1")
    public PrimaryTestBean primaryTestBean1() {
        return new PrimaryTestBean("primaryTestBean-1");
    }

    @Primary
    @Bean("primaryTestBean2")
    public PrimaryTestBean primaryTestBean2() {
        return new PrimaryTestBean("primaryTestBean-2");
    }

}
@Component
public class PrimaryTest {

    /**
     * 没加 @Primary --> 抛错(此处也可以用@Qualifier解决)
     * 加了 @Primary --> 优先注入@Primary修饰的primaryTestBean2
     */
    @Autowired
    private PrimaryTestBean primaryTestBean;

    /**
     * 没加 @Primary --> 根据name注入primaryTestBean1
     * 加了 @Primary --> 优先注入@Primary修饰的primaryTestBean2
     */
    @Autowired
    private PrimaryTestBean primaryTestBean1;

    public void testPrimary() {
        System.out.println(primaryTestBean.getName());
        System.out.println(primaryTestBean1.getName());
    }

}

@Lazy

@Lazy 可以使对应的bean懒加载(Spring启动时不会加载,只有在被调用的时候才会进行初始化)

  • 减少SpringIOC容器启动的加载时间
  • 解决循环依赖

1、加在类上

/**
 * 没加@Lazy --> 在Spring启动时就会加载
 * 加了@Lazy --> 在Spring启动时不会加载,只有在被调用的时候才会加载
 */
@Lazy
@Component
public class LazyOnClass {

}

2、加在配置类上

/**
 * 加了@Lazy --> 内部所有@Bean定义的Bean都是懒加载的bean
 */
@Lazy
@Configuration
public class LazyOnConfiguration {

    @Bean
    public A a() {
        return new A();
    }

    @Bean
    public B b() {
        return new B();
    }

}

3、加在字段上

@Component
public class LazyOnField {

    /**
     * 没加@Lazy --> 注入的是Bean对象
     * 加了@Lazy --> 注入的是CGLIB代理对象
     *          --> 在代理对象执行某方法时,才会从Spring容器去找对应bean对象,并执行bean对象对应的方法
     */
    @Lazy
    @Autowired
    public LazyInjectBean lazyInjectBean;

    public void test() {
        // xxx.LazyInjectBean$$EnhancerBySpringCGLIB$$fd9085ec
        System.out.println(lazyInjectBean.getClass());
        // 执行代理对象的逻辑 --> 找到bean对象 --> 执行bean对象的test
        lazyInjectBean.test();
    }

}

4、加在构造方法上

@Component
public class LazyOnConstruction {

    private LazyInjectBean lazyInjectBean;

    /**
     * 加了@Lazy的构造方法的参数,是代理对象
     */
    @Lazy
    @Autowired
    public LazyOnConstruction(LazyInjectBean lazyInjectBean) {
        // lazyInjectBean是代理对象
        this.lazyInjectBean = lazyInjectBean;
    }
}

5、加在方法上

@Component
public class LazyOnMethod {

    private LazyInjectBean lazyInjectBean;

    /**
     * 加了@Lazy的方法的所有参数,都是代理对象
     */
    @Lazy
    @Autowired
    public void setLazyInjectBean(LazyInjectBean lazyInjectBean1, LazyInjectBean lazyInjectBean2) {
        // lazyInjectBean1 和 lazyInjectBean2 都是代理对象
    }

}

6、加在参数前

@Component
public class LazyBeforeParam {

    private LazyInjectBean lazyInjectBean;

    /**
     * 前面有@Lazy的方法参数是代理对象
     */
    @Autowired
    public void setLazyInjectBean(@Lazy LazyInjectBean lazyInjectBean1,  
                                  LazyInjectBean lazyInjectBean2) {
        // lazyInjectBean1 是 代理对象
        // lazyInjectBean2 是 bean对象
    }

}

@Import

@Import 用于在配置类中引入其他配置类,从而将多个配置类组合在一起。

使用 @Import 注解可以实现模块化开发,将一个大型应用程序拆分成多个小的配置类,分别管理不同功能模块的配置信息,从而提高代码的可读性、可维护性和复用性。

以下是一个简单的示例,演示了如何使用 @Import 注解引入其他配置类:

@Configuration
@Import({AppConfig1.class, AppConfig2.class})
public class MainConfig {

}

MainConfig 是一个配置类,通过 @Import 注解引入了 AppConfig1AppConfig2 两个配置类。当 Spring IoC 容器加载 MainConfig 配置类时,会一并加载 AppConfig1AppConfig2 配置类,并将它们一起注册到容器中。

@Profile

1、配置profile

根据环境决定该创建哪个bean。Spring并不是在构建的时候确定的,而是等到运行时再来确定。

/**
 * 告诉Spring这个配置类中的bean只有在哪些profile激活时才会创建
 */
@Profile({"local", "dev", "test"})
public class DevelopmentProfileConfig {}

注意:没有指定profile的bean始终都会被创建

在Spring 3.1之前,只能在类级别上使用@Profile注解。

从Spring 3.2开始,可以在方法级别上使用@Profile注解, 与@Bean注解一同使用。

在这里插入图片描述

2、激活profile

Spring确定哪个profile处于激活状态时,需要依赖两个属性:spring.profiles.active 和 spring.profiles.default

  • 如果设置了spring.profiles.active属性的话,那么它的值就会用来确定 哪个profile是激活的。
  • 如果没有设置spring.profiles.active属性,那Spring将会查找spring.profiles.default的值。
  • 如果两个属性都没有设置,那就没有激活的profile,只会创建那些没有定义在profile中的bean。

有多种方式来设置这两个属性:

  • 作为DispatcherServlet的初始化参数;
  • 作为Web应用的上下文参数;
  • 作为JNDI条目;
  • 作为环境变量;
  • 作为JVM的系统属性;
  • 在集成测试类上,使用 @ActiveProfiles 注解设置。

@Lookup

@Target(ElementType.METHOD)	// 可以看到,@Lookup只能在方法上使用
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lookup { 
    String value() default "";
}

@Lookup的作用:

  1. 给某个方法赋值一个bean(@Lookup修饰的方法会被重写,在调用时,会返回方法返回值的对象)
  2. 抽象类是不能成为bean对象的,但是在抽象类的方法上标注@LookUp,就可以让这个抽象类被注入到容器中

1、抽象类注入

/**
 * 正常情况下,抽象类是不能成为bean对象的
 *  1)不加@Lookup --> 注入DefineAbstractBean会报错
 *  2)加上@Lookup --> 可以正常注入DefineAbstractBean
 */
@Component
public abstract class DefineAbstractBean {

    /**
     * Spring在遇到LookUp标注的方法时,会重写此方法。
     * 因此返回值为null,对程序也没有影响。
     */
    @Lookup
    public PrototypeBean getPrototypeBean() {
        return null;
    }

}

2、单例bean中使用多例bean

@Component
@Scope("prototype")
public class PrototypeBean {

}
@Component
public class GetPrototypeBean {

    @Autowired
    private PrototypeBean prototypeBean;

    public void compareLookUpAndAutowired() {
        System.out.println("获取两次@Autowired注入的对象 ---> 相同的对象");
        System.out.println("@Autowired--->" + prototypeBean);
        System.out.println("@Autowired--->" + prototypeBean);
    }

}

虽然 PrototypeBean 是多例bean,但是 GetPrototypeBean 是单例bean,在Spring容器启动时,已经注入了prototypeBean(固定了),所以即使是多例,每次取到的也是同一个对象。

可以使用 @Lookup 修饰的方法,每次都获取一个新的对象。(不需要注入了,也不会有循环依赖了)

@Component
public class GetPrototypeBean {

    @Lookup
    public PrototypeBean getPrototypeBean() {
        return null;
    }

    public void usePrototypeBean() {
        System.out.println("获取两次@Lookup返回的对象 ---> 不同的对象");
        System.out.println("@Lookup--->" + getPrototypeBean());
        System.out.println("@Lookup--->" + getPrototypeBean());
    }

}

@Conditional

如果希望一个或多个bean只有在指定条件下才创建,可以使用 @Conditional 注解

1、相关类

@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
/**
 * Spring容器相关信息
 */
public interface ConditionContext {

	/**
	 * 检查bean定义
	 */
	BeanDefinitionRegistry getRegistry();

    /**
	 * 检查bean是否存在,以及bean的属性
	 */
	@Nullable
	ConfigurableListableBeanFactory getBeanFactory();

    /**
	 * 检查环境变量是否存在,以及它的值是什么
	 */
	Environment getEnvironment();
    
    /**
	 * 加载的资源
	 */
	ResourceLoader getResourceLoader();
    
    /**
	 * 检查是否存在某个类
	 */
	@Nullable
	ClassLoader getClassLoader();

}
/**
 * 能够让我们检查带有@Bean注解的方法上还有什么其他的注解
 */
public interface AnnotatedTypeMetadata {

	boolean isAnnotated(String annotationName);

	@Nullable
	Map<String, Object> getAnnotationAttributes(String annotationName);

	@Nullable
	Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);

	@Nullable
	MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);

	@Nullable
	MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);

}

2、基本使用

/**
 * 只有满足@Conditional中指定的,Condition接口的实现类的match方法,才会注册这个bean
 */
@Component
@Conditional(value = {ConditionImpl.class})
public class ConditionalBean {

}
/**
 * 自定义Condition实现类,重写match方法
 */
public class ConditionImpl implements Condition {

    /**
     * 被@Conditional标记的bean,必须满足matches方法才会被放到IOC容器
     *
     * @param context  Spring容器相关信息
     * @param metadata @Conditional所在类的元数据信息
     * @return 是否满足条件
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        try {
            // 是否包含某个注解(注解的类全名)
            if (!metadata.isAnnotated("org.springframework.stereotype.Component")) {
                return false;
            }

            // 是否能够加载某个类
            context.getClassLoader().loadClass("com.demo.spring.ioc.annotation.conditional.Tomcat");
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}

@Value

@Value()注解的value属性可以有三种表现形式:

1、字符串

import org.springframework.core.io.Resource;

/**
 * 静态属性值:普通字符串、URL资源、文件资源
 */
@Component
public class StaticValue {

    /**
     * 注入普通字符串,相当于直接给属性默认值
     */
    @Value("hello")
    private String value;

    /**
     * 注入URL资源,将对应的URL字符串转换成URL
     */
    @Value("https://www.baidu.com")
    private URL url;

    /**
     * 注入Resource文件资源,将对应的字符串值转换成对应的资源文件
     * 注意Resource全限定名:org.springframework.core.io.Resource)
     */
    @Value("classpath:test.text")
    private Resource resource;

    public void printStrValue() {
        System.out.println("value = " + value);
        System.out.println("url = " + url);
        System.out.println("resource = " + resource);
    }

}

2、${} - 属性占位符

# application.yaml
book:
  author: 鲁迅

colors: red, yellow, green
/**
 * 配置文件属性值
 *  1)格式:${}
 *  2)使用:注入配置文件的值,可以使用冒号指定默认值
 */
@Component
public class PropertyValue {

    /**
     * 注入配置文件中的配置(如:book.author=Albert)
     */
    @Value("${book.author}")
    public String author;

    /**
     * 注入数组(自动根据","分割)
     */
    @Value("${colors}")
    private String[] colorArray;

    /**
     * 注入列表形式(自动根据","分割)
     */
    @Value("${colors}")
    private List<String> colorList;

    /**
     * 找不到相关配置,会报错 Could not resolve placeholder 'name' in value "${name}"
     */
//    @Value("${name}")
    private String name;

    /**
     * 配置名称加上 冒号: 来指定默认值
     *      1)找到了配置 -> 使用配置的值
     *      2)没找到配置 -> 使用冒号后的默认值
     */
    @Value("${name:default}")
    private String defaultName1;

    /**
     * 冒号后边为空,则表示空串
     */
    @Value("${name:}")
    private String defaultName2;

    public void printStrValue() {
        System.out.println("author = " + author);
        System.out.println("colorArray = " + colorArray[0]);
        System.out.println("colorList = " + colorList);
        System.out.println("defaultName1 = " + defaultName1);
        System.out.println("defaultName2 = " + defaultName2);
    }

}
author = 鲁迅
colorArray = red
colorList = [red, yellow, green]
defaultName1 = default
defaultName2 = 

3、#{} - SpEL表达式

# application.yaml
book:
  author: 鲁迅

colors: red, yellow, green

nums: 1|2|3|4|5|6
/**
 * SpEL表达式
 *  1)格式:#{}
 *  2)使用:大括号内可以为 环境变量、其他bean、bean的属性、bean的方法调用 等
 */
@Component
public class SpELValue {

    /**
     * 注入操作系统属性,如:Mac OS X
     */
    @Value("#{systemProperties['os.name']}")
    private String systemPropertiesName;

    /**
     * 如果系统属性中未获取到port的值,则使用8888。
     */
    @Value("#{systemProperties['port']?:'8888'}")
    private String port;

    /**
     * 注入表达式结果
     */
    @Value("#{ T(java.lang.Math).random() * 100.0 }")
    private double randomNumber;

    /**
     * 注入其他Bean属性:注入staticValue对象的属性value(必须是public的,否则会报错)
     */
    @Value("#{propertyValue.author}")
    private String author;

    /**
     * #{}和${}的组合注入属性并进行分割处理(根据"|"分割,默认是根据","分割)
     */
    @Value("#{'${nums}'.split('\\|')}")
    private List<String> numList;

    public void printStrValue() {
        System.out.println("systemPropertiesName = " + systemPropertiesName);
        System.out.println("port = " + port);
        System.out.println("randomNumber = " + randomNumber);
        System.out.println("author = " + author);
        System.out.println("numList = " + numList);
    }


}
systemPropertiesName = Mac OS X
port = 8888
randomNumber = 1.61735503976147
author = 鲁迅
numList = [1, 2, 3, 4, 5, 6]

4、@Value的扩展注解

@Component
public class ValueService {

    @Value("${server.port}")
    private String port;

}

如果有很多地方都要用到这个配置值,可以基于@Value做扩展

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
    String value();
}
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Value("${server.port}")
public @interface ServerPort {
}
@Component
public class ValueService {

    @ServerPort
    private String port;

}

AOP 相关的注解

1. @EnableAspectJAutoProxy
		开启AOP注解
2. @Aspect
		声明当前类使用切面类(通知类)
3. @Before
		前置通知:目标方法执行之前执行
4. @AfterReturning
		后置通知:目标方法执行完成之后执行
5. @AfterThrowing
		异常通知:目标方法发生异常时执行
6. @After
		最终通知:不管有没有异常,一定会执行。
7. @Around
		环绕通知:手动执行目标方法,完成各类通知的配置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1460902.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信奥一本通:1085:球弹跳高度的计算

这个题的点在于注意他求得是一共经过的米数&#xff0c;也就是下降起跳都算在里面&#xff0c;例如第一次下降20再起跳15就是一次循环的高度 #include <iostream> # include <iomanip> using namespace std; double h,c1,s; int main(){cin >> h;s h;while…

1902_野火FreeRTOS教程内核在STM32中用到的2个中断PENDSV和SYSTICK

1902_野火FreeRTOS教程内核在STM32中用到的2个中断PENDSV和SYSTICK 全部学习汇总&#xff1a; g_FreeRTOS: FreeRTOS学习笔记 (gitee.com) 上面是涉及到的源代码&#xff0c;而这次需要分析的就是78、79行的两个中断。首先&#xff0c;需要确认NVIC_SYSPRI2寄存器的作用。 进一…

MySQL篇—事务和隔离级别介绍

☘️博主介绍☘️&#xff1a; ✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux&#xff0c;也在积极的扩展IT方向的其他知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章&#xff0c;并且也会默默的点赞收藏加关注❣…

程序员们的“不关机”现象:一种技术文化还是不良习惯?

文章目录 程序员们的“不关机”现象&#xff1a;一种技术文化还是不良习惯&#xff1f;引言程序员为何不爱关机&#xff1f;开发环境的冷启动成本持续集成与持续部署远程办公与全球协作运行中的服务与调试环境随时待命的紧急响应 长期开机是否会对硬件产生损害&#xff1f;最后…

Word第一课

文章目录 1. 文件格式1.1 如何显示文件扩展名1.2 Word文档格式的演变1.3 常见的Word文档格式 3. 文档属性理解文档属性查看文档属性 4. 显示比例方式一&#xff1a; 手动调整方式二&#xff1a; 自动调整 5. 视图、窗口视图 1. 文件格式 1.1 如何显示文件扩展名 文档格式指的…

2-IOC容器的初始化流程

IOC容器的初始化流程 AbstractApplicationContext.refresh() 准备BeanFactory&#xff08;DefaultListableBeanFactory&#xff09; 设置ClassLoader 设置Environment 扫描要放入容器的Bean&#xff0c;得到对应的BeanDefinition 注册BeanPostProcessor 处理国际化 处理事件…

vue+element (el-progress)标签 隐藏百分比(%) ,反向显示 ,自定义颜色, demo 复制粘贴拿去用

1 效果: 2 页面代码: <el-row :gutter"10" ><el-col :span"12"><el-card ><div class"fourqu"><div><span slot"title">{{推送任务TOP5}}</span></div></div><div class&…

Unity NavMesh 清除不可行走区域

通常场景中物体设置为static或Navigation Static后&#xff0c;打开Navigation使用默认设置烘焙NavMesh&#xff0c;模型顶部和底部会出现蓝色网格&#xff0c;但其中有部分属于不可能到达区域&#xff0c;如下图 本文介绍两种可去掉NavMesh中不需要网格的方法&#xff1a; 方…

K210基础实验——点亮LED灯

一、目的是点亮K210开发板左下角的LED0和LED1&#xff0c;LED0是红灯&#xff0c;LED1是绿灯&#xff0c;两颗LED灯都是低电平点亮&#xff0c;高电平熄灭。 二、这是原理图上的硬件连接&#xff0c;LED0连接的是IO0&#xff0c;LED1连接的是IO17。 三、在src目录下新建文件夹 …

适用于高云FPGA的JTAG

目标板卡&#xff1a;小梅哥芯海无涯GOWIN高云ACG525(GW5A-LV25UG324) 1.软件要求&#xff1a;必须用商业版&#xff0c;因为教育版(V1.9.9Beta-4 Education)不支持此封装的GW5A。商业版需要上网申请License&#xff0c;此处提供D4D853392AD8.lic文件&#xff08;此方法为临时…

开启智能互动新纪元——ChatGPT提示词工程的引领力

目录 提示词工程的引领力 高效利用ChatGPT提示词方法 提示词工程的引领力 近年来&#xff0c;随着人工智能技术的迅猛发展&#xff0c;ChatGPT提示词工程正逐渐崭露头角&#xff0c;为智能互动注入了新的活力。这一技术的引入&#xff0c;使得人机交流更加流畅、贴近用户需求&…

MATLAB卷积神经网络——基于ResNet-50进行图像分类

一、ResNet50工具箱安装 &#xff08;1&#xff09;下载工具箱 https://ww2.mathworks.cn/matlabcentral/fileexchange/64626-deep-learning-toolbox-model-for-resnet-50-network &#xff08;2&#xff09;在matlab打开下载的resnet50.mlpkginstall文件 &#xff08;3&…

达梦数据库--DM8两节点DSC集群安装部署(达梦数据库DSC集群搭建)

1 前期规划 系统规划 本地磁盘规划 共享存储规划 DMDSC 集群为了实现多实例同时访问和修改数据&#xff0c;需要数据文件、控制文件和日志文件都放到共享存储上。DM 支持两种共享存储&#xff0c;裸设备和 DMASM&#xff0c;裸设备是未经过格式化的特殊字符设备&#xff0c;…

Unity3d Shader篇(七)— 纹理采样

文章目录 前言一、什么是纹理采样&#xff1f;1. 纹理采样的工作原理2. 纹理采样的优缺点优点缺点 二、使用步骤1. Shader 属性定义2. SubShader 设置3. 渲染 Pass4. 定义结构体和顶点着色器函数5. 片元着色器函数 三、效果四、总结使用场景 前言 纹理采样是一种常用的图形学技…

Ubuntu 23.10:内网安装rapidocr_paddle(GPU)及其前置准备

Ubuntu 23.10&#xff1a;内网安装rapidocr_paddle&#xff08;GPU&#xff09;及其前置准备 – WhiteNights Site 标签&#xff1a;Linux, ocr, ubuntu, 系统相关 安装NVIDIA驱动、安装CUDA&&CUDNN、安装… rapidocr_paddle主要是用于ocr识别的引擎。本文主要针对于…

2024年抖店的市场已经饱和,小白不适合入局了?真实现状如下

我是王路飞。 如今的抖店&#xff0c;在整个电商行业&#xff0c;并不算是个“新人”了&#xff0c;毕竟已经上线四五年时间了。 每个项目都有自己的红利期、爆发期&#xff0c;最后基本都会进入到发展期。 抖店亦是如此&#xff0c;你要说流量红利期吧&#xff0c;确实已经…

如何选择最适合的图纸加密软件?用户体验及性价比

安秉网盾图纸加密软件是一款功能强大的图纸加密工具&#xff0c;具有以下特点和优势&#xff1a; 全盘加密&#xff1a;安秉网盾采用先进的加密算法&#xff0c;能对文件、文件夹、磁盘等数据进行全面加密&#xff0c;确保数据在存储和传输过程中的安全性。 监控与审计&#…

k-means聚类、GMM高斯聚类、canopy聚类、DBSCAN聚类、FCM聚类、ISODATA聚类、k-medoid聚类、层次聚类、谱聚类 对比

k-means聚类、GMM高斯聚类、canopy聚类、DBSCAN聚类、FCM聚类、ISODATA聚类、k-medoid聚类、层次聚类、谱聚类 对比 标 代码获取代码获取代码获取代码获取代码获取代码获取代码获取代码获取代码获取代码获取题 GMM&#xff08;高斯混合模型&#xff09;是一种聚类算法&#xff…

UE蓝图 入口(FunctionEntry)节点和源码

系列文章目录 UE蓝图 Get节点和源码 UE蓝图 Set节点和源码 UE蓝图 Cast节点和源码 UE蓝图 分支(Branch)节点和源码 UE蓝图 入口(FunctionEntry)节点和源码 文章目录 系列文章目录一、FunctionEntry节点功能二、入口节点用法1. 创建函数2. 命名函数3. 定义参数4. 编写函数逻辑5…

【Deep Learning 6】可变形卷积

&#x1f31e;欢迎来到Pytorch的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f4c6;首发时间&#xff1a;&#x1f339;2024年2月20日&a…