Spring通過一種稱作控制反轉(IoC)的技術促進了鬆耦合。當應用了IoC,一個對象依賴的其它對象會通過被動的方式傳遞進來,而不是這個對象自己創建或者查找依賴對象。你可以認爲IoC與JNDI相反——不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動將依賴傳遞給它。
IoC:控制反轉,控制權的轉移,應用程序本身不負責依賴對象的創建和維護,而是由外部容器負責創建和維護。反轉即獲得依賴對象的過程被反轉了,控制被反轉後,獲得依賴對象的過程由自身管理變爲了由IoC容器主動注入。
DI:依賴注入,是IoC的一種實現方式。即是由IoC容器在運行期間,動態地將某種依賴關係注入到對象之中。
目的:創建對象並且組裝對象之間的關係。
Spring注入是指在啓動Spring容器加載bean配置的時候,就完成對變量的賦值行爲
1.設值注入
2.構造注入
設值注入:property,類中要有set方法
若property中的name屬性爲injectionDao,其在執行的時候就是調用setInjectionDao()這個方法
構造注入:constructor-arg,類中要有構造方法,且要傳入對應的參數
若constructor-arg中的name屬性爲injectDao,則類中的構造方法的參數名必須爲injectDao
//設值注入
<bean id="injectionService" class="com.lmr.spring.ioc.injection.service.InjectionServiceImpl">
<property name="injectionDao" ref="injectionDao"></property>
</bean>
//構造注入
<bean id="injectionService" class="com.lmr.spring.ioc.injection.service.InjectionServiceImpl">
<constructor-arg name="injectionDao" ref="injectionDao"></constructor-arg>
</bean>
public class InjectionServiceImpl implements InjectionService{
private InjectionDao injectionDao;
//設值注入
// public void setInjectionDao(InjectionDao injectionDao) {
// this.injectionDao = injectionDao;
// }
//構造器注入
public InjectionServiceImpl(InjectionDao injectionDao) {
// TODO Auto-generated constructor stub
this.injectionDao=injectionDao;
}
@Override
public void save(String arg) {
// TODO Auto-generated method stub
//模擬業務操作
System.out.println("Service層接收數據: "+arg);
injectionDao.save(arg);
}
}
Bean的作用域 Scope
參數:
singleton 單例,指一個Bean容器中只存在一份(若對一個Bean實例化兩個對象,則前者的內容會被後者覆蓋,即兩個對象的值都相同,引用的是同一個內存塊)
prototype 每次請求(每次使用)創建新的實例,destroy方式不生效
request 每次http請求創建一個實例且僅在當前request內有效
session 同上,每次http請求創建,當前session內有效
global session 基於portlet的web中有效(portlet定義了global session),如果是在web中,同session
測試singleton和prototype作用域
public class BeanScope {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void print(){
System.out.println(this.hashCode()+" - - "+this.id);
}
public void say(){
System.out.println("BeanScope say : "+this.hashCode());//通過hashCode()方法來判斷通過容器生成的對象是否爲同一個對象
}
}
@Test
public void TestSay(){
BeanScope beanscope1=super.getBean("beanScope");
beanscope1.say();
beanscope1.setId(2);
beanscope1.print();
BeanScope beanscope2=super.getBean("beanScope");
beanscope2.say();
beanscope2.setId(3);
beanscope2.print();
beanscope1.say();
beanscope1.print();
}
<bean id="beanScope" class="com.lmr.spring.bean.BeanScope" scope="singleton"></bean>
結果:兩個對象的hashCode值和id值都一樣,引用的是同一內存塊
BeanScope say : 858242339
858242339 - - 2
BeanScope say : 858242339
858242339 - - 3
BeanScope say : 858242339
858242339 - - 3
<bean id="beanScope" class="com.lmr.spring.bean.BeanScope" scope="prototype"></bean>
結果:兩個對象的hashCode值和id值都不一樣,後者沒有影響到前者
BeanScope say : 1007653873
1007653873 - - 2
BeanScope say : 836514715
836514715 - - 3
BeanScope say : 1007653873
1007653873 - - 2
Bean的生命週期
定義
graph LR
定義-->初始化
初始化-->使用
使用-->銷燬
初始化
1.實現org.springframework.beans.factory.InitializingBean接口,覆蓋afterPropertiesSet方法
2.配置init-method
銷燬
1.實現org.springframework.beans.factory.DisposableBean接口,覆蓋destroy方法
2.配置destroy-method
<bean id="beanLifeCycle" class="com.lmr.spring.bean.BeanLifeCycle" init-method="Start" destroy-method="Stop"></bean>
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class BeanLifeCycle implements InitializingBean,DisposableBean{
//配置全局默認初始化、銷燬方法
public void defaultInit(){
System.out.println("Bean Default Init");
}
public void defaultDestory(){
System.out.println("Bean Default Destory");
}
//實現InitializingBean、DisposableBean接口
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("Bean Init");
}
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("Bean Destroy");
}
//配置init-method、destroy-method
public void Start(){
System.out.println("Bean Start");
}
public void Stop(){
System.out.println("Bean Stop");
}
}
配置全局默認初始化、銷燬方法(去檢測Bean中是否有對應的方法,若有則執行,沒有也不會報錯)
<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-3.0.xsd"
default-init-method="defaultInit" default-destroy-method="defaultDestory">
</beans>
若Bean實現了InitializingBean、DisposableBean接口或者配置了init-method、destroy-method,則全局默認初始化、銷燬方法將不起作用,不會執行。若Bean同時實現了InitializingBean、DisposableBean接口又配置了init-method、destroy-method,則按先後順序執行,先執行接口的方法,再執行配置的方法
Aware
使實現了Aware接口的bean在被初始化之後,可以獲取相應資源
ApplicationContextAware
BeanNameAware
public class BeanAware implements BeanNameAware,ApplicationContextAware{
private String beanname;
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
// TODO Auto-generated method stub
this.context=arg0;
System.out.println("BeanAware setApplicationContext : "+context.getBean(beanname).hashCode());
}
@Override
public void setBeanName(String name) {
// TODO Auto-generated method stub
this.beanname=name;
System.out.println("BeanAware name : "+beanname);
}
}
Bean的自動裝配(Autowiring)
參數:
No 不做任何操作
byName 根據屬性名自動裝配。此選項將檢查容器並根據名字查找與屬性完全一致的bean,並將其與屬性自動裝配;如果沒有找到,則什麼都不做
byType 如果容器中存在一個與指定屬性類型相同的bean,那麼將於該屬性自動裝配;如果存在多個該類型bean,那麼拋出異常,並指出不能使用byType方式進行自動裝配;如果沒有找到相匹配的bean,則什麼事都不發生
Constructor 與byType方式類似,不同之處在於它應用與構造器參數。如果容器中沒有找到與構造器參數類型一致的bean,那麼拋出異常
<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-3.0.xsd"
default-autowire="byName">
<bean id="beanAutowireService" class="com.lmr.spring.bean.BeanAutowireService"></bean>
<bean id="beanAutowireDao" class="com.lmr.spring.bean.BeanAutowireDao"></bean>
</beans>
public class BeanAutowireService {
private BeanAutowireDao beanAutowireDao;
public void setBeanAutowireDao(BeanAutowireDao beanAutowireDao) {
System.out.println("setBeanAutowireDao");
this.beanAutowireDao = beanAutowireDao;
}
// public BeanAutowireService(BeanAutowireDao beanAutowireDao) {
// System.out.println("Constructor");
// this.beanAutowireDao = beanAutowireDao;
// }
public void say(String word){
System.out.println("BeanAutowireService : "+word);
beanAutowireDao.say(word);
}
}
@Test
public void testAutowire(){
BeanAutowireService beanAutowireService=super.getBean("beanAutowireService");
beanAutowireService.say("hello,Autowire");
}
結果:
setBeanAutowireDao
BeanAutowireService : hello,Autowire
BeanAutowireDao : hello,Autowire
Bean的資源文件的統一接口 Resources
ResourceLoader:資源加載器,所有的ApplicationContext都實現了該接口,可以直接獲取資源
classpath:xx/xx/xx.xx 從classpath中加載
file:/xx/xx/xx.xx
http://xx/xx/xx.xx
(none):/xx/xx/xx.xx 依賴於ApplicationContext
public class BeanResource implements ApplicationContextAware{
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
// TODO Auto-generated method stub
this.applicationContext=arg0;
}
public void resource() throws IOException{
Resource resource=applicationContext.getResource("classpath:config.xml");
// Resource resource=applicationContext.getResource("file:D:\\coding\\codeworksace\\Spring\\src\\main\\resources\\config.xml");
// Resource resource=applicationContext.getResource("url:http://docs.spring.io/spring-boot/docs/2.0.0.BUILD-SNAPSHOT/reference/htmlsingle/");
// Resource resource=applicationContext.getResource("config.xml");
System.out.println(resource.getFilename());
System.out.println(resource.contentLength());
FileInputStream input=new FileInputStream(resource.getFile());
byte[] bytes=new byte[1024];
while(input.read(bytes)!=-1){
System.out.println(new String(bytes));
}
input.close();
}
}
結果:
config.xml
306
<?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-3.0.xsd">
</beans>
註解
@Component是一個通用註解,可用於任何bean
@Repository,@Service,@Controller是更有針對性的註解
@Repository通常用於註解DAO類,即持久層
@Service通常用於註解Service類,即服務層
@Controller通常用於註解Controller類,即控制層(MVC)
引入以上註解時,默認的Bean的id爲類名首字母變小寫,BeanAnnotation->beanannotation;也可加入參數,參數的值即爲Bean的id,@Component(“bean”)
@Scope作用域
默認爲singleton
@Scope(“prototype”)
掃描器
過濾器
通過此掃描器,spring會掃描包下面的註解,通過各個註解的規則,來進行bean的自動裝配
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.lmr.spring.beanannotation"></context:component-scan>
</beans>
Bean的自動裝配
@Required註解使用於bean屬性的setter方法
該註解僅僅表示受影響的bean屬性必須在配置時被填充,通過在bean定義或通過自動裝配一個明確的屬性值
@Autowired註解可用於“傳統”的setter方法,也可用於構造器或成員變量
用於成員變量上,就不需要setter方法了
默認情況下,如果因找不到合適的bean將會導致autowiring失敗拋出異常,可以通過設置required=false來避免。每個類中只能有一個構造器被標記爲required=true
@Autowired的自動注入策略是byType,按類型自動裝配
@Qualifier註解
當按類型自動裝配可能多個bean實例的情況,使用@Qualifier可以縮小範圍,或者指定唯一,也可以用於指定單獨的構造器參數或方法參數
其value值爲所選bean的id,也可在xml中配置bean的qualifier值
<bean class="com.lmr.spring.beanannotation.injection.dao.InjectionDaoImplOne">
<qualifier value="daoOne"></qualifier>
</bean>
@Autowired()
@Qualifier("injectionDaoImplOne")
private InjectionDao injectionDao;
//設值注入
@Autowired()
public void setInjectionDao(@Qualifier("injectionDaoImplOne")InjectionDao injectionDao) {
this.injectionDao = injectionDao;
}
@Autowired 和 @Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName
@Autowired適用於fields,constructors,multi-argument methods這些允許在參數級別使用@Qualifier註解縮小範圍的情況
@Resource使用於成員變量,只有一個參數的setter方法,所以在目標是構造器或一個多參數方法時,最好的方式時使用qualifiers
容器註解
@Bean 標識一個用於配置和初始化一個由SpringIoC容器管理的新對象的方法,類似於XML配置文件中的
屬性: name、init-method、destroy-method
@Configuration 配置
通常與@bean公用的註解是@configuration而不是@component。
在方法頭加上@bean註解,然後方法返回一個bean實例,完成向springIOC容器中註冊一個bean實例。
@Scope 默認是單例的
屬性:value 作用域(singleton,prototype,session,request,global session)
proxyMode 代理方式,代理可以採取延遲解析策略(ScopedProxyMode.NO,ScopedProxyMode.INTERFACES,ScopedProxyMode.TARGET_CLASS)
@Configuration
public class AppConfig { //這裏使用@Configuration註解,標識這個類相當於一個註冊文件
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
加載外部資源文件
一種是Xml獲取,一種是Java註解方式獲取
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config></context:annotation-config>
<context:property-placeholder location="classpath:/jdbc.properties" />
<bean class="org.springframework.jdbc.datasource.DiverManagerDataSource">
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
</beans>
@ImportResource
參數:value爲聲明properties的xml文件的路徑
使用@Value(“${}”)給類中屬性賦值
@Configuration
@ImportResource(value = { "classpath:/resourceconfig.xml" })
public class StoreConfig {
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.url}")
private String url;
@Bean(name="diverManager")
public DiverManager getDiverManager(){
return new DiverManager(username, password, url);
}
}
//resourceconfig.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config></context:annotation-config>
<context:property-placeholder location="classpath:/jdbc.properties" />
</beans>
JSR註解
使用JSR-250的註解
@Resource
默認的名稱是從屬性名或者setter方法得出
可通過name屬性來將其解析爲一個bean的名稱,這是由ApplicationContext中的CommonAnnotationBeanPostProcessor發現並處理的
@PostConstruct 初始化方法
@PreDestroy 銷燬方法
需要先註冊CommonAnnotationBeanPostProcessor類;效果與xml配置的init-method ,destroy-method一致。
使用JSR-330標準註解(依賴注入註解)
掃描方式與Spring註解一致,需要依賴javax.inject包
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
@Inject 等效於@Autowired, 可以使用於類,屬性,方法,構造器
@Named 如果想使用特定名稱進行依賴注入,使用@Named
@Named與@Component是等效的,通常與@Inject一起使用,跟@Qualifier類似
@Service
public class JsrService {
// @Resource
// @Autowired
@Inject
private JsrDao jsrDao;
// @Resource
public void setJsrDao(@Named("jsrDao")JsrDao jsrDao) {
this.jsrDao = jsrDao;
}
public void save(String word){
System.out.println("JsrService : "+word);
jsrDao.save(word);
}
@PostConstruct
public void init(){
System.out.println("JsrService init !");
}
@PreDestroy
public void destory(){
System.out.println("JsrService destory !");
}
}