spring注解開發:常用注解,bean的加載控制,整合第三方技術,Ioc的底層核心原理


知識點梳理

課堂講義

1)注解驅動的意義

1.1)什么是注解驅動

使用注解的形式替代xml配置,將繁雜的配置文件從工程中徹底消除掉,簡化書寫,提高開發效率

 

 

1.2)注解驅動的弊端

  • 為了達成注解驅動的目的,可能會將原先很簡單的書寫,變的更加復雜 

 

 

  • XML中配置第三方開發的資源是很方便的,但使用注解驅動無法在第三方開發的資源中進行編輯,因此會增加一部分的代碼量

 

 

 

2)常用注解(重點)

2.1)啟動注解功能

  • 啟動注解掃描,加載類中配置的注解項

    <!--注解總開關-->
    <context:component-scan base-package="com.itheima"/>
  • 說明:

    • 在進行包所掃描時,會對配置的包及其子包中所有文件進行掃描

    • 掃描過程是以文件夾遞歸迭代的形式進行的

    • 掃描過程僅讀取合法的java文件

    • 掃描時僅讀取spring可識別的注解

    • 掃描結束后會將可識別的有效注解轉化為spring對應的資源加入IoC容器

  • 注意:

    • 無論是注解格式還是XML配置格式,最終都是將資源加載到IoC容器中,差別僅僅是數據讀取方式不同

    • 從開發效率上來說注解優於XML配置文件

2.2)bean的定義

  • 名稱:@Component

  • 類型:類注解

  • 位置:類定義上方

  • 作用:設置該類為spring管理的bean

  • 范例:

    @Component("userService")
    public class UserServiceImpl implements UserService {}
  • 說明:@Component的衍生注解,功能類似@Component

    • @Controller(表現層)

    • @Service(業務層)

    • @Repository(數據層)

  • 相關屬性

    • value(默認):定義bean的訪問id

      @Component(value="beanId")
      public class ClassName{}

       

2.3)bean的作用域

  • 名稱:@Scope

  • 類型:類注解

  • 位置:類定義上方

  • 作用:設置該類作為bean對應的scope屬性

  • 范例:

    @Scope("singleton")
    public class ClassName{}
  • 相關屬性

    • value(默認):定義bean的作用域singleton、prototype,默認為singleton

      @Scope(value="prototype")
      public class ClassName{}

       

2.4)bean的生命周期

  • 名稱:@PostConstruct、@PreDestroy

  • 類型:方法注解

  • 位置:方法定義上方

  • 作用:設置該類作為bean對應的生命周期方法

  • 范例:

    @PostConstruct
    public void init() { System.out.println("init..."); }

    @PreDestroy
    public void destroy() {}

小結

  1. 開啟注解驅動:<context:component-scan base-package="com.itheima"/>

  2. bean的四種注解定義格式:Component, Controller, Service, Repository

  3. bean相關屬性注解格式:Scope, PostConstruct(方法上), PreDestroy(方法上)

2.5)加載第三方資源

  • 名稱:@Bean

  • 類型:方法注解

  • 位置:方法定義上方

  • 作用:設置該方法的返回值作為spring管理的bean

  • 范例:

    @Bean("dataSource")
    public DruidDataSource createDataSource() {    return ……;   }
  • 說明:

    • 因為第三方bean無法在其源碼上進行修改,使用@Bean解決第三方bean的引入問題

    • 該注解用於替代XML配置中的靜態工廠與實例工廠創建bean,不區分方法是否為靜態或非靜態

    • @Bean所在的類必須被spring掃描加載,否則該注解無法生效

      @Component
      public class JDBCConfig {
      }
  • 相關屬性

    • value(默認):定義bean的訪問id

2.6)bean的非引用類型屬性注入

  • 名稱:@Value

  • 類型:屬性注解、方法注解

  • 位置:屬性定義上方,set方法定義上方

  • 作用:設置對應屬性的值或對方法進行傳參

  • 范例:

    @Value("zhangsan")
    private String username;
  • 說明:

    • value值僅支持非引用類型數據

    • value值支持SpEL:@Value("#{'zhangsan'}")

    • @value注解如果添加在屬性上方,可以省略set方法

    • value值支持讀取properties文件中的屬性值,通過類屬性將properties中數據傳入類中

  • 相關屬性

    • value(默認):定義對應的屬性值或參數值

2.7)bean的引用類型屬性注入

  • 名稱:@Autowired、@Qualifier

  • 類型:屬性注解、方法注解

  • 位置:屬性定義上方,方法定義上方

  • 作用:設置對應屬性的對象或對方法進行引用類型傳參

  • 范例:

    @Autowired(required = false)
    private UserDao userDao;
  • 說明:

    • @Autowired默認按類型裝配

    • 指定@Qualifier后可以指定自動裝配的bean的id

      @Autowired
      @Qualifier("userDao")
      private UserDao userDao;
  • 相關屬性

    • required:定義該屬性是否允許為null

2.8)bean裝配優先級配置

  • 名稱:@Primary

  • 類型:類注解

  • 位置:類定義上方

  • 作用:設置類對應的bean按類型裝配時優先裝配

  • 范例:

    @Component
    @Primary
    public class ClassName{}
  • 說明:

    @Autowired默認按類型裝配,當出現相同類型的bean,使用@Primary提高按類型自動裝配的優先級,多個@Primary會導致優先級設置無效

2.9)bean的引用類型屬性原生注解注入(了解)

  • 名稱:@Inject、@Named、@Resource

  • 說明:

    • @Inject與@Named是JSR330規范中的注解

    • @Inject = @Autowired ,@Named = @Qualifier完全相同

  • @Resource是JSR250規范中的注解,可以簡化書寫格式

  • @Resource相關屬性

    • name:設置注入的bean的id

    • type:設置注入的bean的類型,接收的參數為Class類型

小結

屬性注入

  1. 非引用類型注入:@Value

  2. 引用類型注入

    • @Autowired = @Inject

    • @Qualifier = @Named

    • @Primary

    • @Resource: name, type

2.10)加載properties文件

  • 名稱:@PropertySource

  • 類型:類注解

  • 位置:類定義上方

  • 作用:加載properties文件中的屬性值

  • 范例:

    @PropertySource(value={"classpath:jdbc.properties","classpath:abc.properties"},ignoreResourceNotFound = true)
    public class ClassName {
       @Value("${propertiesAttributeName}")
       private String attributeName;
    }
  • 說明:

    • 不支持*通配格式

    • 一旦加載,所有spring控制的bean中均可使用對應屬性值

  • 相關屬性

    • value(默認):設置加載的properties文件名

    • ignoreResourceNotFound:如果資源未找到,是否忽略,默認為false

2.11)純注解格式

  • 名稱:@Configuration、@ComponentScan

  • 類型:類注解

  • 位置:類定義上方

  • 作用:設置當前類為spring核心配置加載類

  • 范例:

    @Configuration
    @ComponentScan("scanPackageName")
    public class SpringConfigClassName{
    }
  • 說明:

    • 核心配合類用於替換spring核心配置文件,此類可以設置空的,不設置變量與屬性

    • bean掃描工作使用注解@ComponentScan替代

AnnotationConfigApplicationContext

  • 加載純注解格式上下文對象,需要使用AnnotationConfigApplicationContext

  • 當配置類作為 AnnotationConfigApplicationContext 對象創建的參數時,@Configuration注解可以不寫

    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

2.12)第三方bean配置與管理

  • 名稱:@Import

  • 類型:類注解

  • 位置:類定義上方

  • 作用:導入第三方bean作為spring控制的資源

  • 范例:

    @Configuration
    @Import(OtherClassName.class)
    public class ClassName {
    }
  • 說明:

    • @Import注解在同一個類上,僅允許添加一次,如果需要導入多個,使用數組的形式進行設定

      @Import({JDBCConfig.class, Abc.class})
    • @Bean所在的類可以使用導入的形式進入spring容器,無需使用@Component聲明

    • 在被導入的類中可以繼續使用@Import導入其他資源(了解)

       

小結

  1. 屬性注入:Value, Autowired, Qualifier, Primary, Inject, Named, Resource

  2. 加載properties文件:PropertySource -> Value("${key}")

  3. 純注解:Configuration, ComponenScan

  4. 第三方導入:Import

 

3)bean加載控制(了解)

3.1)依賴加載

(1)@DependsOn

  • 名稱:@DependsOn

  • 類型:類注解、方法注解

  • 位置:bean定義的位置(類上或方法上)

  • 作用:控制bean的加載順序,使其在指定bean加載完畢后再加載

  • 范例:

    @DependsOn("beanId")
    public class ClassName {
    }
  • 說明:

    • 配置在方法上,使@DependsOn指定的bean優先於@Bean注解配置的bean進行加載

    • 配置在類上,使@DependsOn指定的bean優先於當前類中所有@Bean配置的bean進行加載

    • 配置在類上,使@DependsOn指定的bean優先於@Component等配置的bean進行加載

  • 相關屬性

    • value(默認):設置當前bean所依賴的bean的id

(2)@Order

  • 名稱:@Order

  • 類型:配置類注解

  • 位置:配置類定義的位置(類上)

  • 作用:控制配置類的加載順序,其中值越小優先級越高

  • 范例:

    @Order(1)
    public class SpringConfigClassName {
    }

(3)@Lazy

  • 名稱:@Lazy

  • 類型:類注解、方法注解

  • 位置:bean定義的位置(類上或方法上)

  • 作用:控制bean的加載時機,使其延遲加載

  • 范例:

    @Lazy
    public class ClassName {
    }

3.2)依賴加載應用場景

@DependsOn

  • 微信訂閱號,發布消息和訂閱消息的bean的加載順序控制

@Lazy

  • 某個業務災難出現后對應的應急預案處理bean可以延遲加載

@Order

  • 多個種類的配置出現后,優先加載系統級的,然后加載業務級的

 

4)整合第三方技術(重點)

4.1)注解整合MyBatis分析

 

 

 

 

  • 業務類使用注解形式聲明bean,屬性采用注解注入

  • 建立獨立的配置管理類,分類管理外部資源,根據功能進行分類,並提供對應的方法獲取bean

  • 使用注解形式啟動bean掃描,加載所有注解配置的資源(bean)

  • 使用AnnotationConfigApplicationContext對象加載所有的啟動配置類,內部使用導入方式進行關聯

 

 

4.2)注解整合MyBatis步驟

1.修改mybatis外部配置文件格式為注解格式

public interface AccountDao {

   @Insert("insert into account(name,money)values(#{name},#{money})")
   void save(Account account);

   @Delete("delete from account where id = #{id} ")
   void delete(Integer id);

   @Update("update account set name = #{name} , money = #{money} where id = #{id} ")
   void update(Account account);

   @Select("select * from account")
   List<Account> findAll();

   @Select("select * from account where id = #{id} ")
   Account findById(Integer id);
}

2.業務類使用@Service聲明bean,使用@Autowired注入對象

@Service("accountService")
public class AccountServiceImpl implements AccountService {

   @Autowired
   private AccountDao accountDao;
}

3.編寫Spring配置類:SpringConfig,並加載properties文件

@Configuration
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {
}

4.建立配置文件JDBCConfig與MyBatisConfig類,並將其導入到核心配置類SpringConfig

數據源配置類:JDBCConfig

public class JDBCConfig {

    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean(value = "dataSource")
    public DataSource getDataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}

MyBatis配置類:MyBatisConfig

public class MyBatisConfig {

    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.itheima.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.itheima.dao");
        return msc;
    }

}

5.開啟注解掃描,將JDBCConfig與MyBatisConfig類導入到核心配置類SpringConfig中

@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBatisConfig.class})
public class SpringConfig {
}

6.使用AnnotationConfigApplicationContext對象加載配置項

public class App { 
   public static void main(String[] args) {
       ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
       AccountService accountService = (AccountService) ctx.getBean("accountService");
       Account ac = accountService.findById(3);
       System.out.println(ac);
  }
}

 

4.3)注解整合Junit

1.導入Spring整合Junit坐標,從Spring5.0以后,要求Junit的版本必須是4.12及以上

<dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.12</version>
</dependency>
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>5.1.9.RELEASE</version>
</dependency>

2.Spring接管Junit的運行權,使用Spring專用的Junit類加載器

@RunWith(SpringJUnit4ClassRunner.class)

3.加載Spring配置類

@ContextConfiguration(classes = SpringConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest {
   
   @Test
   public void testSave() {}
}

5)IoC底層核心原理

5.1)IoC核心接口

查看類文件:Ctrl+n

查看類結構:Ctrl+h

查看內部結構:Alt+7

 

 

1.BeanFactory接口 

 

 

2.HierarchicalBeanFactory接口 

 

 

3.AutowireCapableBeanFactory接口 

 

 

4.ListableBeanFactory 

 

 

5.2)組件掃描器

  • 開發過程中,需要根據需求加載必要的bean,排除指定bean

 

 

5.3)設定組件掃描加載過濾器

  • 名稱:@ComponentScan

  • 類型:類注解

  • 位置:類定義上方

  • 作用:設置spring配置加載類掃描規則

  • 范例:

    @ComponentScan(
       value="com.itheima",           //設置基礎掃描路徑
       excludeFilters =                   //設置過濾規則,當前為排除過濾
    @ComponentScan.Filter(            //設置過濾器
       type= FilterType.ANNOTATION,  //設置過濾方式為按照注解進行過濾
       classes=Service.class)     //設置具體的過濾項,過濾所有@Service修飾的bean
      )

excludeFilters:設置排除性過濾器

includeFilters:設置包含性過濾器

type:設置過濾器類型:ANNOTATION, CUSTOM

5.4)自定義組件過濾器(了解)

  • 名稱:TypeFilter

  • 類型:接口

  • 作用:自定義類型過濾器

  • 編寫自定義過濾器

    public class MyTypeFilter implements TypeFilter {
       
       @Override
       public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
           //通過參數獲取加載的類的元數據
           ClassMetadata classMetadata = metadataReader.getClassMetadata();
           //通過類的元數據獲取類的名稱
           String className = classMetadata.getClassName();
           if(className.equals("com.itheima.dao.impl.BookDaoImpl")){
               return true;
          }
           return false;
      }
    }
  • 使用自定義過濾器:設置排除bean,排除的規則是自定義規則(FilterType.CUSTOM),具體的規則定義為(MyTypeFilter.class)

    @ComponentScan(
           value = "com.itheima",
           excludeFilters = @ComponentScan.Filter(
                   type= FilterType.CUSTOM,
                   classes = MyTypeFilter.class
          )
    )

5.5)自定義導入器(了解)

1.回顧如何讓Spring管理bean

  • 配置bean的方式如下:

    • XML文件中使用<bean />標簽配置

    • 使用@Component及衍生注解配置

2.為什么需要使用自定義導入器

  • 企業開發過程中,通常需要配置大量的bean,需要一種快速高效配置大量bean的方式

    實現接口ImportSelector

    • 名稱:ImportSelector

  • 類型:接口

    • 作用:自定義bean導入器

  • 范例:

  public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata icm) {
          return new String[]{"com.itheima.dao.impl.AccountDaoImpl"};
      }
  }
  @Configuration
@ComponentScan("com.itheima")
  @Import(MyImportSelector.class)
  public class SpringConfig {
  }

1.從properties文件讀取需要導入的單個類

  public class MyImportSelector implements ImportSelector {
      @Override
      public String[] selectImports(AnnotationMetadata importingClassMetadata) {
  	    //2.加載import.properties文件中的單個類名
        	ResourceBundle bundle = ResourceBundle.getBundle("import");
          String className = ,bundle.getString("className");
          return new String[] {className};
      }
  }
  #2.加載import.properties文件中的單個類名
  className=com.itheima.dao.impl.BookDaoImpl

2.從properties文件讀取需要導入的多個類

  public class MyImportSelector implements ImportSelector {
      @Override
      public String[] selectImports(AnnotationMetadata importingClassMetadata) {
  //      3.加載import.properties文件中的多個類名
          ResourceBundle bundle = ResourceBundle.getBundle("import");
          String className = bundle.getString("className");
          return className.split(",");
      }
  }
  #3.加載import.properties文件中的多個類名
  className=com.itheima.dao.impl.BookDaoImpl,com.itheima.dao.impl.AccountDaoImpl

3.從properties文件讀取需要導入的指定包下的所有類:使用自定義的CustomerImportSelector

  path=com.itheima.dao.impl.*

 

5.6)自定義注冊器(了解)

  • 名稱:ImportBeanDefinitionRegistrar

  • 類型:接口

  • 作用:自定義bean定義注冊器

  • 范例:

    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        public void registerBeanDefinitions(AnnotationMetadata icm, BeanDefinitionRegistry r) {
            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(r, false);
            TypeFilter tf = new TypeFilter() {
                public boolean match(MetadataReader mr, MetadataReaderFactory mrf) throws IOException {
                    return true;
                }
            };
            scanner.addIncludeFilter(tf);
            scanner.scan("com.itheima");
        }
    }

5.7)bean初始化過程解析(理解)

 

 

  • BeanFactoryPostProcessor

    • 作用:定義了在bean工廠對象創建后,bean對象創建前執行的動作,用於對工廠進行創建后業務處理

    • 運行時機:當前操作用於對工廠進行處理,僅運行一次

      public class MyBeanFactory implements BeanFactoryPostProcessor {
          @Override
          //工廠后處理bean接口核心操作
          public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
              //第1階段
              System.out.println("第1階段:采購手機屏幕和電池...");
          }
      }
    • 注意:導入自定義的MyBeanFactory

      @Configuration
      @ComponentScan("com.itheima")
      @Import(MyBeanFactory.class)
      public class SpringConfig {...}
  • BeanPostProcessor

    • 作用:定義了所有bean初始化前后進行的統一動作,用於對bean進行創建前業務處理與創建后業務處理

    • 運行時機:當前操作伴隨着每個bean的創建過程,每次創建bean均運行該操作

      public class MyBean implements BeanPostProcessor {
          
          //所有bean初始化前置操作
          public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
              //第2階段
              System.out.println(beanName);
              System.out.println("第2階段:檢查手機屏幕和電池是否正常");
              return bean;
          }
      
          //所有bean初始化后置操作
          public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
              //第4階段
              System.out.println("第4階段:檢查手機功能是否正常");
              return bean;
          }
      }
      @Configuration
      @ComponentScan("com.itheima")
      @Import({MyBeanFactory.class, MyBean.class})
      public class SpringConfig {
      }
  • InitializingBean

    • 作用:定義了每個bean的初始化前進行的動作,屬於非統一性動作,用於對bean進行創建前業務處理

    • 運行時機:當前操作伴隨着任意一個bean的創建過程,保障其個性化業務處理

      @Service("mobileService")
      public class MobileServiceImpl implements MobileService, InitializingBean {
      
          @Override
          public void create() {
              System.out.println("手機要出廠啦...");
          }
      
          @Override
          //定義當前bean初始化操作,功效等同於init-method屬性配置
          public void afterPropertiesSet() {
              //第3階段
              System.out.println("第3階段:購買紅外設備");
          }
      }

5.8)繁瑣的bean初始化過程處理(了解)

 

 

  • 舉例實現FactoryBean接口:MyBatis中的SqlSessionFactoryBean實現

     

     

    對單一的bean的初始化過程進行封裝,達到簡化配置的目的:

    public class EquipmentDaoImplFactoryBean implements FactoryBean {

       @Override
       public Object getObject() throws Exception {
           return new EquipmentDaoImpl();
      }

       @Override
       public Class<?> getObjectType() {
           return null;
      }

       @Override
       public boolean isSingleton() {
           return false;
      }
    }

    配置FactoryBean:

    @Configuration
    public class SpringConfig {

       @Bean("equipmentDaoImplBean")
       public EquipmentDaoImplFactoryBean equipmentDaoImplBean() {
           return new EquipmentDaoImplFactoryBean();
      }
    }

    獲取方式:

    public class App {
         public static void main(String[] args) {
             ApplicationContext ctx =
                     new AnnotationConfigApplicationContext(SpringConfig.class);
             EquipmentDao equipmentDao = (EquipmentDao)ctx.getBean("equipmentDaoImplBean");
             equipmentDao.save();
    }

FactoryBean與BeanFactory區別

  • FactoryBean:封裝單個繁瑣bean的創建過程

  • BeanFactory:Spring容器頂層接口,定義了bean相關的獲取操作''[[


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM