1. 程式人生 > 程式設計 >Spring一些註解配置和類的解讀

Spring一些註解配置和類的解讀

利用Maven匯入座標

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.1.9.RELEASE</version>
</dependency>複製程式碼

因為spring-context依賴於Spring其他幾個核心模組的Jar包,所以使用Spring,匯入此座標即可

例項化容器的方法

  • 通過ClassPathXmlApplicationContext
    獲取容器,這個方法需要配置applicationConfig.xml來配合初始化容器中的Bean
    • ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
      context.refresh();複製程式碼
    • <!-- applicationConfig.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 建立專案根目錄下的src/main/resources目錄,這是maven專案約定的資原始檔目錄。 --> <!-- 建立resources目錄後,記得reimport Maven Project,否則idea識別不了。 --> <!-- 在resources目錄下,使用idea建立spring context的xml配置檔案。 --> <bean id="customerBean"
      class="cn.edu.dgut.sai.Customer"> <property name="name" value="hello"/> </bean> </beans>複製程式碼

  • 通過AnnotationConfigApplicationContext獲取容器,這個方法需要配置一個 配置類 來配合初始化容器中的Bean
    • AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
      context.refresh();複製程式碼
    • /**
      * Configuration:此類為配置類
      * ComponentScan:告知容器掃描此類所在包所有的類以及子包下所有的類的註解
      */
      @Configuration
      @ComponentScan
      public class Config {
          
      }複製程式碼

註冊Bean

使用Component在類上面註解,容器進行掃描時,容器自動將此類註冊為Bean

@Component
public class Player {
    private String name;
    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }


    public void setAge(String age) {
        this.age = age;
    }
}複製程式碼

此外,還可以用@Service@Controller等註解配置Bean,區別在於Component是通用註解,而Service則是適用與Service業務層,Controller適用於Web檢視層

動態註冊Bean

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
context.registerBean("UserBean",User.class);
context.refresh();複製程式碼

使用Junit進行測試

使用Junit進行測試,不再需要機械重複地建立一個類,然後加入main方法,然後在main方法獲取容器例項ApplicationContext

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = Config.class)
public class SpringTest {
    @Test
    public void testUser() {
        
    }
}複製程式碼

classes需要填寫配置類的位元組碼檔案

Bean的相關生命週期回撥介面:InitializingBean、DisposableBean

當一個類成為Bean,繼承InitializingBean介面並且實現介面的afterPropertiesSet方法的時候,當該Bean被容器註冊並且例項化後的時候,afterPropertiesSet方法就會被呼叫(回撥)。

同樣的,當一個類成為Bean,繼承了DisposableBean介面並且實現介面的destroy方法的時候,當該Bean被銷燬之前,destroy方法就會自動被呼叫(回撥)

public class User implements InitializingBean,DisposableBean {
    private String name;
    private String sex;

    public User() {
    }
	
	@Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet回撥");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("User: destroy");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
    
}
複製程式碼

ApplicationContextAware

這個介面的作用是令Bean能夠獲取容器的例項,進而通過程式設計的方法進行操作,例如下面,User Bean希望獲取其他Customer Bean,就建立一個成員變數,通過這個介面獲取容器例項,用成員變數儲存容器例項,進而獲取Customer Bean。

public class User implements InitializingBean,DisposableBean,ApplicationContextAware {
    private String name;
    private String sex;

    private ApplicationContext context;

    public User() {
        System.out.println("初始化");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("User: destroy");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet回撥");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("applicationContext注入");
        this.context = applicationContext;
        Customer customerBean = (Customer)this.context.getBean("customerBean");
        System.out.println(customerBean);
    }
}複製程式碼

@Lazy

此註解主要作用是懶載入,一個Bean被ApplicationContext註冊的時候,不會立即例項化,需要等到呼叫的時候,才會被例項化

@Scope

定義Bean的範圍,一般是選值為singletonprototypesingleton表示為單例,prototype表示為多例

BeanPostProcessor

回撥函式,當一個類註冊為Bean,並且繼承BeanPostProcessor介面,實現其介面的postProcessBeforeInitializationpostProcessAfterInitialization方法的時候,在這個容器中,只要有Bean註冊例項化,就會自動呼叫此介面的兩個方法。有多少個Bean註冊例項化,就呼叫多少次postProcessBeforeInitialization會在Bean註冊例項化之前自動被呼叫,postProcessAfterInitialization會在Bean註冊例項化被自動呼叫。可以從這兩個方法之中的引數,獲取Bean的例項和註冊為Bean的命名

注意:當使用context.register方法動態註冊Bean的時候,Bean並不會例項化,不會觸發InitializingBean、ApplicationContextAware等回撥函式。當使用getBean的方法的時候,即初始化、例項化Bean,就會觸發Spring容器呼叫該Bean相關介面的呼叫

@Component和@Bean的區別

相同@Component@Bean都是註冊Bean

區別@Component被動的方式,等待Spring容器掃描註解,將類註冊為Bean,而@Bean則是主動的方式,主動地將類註冊為Spring容器的Bean。

用法:@Component是通用寫法,而@Bean通常用於引入外部類(從外部匯入Jar包),又想將此類註冊為Bean,但是又沒有辦法修改外部類的原始碼,就可以利用@Bean將外部類註冊成為Bean

@Configuration
@ComponentScan
public class Config {

    @Bean("customerBean")
    public Customer createCustomer() {
        Customer customer = new Customer();
        return customer;
    }
}複製程式碼

依賴注入Bean

注意:依賴的注入,需要依賴已經成為Bean被Spring容器管理

@Autowired

@Ccomponent
public class User{

    @Autowired
    private Player player;
}複製程式碼

建構函式

@Component
public class User {

    private final Player player;

    public User (Player player) {        System.out.println("Config構造方法:" + player);
        this.player = player;
    }

}複製程式碼

setter方法

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}複製程式碼

通過@Autowired注入ApplicationContext

public class SimpleMovieLister {
    @Autowired
    private ApplicationContext context;

    private MovieFinder movieFinder;

    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}複製程式碼

@Primary

根據型別自動注入Bean,當有兩個相同型別的候選者,優先注入有@Primary註解的類

@Configuration
public class MovieConfiguration {

    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }

}複製程式碼

@import

在一個配置類裡面,引入其他配置類

@Configuration
@Import({TestConfig.class,DataSourceConfig.class})
public class ImportConfig {
}複製程式碼

@DependsOn

表明某個Bean需要依賴其他Bean

@Configuration
public class TestConfig {

    @Bean("depend")
    public AccountRepository createJdbcAccountRepo() {
        return new JdbcAccountRepository();
    }

    @Bean
    @DependsOn("depend")
    public TransferService createTransferSvc() {
        return new TransferServiceImpl(createJdbcAccountRepo());
    }

}複製程式碼

Environment

通過Environment可以獲取JVM屬性集

ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsMyProperty = env.containsProperty("my-property");
System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);複製程式碼

獲取Spring容器的環境物件,判斷JVM是否存在my-property屬性,如果存在則返回true,否則返回flase

System.getProperties()表示JVM系統屬性集,System.getenv()表示系統環境變數集。可以用此功能來判斷使用者環境變數是否存在某個屬性值,判斷是否存在然後進行相應的操作

可以使用System.getProperties().put(key,value)來自動存入自定義JVM屬性集

也可以使用@PropertySource,新增Property到Spring的Enviroment

自定義Spring註解

通過Resource獲取本地或者網路資源

public void testResourceLoader() throws IOException {

	// 本地資源可以使用類路徑載入
	Resource resource = ctx.getResource("https://www.baidu.com/img/bd_logo1.png");


	Path path = Paths.get("C:\Users\admin\Desktop\java");

	Files.createDirectories(path);

	//@formatter:off
	Files.copy(
			resource.getInputStream(),path.resolve(UUID.randomUUID().toString() + resource.getFilename()),StandardCopyOption.REPLACE_EXISTING
	);
	//@formatter:on
}複製程式碼

注意:使用此處的Paths和Resource需要使用JDK11以上

自定義事件

Spring的容器ApplicationContext是通過ApplicationEvent類和ApplicationListener介面處理事件。ApplicationEvent用於定義事件,ApplicationListener用於監聽事件。

建立自定義事件

建立自定義事件,需要建立一個自定義事件的類,繼承ApplicationEvent類

public class MyEvent extends ApplicationEvent {
    private String name;
    private String address;

    public MyEvent(Object source,String name,String address) {
        super(source);
        this.name = name;
        this.address = address;
    }
}複製程式碼

釋出自定義事件

釋出一個自定義事件,需要建立一個類,實現ApplicationEventPublisherAware介面並將其註冊為Spring Bean的類,然後呼叫ApplicationEventPublisher釋出事件。這裡,我選擇用EventConfig的配置類實現介面,進而將ApplicationEventPublisher注入到Spring Bean,最後會通過測試類來呼叫該方法釋出事件

@Configuration
public class EventConfig implements ApplicationEventPublisherAware{

    private ApplicationEventPublisher publisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    @EventListener({MyEvent.class})
    public void publisCustomEvent(MyEvent event) {
        System.out.println("--------開始監聽事件---------");
        System.out.println("--獲取自定義事件--"+event);
        System.out.println("--------處理事件-----"+event.getSource());
    }
}複製程式碼

監聽自定義事件

需要監聽自定義事件的執行,則需要建立一個實現ApplicationListener並註冊為SpringBean的類,在這裡我選擇使用基於註解的事件監聽器類在EventConfig配置類實現該功能

@Configuration
public class EventConfig implements ApplicationEventPublisherAware{

    private ApplicationEventPublisher publisher;


    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    @EventListener({MyEvent.class})
    public void publisCustomEvent(MyEvent event) {
        System.out.println("--------開始監聽事件---------");
        System.out.println("--獲取自定義事件--"+event);
        System.out.println(event.getSource());
        System.out.println(event.getName()+":"+event.getAddress());
        System.out.println("--------處理事件-----");

    }
}複製程式碼

測試類釋出事件

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {ImportConfig.class})
public class EventTest {
    @Autowired
    private ApplicationEventPublisher publisher;

    @Autowired
    private AopAdvice aopAdvice;

    @Test
    public void testEvent() {
        System.out.println("開始釋出事件");
        publisher.publishEvent(new MyEvent("這是一個自定義事件噢","Test","測試自定義監聽"));
        System.out.println("結束髮布事件");
    }
}複製程式碼

AOP

匯入座標

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
	<version>5.1.9.RELEASE</version>
</dependency>複製程式碼

開啟AOP支援

在Spring配置類基於註解@EnableAspectJAutoProxy開啟AOP支援

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}複製程式碼

宣告一個切面

使用@Aspect表明這個類為切面類

@Aspect
public class NotVeryUsefulAspect {

}複製程式碼

通知型別

前置通知 @Before

後置通知 @AfterReturing

異常通知 @AfterThrowing

最終通知 @After

環繞通知 @Around

切入點表示式

  • 執行任何公共方法
    • execution(public * *(..))
  • 執行名稱為set開頭的所有方法
    • execution(* set*(..))
  • 執行AccountService介面定義的任何方法
    • execution(* cn.edu.dddd.AccountService.* (..))
  • 執行service包中定義的任何方法
    • execution(* cn.edu.dddd.service.*.* (..)
  • 執行service包及其子包的任何方法
    • execution(* cn.edu.dddd.service..*.* ( . . ))