學習Sping註解,編寫示例,最終整理成文章。如有錯誤,請指出。
該文章主要是針對新手的簡單使用示例,講述如何使用該註釋,沒有過多的原理解析。
已整理的註解請看右側目錄。寫的示例程式碼也會在結尾附出。
配置類相關注解
@Configuration
宣告當前類為配置類,把一個類作為一個IoC容器,它的某個方法頭上如果註冊了@Bean,就會作為這個Spring容器中的Bean。該類等價 與XML中配置beans。
@Configuration public class MyConfig { }
@Bean
註解在方法上,宣告當前方法的返回值為一個Bean。返回的Bean對應的類中可以定義init()的方法和destroy()方法,然後在@Bean(initMethod="init",destroyMethod="destroy")定義,在構造之後執行init,在銷燬之前執行destory。
@Configuration
@ComponentScan("com.blackcat.annotation.bean")
public class MyConfig {
/**
* <p> 描述 : @Bean的形式是使用的話, bean的預設名稱是方法名
* @author : blackcat
* @date : 2020/5/23 16:20
* @see App ctx.getBean("user") 得到bean
*/
@Bean(initMethod="init",destroyMethod="destroy")
public User user(){
return new User();
}
}
/** * <p> 描述 :Bean * @author : blackcat * @date : 2020/5/23 16:06 * @see MyConfig 需要看MyConfig * */ @Data public class User { private String name="zhang"; public void init(){ System.out.println("init"); } public void destroy(){ System.out.println("destroy"); } }
@ComponentScan
註解用於啟用元件掃描,其作用同xml中配置<context:component-scan>。若不配置其value值,它會以配置類所在的包作為基礎包(base package)來掃描元件。
引數說明:
basePackages:掃描的路徑
excludeFilters:排除 過濾條件
includeFilters:包含 過濾條件
useDefaultFilters:若使用包含的用法,需要把useDefaultFilters屬性設定為false(true表示掃描全部的)
FilterType的型別:
FilterType.ANNOTATION: @Controller @Service @Repository @Compent
FilterType.ASSIGNABLE_TYPE:指定元件
FilterType.CUSTOM: 自定義的
FilterType.REGEX: 正規表示式的(不常用,沒寫示例)
FilterType.ASPECTJ: aspectj型別的(不常用,沒寫示例)
最簡單示例
自動掃描指定包com.blackcat.annotation.compentscan下所有使用@Service,@Component,@Controller,@Repository的類並註冊。
@ComponentScan(basePackages = {"com.blackcat.annotation.compentscan"}) @Configuration public class MyConfig { }
排除excludeFilters
將不會載入所有Controller類 及UserService類(指定類)。
@ComponentScan(basePackages = {"com.blackcat.annotation.componentscan"},excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {UserService.class}) }) @Configuration public class MyConfig { }
包含includeFilters
載入除所有使用@Service,@Component,@Controller,@Repository外,以及包含過濾條件的MyFilterType,需要把useDefaultFilters屬性設定為false(true表示掃描全部的)。
@ComponentScan(basePackages = {"com.blackcat.annotation.componentscan"},includeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM,value = MyFilterType.class) },useDefaultFilters = false) @Configuration public class MyConfig { }
/** * <p> 描述 :FilterType.CUSTOM 自定義型別使用 * @author : blackcat * @date : 2020/5/23 16:40 */ public class MyFilterType implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //獲取當前類的註解源資訊 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //獲取當前類的class的源資訊 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //獲取當前類的資源資訊 Resource resource = metadataReader.getResource(); System.out.println("類的路徑:"+classMetadata.getClassName()); if(classMetadata.getClassName().contains("dao")) { return true; } return false;
}
}
組合使用
將excludeFilters與includeFilters組合使用。
@ComponentScan(basePackages = {"com.blackcat.annotation.componentscan"}, excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class})}, includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = Repository.class) }) @Configuration public class MyConfig { }
@Bean的屬性支援
@Scope
設定Spring容器如何新建Bean例項(方法上,得有@Bean) 作用域 。在不指定@Scope的情況下,所有的bean都是單例項的bean,而且是餓漢載入(容器啟動例項就建立好了)。
其設定型別包括:Singleton,Protetype,Request(無程式碼示例) ,Session (無程式碼示例) ,GlobalSession(無程式碼示例)
@Bean
@Scope(value = "singleton")
public User user4(){
System.out.println("容器開始建立bean.........");
return new User();
}
Singleton
單例項的(預設) 全域性有且僅有一個例項。
Protetype
表示為多例項的,每次獲取Bean的時候會有一個新的例項,而且還是懶漢模式載入(IOC容器啟動的時候,並不會建立物件,而是 在第一次使用的時候才會建立)。
每次連線請求,都會生成一個bean例項,也會導致一個問題,當請求數越多,效能會降低,因為建立的例項,導致GC頻繁,gc時長增加。
request
同一次請求 ,表示該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP request內有效 (web階段時使用,無示例)。
session
同一個會話級別,表示該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP session內有效 (web階段時使用,無示例)。
globalsession
全域性session中的一般不常用。給每一個 global http session新建一個Bean例項 類似於標準的HTTP Session作用域,不過它僅僅在基於portlet的web應用中才有意義 。
@PostConstruct
由JSR-250提供,在建構函式執行完之後執行,等價於xml配置檔案中bean的initMethod,用於指定初始化方法 標註在方法上方,該方法在建構函式執行完成之後執行。
被@PostConstruct修飾的方法會在伺服器載入Servle的時候執行,並且只會被伺服器執行一次。
伺服器載入:Servlet -> servlet 建構函式的載入 -> postConstruct ->init(init是在service 中的初始化方法. 建立service 時發生的事件.) ->Service->destory->predestroy->伺服器解除安裝serlvet。
如果想在生成物件時候完成某些初始化操作,而偏偏這些初始化操作又依賴於依賴注入,那麼就無法在建構函式中實現。為此,可以使用@PostConstruct註解一個方法來完成初始化,@PostConstruct註解的方法將會在依賴注入完成後被自動呼叫。
public class User {
private String name="zhang";
public void init(){
System.out.println("init");
}
public void destroy(){
System.out.println("destroy");
}
@PostConstruct
public void PostConstruct(){
System.out.println("@PostConstruct將在依賴注入完成後被自動呼叫");
}
}
@PreDestroy
由JSR-250提供,在Bean銷燬之前執行,等價於xml配置檔案中bean的destroyMethod,用於指定銷燬方法(用在方法上) 摧毀註解 預設 單例 啟動就載入 。
伺服器載入:Servlet -> servlet 建構函式的載入 -> postConstruct ->init(init是在service 中的初始化方法. 建立service 時發生的事件.) ->Service->destory->predestroy->伺服器解除安裝serlvet。
根據上個註釋程式碼,就不過多貼上。
@PreDestroy
public void PreDestroy(){
System.out.println("XXX 正在被容器刪除");
}
@Lazy
用於標識bean是否需要延遲載入。主要針對單例項的bean 容器啟動的時候,不建立物件,在第一次使用的時候才會建立該物件。
@Bean
@Lazy
public User user4(){
System.out.println("容器開始建立bean.........");
return new User();
}
@Primary
自動裝配時當出現多個Bean候選者時,被註解為@Primary的Bean將作為首選者。
該示例請看:com.blackcat.annotation.autowired包下的程式碼。
@Configuration
@ComponentScan(basePackages = "com.blackcat.annotation.autowired")
public class MyConfig {
@Primary
@Bean
public UserDao userDao2() {
UserDao userDao = new UserDao();
userDao.setFlag(2);
return userDao;
}
@Bean
public UserDao userDao() {
UserDao userDao = new UserDao();
userDao.setFlag(1);
return userDao;
}
}
@Service
public class BaiDuService {
@Autowired
private UserDao userDao;
@Override
public String toString() {
return "BaiDuService{" +
"userDao=" + userDao +
'}';
}
}
public class UserDao {
private int flag=1;
@Override
public String toString() {
return "UserDao{" +
"flag=" + flag +
'}';
}
}
@DependsOn
定義Bean初始化及銷燬時的順序。有很多場景需要bean B應該被先於bean A被初始化,從而避免各種負面影響。我們可以在bean A上使用@DependsOn註解,告訴容器bean B應該先被初始化。
例如:bean A 間接依賴 bean B。如Bean B應該需要更新一些全域性快取,可能通過單例模式實現且沒有在spring容器註冊,bean A需要使用該快取;因此,如果bean B沒有準備好,bean A無法訪問。
示例通過事件機制說明,釋出者和監聽者,然後通過spring配置執行。
/**
* <p> 描述 : 事件管理類,維護監聽器列表,通過單例方法獲取事件管理器,可以增加監聽器或釋出事件。
* @author : blackcat
* @date : 2020/7/31 10:03
*/
public class EventManager {
private final List<Consumer<String>> listeners = new ArrayList<>();
private EventManager() {
}
private static class SingletonHolder {
private static final EventManager INSTANCE = new EventManager();
}
public static EventManager getInstance() {
return SingletonHolder.INSTANCE;
}
public void publish(final String message) {
listeners.forEach(l -> l.accept(message));
}
public void addListener(Consumer<String> eventConsumer) {
listeners.add(eventConsumer);
}
}
/**
* <p> 描述 : 事件監聽者,可以增加監聽器。
* @author : blackcat
* @date : 2020/7/31 10:04
*/
public class EventListenerBean {
private void initialize() {
EventManager.getInstance().
addListener(s ->
System.out.println("事件監聽者 : " + s));
}
}
/**
* <p> 描述 : 事件釋出類,通過EventManager類釋出事件。
* @author : blackcat
* @date : 2020/7/31 10:04
*/
public class EventPublisherBean {
public void initialize() {
System.out.println("事件釋出類 initializing");
EventManager.getInstance().publish("event published from EventPublisherBean");
}
}
總結
如果我們註釋掉@DependsOn("eventListener")
,我們可能不確定獲得相同結果。嘗試多次執行main方法,偶爾我們將看到EventListenerBean 沒有收到事件。為什麼是偶爾呢?因為容器啟動過程中,spring按任意順序載入bean。
那麼當不使用@DependsOn
可以讓其100%確定嗎?可以使用@Lazy
註解放在eventListenerBean ()
上。因為EventListenerBean
在啟動階段不載入,當其他bean需要其時才載入。這次我們僅EventListenerBean
被初始化。
EventPublisherBean initializing
現在從新增加
@DependsOn
,也不刪除@Lazy
註解,輸出結果和第一次一致,雖然我們使用了@Lazy
註解,eventListenerBean
在啟動時仍然被載入,因為@DependsOn
表明需要EventListenerBean
。
該示例參考文章:https://blog.csdn.net/neweastsun/article/details/78775371
宣告bean的註解
@Component
泛指元件,當元件不好歸類的時候,我們可以使用這個註解進行標註。可通過@Component("XX")宣告bean的名字,預設名稱是類名頭字母小寫。
@Component public class MyLog { private String name="123456"; }
@Component("info")
public class MyInfo {
private String name="123456";
}
@Configuration
@ComponentScan(basePackages = "com.blackcat.annotation.component")
public class MyConfig {
}
public static void main(String[] args) { // 容器中讀取Bean AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class); System.out.println(ctx.getBean(MyLog.class)); System.out.println(ctx.getBean("info")); }
@Service
在業務邏輯層使用(service層),對應的是業務層Bean。用於標註業務層元件。可通過@Service("XX")宣告bean的名字,預設名稱是類名頭字母小寫。
public interface UserService {
.....
}
@Service public class UserServiceImpl implements UserService { ..... }
// 注入userService @Resourceprivate UserService userService;
@Repository
在資料訪問層使用(dao層),對應資料訪問層Bean,用於標註資料訪問元件,即DAO元件。可通過@Repository("XX")宣告bean的名字,預設名稱是類名頭字母小寫。
public interface BaseDao<T> {
}
@Repository("userDao")
public class UserDaoImpl implements BaseDao<User> {
}
// 注入userDao @Resource(name = "userDao") private BaseDao<User> userDao;
@Controller
在展現層使用,控制器的宣告。@Controller對應表現層的Bean,也就是Action。
@Controller public class UserController { }
注入bean的註解
@Autowired
由Spring提供。預設按型別裝配。
自動裝配首先時按照型別進行裝配,若在IOC容器中發現了多個相同型別的元件,那麼就按照 屬性名稱來進行裝配。
例如:現在容器中有兩個userDao型別的元件,一個叫userDao 一個叫userDao2,我們通過@AutoWired 來修飾的屬性名稱時userDao,就載入容器的userDao元件,若屬性名稱為 userDao2 那麼他就載入的時userDao2元件。
public class UserDao { private int flag=1; @Override public String toString() { return "BaseDao{" + "flag=" + flag + '}'; } }
@Configuration @ComponentScan(basePackages = "com.blackcat.annotation.autowired") public class MyConfig { @Bean public UserDao userDao2() { UserDao userDao = new UserDao(); userDao.setFlag(2); return userDao; } @Bean public UserDao userDao() { UserDao userDao = new UserDao(); userDao.setFlag(1); return userDao; } @Bean(autowire = Autowire.BY_NAME) public UserService userService() { return new UserService(); } }
public class UserService { private UserDao userDao; @Override public String toString() { return "UserService{" + "userDao=" + userDao + '}'; } }
如果我們想使用按名稱裝配,可以結合@Qualifier註解一起使用, @Autowired @Qualifier("XX") 存在多個例項配合使用。
@Service public class BaiDuService { @Qualifier("userDao") @Autowired private UserDao userDao2; @Override public String toString() { return "BaiDuService{" + "userDao=" + userDao2 + '}'; } }
@Autowired(required = false) 的意思是裝配的上就裝,裝不上就不裝。
示例:@Qualifier("userDao3") 容器中即沒有userDao3也沒有userDao2,那麼在裝配的時候就會丟擲異常。若我們想不拋異常 ,我們需要指定 required為false的時候可以了。
@Autowired 可標註在set方法上或構造方法上。
public class MyAspect { private MyLog myLog; @Override public String toString() { return "MyAspect{" + "myLog=" + myLog + '}'; } /** * <p> 描述 : 標註在set方法上 * @author : blackcat * @date : 2020/5/26 14:56 */ //@Autowired public void setMyLog(MyLog myLog) { this.myLog = myLog; } /** * <p> 描述 : 標註在構造方法上 * @author : blackcat * @date : 2020/5/26 14:56 */ @Autowired public MyAspect(MyLog myLog) { this.myLog = myLog; } }
@Component @ToString public class MyLog { }
@Autowired 也可標註在配置類上的入參中(可以不寫@Autowired)。
@Bean public MyAspect myAspect(@Autowired MyLog myLog) { MyAspect myAspect = new MyAspect(myLog); return myAspect; }
@Inject
由JSR-330提供。需要匯入jar包依賴。
<!--JSR330規範--> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
@Service public class UserService { /** * 需要匯入jar包依賴 * 支援@Primary功能 ,但是沒有Require=false的功能 */ @Inject private UserDao userDao; @Override public String toString() { return "UserService{" + "userDao=" + userDao + '}'; } }
@Resource
由JSR-250提供。預設按名稱裝配,當找不到與名稱匹配的bean才會按型別裝配。
@Service public class UserService { /** * 功能和@AutoWired的功能差不多一樣,但是不支援@Primary 和 @Qualifier的支援 */ @Resource private UserDao userDao; @Override public String toString() { return "UserService{" + "userDao=" + userDao + '}'; } }
@Value註解
/** * <p> 描述 : Value註解 * @author : blackcat * @date : 2020/5/25 17:45 */ @Data @Component public class User { /** 注入普通字元 */ @Value("cat") private String userName; /** spel方式來賦值 */ @Value("#{28-3}") private Integer age;
/** * 注入作業系統屬性 * 需要有系統配置檔案類SystemProperties */ @Value("#{systemProperties['os.name']}") private String osName; /** 注入表示式結果 */ @Value("#{ T(java.lang.Math).random() * 100 }") private String randomNumber; /** * 注入配置檔案 * 1.編寫配置檔案 test.properties * 2.載入配置檔案類@PropertySource */ @Value("${book.name}") private String book; /** 注入網站資源 */ @Value("http://www.baudu.com") private Resource url; }
/** * <p> 描述 : * @author : blackcat * @date : 2020/5/25 17:45 */ @Configuration @PropertySource(value = {"classpath:test.properties"})// 指定外部檔案的位置 public class MyConfig { @Bean public User user() { return new User(); } }
test.properties
book.name=電子書
條件註解
以@ConditionalOnXX 為SpringBoot註解。瞭解完Conditional註解的原理之後,就方便了解ConditionalOnXX註解的原理了。
@Conditional
它的作用是按照一定的條件進行判斷,滿足條件給容器註冊bean。
@Conditional的定義:
//此註解可以標註在類和方法上 @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { Class<? extends Condition>[] value(); }
從程式碼中可以看到,需要傳入一個Class陣列,並且需要繼承Condition介面。
示例:現在有兩個元件MyAspect,MyLog。現在有個條件判斷,當容器內沒有MyAspect就不注入MyLog。
public class MyAspect { public MyAspect() { System.out.println("MyAspect元件"); } }
public class MyLog { public MyLog() { System.out.println("MyLog元件"); } }
條件判斷類MyCondition,需要實現Condition介面,並重寫方法來自定義match規則。
/** * <p> 描述 :自定義條件判斷 * * @author : blackcat * @date : 2020/5/24 14:05 */ public class MyCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { //判斷容器中是否有MyAspect的元件 if(conditionContext.getBeanFactory().containsBean("myAspect")) { return true; } return false; } }
從下面的執行結果可以看出,當MyAspect注入成功時,MyLog注入成功。而MyAspect沒注入成功,MyLog也沒有注入成功。
條件的判斷是根據MyCondition類中判斷,判斷MyAspect是否存在。
標註在方法上:
一個方法只能注入一個bean例項,所以@Conditional標註在方法上只能控制一個bean例項是否注入。
標註在類上:
一個類中可以注入很多例項,@Conditional標註在類上就決定了一批bean是否注入。
多個條件類:
前言中說,@Conditional註解傳入的是一個Class陣列,存在多種條件類的情況。
第一個條件類實現的方法返回true,第二個返回false,則結果false,不注入進容器。
第一個條件類實現的方法返回true,第二個返回true,則結果true,注入進容器中。
@ConditionalOnBean
當給定的在bean存在時,則例項化當前Bean。
@Data public class City { /** 城市名稱 */ private String cityName; /** 城市code */ private Integer cityCode; }
@Data @AllArgsConstructor @NoArgsConstructor public class People { /** 姓名 */ private String name; /** 年齡 */ private Integer age; /** 城市資訊 */ private City city; }
@ConditionalOnMissingBean
當給定的在bean不存在時,則例項化當前Bean。
@ConditionalOnClass
當給定的類名在類路徑上存在,則例項化當前Bean。原理同@ConditionalOnBean差不多。City2並不存在,所以People沒有注入成功。
@ConditionalOnMissingClass
當給定的類名在類路徑上不存在,則例項化當前Bean。結果同ConditionalOnClass相反。City2並不存在,所以People注入成功。
@ConditionalOnExpression
基於SpEL表示式作為判斷條件。當括號中的內容為true時,使用該註解的類被例項化。
@ConditionalonalOnJava
基於JVM版本作為判斷條件。只有執行指定版本的 Java 才會載入 Bean。
/** * 只有執行指定版本的 Java 才會載入 Bean */ @ConditionalOnJava(JavaVersion.EIGHT) @Bean public People people() { return new People(); }
@ConditionalOnJndi
在JNDI存在的條件下查詢指定的位置。只有指定的資源通過 JNDI 載入後才載入 bean。JNDI(Java Naming and Directory Interface,Java命名和目錄介面)。
@ConditionalOnJndi("java:comp/env/foo") @Bean public People people() { return new People(); }
@ConditionalOnWebApplication
當前專案是web專案的情況下。只有執行在 web 應用裡才會載入這個 bean。
@ConditionalOnWebApplication @Bean public People people() { return new People(); }
@ConditionalOnNotWebApplication
當前專案不是web專案的條件下。與@ConditionalOnWebApplication相反,在非 web 環境才載入 bean。
@ConditionalOnNotWebApplication @Bean public People people() { return new People(); }
@ConditionalOnResource
類路徑是否有指定的值。如果我們要載入的 bean 依賴指定資源是否存在於 classpath 中,那麼我們就可以使用這個註解。
@ConditionalOnSingleCandidate
當指定Bean在容器中只有一個,後者雖然有多個但是指定首選的Bean。
只有指定類已存在於 BeanFactory 中,並且可以確定單個候選項才會匹配成功 BeanFactory 存在多個 bean 例項,但是有一個 primary 候選項被指定(通常在類上使用 @Primary 註解),也會匹配成功。
沒有寫示例,額.....因為我還不會。有會的人可以告訴一下。我學習一下後面再補上。
方法原註釋:
@Conditional,僅當指定類的bean已包含在BeanFactory中並且可以確定單個候選時匹配。如果BeanFactory中已經包含多個匹配的bean例項,但是已經定義了一個主候選例項,那麼這個條件也將匹配;實際上,如果一個bean與定義的型別自動連線成功,那麼條件匹配。
該條件只能匹配到目前為止由應用程式上下文處理的bean定義,因此,強烈建議僅在自動配置類上使用此條件。如果候選bean可能由另一個自動配置建立,請確保使用此條件的bean在之後執行。
非同步相關
@Async
在實際執行的bean方法使用該註解來申明其是一個非同步任務(方法上或類上所有的方法都將非同步,需要@EnableAsync開啟非同步任務)。
@Async 必須不同類間呼叫: A類--》B類.C方法()(@Async註釋在B類/方法中),如果在同一個類中呼叫,會變同步執行,例如:A類.B()-->A類.@Async C(),
原因是:底層實現是代理對註解掃描實現的,B方法上沒有註解,沒有生成相應的代理類。
@Configuration @EnableAsync public class ThreadPoolConfig { private static int corePoolSize=30; private static int maxPoolSize=100; private static int queueCapacity=100; private static int keepAliveSeconds=300; @Bean public TaskExecutor jobExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 設定核心執行緒數 executor.setCorePoolSize(corePoolSize); // 設定最大執行緒數 executor.setMaxPoolSize(maxPoolSize); // 設定佇列容量 executor.setQueueCapacity(queueCapacity); // 設定執行緒活躍時間(秒) executor.setKeepAliveSeconds(keepAliveSeconds); // 設定預設執行緒名稱 executor.setThreadNamePrefix("async-job-thread-"); // 設定拒絕策略rejection-policy:當pool已經達到max size的時候,如何處理新任務 CALLER_RUNS:不在新執行緒中執行任務,而是有呼叫者所在的執行緒來執行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 等待所有任務結束後再關閉執行緒池 executor.setWaitForTasksToCompleteOnShutdown(true); return executor; } @Bean public AsyncBean asyncBean(){ return new AsyncBean(); } }
public class AsyncBean { /** * Async 必須不同類間呼叫: A類--》B類.C方法()(@Async註釋在B類/方法中), * 如果在同一個類中呼叫,會變同步執行,例如:A類.B()-->A類.@Async C() * * 如果在同一個類中呼叫,會變同步執行 */ @Async("jobExecutor") public void asyncMethod() throws InterruptedException { System.out.println(Thread.currentThread().getName()+"非同步執行"); Thread.sleep(1000); } public static void main(String[] args) throws InterruptedException { /** 如果在同一個類中呼叫,會變同步執行 */ System.out.println(Thread.currentThread().getName()+"主執行緒請求非同步執行asyncMethod"); AsyncBean asyncBean = new AsyncBean(); asyncBean.asyncMethod(); System.out.println(Thread.currentThread().getName()+"主執行緒請求非同步執行syncMethod結束"); } }
@Enable*註解說明
這些註解主要用來開啟對xxx的支援。 此處沒有程式碼示例。
@EnableAspectJAutoProxy
開啟對AspectJ自動代理的支援。
@EnableAsync
開啟非同步方法的支援。@EnableAsync 開啟spring非同步執行器,類似xml中的task標籤配置(其實是一樣的,如果同時存在還會報錯),需要聯合@Configuration註解一起使用 。
@EnableScheduling
開啟計劃任務的支援。
@EnableWebMvc
開啟Web MVC的配置支援。
@EnableConfigurationProperties
開啟對@ConfigurationProperties註解配置Bean的支援。
@EnableJpaRepositories
開啟對SpringData JPA Repository的支援。
@EnableTransactionManagement
開啟註解式事務的支援。
@EnableCaching
開啟註解式的快取支援。
定時任務相關
@EnableScheduling和@Scheduled為SpringBoot註解。這裡給出示例程式碼片段,示例程式碼中並未有示例。
@EnableScheduling
在配置類上使用,開啟計劃任務的支援(類上)。
@SpringBootApplication @EnableScheduling //開啟定時任務 public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } }
@Scheduled
來申明這是一個任務,包括cron,fixDelay,fixRate等型別(方法上,需先開啟計劃任務的支援)。
@Component public class Jobs { //表示方法執行完成後5秒 @Scheduled(fixedDelay = 5000) public void fixedDelayJob() throws InterruptedException { System.out.println("fixedDelay 每隔5秒" + new Date()); } //表示每隔3秒 @Scheduled(fixedRate = 3000) public void fixedRateJob() { System.out.println("fixedRate 每隔3秒" + new Date()); } //表示每天8時30分0秒執行 @Scheduled(cron = "0 0,30 0,8 ? * ? ") public void cronJob() { System.out.println(new Date() + " ...>>cron...."); } }
環境切換
@Profile
通過設定Environment的ActiveProfiles來設定當前context需要使用的配置環境。
package com.blackcat.annotation.profiles.config; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.PropertySource; import org.springframework.util.StringValueResolver; import javax.sql.DataSource; /** * <p> 描述 : 通過@Profile註解 來根據環境來啟用標識不同的Bean * @author : blackcat * @date : 2020/5/25 17:45 * * Profile標識在類上,那麼只有當前環境匹配,整個配置類才會生效 * Profile標識在Bean上 ,那麼只有當前環境的Bean才會被啟用 * 沒有標誌為Profile的bean 不管在什麼環境都可以被啟用 */ @Configuration @PropertySource(value = {"classpath:ds.properties"}) public class MyConfig implements EmbeddedValueResolverAware { @Value("${ds.username}") private String userName; @Value("${ds.password}") private String password; private String jdbcUrl; private String classDriver; @Override public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) { this.jdbcUrl = stringValueResolver.resolveStringValue("${ds.jdbcUrl}"); this.classDriver = stringValueResolver.resolveStringValue("${ds.classDriver}"); } // 標識為測試環境才會被裝配 @Bean @Profile(value = "test") public DataSource testDs() { return buliderDataSource(new DruidDataSource()); } // 標識開發環境才會被啟用 @Bean @Profile(value = "dev") public DataSource devDs() { return buliderDataSource(new DruidDataSource()); } // 標識生產環境才會被啟用 @Bean @Profile(value = "prod") public DataSource prodDs() { return buliderDataSource(new DruidDataSource()); } private DataSource buliderDataSource(DruidDataSource dataSource) { dataSource.setUsername(userName); dataSource.setPassword(password); dataSource.setDriverClassName(classDriver); dataSource.setUrl(jdbcUrl); return dataSource; } }
切面相關注解
Spring支援AspectJ的註解式切面程式設計。這裡只寫了示例程式碼,沒有controller呼叫過程。
@Aspect
宣告一個切面(類上) 使用@After、@Before、@Around定義建言(advice),可直接將攔截規則(切點)作為引數。
@PointCut
宣告切點 在java配置類中使用@EnableAspectJAutoProxy註解開啟Spring對AspectJ代理的支援(類上)。
Pointcut是植入Advice的觸發條件。每個Pointcut的定義包括2部分,一是表示式,二是方法簽名。方法簽名必須是 public及void型。可以將Pointcut中的方法看作是一個被Advice引用的助記符,因為表示式不直觀,因此我們可以通過方法簽名的方式為 此表示式命名。因此Pointcut中的方法只需要方法簽名,而不需要在方法體內編寫實際程式碼。
@Around
屬於環繞增強,能控制切點執行前,執行後,,用這個註解後,程式拋異常,會影響@AfterThrowing這個註解。
@AfterReturning
切點方法返回後執行。後置增強,相當於AfterReturningAdvice,方法正常退出時執行。
@Before
標識一個前置增強方法,相當於BeforeAdvice的功能。在切點方法之前執行。
@AfterThrowing
切點方法拋異常執行。異常丟擲增強,相當於ThrowsAdvice。
@After
在切點方法之後執行。 @Before 在方法執行之前執行(方法上) @Around 在方法執行之前與之後執行(方法上)。final增強,不管是丟擲異常或者正常退出都會執行。
程式碼
package com.blackcat.annotation.aspect.component; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * <p> 描述 : aop通知註解 * @author : blackcat * @date : 2020/8/3 16:30 */ @Aspect @Component public class UserAspect { /** * @Pointcut 定義一個切點,避免重複引用 */ @Pointcut("execution(* com.blackcat.annotation.aspect.component.UserServiceImpl.test(..))") public void print(){} @Before("print()") public void before(){ System.out.println("我是前置通知"); } @After("print()") public void After(){ System.out.println("我是後置通知"); } @AfterReturning("print()") public void AfterReturning(){ System.out.println("我是返回通知"); } @AfterThrowing("print()") public void AfterThrowing(){ System.out.println("我是異常通知"); } }
public interface UserService { void test(); }
@Service public class UserServiceImpl implements UserService { @Override public void test() { System.out.println("測試"); } }
@Controller public class TestAction { @Autowired private UserService userService; @RequestMapping("/test.do") public String info(HttpServletRequest request, HttpServletResponse response){ userService.test(); return "index"; } }
當呼叫 /test.do 時,會列印一下內容。
本節程式碼示例參考:https://blog.csdn.net/qq_34775355/article/details/88431247
示例程式碼
https://gitee.com/kylin_lawliet/learn-spring