以前,咱们在【Spring】专题中更新了很多关于Spring注解相关的文章,有些小伙伴反馈说,看历史文章的话比较零散,常常会忘记本身看到哪一篇了。当打开一篇新文章时,总感受本身彷佛是看到过了,又感受本身没有看到过。那怎么办呢?为了小伙伴们查看方便,我在这里将Spring注解的使用方式作个汇总,也算是对以前写的Spring文章的一个总结吧!java
若是文章对你有点帮助,请点个赞,给个在看和转发,你们的支持是对我持续创做的最大动力。面试
微信搜索并关注“冰河技术”微信公众号,天天推送超硬核技术干货!正则表达式
<?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/sp <bean id="person" class="com.binghe.spring.Person"></bean> </beans>
获取Person实例以下所示。spring
public static void main( String[] args ){ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); System.out.println(ctx.getBean("person")); }
@Configuration public class MainConfig { @Bean public Person person(){ return new Person(); } }
这里,有一个须要注意的地方:经过@Bean的形式是使用的话, bean的默认名称是方法名,若@Bean(value="bean的名称")那么bean的名称是指定的 。设计模式
获取Person实例以下所示。bash
public static void main( String[] args ){ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); System.out.println(ctx.getBean("person")); }
咱们可使用@CompentScan注解来进行包扫描,以下所示。微信
@Configuration @ComponentScan(basePackages = {"com.binghe.spring"}) public class MainConfig { }
当咱们使用@CompentScan注解进行扫描时,可使用@CompentScan注解的excludeFilters 属性来排除某些类,以下所示。session
@Configuration @ComponentScan(basePackages = {"com.binghe.spring"},excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {PersonService.class}) }) public class MainConfig { }
当咱们使用@CompentScan注解进行扫描时,可使用@CompentScan注解的includeFilters属性将某些类包含进来。这里须要注意的是:须要把useDefaultFilters属性设置为false(true表示扫描所有的)并发
@Configuration @ComponentScan(basePackages = {"com.binghe.spring"},includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class, PersonService.class}) },useDefaultFilters = false) public class MainConfig { }
public enum FilterType { //注解形式 好比@Controller @Service @Repository @Compent ANNOTATION, //指定的类型 ASSIGNABLE_TYPE, //aspectJ形式的 ASPECTJ, //正则表达式的 REGEX, //自定义的 CUSTOM }
public class CustomFilterType 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(); return classMetadata.getClassName().contains("Service"); } @ComponentScan(basePackages = {"com.binghe.spring"},includeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM,value = CustomFilterType.class) },useDefaultFilters = false) public class MainConfig { }
在不指定@Scope的状况下,全部的bean都是单实例的bean,并且是饿汉加载(容器启动实例就建立好了)app
@Bean public Person person() { return new Person(); }
指定@Scope为 prototype 表示为多实例的,并且仍是懒汉模式加载(IOC容器启动的时候,并不会建立对象,而是在第一次使用的时候才会建立)
@Bean @Scope(value = "prototype") public Person person() { return new Person(); }
Bean的懒加载@Lazy(主要针对单实例的bean 容器启动的时候,不建立对象,在第一次使用的时候才会建立该对象)
@Bean @Lazy public Person person() { return new Person(); }
场景,有二个组件CustomAspect 和CustomLog ,个人CustomLog组件是依赖于CustomAspect的组件
应用:本身建立一个CustomCondition的类 实现Condition接口
public class CustomCondition implements Condition { /**** @param context * @param metadata * @return */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //判断容器中是否有CustomAspect的组件 return context.getBeanFactory().containsBean("customAspect"); } } public class MainConfig { @Bean public CustomAspect customAspect() { return new CustomAspect(); } @Bean @Conditional(value = CustomCondition.class) public CustomLog customLog() { return new CustomLog(); } }
(1)经过@CompentScan +@Controller @Service @Respository @compent。适用场景: 针对咱们本身写的组件能够经过该方式来进行加载到容器中。
(2)经过@Bean的方式来导入组件(适用于导入第三方组件的类)
(3)经过@Import来导入组件 (导入组件的id为全类名路径)
@Configuration @Import(value = {Person.class}) public class MainConfig { }
经过@Import 的ImportSeletor类实现组件的导入 (导入组件的id为全类名路径)
public class CustomImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.binghe.spring"}; } } Configuration @Import(value = {Person.class} public class MainConfig { }
经过@Import的 ImportBeanDefinitionRegister导入组件 (能够指定bean的名称)
public class DogBeanDefinitionRegister implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //建立一个bean定义对象 RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Dog.class); //把bean定义对象导入到容器中 registry.registerBeanDefinition("dog",rootBeanDefinition); } } @Configuration @Import(value = {Person.class, Car.class, CustomImportSelector.class, DogBeanDefinitionRegister.class}) public class MainConfig { }
经过实现FacotryBean接口来实现注册 组件
public class CarFactoryBean implements FactoryBean<Car> { @Override public Car getObject() throws Exception { return new Car(); } @Override public Class<?> getObjectType() { return Car.class; } @Override public boolean isSingleton() { return true; } }
由容器管理Bean的生命周期,咱们能够经过本身指定bean的初始化方法和bean的销毁方法
@Configuration public class MainConfig { //指定了bean的生命周期的初始化方法和销毁方法.@Bean(initMethod = "init",destroyMethod = "destroy") public Car car() { return new Car(); } }
针对单实例bean的话,容器启动的时候,bean的对象就建立了,并且容器销毁的时候,也会调用Bean的销毁方法
针对多实例bean的话,容器启动的时候,bean是不会被建立的而是在获取bean的时候被建立,并且bean的销毁不受IOC容器的管理
经过 InitializingBean和DisposableBean个接口实现bean的初始化以及销毁方法
@Component public class Person implements InitializingBean,DisposableBean { public Person() { System.out.println("Person的构造方法"); } @Override public void destroy() throws Exception { System.out.println("DisposableBean的destroy()方法 "); } @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean的 afterPropertiesSet方法"); } }
经过JSR250规范 提供的注解@PostConstruct 和@ProDestory标注的方法
@Component public class Book { public Book() { System.out.println("book 的构造方法"); } @PostConstruct public void init() { System.out.println("book 的PostConstruct标志的方法"); } @PreDestroy public void destory() { System.out.println("book 的PreDestory标注的方法"); } }
经过Spring的BeanPostProcessor的 bean的后置处理器会拦截全部bean建立过程
@Component public class CustomBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("CustomBeanPostProcessor...postProcessBeforeInitialization:"+beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("CustomBeanPostProcessor...postProcessAfterInitialization:"+beanName); return bean; } }
populateBean(beanName, mbd, instanceWrapper) initializeBean{ applyBeanPostProcessorsBeforeInitialization() invokeInitMethods{ isInitializingBean.afterPropertiesSet() 自定义的init方法 } applyBeanPostProcessorsAfterInitialization()方法 }
public class Person { //经过普通的方式 @Value("独孤") private String firstName; //spel方式来赋值 @Value("#{28-8}") private Integer age; 经过读取外部配置文件的值 @Value("${person.lastName}") private String lastName; } @Configuration @PropertySource(value = {"classpath:person.properties"}) //指定外部文件的位置 public class MainConfig { @Bean public Person person() { return new Person(); } }
自动注入
@Repository public class CustomDao { } @Service public class CustomService { @Autowired private CustomDao customDao; }
结论:
(1)自动装配首先时按照类型进行装配,若在IOC容器中发现了多个相同类型的组件,那么就按照 属性名称来进行装配
@Autowired private CustomDao customDao;
好比,我容器中有二个CustomDao类型的组件 一个叫CustomDao 一个叫CustomDao2那么咱们经过@AutoWired 来修饰的属性名称时CustomDao,那么拿就加载容器的CustomDao组件,若属性名称为tulignDao2 那么他就加载的时CustomDao2组件
(2)假设咱们须要指定特定的组件来进行装配,咱们能够经过使用@Qualifier("CustomDao")来指定装配的组件
或者在配置类上的@Bean加上@Primary注解
@Autowired @Qualifier("CustomDao") private CustomDao customDao2
(3)假设咱们容器中即没有CustomDao 和CustomDao2,那么在装配的时候就会抛出异常
No qualifying bean of type 'com.binghhe.spring.dao.CustomDao' available
若咱们想不抛异常 ,咱们须要指定 required为false的时候能够了
@Autowired(required = false) @Qualifier("customDao") private CustomDao CustomDao2;
(4)@Resource(JSR250规范)
功能和@AutoWired的功能差很少同样,可是不支持@Primary 和@Qualifier的支持
(5)@InJect(JSR330规范)
须要导入jar包依赖,功能和支持@Primary功能 ,可是没有Require=false的功能
<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
(6)使用@Autowired 能够标注在方法上
//@Autowired public void setCustomLog(CustomLog customLog) { this.customLog = customLog; }
@Autowired public CustomAspect(CustomLog customLog) { this.customLog = customLog; }
标注在配置类上的入参中(能够不写)
@Bean public CustomAspect CustomAspect(@Autowired CustomLog customLog) { CustomAspect customAspect = new CustomAspect(customLog); return ustomAspect; }
咱们本身的组件 须要使用spring ioc的底层组件的时候,好比 ApplicationContext等咱们能够经过实现XXXAware接口来实现
@Component public class CustomCompent implements ApplicationContextAware,BeanNameAware { private ApplicationContext applicationContext; @Override public void setBeanName(String name) { System.out.println("current bean name is :【"+name+"】"); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
经过@Profile注解 来根据环境来激活标识不一样的Bean
@Configuration @PropertySource(value = {"classpath:ds.properties"}) public class MainConfig implements EmbeddedValueResolverAware { @Value("${ds.username}") private String userName; @Value("${ds.password}") private String password; private String jdbcUrl; private String classDriver; @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.jdbcUrl = resolver.resolveStringValue("${ds.jdbcUrl}"); this.classDriver = resolver.resolveStringValue("${ds.classDriver}"); } @Bean @Profile(value = "test") public DataSource testDs() { return buliderDataSource(new DruidDataSource()); } @Bean @Profile(value = "dev") public DataSource devDs() { return buliderDataSource(new DruidDataSource()); } @Bean @Profile(value = "prod") public DataSource prodDs() { return buliderDataSource(new DruidDataSource()); } private DataSource buliderDataSource(DruidDataSource dataSource) { dataSource.setUsername(userName); dataSource.setPassword(password); dataSource.setDriverClassName(classDriver); dataSource.setUrl(jdbcUrl); return dataSource; } }
激活切换环境的方法
(1)运行时jvm参数来切换
-Dspring.profiles.active=test|dev|prod
(2)经过代码的方式来激活
public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.getEnvironment().setActiveProfiles("test","dev"); ctx.register(MainConfig.class); ctx.refresh(); printBeanName(ctx); }
关注「 冰河技术 」微信公众号,后台回复 “设计模式” 关键字领取《深刻浅出Java 23种设计模式》PDF文档。回复“Java8”关键字领取《Java8新特性教程》PDF文档。回复“限流”关键字获取《亿级流量下的分布式限流解决方案》PDF文档,三本PDF均是由冰河原创并整理的超硬核教程,面试必备!!
好了,今天就聊到这儿吧!别忘了点个赞,给个在看和转发,让更多的人看到,一块儿学习,一块儿进步!!
若是你以为冰河写的还不错,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发、分布式、微服务、大数据、互联网和云原生技术,「 冰河技术 」微信公众号更新了大量技术专题,每一篇技术文章干货满满!很多读者已经经过阅读「 冰河技术 」微信公众号文章,吊打面试官,成功跳槽到大厂;也有很多读者实现了技术上的飞跃,成为公司的技术骨干!若是你也想像他们同样提高本身的能力,实现技术能力的飞跃,进大厂,升职加薪,那就关注「 冰河技术 」微信公众号吧,天天更新超硬核技术干货,让你对如何提高技术能力再也不迷茫!