Spring注释

@ComponentScan

public enum FilterType {
    ANNOTATION,      //按照注解
    ASSIGNABLE_TYPE, //扫描指定的类型
    ASPECTJ,         //按照Aspectj的表达式
    REGEX,           //使用正则表达式
    CUSTOM           //自定义过滤规则
}
@Configuration
@ComponentScan(
    basePackages = {"com.tuling.testcompentscan"},
    includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, 
        value = {Controller.class, Service.class})
    },
    useDefaultFilters = false
)
public class MainConfig {}

includeFilters 若需要使用,则需把useDefaultFilters设置为false。useDefaultFilters 默认为 true ,自动扫描带有 @Component(包括其子类)的类。

ClassPathBeanDefinitionScanner的构造器会传入 useDefaultFilters 。
if (useDefaultFilters) {registerDefaultFilters();}
若 useDefaultFilters 设置为 true 则会进行全部扫描,从而 includeFilters 也便会失效。

当扫描的包是多个时,应当使用数组的形式

@ComponentScan({"com.example.service.rpc","com.example.web.controller"})

FilterType.CUSTOM

示例:通过重写 match 方法,当 className 包括 dao 字样则返回匹配。

@Configuration
@ComponentScan(
    basePackages = {"com.tuling.testcompentscan"},
    includeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM,
        value = TulingFilterType.class)
    },
    useDefaultFilters = false
)
public class MainConfig {}
public class TulingFilterType implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取当前类的注解源信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前类的class的源信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前类的资源信息
        Resource resource = metadataReader.getResource();
        if(classMetadata.getClassName().contains("dao")) {
            return true;
        }
        return false;
    }
}

@Bean

大白话讲解@Bean
Spring的 @Bean 注解用于告诉方法,产生一个Bean对象,然后这个 Bean 对象交给 Spring 管理。 产生这个 Bean 对象的方法 Spring 只会调用一次,随后这个 Spring 将会将这个 Bean 对象放在自己的 IOC 容器中。 @Bean 明确地指示了一种方法,什么方法呢?产生一个bean的方法,并且交给 Spring 容器管理;从这我们就明白了为啥 @Bean 是放在方法的注释上了,因为它很明确地告诉被注释的方法,你给我产生一个 Bean ,然后交给 Spring 容器,剩下的你就别管了。记住, @Bean 就放在方法上,就是让方法去产生一个 Bean ,然后交给 Spring 容器。

当 @Bean 里未指定名称,则bean的默认名称是首字母小写的方法名
当 @Bean 里指定名称,则默认的名称会失效,可以通过 value 或者 name 指定。 value 和name 功能相同,若存在多个别名应采用数组形式。

//三者等价
@Bean({"userService1", "userService2"})
@Bean(name = {"userService1", "userService2"})
@Bean(value = {"userService1", "userService2"})

若使用了以上别名且 userService 不在别名里时,则会报找不到 bean 的异常

Exception in thread “main” org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named ‘userService’ available

有兴趣的可以自行研究 Spring 源码里的org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod方法,关于解析 Spring 注解方法相关。

@Scope

@Scope 在 SpringIOC 中是 @Bean 的属性,用来形容当前 @Bean 的作用域(范围)

不明白什么意思?接着往下看就明白了~

@Scope的可选值

  • singleton:默认类型(不指定Scope即为单例),在整个 bean 容器中只会有一个实例。

  • prototype:每次通过容器的getBean方法获取Bean时,都将产生一个新的Bean实例

  • request:仅适用于 Web 环境下的 ApplicationContext ,表示每一个 HttpRequest 生命周期内会有一个单独的实例,即每一个Http请求都会拥有一个单独的实例。

  • session:仅适用于Web环境下的ApplicationContext,表示每一个HttpSession生命周期内会有一个单独的实例,即每一个HttpSession下都会拥有一个单独的实例,即每一个用户都将拥有一个单独的实例。

  • globalSession:仅适用于Web环境下的ApplicationContext,一般来说是Portlet环境下。表示每一个全局的Http Session下都会拥有一个单独的实例。

@Scope 分为基本作用域(singleton、prototype)和Web作用域(request、session、globalSession)。项目中最常用的就是基本作用域,其他比较次要等项目有需要或者有私下有时间再研究。

场景:单例调用多例

@Component
public class SingletonBean {
    @Autowired
    private PrototypeBean bean;
}

前提:SingletonBean 为单例模式,PrototypeBean 为原型模式。
结果:这里并不能达到每次创建返回新实例的效果。
原因:@Autowired 只会在单例 SingletonBean 初始化的时候注入一次,再次调用 SingletonBean 的时候,PrototypeBean 不会再创建了(注入时创建,而注入只有一次)。
解决方案:
①:不使用 @Autowired ,每次调用多例的时候,直接调用Bean;
②:Spring 的解决办法:设置 proxyMode , 每次请求的时候实例化。

全注解攻略:@Scope
两种代理模式的区别:
ScopedProxyMode.INTERFACES: 创建一个JDK代理模式
ScopedProxyMode.TARGET_CLASS: 基于类的代理模式
前者只能将其注入一个接口,后者可以将其注入类。


@Primary

在注入过程中由于多个bean满足某一类型,同时这些bean之间又没有主次之分,所以Spring无法擅自做主为我们选择注入其中的某一个bean。

@Primary 则会告之Spring哪个bean的地位是主要的、首要的、要优先考虑的,此时当发生类型情况下,Spring在注入时则会优先使用这个主bean

场景一:接口存在多个实现类

public interface Singer {}

@Component
public class MetalSinger implements Singer{
    public MetalSinger() {System.out.println("MetalSinger");}
}

public class OperaSinger implements Singer{
    public OperaSinger() {System.out.println("OperaSinger");}
}

@Controller
public class UserController {
	@Autowired
    private Singer singer;
}

结果是打印出:MetalSinger。因为OperaSinger 类没有加上注解,所以spring 注入的时候,只能找到 MetalSinger 这个实现类,所以才有这个结果。

如果你给OperaSinger类也加上@Component注解。则会报以下错误。并且也给出提示:如果同个类型存在多个bean考虑将其中一个bean标记为@Primary,或者使用@Qualifier来作为标识可能被消费的bean

Field singer in com.ailpha.controller.UserController required a single bean, but 2 were found:
- metalSinger: defined in file [文件绝对路径/MetalSinger.class]
- operaSinger: defined in file [文件绝对路径/OperaSinger.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed


场景二:同一类型注册多个bean

@Bean
@Primary
public Employee JohnEmployee() {
    return new Employee("John");
}
@Bean
@Primary
public Employee TonyEmployee() {
    return new Employee("Tony");
}

@Controller
public class EmployeeController {
	@Autowired
    private Employee employee;
}

此时Spring在注入时发现了多个被声明为主的bean,两个主bean的优先级相同。Spring没有办法自作主张的注入一个其中一个bean给我们,编译时会报如下错误。所以对于同一种类型只能存在一个@Primary注解

Could not autowire. There is more than one bean of ‘Employee’ type.
Beans: JohnEmployee (BeanConfig.java)
TonyEmployee (BeanConfig.java)


@Lazy

Spring高级之注解@lazy详解
该注解的作用是用于指定单例bean实例化的时机。未使用此注解时在容器初始化时就会被创建;而使用了该注解,会在该bean在被第一次使用时创建,并且只创建一次。

//可以作用在类上、方法上、构造器上、方法参数上、成员变量中。
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lazy {
	//是否延迟加载,默认是,如果设置为false,那跟不使用该注解一样。
    boolean value() default true;
}

@DependsOn

Spring高级之注解@DependsOn详解
标记有这个注释的组件,要依赖于另一个组件,也就是说被依赖的组件会比该组件先注册到IOC容器中。

//可以作用在方法和类上。
//当作用在类上时,通常会与@Component及其衍生注解等注解配合使用。
//当作用在方法上时,通常会与@Bean注解配合使用。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DependsOn {
	//要依赖的bean id,是个数组,也就是说可以依赖多个bean。
	//效果是该注解作用的bean会比value设置的依赖bean晚实例化到容器中。
    String[] value() default {};
}

@Qualifier

按照name注入某个 bean ,和 @Autowired 配合使用。主要是为了区别于 @Autowired 默认按 type 注入。(按照 @Autowired 注入的bean 名称为类名的首字母小写

你问我实际使用场景?根据 name注入和 type 有什么区别?以下我列举了俩个场景,相信你看完之后,对这个注解的理解可以更深一层。

场景一:配置类中配置了多个同类型的bean

//配置类:由同个User类所生成的俩个bean
@Configuration
public class BeanConfig {
    @Bean
    public User getUser1() {
        return new User();
    }

    @Bean
    public User getUser2() {
        return new User();
    }
}

//某个需要注入User的类
@Controller
public class UserController {
    @Autowired
    private User user;
}

这时我们编译都无法正常通过。并且会给你以下提示:有超过1个按照 User type 生成的bean。

Could not autowire. There is more than one bean of ‘User’ type.
Beans:
getUser1 (BeanConfig.java)
getUser2 (BeanConfig.java)

思考下为什么会有这种提示?上面我们也提到,@Autowired 是默认按 type 注入,我们自然而然可以想到 type 指的就是 由User类型生成的bean同属于一个type。我们就需要根据 name 去查找唯一bean。

@Configuration
public class BeanConfig {
    @Bean(name = "user1")
    public User getUser1() {
        return new User();
    }

    @Bean(name = "user2")
    public User getUser2() {
        return new User();
    }
}

@Controller
public class UserController {
    @Autowired
    @Qualifier("user1")
    private User user;
}

总结:

  1. 先根据 入参类型 找,如果只找到一个,那就直接用来作为入参
  2. 如果根据类型找到多个,则再根据 入参名字 来确定唯一一个
  3. 最终如果没有找到,则会报错,无法创建当前Bean对象

场景二:接口有多个实现类

@Controller
public class UserController {
	@Autowired
    private UserService userService;
}

@Service
public interface UserService {
}

@Service
public class UserService1 implements UserService {
}
@Service
public class UserService2 implements UserService {
}

注意,这时候编译会通过,但是在项目启动时又会报错了。并且会给你以下提示:根据 type 查找预期匹配单个bean但是却找到了俩个。

nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.ailpha.controller.UserService’ available: expected single matching bean but found 2: userService1,userService2

这里便犯了和场景一一样的问题,我们根据提示来修改,根据 type 查找只允许他找到一个,这个虽然目的能达到但是可能不符合实际。这时我们就可以换一个思路,根据 name 去查找唯一bean,只需要在注入的时候通过 @Qualifier 来指定要注入的类

@Controller
public class UserController {
    @Autowired
    @Qualifier("userService1")
    private UserService userService;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值