Spring的各種注解

一.元注解:——就是定義注解的注解,或者說注解注解的注解

@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)系圖:

各層級(jí)之間的關(guān)系.png

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。

  1. value :屬性是必須指定的,指定將我們第一次調(diào)用的方法的返回的值存儲(chǔ)在內(nèi)存的哪里,就是名稱;
  2. 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)于包下的具體文件。


image.png

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é)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容