springboot中的常用注解
個人覺得springboor中常用的注解主要可以分為三種:放入容器型注解、從容器中取出型注解和功能型注解。其中的放入容器型和從容器中取出型就是我們平時所說的控制反轉和依賴注入的概念(個人版本- - - 可以看一看,別太當真 0.0)
放入容器型注解
簡介:個人覺得就是申明一個實例對象,然后將這個對象交給spring管理。
1、@Component:放在類上,把普通類實例化到spring容器中。可以說很多注解都是基於這個注解的。
2、@Bean: 放在方法上,用@Bean標注方法等價於XML中配置bean,這個方法一般返回一個實體對象,告訴spring這里產生一個對象,然后這個對象會交給Spring管理。產生這個對象的方法Spring只會調用一次,隨后這個Spring將會將這個Bean對象放在自己的容器中。
3、@Configuration:標注當前類是配置類,並會將當前類內聲明的一個或多個以@Bean注解標記的方法的實例納入到srping容器中,並且實例名就是方法名。(其實就是靠@Component注解)
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { String value() default ""; }
4、@ConfigurationProperties:將配置文件中的參數映射成一個對象,通過prefix來設定前綴,然后將后面的和對象的屬性名一致就能實現注入(當然這個對象需要注入的屬性需要提供get和set方法 - - - 因為spring底層其實就是通過反射調用該對象的set方法)
實例: @ConfigurationProperties(prefix = “spring.datasource.test1”) public class Datasource1 { private String url; private String username; private String password; get - - set方法 } application.properties: spring.datasource.test1.url=jdbc:mysql://localhost:3307/multipledatasource1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false spring.datasource.test1.username=root spring.datasource.test1.password=root spring.datasource.test1.driver-class-name=com.mysql.cj.jdbc.Driver
5、@Value : value的作用其實和ConfigurationProperties作用差不多,就是讀取配置文件中參數的值,但是value是放在變量上面的,且是單值讀取,還有一點就是value標注的變量並不需要和配置文件的參數名字一致。
語法: @Value(“${參數}”) private String 變量名字 application.properties: 參數=值 注意:配置文件中的參數和value表達式中的參數名字是要保持一致的
6、@RestController、@Controller、@Service、@Repository:
這四個注解熟悉嗎?我想大家一定知道這四個注解各自的作用,但是這里需要強調一點就是他們其實是一樣的,至少在spring5.x之前,他們實質上是沒有什么區別的(當然你非要說@RestController=@Controller+@ResponseBody 這個也對)。我這邊主要是要強調,他們實質上都是依靠於@Component注解的,我們可以查看源碼。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller { String value() default ""; }
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { String value() default ""; }
我們可以看到Service 和Controller 這兩個注解里面的東西是一模一樣的,沒什么區別。我不知道你們有沒有做過這樣的騷操作:就是在controller層中不用@Controller,而用@Service;在service中不用@Service,而用@Controller。其實這樣寫對於spring本身來說是沒什么區別的,而且項目也能正常運行,這里我是親自測過的。。。據說分為這三個注解的目的是為了從結構上分層。
從容器中取出型注解
簡介:個人覺得就是我們平時所說的依賴注入。
1、@Resource:是按照名稱來注入的,當找不到與名稱匹配的bean才會按照類型來注入。其實我們平時用的@Resource都是用了他的默認的方式,即都不指定名字和類型。spring通過反射機制使用byName方法自動注入。
@Resource(type = ShiroService.class, name = "shiroService") private ShiroService shiroService;
@Resource 的裝配順序:
1. 如果同時指定了 name 屬性和 type 屬性,那么 Spring 將從容器中找唯一匹配的 bean 進行裝配,找不到則拋出異常
2. 如果指定了 name 屬性值,則從容器中查找名稱匹配的 bean 進行裝配,找不到則拋出異常
3. 如果指定了 type 屬性值,則從容器中查找類型匹配的唯一的 bean 進行裝配,找不到或者找到多個都會拋出異常
4. 如果都不指定,則會自動按照 byName 方式進行裝配(我們一般都用的是這個。。。)
2、@Autowried:默認是按照類型進行裝配注入,如果允許 null 值,可以設置它 required 為false。即:當不能確定 Spring 容器中一定擁有某個類的 Bean 時,可以在需要自動注入該類 Bean 的地方可以使用 @Autowired(required = false) ,這等於告訴 Spring:在找不到匹配 Bean 時也不報錯。
@Autowired(required = false) private ShiroService shiroService;
3、@Qualifier:@Autowired是根據類型進行自動裝配的。如果當spring上下文中存在不止一個A類型的bean時,就會拋出BeanCreationException異常;如果Spring上下文中不存在A類型的bean,而且我們又使用A類型,也會拋出BeanCreationException異常。針對存在多個A類型的Bean,我們可以聯合使用@Qualifier和@Autowired來解決這些問題。
@Autowried @Qualifier("adminDAO") private AdminDAO adminDAO;
簡單來說,Qualifier就是規定一下Bean的名字,相當於@Resource規定了name屬性。
功能型注解
1、@SpringBootApplication:這個注解就是集成了:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan這三個注解。其中@SpringBootConfiguration:表示這個類為配置類;@EnableAutoConfiguration:表示開啟自動配置,我們平時所說springboot無配置就是這個參數起的作用,他讀取了springboot默認的配置;@ComponentScan:表示自動掃描,這個掃描默認只能掃同一級的目錄。
2、@EnableConfigurationProperties:將帶有@ConfigurationProperties注解的類注入為Spring容器的Bean。如果使用了@ConfigurationProperties但是沒有在啟動類上增加這個注解,則@ConfigurationProperties將不起作用。
3、@Async與@EnableAsync:其中@Async表示這個方法為異步方法;@EnableAsync這個注解需要加在啟動類上,表示支持異步操作;如果不加,則@Async將不起作用。
@RestController public class AsyncController { @Autowired private AsyncService asyncservice; @RequestMapping("/asyncTest.do") public String asyncTest() { System.out.println("############asyncTest############"); System.out.println("############a############"); asyncservice.test(); System.out.println("############b############"); return "success"; } @RequestMapping("/asyncTest2.do") public String asyncTest2() { System.out.println("############asyncTest2############"); System.out.println("############a############"); asyncservice.test2(); System.out.println("############b############"); return "success"; } @RequestMapping("/asyncTest3.do") public String asyncTest3() { System.out.println("############asyncTest3############"); System.out.println("############a############"); asyncservice.test3(); System.out.println("############b############"); return "success"; } }
@Service public class AsyncService { public void test() { System.out.println("############c############"); for (int i = 0; i < 5; i++) { System.out.println(i); } System.out.println("############d############"); } @Async public void test2() { System.out.println("############c############"); for (int i = 0; i < 5; i++) { System.out.println(i); } System.out.println("############d############"); } /** * @Async就相當於另起一個線程 */ public void test3() { new Thread() { @Override public void run() { System.out.println("############c############"); for (int i = 0; i < 5; i++) { System.out.println(i); } System.out.println("############d############"); } }.start(); } }
實驗結果是: asyncTest.do輸出為: acdb; asyncTest2.do輸出為: abcd; asyncTest3.do輸出為: abcd;
所以簡單來說:@Async就相當於另起一個線程。
4、@Scheduled與@EnableScheduling: 定時任務。@EnableScheduling這個注解需要加在啟動類上,表示支持定時任務。
@Scheduled(cron = "0/5 * * * * ? ") public void doTest() { System.out.println(new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date())); }