9. 由Spring管理的對象的生命周期
如果需要管理Bean的生命周期,可以在對應(yīng)的類中自定義生命周期的初始化方法和銷毀方法,關(guān)于這2個(gè)方法的聲明:
- 應(yīng)該使用
public
權(quán)限; - 使用
void
表示返回值類型; - 方法名稱可以自定義;
- 參數(shù)列表為空。
例如:
package cn.tedu.spring;
public class User {
public User() {
System.out.println("User.User()");
}
public void init() {
System.out.println("User.init()");
}
public void destroy() {
System.out.println("User.destroy()");
}
}
在配置Spring管理對象的@Bean
注解中,配置注解參數(shù),以指定以上2個(gè)方法分別是初始化方法和銷毀方法:
package cn.tedu.spring;
@Configuration
public class BeanFactory {
@Bean(initMethod = "init", destroyMethod = "destroy")
public User user() {
return new User();
}
}
最終,可以看到:
- 初始化方法會在構(gòu)造方法之后執(zhí)行,且只執(zhí)行1次;
- 銷毀方法會在Spring容器被銷毀之前執(zhí)行,且只執(zhí)行1次。
10. 使用組件掃描使得Spring管理類的對象
首先,自定義某個(gè)類(類名、包名均沒有要求),在類的聲明之前添加@ComponentScan
注解,該注解用于配置組件掃描,注解的參數(shù)是String
類型的,表示“被掃描的根包”:
package cn.tedu.spring;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan("cn.tedu.spring")
public class SpringConfig {
}
在組件掃描的包下創(chuàng)建類,該類的聲明之前需要添加@Component
注解,以表示這個(gè)類是一個(gè)“組件類”,后續(xù),當(dāng)Spring掃描時(shí),會自動創(chuàng)建所有組件類的對象:
package cn.tedu.spring;
import org.springframework.stereotype.Component;
@Component
public class User {
}
當(dāng)完成以后配置后,后續(xù),程序執(zhí)行時(shí),只要加載了SpringConfig
類,由于類之前配置了組件掃描,Spring框架就會掃描對應(yīng)的包下所有的類,并逐一檢查是否為“組件類”,如果是,則創(chuàng)建對象,如果不是,則不創(chuàng)建!
使用@ComponentScan
時(shí),配置的是需要掃描的“根包”,假設(shè)需要掃描的是cn.tedu.spring
,在配置時(shí),配置為cn.tedu
甚至配置為cn
都是可用的,但是,強(qiáng)烈不推薦使用過于簡單的設(shè)置,避免出現(xiàn)掃描范圍過多而導(dǎo)致的浪費(fèi)資源!
另外,在@ComponentScan
注解的源代碼中:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
}
可以看出,配置的值可以是String[]
,也就是可以指定多個(gè)包名。
在使用這種做法時(shí),必須保證被Spring管理的對象所歸屬的類存在無參數(shù)構(gòu)造方法!
在使用這種做法時(shí),Spring創(chuàng)建對象后,默認(rèn)會使用以下原則作為Bean的名稱:
- 如果類名的第1個(gè)字母是大寫的,第2個(gè)字母是小寫的(不關(guān)心其它字母的大小寫),則會把類名的第1個(gè)字母改為小寫,其它不變,作為Bean的名稱,例如類名是
User
時(shí),Bean的名稱就是user
,類名是UserDao
時(shí),Bean的名稱就是userDao
; - 如果不滿足以上條件,則類名就是Bean的名稱。
如果希望使用自定義的名稱作為Bean的名稱,可以在@Component
注解中配置參數(shù),例如:
package cn.tedu.spring;
import org.springframework.stereotype.Component;
@Component("uuu")
public class User {
}
則后續(xù)調(diào)用getBean()
方法時(shí),就必須使用"uuu"
作為參數(shù)來獲取對象!
在Spring框架的作用范圍內(nèi),除了@Component
以外,另外還有3個(gè)注解,可以起到完全等效的效果:
@Controller
:通常添加在控制器類的聲明之前;@Service
:通常添加在業(yè)務(wù)類的聲明之前;@Repository
:通常添加在持久層的類(負(fù)責(zé)數(shù)據(jù)的持久化管理)的聲明之前。
也就是說,這4種注解作用、用法完全相同,只是語義不同。
目前,已經(jīng)介紹了2種使得Spring框架管理類的對象的做法:
- 自定義方法返回某個(gè)對象,并在方法的聲明之前添加
@Bean
注解; - 將類放在組件掃描的包或其子孫包中,并在類的聲明之前添加
@Component
/@Controller
/@Service
/@Repository
注解。
以上的第1種做法是萬能的,適用于任何條件,但是,在設(shè)計(jì)代碼時(shí)相對麻煩,管理起來相對不便利;而第2種做法就更加簡單、直觀,卻只適用于自定義的類。
所以,只要是自行編寫的類,都應(yīng)該采取第2種做法,如果需要Spring管理其它類(JDK中的,或某框架中的)的對象,只能使用第1種做法!
11. 使用Spring讀取.properties文件
假設(shè)在項(xiàng)目的src/main/resources下存在jdbc.properties文件,其內(nèi)容是:
url=jdbc:mysql://localhost:3306/db_name
driver=com.mysql.jdbc.Driver
然后,在項(xiàng)目中,自定義某個(gè)類,在這個(gè)類中,聲明對應(yīng)數(shù)量的屬性,這些屬性的值將會是以上配置信息的值!
public class JdbcProperties {
private String url;
private String driver;
// 生成以上2個(gè)屬性的Getters & Setters
}
當(dāng)需要讀取以上jdbc.properties配置文件時(shí),需要在以上類的聲明之前添加@PropertySource
注解,并配置需要讀取的文件的位置:
// 以下注解的參數(shù)是配置文件的名稱
@PropertySource("jdbc.properties")
public class JdbcProperties {
private String url;
private String driver;
// 生成以上2個(gè)屬性的Getters & Setters
}
接下來,就可以把讀取到的值賦值給類中的2個(gè)屬性,可以通過@Value
注解來實(shí)現(xiàn):
// 以下注解的參數(shù)是配置文件的名稱
@PropertySource("jdbc.properties")
public class JdbcProperties {
@Value("${url}") // 在注解參數(shù)的大括號的值,是jdbc.properties配置中等于號左側(cè)的名稱
private String url;
@Value("${driver}")
private String driver;
// 生成以上2個(gè)屬性的Getters & Setters
}
最后,整個(gè)的讀取過程是由Spring框架來完成的,所以,以上JdbcProperties
類還應(yīng)該被Spring框架所管理,可以采取組件掃描的做法,則創(chuàng)建SpringConfig
類,用于指定組件掃描的包:
// 以下注解參數(shù)配置的就是組件掃描的包,同時(shí),請保證JdbcProperties類是在這個(gè)包或其子孫包中的
@ComponentScan("cn.tedu.spring")
public class SpringConfig {
}
然后,在JdbcProperties
類的聲明之前,補(bǔ)充添加@Component
注解,使得Spring框架掃描到這個(gè)類時(shí),能明確的知道“這個(gè)類是組件類”,從而創(chuàng)建該類的對象:
@Component
// 以下注解的參數(shù)是配置文件的名稱
@PropertySource("jdbc.properties")
public class JdbcProperties {
@Value("${url}") // 在注解參數(shù)的大括號的值,是jdbc.properties配置中等于號左側(cè)的名稱
private String url;
@Value("${driver}")
private String driver;
// 生成以上2個(gè)屬性的Getters & Setters
}
全部完成后,可以自定義某個(gè)類,用于測試運(yùn)行:
package cn.tedu.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringTests {
public static void main(String[] args) {
// 1. 加載配置類,得到Spring容器
AnnotationConfigApplicationContext ac
= new AnnotationConfigApplicationContext(SpringConfig.class);
// 2. 從Spring容器中獲取對象
JdbcProperties jdbcProperties
= (JdbcProperties) ac.getBean("jdbcProperties");
// 3. 測試
System.out.println(jdbcProperties.getUrl());
System.out.println(jdbcProperties.getDriver());
// 4. 關(guān)閉
ac.close();
}
}
注意:在類似于jdbc.properties這樣的配置文件中,如果某個(gè)屬性的名稱是username
,且最終項(xiàng)目是在Windows操作系統(tǒng)的平臺上運(yùn)行時(shí),讀取到的值將是“當(dāng)前登錄Windows系統(tǒng)的系統(tǒng)用戶名稱”,而不是jdbc.properties文件中配置的屬性值!所以,一般推薦在編寫jdbc.properties這類配置文件時(shí),各屬性之前最好都添加一些特有的前綴,使得屬性名一定不與某些關(guān)鍵名稱發(fā)生沖突,例如:
project.jdbc.url=jdbc:mysql://localhost:3399/db_name
project.jdbc.driver=com.mysql.jdbc.Driver
project.jdbc.username=root
project.jdbc.password=1234
并且,在使用@Value
注解時(shí),也配置為以上各個(gè)等于號左側(cè)的完整名稱:
@Component
@PropertySource("jdbc.properties")
public class JdbcProperties {
@Value("${project.jdbc.url}")
private String url;
@Value("${project.jdbc.driver}")
private String driver;
@Value("${project.jdbc.username}")
private String username;
@Value("${project.jdbc.password}")
private String password;
// Getters & Setters
}
最后,使用Spring框架時(shí),如果屬性的值是由Spring框架進(jìn)行賦值的,Spring框架會自動的處理數(shù)據(jù)類型的轉(zhuǎn)換,所以,在聲明屬性時(shí),聲明為所期望的類型即可,例如,在配置文件中存在:
project.jdbc.initialSize=5
project.jdbc.maxTotal=20
這2個(gè)屬性分別表示“初始化連接數(shù)”和“最大連接數(shù)”,應(yīng)該是數(shù)值類型的,在類中聲明屬性時(shí),就可以使用int
或Integer
類型:
@Value("${project.jdbc.initialSize}")
private int initialSize;
@Value("${project.jdbc.maxTotal}")
private int maxTotal;
當(dāng)然,必須保證類型的轉(zhuǎn)換是可以成功的,例如數(shù)字5
既可以轉(zhuǎn)換為String
,又可以是int
或Integer
,所以,聲明以上initialSize
時(shí),這幾個(gè)數(shù)據(jù)類型都是可用的,根據(jù)使用需求進(jìn)行選取即可!
另外,還有另一種做法讀取**.properties**類型的文件,就是使用@Autowired
注解為Environment
類型的屬性自動賦值:
@Component
@PropertySource("jdbc.properties")
public class JdbcProperties {
@Autowired
private Environment environment;
public Environment getEnvironment() {
return environment;
}
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
最終,測試運(yùn)行:
package cn.tedu.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringTests {
public static void main(String[] args) {
// 1. 加載配置類,得到Spring容器
AnnotationConfigApplicationContext ac
= new AnnotationConfigApplicationContext(SpringConfig.class);
// 2. 從Spring容器中獲取對象
JdbcProperties jdbcProperties
= (JdbcProperties) ac.getBean("jdbcProperties");
// 3. 測試
System.out.println(jdbcProperties.getEnvironment().getProperty("project.jdbc.url"));
System.out.println(jdbcProperties.getEnvironment().getProperty("project.jdbc.driver"));
System.out.println(jdbcProperties.getEnvironment().getProperty("project.jdbc.username"));
System.out.println(jdbcProperties.getEnvironment().getProperty("project.jdbc.password"));
System.out.println(jdbcProperties.getEnvironment().getProperty("project.jdbc.initialSize"));
System.out.println(jdbcProperties.getEnvironment().getProperty("project.jdbc.maxTotal"));
// 4. 關(guān)閉
ac.close();
}
}
可以看到,使用這種做法時(shí),Spring框架會把讀取到的所有配置信息都封裝到了Environment
類型的對象中,當(dāng)需要獲取某個(gè)配置值時(shí),調(diào)用Environment
對象的getProperty()
方法再獲取,同時(shí),getProperty()
方法返回的是String
類型的數(shù)據(jù),如果希望的數(shù)據(jù)類型不是String
,則需要開發(fā)人員自行轉(zhuǎn)換類型!
一般,還是推薦使用@Value
注解逐一讀取各配置值,使用起來更加靈活一些!
抽象類與接口的區(qū)別
1. 共同點(diǎn)
都可以包含抽象方法;
2. 區(qū)別
- 抽象類是一種“類”,是使用
class
作為關(guān)鍵字來聲明的;而接口是另一種數(shù)據(jù),是使用interface
作為關(guān)鍵字來聲明的; - 抽象類中可以有各種權(quán)限不同、修飾符不同的屬性,也可以包含普通方法、抽象方法,或者完全沒有普通方法,或者完全沒有抽象方法;而接口中的所有成員都是
public
的,所有屬性都是static
、final
的,在JDK 1.8之前,所有的方法都是抽象的; - 普通的類與抽象類的關(guān)系是“繼承”的關(guān)系,當(dāng)普通的類繼承了抽象類后,就有義務(wù)重寫抽象類中的抽象方法,在Java語句中,類之間的繼承是1對1的關(guān)系;普通的類與接口的關(guān)系是”實(shí)現(xiàn)“的關(guān)系,當(dāng)普通的類實(shí)現(xiàn)了接口后,也有義務(wù)重寫接口中的所有抽象方法,類與接口的實(shí)現(xiàn)關(guān)系是1對多的,即1個(gè)類可以同時(shí)實(shí)現(xiàn)若干個(gè)接口;接口與接口之間也可以存在繼承關(guān)系,且是1對多的關(guān)系,即某1個(gè)接口可以同時(shí)繼承若干個(gè)接口;
3. 使用心得 / 裝
類,是描述”類別“的;接口,是描述形為模式、行為特征、規(guī)范、標(biāo)準(zhǔn)的!
類與類之間是is a
的關(guān)系;類與接口之間是has a
的關(guān)系。
public class Person { public String name; }
public class Student extends Person {}
public class Teacher extends Person {}
public class Animal { }
public class Cat extends Animal {}
public interface 學(xué)習(xí) { void 學(xué)習(xí)(某參數(shù)); }
public interface 授課 {}
public interface 駕駛 { void 駕駛(某參數(shù)); }
public class Person implements 學(xué)習(xí), 授課, 駕駛 {}
Person 張三 = new Person();
Person 李四 = new Person();
附1:Eclipse常用快捷鍵
Ctrl + Shift + F | 格式化代碼(代碼排版) |
---|
Ctrl + Shift + O | 整理import 語句(增加所必須的,刪除不必要的) |
Alt + 方向上/方向下 | 移動單行代碼,操作之前需要將光標(biāo)定位在那一行;移動若干行代碼,操作之前需要先選中 |
Ctrl + Alt + 方向上/方向下 | 向上/向下復(fù)制若干行代碼,操作模式與移動整行代碼相同 |
Alt + Shift + R | 在當(dāng)前源文件中,對某個(gè)變量、方法重命名,操作之前需先選中整個(gè)名稱 |
Ctrl + D | 刪除整行或若干行代碼,操作模式與移動整行代碼相同 |