SpringBoot - 全注解下的SpringIOC
springboot 是基于 springframework的全注解的web框架,它無需配置xml逮诲,所以相比較于spring+springmvc它可以節(jié)省更多的開發(fā)周期
什么是IOC(Inversion of Controller)帜平?
IOC即是控制反轉(zhuǎn),把創(chuàng)建對象的控制權(quán)交給第三方則叫反轉(zhuǎn)梅鹦。正轉(zhuǎn)則是一般性的j2ee new對象式的開發(fā)裆甩,多個(gè)Bean的容器則叫做IOC容器。反轉(zhuǎn)大大降低了代碼的耦合性齐唆。
準(zhǔn)備 - @Configuration @Bean的簡單使用
@Configuration
public class Appconfig{
@Bean(name="user")
public User initUser(){
User user = new User();
user.setId(1L);
user.setUserName("user_name_1");
user.setNote("note_1");
return user;
}
}
@Configuration 代表這是一個(gè)java配置文件嗤栓,Spring的容器會根據(jù)它來生成IOC容器去裝配Bean
@Bean 代表將initUser方法返回的POJO裝配到IOC容器中,而其屬性name定義這個(gè)bean的名稱箍邮,如果沒有配置它抛腕,則將方法名稱"initUser"作為Bean的名稱裝配到Spring IOC容器中。
使用實(shí)例:
public class IOCTest{
private static Logger log = Logger.getLogger(IOCTest.class);
public static void main(String[] args){
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
User user = ctx.getBean(User.class);
log.info(user.getId());
}
}
起步 - 如何去裝配Bean
1.掃描裝配Bean
因?yàn)槿绻聾Bean去一個(gè)個(gè)裝配的話會顯得很繁瑣媒殉,所以推薦使用掃描裝配担敌。
@Component:標(biāo)明哪個(gè)類被掃描進(jìn)入Spring IOC容器。
@ComponentScan:標(biāo)明采用何種策略去掃描裝配Bean廷蓉。
@Component("user")
public class user{
@Value("1")
private Long id;
@Value("user_name_1")
private String usename;
@Value("note_1")
private String note;
/**setter and getter**/
}
@Component表示這個(gè)類將會被SpringIOC容器掃描裝配全封,其中配置的"user"則是作為Bean的名稱。為了讓Spring IOC容器裝配這個(gè)類桃犬,需要改造類APPConfig
@Configuration
@ComponentScan
public class Appconfig{
}
這里加入@ComponentScan意味著它會進(jìn)行掃描刹悴,但是它只會掃描類APPConfig所在的當(dāng)前包和其子包(還可以指定掃描包,看下)攒暇。所以也要把User.java移動(dòng)一下位置土匀。
測試
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
User user = ctx.getBean(User.class);
log.info(user.getId());
@ComponentScan掃描指定包:
@ComponentScan("com.springboot.demo.*")
@ComponentScan(basePackage={"com.springboot.demo.pojo"})
@ComponentScan(basePackageClasses = {User.class})
Q:當(dāng)標(biāo)注了@Service的類在被掃描的包當(dāng)中,則如何使它不被掃描形用?
A:還是使用@ComponentScan就轧,不過另加一個(gè)參數(shù)即可:
@ComponentScan("com.springboot.demo.*",excludeFilters = {@Filter(classes = {Service.class})})
2.裝配第三方Bean
當(dāng)我們需要引入第三包的時(shí)候证杭,很可能 希望把第三方包的類對象也放入到SpringIOC的容器中,這是使用@Bean注解就ok了妒御。
舉栗(引入DBCP數(shù)據(jù)源):
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
@Bean(name="dataSource")
public DataSource getDataSource() {
Properties props = new Properties();
props.setProperty("driver", "com.mysql.jdbc.Driver");
props.setProperty("url", "jdbc:mysql://localhost:3306/demo");
props.setProperty("username", "root");
props.setProperty("password", "123456");
DataSource dataSource = null;
try {
dataSource = BasicDataSourceFactory.createDataSource(props);
} catch (Exception e) {
e.printStackTrace();
}
return dataSource;
}
加深 - 依賴注入(Dependency Injection)
依賴注入就是將實(shí)例變量傳入到一個(gè)對象中去解愤。(Dependency injection means giving an object its instance variables)
- 控制反轉(zhuǎn)是一種思想
- 依賴注入是一種設(shè)計(jì)模式
IoC框架使用依賴注入作為實(shí)現(xiàn)控制反轉(zhuǎn)的方式,但是控制反轉(zhuǎn)還有其他的實(shí)現(xiàn)方式乎莉,例如說ServiceLocator送讲,所以不能將控制反轉(zhuǎn)和依賴注入等同。
1. @Autowired
它是Spring中最常用的注解之一惋啃,十分重要哼鬓,它會根據(jù)屬性的類型(by type)找到對應(yīng)對的Bean進(jìn)行注入。
栗子:
//動(dòng)物接口
public interface Animal {
public void use();
}
//人類接口
public interface Person {
//使用動(dòng)物服務(wù)
public void service();
//設(shè)置動(dòng)物
public void setAnimal(Animal animal);
}
public class Dog implements Animal{
@Override
public void use() {
System.out.println("狗【"+Dog.class.getSimpleName()+"】是看門的");
}
}
public class BussinessPerson implements Person {
@Autowired
private Animal animal = null;
@Override
public void service() {
this.animal.use();
}
@Override
public void setAnimal(Animal animal) {
this.animal = animal;
}
}
如上边灭,spring容器會通過注解@Autowired將Dog注入到BussinessPerson實(shí)例中
Q:但是魄宏,加入又有一個(gè)動(dòng)物類cat,那么它會選擇哪個(gè)注入?怎么解決這個(gè)錯(cuò)誤存筏?
A: 因?yàn)?@Autowired首先會根據(jù)類型找到對應(yīng)的Bean,如果類型不是唯一的味榛,那么它會根據(jù)其屬性名稱和Bean的名稱進(jìn)行匹配椭坚。如果匹配得上,就用該Bean搏色,如果還是無法匹配就會拋出異常善茎。
@Autowired
private Animal dog = null;
因?yàn)閷nimal修改為了dog,所以它會找到dog類
注:
- 因?yàn)锧Autowired是一個(gè)默認(rèn)必須要找到對應(yīng)Bean的注解频轿,所以如果不能確定其標(biāo)注屬性是否存在垂涯,或者允許這個(gè)被標(biāo)注的屬性為null。則可以配置@Autowired屬性required為false航邢。
@Autowired(required =false)
- 它除了可以標(biāo)注屬性外還可以標(biāo)注方法耕赘。甚至還可以使用在方法參數(shù)上
@Override
@Autowired
public void setAnimal(Animal animal) {
this.animal = animal;
}
它會使用setAnimal方法從IOC容器中找到對應(yīng)的動(dòng)物進(jìn)行注入。
2. @Primary和@Quelifier
@Primary:它是一個(gè)修改優(yōu)先權(quán)的注解膳殷,比如上面的例子操骡,當(dāng)我們有貓有狗時(shí),假設(shè)這次使用貓赚窃,name只需要在貓類的定義上加入@Primary就可以了
@Component
@Primary
public class Cat implements Animal{
......
}
這里的@Primary的含義告訴SpringIOC容器册招,當(dāng)發(fā)現(xiàn)有多個(gè)同類型的Bean時(shí),請優(yōu)先使用我進(jìn)行注入.
Q: 如果@Primary作用在多個(gè)類上,其結(jié)果是IOC容器還是無法區(qū)分采用哪個(gè)Bean的實(shí)例進(jìn)行注入,那么該采取什么樣的情況呢?
A: 可以使用@Quelifier結(jié)合@Autowired一起使用勒极,則可以通過類型和名稱一起查找Bean是掰。
@Autowired
@Quelifier("dog")
private Animal animal = null;
通過上面的代碼,即使cat已經(jīng)標(biāo)注了@Primary辱匿,但是我們還是可以拿到dog提供服務(wù)键痛。
3. 帶參數(shù)的構(gòu)造方法實(shí)現(xiàn)注入
public class BussinessPerson implements Person {
private Animal animal = null;
public BussinessPerson(@Autowired @Quelifier("dog") Animal animal){
this.animal = animal;
}
@Override
public void service() {
this.animal.use();
}
@Override
public void setAnimal(Animal animal) {
this.animal = animal;
}
}
上面取消了對animal的@Autowired注解而是在構(gòu)造參數(shù)中加入了@Autowired@Quelifier注解