聊聊Spring中的各项注解

作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址:https://github.com/sunshinelyz/mykit-delay

成都创新互联公司制作网站网页找三站合一网站制作公司,专注于网页设计,成都网站建设、成都网站制作,网站设计,企业网站搭建,网站开发,建网站业务,680元做网站,已为成百上千服务,成都创新互联公司网站建设将一如既往的为我们的客户提供最优质的网站建设、网络营销推广服务!

写在前面

由于在更新其他专题的文章,Spring系列文章有很长一段时间没有更新了,很多小伙伴都在公众号后台留言或者直接私信我微信催更Spring系列文章。

看来是要继续更新Spring文章了。想来想去,写一篇关于Spring中注解相关的文章吧,因为之前更新Spring系列的文章一直也是在更新Spring注解驱动开发。这篇文章也算是对之前文章的一个小小的总结吧,估计更新完这篇,我们会进入Spring的AOP章节的更新。

文章已收录到:

https://github.com/sunshinelyz/technology-binghe

https://gitee.com/binghe001/technology-binghe

xml配置与类配置

1.xml配置

 
 
 
 
  1.  
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/sp 
  4.   
  5.  

获取Person实例如下所示。

 
 
 
 
  1. public static void main( String[] args ){ 
  2.  ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 
  3.  System.out.println(ctx.getBean("person")); 

2.类配置

 
 
 
 
  1. @Configuration 
  2. public class MainConfig { 
  3.     @Bean 
  4.     public Person person(){ 
  5.      return new Person(); 
  6.     } 
  7. }   

这里,有一个需要注意的地方:通过@Bean的形式是使用的话, bean的默认名称是方法名,若@Bean(value="bean的名称")那么bean的名称是指定的 。

获取Person实例如下所示。

 
 
 
 
  1. public static void main( String[] args ){ 
  2.  AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); 
  3.  System.out.println(ctx.getBean("person")); 

@CompentScan注解

我们可以使用@CompentScan注解来进行包扫描,如下所示。

 
 
 
 
  1. @Configuration 
  2. @ComponentScan(basePackages = {"com.binghe.spring"}) 
  3.  public class MainConfig { 
  4. }  

excludeFilters 属性

当我们使用@CompentScan注解进行扫描时,可以使用@CompentScan注解的excludeFilters 属性来排除某些类,如下所示。

 
 
 
 
  1. @Configuration 
  2. @ComponentScan(basePackages = {"com.binghe.spring"},excludeFilters = { 
  3. @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}), 
  4. @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {PersonService.class}) 
  5. }) 
  6. public class MainConfig { 

includeFilters属性

当我们使用@CompentScan注解进行扫描时,可以使用@CompentScan注解的includeFilters属性将某些类包含进来。这里需要注意的是:需要把useDefaultFilters属性设置为false(true表示扫描全部的)

 
 
 
 
  1. @Configuration 
  2. @ComponentScan(basePackages = {"com.binghe.spring"},includeFilters = { 
  3. @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class, PersonService.class}) 
  4. },useDefaultFilters = false) 
  5. public class MainConfig { 

@ComponentScan.Filter type的类型

注解形式的FilterType.ANNOTATION @Controller @Service @Repository @Compent

  • 指定类型的 FilterType.ASSIGNABLE_TYPE @ComponentScan.Filter(type =FilterType.ASSIGNABLE_TYPE,value = {Person.class})
  • aspectj类型的 FilterType.ASPECTJ(不常用)
  • 正则表达式的 FilterType.REGEX(不常用)
  • 自定义的 FilterType.CUSTOM
 
 
 
 
  1. public enum FilterType { 
  2.     //注解形式 比如@Controller @Service @Repository @Compent 
  3.     ANNOTATION, 
  4.     //指定的类型 
  5.     ASSIGNABLE_TYPE, 
  6.     //aspectJ形式的 
  7.     ASPECTJ, 
  8.     //正则表达式的 
  9.     REGEX, 
  10.     //自定义的 
  11.     CUSTOM 

FilterType.CUSTOM 自定义类型

 
 
 
 
  1. public class CustomFilterType implements TypeFilter { 
  2. @Override 
  3. public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { 
  4.     //获取当前类的注解源信息 
  5.     AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); 
  6.     //获取当前类的class的源信息 
  7.     ClassMetadata classMetadata = metadataReader.getClassMetadata(); 
  8.     //获取当前类的资源信息 
  9.     Resource resource = metadataReader.getResource(); 
  10.   return classMetadata.getClassName().contains("Service"); 
  11.      
  12. @ComponentScan(basePackages = {"com.binghe.spring"},includeFilters = { 
  13. @ComponentScan.Filter(type = FilterType.CUSTOM,value = CustomFilterType.class) 
  14. },useDefaultFilters = false) 
  15. public class MainConfig { 

配置Bean的作用域对象

不指定@Scope

在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载(容器启动实例就创建好了)

 
 
 
 
  1. @Bean 
  2. public Person person() { 
  3.  return new Person(); 
  4. }  

@Scope为 prototype

指定@Scope为 prototype 表示为多实例的,而且还是懒汉模式加载(IOC容器启动的时候,并不会创建对象,而是在第一次使用的时候才会创建)

 
 
 
 
  1. @Bean 
  2. @Scope(value = "prototype") 
  3. public Person person() { 
  4.     return new Person(); 

@Scope取值

  • singleton 单实例的(默认)
  • prototype 多实例的
  • request 同一次请求
  • session 同一个会话级别

懒加载

Bean的懒加载@Lazy(主要针对单实例的bean 容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象)

 
 
 
 
  1. @Bean 
  2. @Lazy 
  3. public Person person() { 
  4.  return new Person(); 

@Conditional条件判断

场景,有二个组件CustomAspect 和CustomLog ,我的CustomLog组件是依赖于CustomAspect的组件 应用:自己创建一个CustomCondition的类 实现Condition接口

 
 
 
 
  1. public class CustomCondition implements Condition { 
  2. /**** 
  3. @param context 
  4. * @param metadata 
  5. * @return 
  6. */ 
  7.     @Override 
  8.     public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 
  9.         //判断容器中是否有CustomAspect的组件 
  10.         return context.getBeanFactory().containsBean("customAspect"); 
  11.     }  
  12. }  
  13.  
  14. public class MainConfig { 
  15.     @Bean 
  16.     public CustomAspect customAspect() { 
  17.         return new CustomAspect(); 
  18.     }  
  19.     @Bean 
  20.     @Conditional(value = CustomCondition.class) 
  21.     public CustomLog customLog() { 
  22.      return new CustomLog(); 
  23.     } 

向IOC 容器添加组件

(1)通过@CompentScan +@Controller @Service @Respository @compent。适用场景: 针对我们自己写的组件可以通过该方式来进行加载到容器中。

(2)通过@Bean的方式来导入组件(适用于导入第三方组件的类)

(3)通过@Import来导入组件 (导入组件的id为全类名路径)

 
 
 
 
  1. @Configuration 
  2. @Import(value = {Person.class}) 
  3. public class MainConfig { 

通过@Import 的ImportSeletor类实现组件的导入 (导入组件的id为全类名路径)

 
 
 
 
  1. public class CustomImportSelector implements ImportSelector {  
  2.     @Override 
  3.     public String[] selectImports(AnnotationMetadata importingClassMetadata) { 
  4.      return new String[]{"com.binghe.spring"}; 
  5.     } 
  6. }  
  7. Configuration 
  8. @Import(value = {Person.class} 
  9. public class MainConfig { 

通过@Import的 ImportBeanDefinitionRegister导入组件 (可以指定bean的名称)

 
 
 
 
  1. public class DogBeanDefinitionRegister implements ImportBeanDefinitionRegistrar { 
  2.     @Override 
  3.     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 
  4.         //创建一个bean定义对象 
  5.         RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Dog.class); 
  6.         //把bean定义对象导入到容器中 
  7.         registry.registerBeanDefinition("dog",rootBeanDefinition); 
  8.     } 
  9. }  
  10. @Configuration 
  11. @Import(value = {Person.class, Car.class, CustomImportSelector.class, DogBeanDefinitionRegister.class}) 
  12. public class MainConfig { 

通过实现FacotryBean接口来实现注册 组件

 
 
 
 
  1. public class CarFactoryBean implements FactoryBean { 
  2.     @Override 
  3.     public Car getObject() throws Exception { 
  4.      return new Car(); 
  5.     }  
  6.     @Override 
  7.     public Class getObjectType() { 
  8.      return Car.class; 
  9.     }  
  10.  
  11.     @Override 
  12.     public boolean isSingleton() { 
  13.      return true; 
  14.     } 

Bean的初始化与销毁

指定bean的初始化方法和bean的销毁方法

由容器管理Bean的生命周期,我们可以通过自己指定bean的初始化方法和bean的销毁方法

 
 
 
 
  1. @Configuration 
  2. public class MainConfig { 
  3.     //指定了bean的生命周期的初始化方法和销毁方法.@Bean(initMethod = "init",destroyMethod = "destroy") 
  4.     public Car car() { 
  5.      return new Car(); 
  6.     } 

针对单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法

针对多实例bean的话,容器启动的时候,bean是不会被创建的而是在获取bean的时候被创建,而且bean的销毁不受IOC容器的管理

通过 InitializingBean和DisposableBean实现

通过 InitializingBean和DisposableBean个接口实现bean的初始化以及销毁方法

 
 
 
 
  1. @Component 
  2. public class Person implements InitializingBean,DisposableBean { 
  3.     public Person() { 
  4.      System.out.println("Person的构造方法"); 
  5.     }  
  6.     @Override 
  7.     public void destroy() throws Exception { 
  8.      System.out.println("DisposableBean的destroy()方法 "); 
  9.     }  
  10.     @Override 
  11.     public void afterPropertiesSet() throws Exception { 
  12.      System.out.println("InitializingBean的 afterPropertiesSet方法"); 
  13.     } 

通过JSR250规范

通过JSR250规范 提供的注解@PostConstruct 和@ProDestory标注的方法

 
 
 
 
  1. @Component 
  2. public class Book { 
  3.     public Book() { 
  4.      System.out.println("book 的构造方法"); 
  5.     }  
  6.     @PostConstruct 
  7.     public void init() { 
  8.      System.out.println("book 的PostConstruct标志的方法"); 
  9.     }  
  10.     @PreDestroy 
  11.     public void destory() { 
  12.      System.out.println("book 的PreDestory标注的方法"); 
  13.     } 

通过BeanPostProcessor实现

通过Spring的BeanPostProcessor的 bean的后置处理器会拦截所有bean创建过程

  • postProcessBeforeInitialization 在init方法之前调用
  • postProcessAfterInitialization 在init方法之后调用
 
 
 
 
  1. @Component 
  2. public class CustomBeanPostProcessor implements BeanPostProcessor { 
  3.     @Override 
  4.     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 
  5.      System.out.println("CustomBeanPostProcessor...postProcessBeforeInitialization:"+beanName); 
  6.      return bean; 
  7.     }  
  8.     @Override 
  9.     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
  10.         System.out.println("CustomBeanPostProcessor...postProcessAfterInitialization:"+beanName); 
  11.         return bean; 
  12.     } 
  13. }  

BeanPostProcessor的执行时机

 
 
 
 
  1. populateBean(beanName, mbd, instanceWrapper) 
  2. initializeBean{ 
  3.     applyBeanPostProcessorsBeforeInitialization() 
  4.     invokeInitMethods{ 
  5.     isInitializingBean.afterPropertiesSet() 
  6.     自定义的init方法 
  7. applyBeanPostProcessorsAfterInitialization()方法 

通过@Value +@PropertySource来给组件赋值

 
 
 
 
  1. public class Person { 
  2.     //通过普通的方式 
  3.     @Value("独孤") 
  4.     private String firstName; 
  5.     //spel方式来赋值 
  6.     @Value("#{28-8}") 
  7.     private Integer age; 
  8.     通过读取外部配置文件的值 
  9.     @Value("${person.lastName}") 
  10.     private String lastName; 
  11. }  
  12. @Configuration 
  13. @PropertySource(value = {"classpath:person.properties"}) //指定外部文件的位置 
  14. public class MainConfig { 
  15.     @Bean 
  16.     public Person person() { 
  17.         return new Person(); 
  18.     } 

自动装配

@AutoWired的使用

自动注入

 
 
 
 
  1. @Repository 
  2. public class CustomDao { 
  3. }  
  4. @Service 
  5. public class CustomService { 
  6.     @Autowired 
  7.     private CustomDao customDao; 
  8. } 

结论: (1)自动装配首先时按照类型进行装配,若在IOC容器中发现了多个相同类型的组件,那么就按照 属性名称来进行装配

 
 
 
 
  1. @Autowired 
  2. private CustomDao customDao; 

比如,我容器中有二个CustomDao类型的组件 一个叫CustomDao 一个叫CustomDao2那么我们通过@AutoWired 来修饰的属性名称时CustomDao,那么拿就加载容器的CustomDao组件,若属性名称为tulignDao2 那么他就加载的时CustomDao2组件

(2)假设我们需要指定特定的组件来进行装配,我们可以通过使用@Qualifier("CustomDao")来指定装配的组件 或者在配置类上的@Bean加上@Primary注解

 
 
 
 
  1. @Autowired 
  2. @Qualifier("CustomDao") 
  3. private CustomDao customDao2 

(3)假设我们容器中即没有CustomDao 和CustomDao2,那么在装配的时候就会抛出异常

 
 
 
 
  1. No qualifying bean of type 'com.binghhe.spring.dao.CustomDao' available 

若我们想不抛异常 ,我们需要指定 required为false的时候可以了

 
 
 
 
  1. @Autowired(required = false) 
  2. @Qualifier("customDao") 
  3. private CustomDao CustomDao2; 

(4)@Resource(JSR250规范) 功能和@AutoWired的功能差不多一样,但是不支持@Primary 和@Qualifier的支持

(5)@InJect(JSR330规范) 需要导入jar包依赖,功能和支持@Primary功能 ,但是没有Require=false的功能

 
 
 
 
  1.  
  2.     javax.inject 
  3.     javax.inject 
  4.     1 
  5.  

(6)使用@Autowired 可以标注在方法上

  • 标注在set方法上
 
 
 
 
  1. //@Autowired 
  2. public void setCustomLog(CustomLog customLog) { 
  3.  this.customLog = customLog; 
  • 标注在构造方法上
 
 
 
 
  1. @Autowired 
  2. public CustomAspect(CustomLog customLog) { 
  3.  this.customLog = customLog; 

标注在配置类上的入参中(可以不写)

 
 
 
 
  1. @Bean 
  2. public CustomAspect CustomAspect(@Autowired CustomLog customLog) { 
  3.     CustomAspect customAspect = new CustomAspect(customLog); 
  4.     return ustomAspect; 

XXXAwarce接口

我们自己的组件 需要使用spring ioc的底层组件的时候,比如 ApplicationContext等我们可以通过实现XXXAware接口来实现

 
 
 
 
  1. @Component 
  2. public class CustomCompent implements ApplicationContextAware,BeanNameAware { 
  3.     private ApplicationContext applicationContext; 
  4.     @Override 
  5.     public void setBeanName(String name) { 
  6.      System.out.println("current bean name is :【"+name+"】"); 
  7.     }  
  8.     @Override 
  9.     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 
  10.      this.applicationContext = applicationContext; 
  11.     } 

@Profile注解

通过@Profile注解 来根据环境来激活标识不同的Bean

@Profile标识在类上,那么只有当前环境匹配,整个配置类才会生效

@Profile标识在Bean上 ,那么只有当前环境的Bean才会被激活

没有标志为@Profile的bean 不管在什么环境都可以被激活

 
 
 
 
  1. @Configuration 
  2. @PropertySource(value = {"classpath:ds.properties"}) 
  3. public class MainConfig implements EmbeddedValueResolverAware { 
  4.     @Value("${ds.username}") 
  5.     private String userName; 
  6.     @Value("${ds.password}") 
  7.     private String password; 
  8.     private String jdbcUrl; 
  9.     private String classDriver; 
  10.     @Override 
  11.     public void setEmbeddedValueResolver(StringValueResolver resolver) { 
  12.         this.jdbcUrl = resolver.resolveStringValue("${ds.jdbcUrl}"); 
  13.         this.classDriver = resolver.resolveStringValue("${ds.classDriver}"); 
  14.     }  
  15.     @Bean 
  16.     @Profile(value = "test") 
  17.     public DataSource testDs() { 
  18.      return buliderDataSource(new DruidDataSource()); 
  19.     } 
  20.     @Bean 
  21.     @Profile(value = "dev") 
  22.     public DataSource devDs() { 
  23.      return buliderDataSource(new DruidDataSource()); 
  24.     }  
  25.     @Bean 
  26.     @Profile(value = "prod") 
  27.     public DataSource prodDs() { 
  28.      return buliderDataSource(new DruidDataSource()); 
  29.     }  
  30.     private DataSource buliderDataSource(DruidDataSource dataSource) { 
  31.         dataSource.setUsername(userName); 
  32.         dataSource.setPassword(password); 
  33.         dataSource.setDriverClassName(classDriver); 
  34.         dataSource.setUrl(jdbcUrl); 
  35.      return dataSource; 
  36.     } 

激活切换环境的方法

(1)运行时jvm参数来切换

 
 
 
 
  1. -Dspring.profiles.active=test|dev|prod   

(2)通过代码的方式来激活

 
 
 
 
  1. public static void main(String[] args) { 
  2.     AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); 
  3.     ctx.getEnvironment().setActiveProfiles("test","dev"); 
  4.     ctx.register(MainConfig.class); 
  5.     ctx.refresh(); 
  6.     printBeanName(ctx); 

本文转载自微信公众号「冰河技术」,可以通过以下二维码关注。转载本文请联系冰河技术公众号。

新闻名称:聊聊Spring中的各项注解
文章源于:http://www.shufengxianlan.com/qtweb/news32/255332.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联