Java框架-Spring基於註解的IOC配置及純註解
阿新 • • 發佈:2018-11-01
- 註解配置與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註解建立物件
- 使用註解建立物件的條件:
- 使用@Component對類進行註解;
- 開啟註解掃描
@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 定義到欄位
查詢順序:
- 先根據欄位的型別,尋找容器中該型別對應的物件注入;
- 如果該型別的物件有多個
- 再根據欄位的名稱查詢容器中的物件,成功找到就注入;
- 如果根據名稱找不到對應物件,則報錯。
2.2.1.2 定義到方法
查詢順序:
- 先根據引數的型別,尋找容器中該型別對應的物件注入;
- 如果該型別的物件有多個
- 再根據引數的名稱查詢容器中的物件,成功找到就注入;
- 如果根據名稱找不到對應物件,則報錯。
2.2.1.3 演示示例
-
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>
-
定義到欄位
@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型別的物件,則會把該物件注入
-
定義到方法
/** * @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的型別
-
查詢順序:
- 根據欄位的名稱,在容器中找對應名稱物件注入;
- 如果該型別的物件有多個
- 再根據引數的名稱查詢容器中的物件,成功找到就注入;
- 如果根據名稱找不到對應物件,則報錯。
-
注意事項:只能注入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的所有配置都用註解實現
- 使用純註解比較少見,因為如果要修改部分功能,可能會修改原始碼,反而會增加耦合性。
- 根據企業要求或者簡化開發的原則來