1. 程式人生 > >Java框架-Spring基於註解的IOC配置及純註解

Java框架-Spring基於註解的IOC配置及純註解

  • 註解配置與xml配置都實現了減低程式間耦合的功能。
  • 不同公司由不同習慣,有可能是純xml、純註解(很少)或者xml與註解混合使用(基於註解的IOC配置)。

1. 基於註解的IOC配置

1.1 建立一個簡單案例

1.1.1 建立專案,新增依賴(pom.xml)

<!--匯入spring元件包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId
>
<version>5.0.2.RELEASE</version> </dependency> <!--匯入junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>

1.1.2 使用@Component註解建立物件

  • 使用註解建立物件的條件:
    1. 使用@Component對類進行註解;
    2. 開啟註解掃描
@Component
public class User {
}

1.1.3 bean.xml配置

1.1.3.1 開啟註解掃描(重要)
  • 語法:<context:component-scan base-package="指定掃描的包名"/>
  • 開啟後會掃描當前包下所有的類,以及當前包下所有子包下所有的類的註解,會掃描@Component註解及器衍生註解@Controller @Service @Repository
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--開啟註解掃描-->
    <!--
        base-package
        1. 指定掃描的包名稱
        2. 會掃描當前包下所有的類,以及當前包下所有子包下所有的類
        3. 如果要掃描多個包,用逗號隔開
    -->
    <context:component-scan base-package="com.azure.entity"/>

</beans>

1.1.4 測試類

public class annoTest {
    @Test
    public void test(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        User user = (User) ac.getBean("user");
        System.out.println(user);
    }
}

2. 常用註解

2.1 建立物件註解

2.1.1 @Component

  • 作用:把物件交給 spring的ioc容器來管理。相當於在 xml 中配置一個 bean標籤
  • 屬性:value:指定物件的名稱,相當於bean標籤的id。**如果不指定 value 屬性,預設物件名稱為當前類的類名,且首字母小寫。**如@Component(value=“user”),屬性值只有一個時,可以省略成@Component(“user”)

2.1.2 @Controller @Service @Repository

  • @Component的衍生註解,其作用及屬性與@Component一致

  • 作用範圍不同:

    @Controller: 一般用於表現層的註解。
    @Service: 一般用於業務層的註解。
    @Repository: 一般用於持久層的註解。
    @Component: 除了上述三層架構的其餘註解,如工具類等。

  • 如果註解中有且只有一個屬性要賦值時,且名稱是 value, value 在賦值是可以不寫。

2.2 依賴注入註解

2.2.1 @Autowired(重點)

  • 作用:自動按照型別注入依賴,相當於使用set方法注入。
  • 屬性:required(一般很少會設定)
    • true:預設值,標識必須從容器中找到對應的物件注入,否則報錯;
    • false:如果去容器中沒有找到對應的物件注入不會報錯。
  • 注意事項:只能注入bean型別
2.2.1.1 定義到欄位

查詢順序:

  1. 先根據欄位的型別,尋找容器中該型別對應的物件注入;
  2. 如果該型別的物件有多個
    • 再根據欄位的名稱查詢容器中的物件,成功找到就注入;
    • 如果根據名稱找不到對應物件,則報錯。
2.2.1.2 定義到方法

查詢順序:

  1. 先根據引數的型別,尋找容器中該型別對應的物件注入;
  2. 如果該型別的物件有多個
    • 再根據引數的名稱查詢容器中的物件,成功找到就注入;
    • 如果根據名稱找不到對應物件,則報錯。
2.2.1.3 演示示例
  1. bean.xml配置

    <!--建立String型別物件並放入ioc容器中-->
    <bean id="str" class="java.lang.String">
        <constructor-arg value="JoJo"></constructor-arg>
    </bean>
    <bean id="name" class="java.lang.String">
        <constructor-arg value="Dio噠"></constructor-arg>
    </bean>
    
  2. 定義到欄位

    @Component("user")
    public class User {
    
        /**
         * @Autowired
         * 1.先根據修飾的欄位的型別,去容器中找該型別對應的物件注入;
         * 2.如果該型別的物件有多個,
         *   a.會根據欄位的名稱去容器中找物件注入,找到就成功注入;
         *   b.根據欄位的名稱去容器中沒有找到對應的物件,就報錯。
         * 3.細節
         *   @Autowired註解的屬性required
         *   true 預設值,標識必須從容器中找到對應的物件注入否則報錯。
         *   false 如果去容器中沒有找到對應的物件注入不會報錯
         */
        @Autowired
        private String name;
    
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    

    由於ioc容器中有多個String型別的物件,所以會根據欄位名“name”查詢,如果只有一個String型別的物件,則會把該物件注入

  3. 定義到方法

    /**
     * @Autowired 定義在方法上
     * 1.先根據引數型別去容器找物件注入
     * 2.如果型別有多個,會根據引數名稱去容器中找物件注入;根據名稱也沒有找到就報錯。
     */
    @Autowired
    public void setName(String name) {
        System.out.println(name);
    }
    

    同樣,由於ioc容器中有多個String型別的物件,所以會根據欄位名“name”查詢,如果只有一個String型別的物件,則會把該物件注入

2.2.2 @Qualifier

  • 作用:在自動按照型別注入依賴的基礎上,在按照Bean的id注入。
  • 屬性:value:用於指定bean的id
  • 注意事項:給欄位注入時不能獨立使用,必須和@Autowire 一起使用;只有給方法引數注入時,才可以獨立使用(純註解時使用)
/**
 * @Qualifier("str")
 * 1.表示根據str這個名稱,去容器中找物件注入
 * [email protected]通常都要與@Autowired一起使用
 *   只要@Qualifier修飾方法引數時候才可以單獨使用(純註解時候使用)
 * 3.根據@Autowired的特性,注入的應該是名為name的String型別物件,而使用@Qualifier
 *   則會將str物件注入
 */
@Autowired
@Qualifier("str")
private String nickname;

2.2.3 @Resource

  • 作用:按照 bean 的 id 注入或者型別注入。

  • 屬性:name:指定 bean 的 id(名稱),name不能省略

    type:指定bean的型別

  • 查詢順序:

    1. 根據欄位的名稱,在容器中找對應名稱物件注入;
    2. 如果該型別的物件有多個
      • 再根據引數的名稱查詢容器中的物件,成功找到就注入;
      • 如果根據名稱找不到對應物件,則報錯。
  • 注意事項:只能注入bean型別;jdk9及之後版本不再支援此註解

/**
 * @Resource
 * 1.根據欄位名稱去容器中找物件注入
 * 2.如果根據名稱會根據型別找,如果型別有多個就報錯
 * 3.也可以通過註解的name屬性指定只根據名稱找
 *   如果指定type只會根據型別找。
 * 不推薦使用。因為jdk8以後的版本不支援此註解。
 */
@Resource(name = "str")
private String oldName;

2.2.4 @Value

  • 作用:給基本資料型別和String型別的欄位注入值;獲取載入的 properties配置檔案的值(純註解中使用)
  • 屬性:value:被注入的值
/**
 * @Value
 * 1.給簡單型別的欄位賦值
 * 2.獲取載入的properties配置檔案的值(純註解中講解)
 */
@Value("100")
private int id;
@Value("女")
private String sex;

2.3 改變作用範圍註解

2.3.1 @Scope

  • 作用:指定 bean 的作用範圍。類似於bean標籤的scope屬性

  • 屬性:value:指定範圍的值。

    取值:

    • singleton:單例,預設值
    • prototype:多例
    • request:建立的物件的範圍與request域一樣。web專案
    • session:建立的物件的範圍與session域一樣。web專案
    • global session:全域性session。分散式系統中使用。web專案
/*@Scope註解*/
@Component("user")
//@Scope("singleton")// 單例,預設值
@Scope("prototype")// 多例
public class User {
	//...
}

2.4 與生命週期相關的註解

2.4.1 @PostConstruct

  • 作用:指定初始化方法。相當於bean標籤中的init-method屬性

2.4.2 @PreDestroy

  • 作用:指定容器銷燬方法。相當於bean標籤中的destroy-method屬性

2.4.3 @Lazy

  • 作用:延遲初始化。在第一次從容器中獲取物件時才會建立物件。相當於bean標籤中的lazy-init屬性
  • 注意事項:只對單例有效

2.4.4 生命週期註解示例

/*生命週期註解*/
@Component("user2")
@Scope("singleton")
@Lazy  // 延遲初始化,再第一次從容器中獲取物件時候才建立物件。只對單例有效
public class User2 {
    
    // 建立物件之後執行
    @PostConstruct
    public void init() {
        System.out.println("建立物件之後執行");
    }

    // 銷燬容器之前執行
    @PreDestroy
    public void preDestroy() {
        System.out.println("銷燬容器之前執行");
    }
}

2.5 Spring中註解與xml方式的選擇

  • 註解的優勢:

    配置簡單,維護方便(找到類相當於找到對應的配置)。

  • XML 的優勢:

    修改時,不用改原始碼。不涉及重新編譯和部署。

Spring 管理 Bean,XML與註解方式的比較:

在這裡插入圖片描述

小結

  • 基於註解的 spring IoC 配置中, bean 物件的特點和基於 XML 配置是一致的。
  • xml 配置 可以與註解配置一起使用。

目前開發比較常用的是:

  • XML + 註解 混合使用
    • XML: 配置一些全域性的物件(舉例:DataSource/JdbcTemplate…)
    • 註解: 每一個模組的dao/service等物件的建立可以用註解,簡化配置

3. 使用註解 IOC 改造三層架構案例

  • 需求:使用xml+註解的形式改造純xml配置的三層架構案例

3.1 環境搭建和實體類

  • 不用修改
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.azure</groupId>
    <artifactId>day52projects_spring_xml</artifactId>
    <version>1.0-SNAPSHOT</version>
<dependencies>
    <!--匯入spring元件包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <!--匯入druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    <!--匯入mysql驅動包-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.32</version>
    </dependency>
    <!--匯入junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>  
</project>

實體類

public class account {
    private int accountId;
    private int uid;
    private double money;
    /*省略無參、有參、toString、getter&setter*/
}

3.3 dao層

3.3.1 dao介面

  • 不用修改
public interface IAccountDao {
    //新增
    void save(Account account);
    //更新
    void update(Account account);
    //刪除
    void delete(int id);
    //查詢
    List<Account> findAll();
}

3.3.2 dao實現類

/*標識持久層,加入ioc容器中*/
@Repository
public class AccountDaoImpl implements IAccountDao {

    //從容器中獲取JdbcTemplate物件並自動注入
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void save(Account account) {
        jdbcTemplate.update("insert into account values (null,?,?)",account.getUid(),account.getMoney());
    }
    @Override
    public void update(Account account) {
        jdbcTemplate.update("update account set  uid=?,money=? where accountId=?",account.getUid(),account.getMoney(),account.getAccountId());
    }
    @Override
    public void delete(int id) {
        jdbcTemplate.update("delete from account where accountId=?",id);
    }
    @Override
    public List<Account> findAll() {
        return jdbcTemplate.query("select * from account",new BeanPropertyRowMapper<>(Account.class));
    }
}

3.4 service層

3.4.1 service介面

  • 不用修改
public interface IAccountService {
    //新增
    void save(Account account);
    //更新
    void update(Account account);
    //刪除
    void delete(int id);
    //查詢
    List<Account> findAll();
}

3.4.2 service實現類

/*標識業務層,加入ioc容器中*/
@Service
public class AccountServiceImpl implements IAccountService {

    //從容器中獲取dao物件並自動注入
    @Autowired
    private IAccountDao accountDao;

    @Override
    public void save(Account account) {
        accountDao.save(account);
    }
    @Override
    public void update(Account account) {
        accountDao.update(account);
    }
    @Override
    public void delete(int id) {
        accountDao.delete(id);
    }
    @Override
    public List<Account> findAll() {
        return accountDao.findAll();
    }
}

3.5 bean_IOC.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

     <!--開啟註解掃描-->   
    <context:component-scan base-package="com.azure"/>
    <!--載入jdbc.properties配置檔案-->
    <!--注意整個語句的寫法-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--1.建立連線池物件-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--使用${key}根據指定key在properties檔案中獲取對應的值}-->
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--2.建立jdbcTemplate物件,注入連線池物件-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

3.6 測試類

  • 重點:改造後,service物件由ioc容器中獲取。根據@Service標籤的特性,由於設定標籤時沒有指定物件名稱(即value屬性值),所以按照預設物件名稱(即當前類的類名,且首字母小寫)!!所以getBean方法傳入的物件名稱是accountServiceImpl而不是accountService
public class WebApp {

    /*測試類中建立容器獲取service物件*/
    private ApplicationContext ac = new ClassPathXmlApplicationContext("bean_IOC.xml");
    private IAccountService accountService = (IAccountService) ac.getBean("accountServiceImpl");
    
    @Test
    public void save() {
        Account account = new Account();
        account.setUid(46);
        account.setMoney(99D);
        accountService.save(account);
    }
    @Test
    public void update() {
        Account account = new Account();
        account.setAccountId(1);
        account.setUid(46);
        account.setMoney(99D);
        accountService.update(account);
    }
    @Test
    public void delete() {
        accountService.delete(4);
    }
    @Test
    public void findAll() {
        System.out.println(accountService.findAll());
    }
}

4. spring 的純註解配置

  • 將bean.xml的所有配置都用註解實現
  • 使用純註解比較少見,因為如果要修改部分功能,可能會修改原始碼,反而會增加耦合性。
  • 根據企業要求或者簡化開發的原則來