以前,咱們在【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均是由冰河原創並整理的超硬核教程,面試必備!!
好了,今天就聊到這兒吧!別忘了點個贊,給個在看和轉發,讓更多的人看到,一塊兒學習,一塊兒進步!!
若是你以爲冰河寫的還不錯,請微信搜索並關注「 冰河技術 」微信公衆號,跟冰河學習高併發、分佈式、微服務、大數據、互聯網和雲原生技術,「 冰河技術 」微信公衆號更新了大量技術專題,每一篇技術文章乾貨滿滿!很多讀者已經經過閱讀「 冰河技術 」微信公衆號文章,吊打面試官,成功跳槽到大廠;也有很多讀者實現了技術上的飛躍,成爲公司的技術骨幹!若是你也想像他們同樣提高本身的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 冰河技術 」微信公衆號吧,天天更新超硬核技術乾貨,讓你對如何提高技術能力再也不迷茫!