一.元注解:——就是定義注解的注解,或者說注解注解的注解
@Retention :用于提示注解被保留多長時(shí)間,有三種取值:
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
RetentionPolicy.SOURCE 保留在源碼級(jí)別,被編譯器拋棄(@Override就是此類);
RetentionPolicy.CLASS被編譯器保留在編譯后的類文件級(jí)別,但是被虛擬機(jī)丟棄;
RetentionPolicy.RUNTIME保留至運(yùn)行時(shí),可以被反射讀取。
@Target: :用于提示該注解使用的地方,取值有:
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
* @since 1.8
*/
TYPE_USE
}
分別表示該注解可以被使用的地方:
1.(類,接口,注解,enum); 2. 屬性域;3.方法;4.參數(shù);5.構(gòu)造函數(shù);6.局部變量;7.注解類型;8.包
@Documented :表示注解是否能被 javadoc 處理并保留在文檔中。
我們熟悉的注解:@Override:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
可以被理解為:
只能使用在方法上,保留在源碼級(jí)別,被編譯器處理,然后拋棄掉。
二.Spring中和bean容器相關(guān)的注解
@Repository("標(biāo)識(shí)符") : 指定相應(yīng)的bean類在ioc容器中的標(biāo)識(shí)符的名稱
@Repository("userRepository")
public class UserRepositoryImps implements UserRepository{
@Override
public void save() {
System.out.println("UserRepositoryImps save");
}
}
定義一個(gè)UserRepository接口的實(shí)現(xiàn)類,并實(shí)現(xiàn)save方法,在這里指定了該bean在IoC中標(biāo)識(shí)符名稱為userRepository
@Autowired :其實(shí)就是 autowire=byType 就是根據(jù)類型的自動(dòng)注入依賴(基于注解的依賴注入)。
我們先看他的定義:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
根據(jù)元注解我們可以知道:@Autowired注解使用的地方有:屬性域,構(gòu)造函數(shù),方法,參數(shù),注解類型。此注解保留至運(yùn)行時(shí),可以被反射讀取。
其使用方法舉例說明:
package com.proc.bean.repository;
public interface UserRepository {
void save();
}
package com.proc.bean.repository;
import org.springframework.stereotype.Repository;
@Repository("userRepository") //指定了該bean在IOC容器之中的標(biāo)識(shí)符的名稱為userRepository
public class UserRepositoryImps implements UserRepository{
@Override
public void save() {
System.out.println("UserRepositoryImps save");
}
}
package com.proc.bean.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.proc.bean.repository.UserRepository;
@Service
public class UserService {
@Autowired //這里需要一個(gè)UserRepository類型的屬性,通過@Autowired自動(dòng)裝配方式,從IoC容器中去查找到,并返回給該屬性
private UserRepository userRepository;
public void save(){
userRepository.save();
}
}
測試代碼:
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService=(UserService) ctx.getBean("userService");
userService.save();
返回結(jié)果:
UserRepositoryImps save
注意事項(xiàng):
在使用@Autowired時(shí),首先在容器中查詢對(duì)應(yīng)類型的bean
如果查詢結(jié)果剛好為一個(gè),就將該bean裝配給@Autowired指定的數(shù)據(jù)
如果查詢的結(jié)果不止一個(gè),那么@Autowired會(huì)根據(jù)名稱來查找。
如果查詢的結(jié)果為空,那么會(huì)拋出異常。解決方法時(shí),使用required=false
若是我們?cè)谏厦娴睦拥幕A(chǔ)上面再定義一個(gè)類,來是想U(xiǎn)serRepository接口
package com.proc.bean.repository;
import org.springframework.stereotype.Repository;
@Repository
public class UserJdbcImps implements UserRepository {
@Override
public void save() {
System.out.println("UserJdbcImps save");
}
}
輸出結(jié)果:UserRepositoryImps save
@Qualifier : 就是 autowire=byName, @Autowired注解判斷多個(gè)bean類型相同時(shí),就需要使用 @Qualifier("xxBean") 來指定依賴的bean的id:
同上面的例子:若是想要裝載userJdbcImps的實(shí)例,除了將字段userRepository名稱改成userJdbcImps外,可以提供了一個(gè)@Qualifier標(biāo)記,來指定需要裝配bean的名稱,代碼這樣寫
@Autowired
@Qualifier("userJdbcImps")
private UserRepository userRepository;
輸出結(jié)果:UserJdbcImps save
@Resource : 和@Autowired作用相似,也是可以基于注解式的依賴的注入;
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Resource {
我們可以知道其作用于1.(類,接口,注解,enum); 2. 屬性域;3.方法,但是一般其使用在成員屬性和Setter方法上面。
注意:
@Resource查詢?nèi)萜髦械膶?duì)應(yīng)類型的Bean原則是:
名稱優(yōu)先——>類型次之——>Qualifier約束——>裝配失敗后異常
但是name屬性一旦指定,就只會(huì)按照名稱進(jìn)行匹配。
@Resource(name=“personDaoBean”)
@Autowired查詢?nèi)萜髦械膶?duì)應(yīng)類型的Bean原則是:
類型優(yōu)先——>其次是Qualifier約束——>名稱查詢——>最后判斷是否required
這里的名稱查詢是將所定義的名稱和Bean的name標(biāo)簽進(jìn)行比較
- 推薦使用:@Resource注解在字段上,且這個(gè)注解是屬于J2EE的,減少了與spring的耦合。
@Component : 使用@Component注解在一個(gè)類上,表示將此類標(biāo)記為Spring容器中的一個(gè)Bean。
@Repository : 用于將數(shù)據(jù)訪問層(DAO層)的類標(biāo)識(shí)為Spirng Bean。具體只需要將注解標(biāo)識(shí)在DAO類上即可。
@service : 用于將服務(wù)層的類標(biāo)識(shí)為Spring Bean,主要用來進(jìn)行業(yè)務(wù)的邏輯處理。
@controller : 用于將控制層的類標(biāo)識(shí)為Spring Bean,相當(dāng)于struts中的action層。
各層級(jí)之間的關(guān)系圖:

Spring——功能注解
@Transactional : 使用這個(gè)注解的類或者方法表示該類里面的所有方法或者這個(gè)方法的事務(wù)由spring處理,來保證事務(wù)的原子性,即是方法里面對(duì)數(shù)據(jù)庫操作,如果失敗則spring負(fù)責(zé)回滾操作,成功則提交操作。
- 當(dāng)對(duì)Service方法添加事物的時(shí)候,加上這個(gè)注解,就能保證事物的原子性,當(dāng)出現(xiàn)unchecked exception的錯(cuò)誤,就能夠發(fā)生回滾。
- @Transactional注解 可以作用于接口、接口方法、類以及類方法上。由于基于AOP,因此用于方法上時(shí),必須是public方法才會(huì)生效,使用到其他類型方法上也不會(huì)拋異常,只是會(huì)自動(dòng)忽略。
- 類中的方法若沒有來自外部的方法調(diào)用;或者同一個(gè)類中有來自外部方法調(diào)用卻沒有添加@Transactional注解,再調(diào)用本類內(nèi)部添加了@Transactional注解的其他方法,事務(wù)均不會(huì)生效。比如說:方法A添加了注解@Transactional,而B沒有,A和B同屬于一個(gè)類中的兩個(gè)方法,且A調(diào)用了B,那么運(yùn)行之后,兩個(gè)事物都不會(huì)回滾,因?yàn)橹挥型獠康淖⒔鈺?huì)生效,內(nèi)部的注解不會(huì)生效;
注解中定義的屬性:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
value()和transactionManager()屬性可以指定存在多個(gè)TransactionManager的其中一個(gè)qualifier屬性值或者bean名稱。
propagation()屬性可以設(shè)置Propagation枚舉類中的七個(gè)傳播行為,參考1.2.1,默認(rèn)為REQUIRED。
isolation()屬性可以設(shè)置Isolation枚舉類中的五個(gè)隔離級(jí)別,參考1.2.2,默認(rèn)為DEFAULT。
timeout()設(shè)置事務(wù)超時(shí)時(shí)間,默認(rèn)不超時(shí)。
readOnly()設(shè)置事務(wù)是否是只讀事務(wù)。
rollbackFor()和rollbackForClassName()設(shè)置哪些異常事務(wù)會(huì)回滾。
noRollbackFor()和noRollbackForClassName()設(shè)置哪些異常事務(wù)不會(huì)回滾。
@Transactional應(yīng)用舉例:
@Transactional
public String submitOrder(Order order, Model model, HttpSession session) {
order.setBusertable_id(MyUtil.getUser(session).getId());
//生成訂單
cartRepository.addOrder(order);
//生成訂單詳情
cartRepository.addOrderDetail(order.getId(), MyUtil.getUser(session).getId());
//減少商品庫存
List<Map<String,Object>> listGoods = cartRepository.selectGoodsShop(MyUtil.getUser(session).getId());
for (Map<String, Object> map : listGoods) {
cartRepository.updateStore(map);
}
//清空購物車
cartRepository.clear(MyUtil.getUser(session).getId());
model.addAttribute("order", order);
return "user/pay";
}
這是在一個(gè)Spring 結(jié)合 Mybatis的網(wǎng)上商城項(xiàng)目之中的一段代碼,使用@Transactional標(biāo)注整個(gè)方法,因?yàn)檫@個(gè)方法添加了,結(jié)合Mybatis的對(duì)于數(shù)據(jù)庫數(shù)據(jù)的修改,的事物。使得其中的數(shù)據(jù)具有原子性和一致性。
@Cacheable : @Cacheable可以標(biāo)記在一個(gè)方法上,也可以標(biāo)記在一個(gè)類上。當(dāng)標(biāo)記在一個(gè)方法上時(shí)表示該方法是支持緩存的,當(dāng)標(biāo)記在一個(gè)類上時(shí)則表示該類所有的方法都是支持緩存的。
緩存的作用:我們希望的是,第一次調(diào)用這個(gè)方法時(shí),返回的數(shù)據(jù)能被放到服務(wù)器端的緩存里,以便于后面要調(diào)用這個(gè)方法時(shí),能直接從緩存里取到,這樣就不用再查數(shù)據(jù)庫占用資源了。而@Cacheable的作用就是這個(gè)。
- 對(duì)于一個(gè)支持緩存的方法,Spring會(huì)在其被調(diào)用后將其返回值緩存起來,以保證下次利用同樣的參數(shù)來執(zhí)行該方法時(shí)可以直接從緩存中獲取結(jié)果,而不需要再次執(zhí)行該方法。
- Spring在緩存方法的返回值時(shí)是以鍵值對(duì)進(jìn)行緩存的,值就是方法的返回結(jié)果,
- 需要注意的是當(dāng)一個(gè)支持緩存的方法在對(duì)象內(nèi)部被調(diào)用時(shí)是不會(huì)觸發(fā)緩存功能的。
@Cacheable可以指定三個(gè)屬性,value、key和condition。
- value :屬性是必須指定的,指定將我們第一次調(diào)用的方法的返回的值存儲(chǔ)在內(nèi)存的哪里,就是名稱;
- key : 用于動(dòng)態(tài)計(jì)算密鑰的SpEL (Spring Expression Language)表達(dá)式。指定Spring緩存方法的返回結(jié)果時(shí)對(duì)應(yīng)的key的。使用方法參數(shù)時(shí)我們可以直接使用“#參數(shù)名”或者“#p參數(shù)index”。參默認(rèn)使用的是方法參數(shù)的值;
注意的是:當(dāng)我們的指定的key為參數(shù)的時(shí)候,若是有N個(gè)不同的參數(shù)調(diào)用了相應(yīng)的方法,就會(huì)產(chǎn)生N個(gè)緩存。
使用舉例:
@Cacheable(value = "CACHE_BOOK",key = "#root.args[0]", condition = "#language = 1")
public List<Book> getBooksByUsernameAndLanguage(int id, int language) {
// balabalabala...里面的代碼不重要
return bookList;
}
這里我們使用root.args[0]作為key,表示可以根據(jù)所傳入的參數(shù):id的不同,建立多個(gè)緩存的文件,每個(gè)不同的緩存文件的數(shù)據(jù)都是根據(jù)第一次輸入id參數(shù)的不同,調(diào)用方法返回的結(jié)果的值。
其實(shí)我們這樣看來value相當(dāng)于存儲(chǔ)數(shù)據(jù)的包名,key相當(dāng)于包下的具體文件。

3.keyGenerator:key的生成器;可以自己指定key的生成器的組件id, key/keyGenerator:二選一使用(自己配置類)
其實(shí)我們了解了key之后,keyGenerator是比較簡單的:
無非就是自己定義一個(gè)方法來定義我們的key的格式。
@Bean
public KeyGenerator springCacheCustomKeyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
String key = target.getClass().getSimpleName() + "_" +method.getName() + "_" + StringUtils.arrayToDelimitedString(params,"_");
return key;
}
};
}
@Cacheable(value = "litenfei",key ="springCacheCustomKeyGenerator")
4.condition:緩存的條件,表示再怎樣的條件下才會(huì)進(jìn)行緩存。如:condition = "#a0>1" 即第一個(gè)參數(shù)值大于1時(shí)才進(jìn)行緩存
5.unless :否定緩存;當(dāng)unless指定的條件為true,方法的返回值就不會(huì)被緩存;可以獲取到的結(jié)果進(jìn)行判斷
如: unless = “#a0”:如果第一個(gè)參數(shù)值是2,則結(jié)果不緩存
unless = “#result == null” 結(jié)果為null不緩存
以上就是Spring的各種的注釋,SpingMVC的注釋和SpringBoot的注釋將在別的文章之中總結(jié)。