【Spring5】IOC
1 Spring概念
Spring是輕量級的開源JavaEE框架。可以解決企業開發的複雜性。
Spring有兩個核心部分:IOC和Aop
①IOC控制反轉:把建立物件過程交給Spring管理
②Aop:面向切面,不修改原始碼的情況下進行功能增強
Spring5相關jar包:spring-beans-5.2.6.RELEASE.jar
spring-context-5.2.6.RELEASE.jar
spring-core-5.2.6.RELEASE.jar
spring-expression-5.2.6.RELEASE.jar
通過Spring配置檔案建立物件
@Test public void test() { // 載入Spring配置檔案 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); // 獲取配置建立的物件 user u = (user) context.getBean("user"); u.add(); }
ClassPathXmlApplicationContext對應src下的檔案
FileSystemXmlApplicationContext對應磁碟路徑
bean1.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"> <bean id="user" class="com.hikaru.spring5.user"></bean> </beans>
2 IOC容器
控制反轉:把物件建立和物件之間的呼叫過程,交給Spring進行管理。
IOC底層原理
IOC過程: xml解析+工廠模式+反射
①配置xml檔案,配置建立的對象
<bean id="user" class="com.hikaru.spring5.user"></bean>
②建立工廠類
xml解析後通過反射建立物件,進一步降低耦合度。
public class userFactory { public static user getUser() { String classValue = classuser; Class clazz = Class.forName(classValue); return clazz.newInstance(); } }
比如user類路徑需要修改,那麼只需要修改配置檔案
IOC介面
Spring提供IOC容器的兩種方式(兩個介面):
①BeanFactory:IOC容器基本實現,是Spring內部介面,不提供開發人員使用
載入配置檔案的時候不會去建立物件,只有在獲取使用的時候才建立物件
②ApplicationContext:BeanFactory介面的子介面,提供了更多更強大的功能,面向開發人員使用
載入配置檔案的時候就建立物件
web專案一般選擇第二種方式,當伺服器啟動的時候就完成這些耗時的工作
IOC操作Bean管理
兩種實現方式:
①基於配置檔案方式
建立物件
<bean id="user" class="com.hikaru.spring5.user"></bean>
bean標籤的常用屬性 | |
---|---|
id | 標籤的唯一標識,不是例項名 |
class | 安全路徑(包類路徑) |
name | 與id類似,但是name可以用特殊字元 |
這種方式建立物件時,預設執行的是無參構造器完成物件建立,如果類中只有有引數構造器沒有無參構造器則會報錯
DI依賴注入(屬性):
①使用set方法注入
<property name="userName" value="劉"></property>
②使用有引數構造器注入
<constructor-arg name="userName" value="ryuu"></constructor-arg>
<constructor-arg index="1" value="9512891"></constructor-arg>
可以通過屬性名、屬性在構造器的索引號注入依賴
③使用p名稱空間注入
首先新增p名稱空間在配置檔案中
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<bean id="user" class="com.hikaru.spring5.user" p:userName="liu" p:pwd="pwd123">
</bean>
※ xml注入其它型別屬性
- 字面量
(1)空值
<property name="userName">
<null></null>
</property>
<property name="pwd">
<null></null>
</property>
(2)包含特殊字元 <![CDATA[...]>
<property name="userName">
<value><![CDATA[<><><>]]]></value>
</property>
- 注入外部bean
<bean id="service" class="com.hikaru.spring5.testDemo.service">
<property name="d" ref="daoImpl"></property>
</bean>
<bean id="daoImpl" class="com.hikaru.spring5.testDemo.daoImpl">
</bean>
必須對外部bean進行宣告
- 注入內部bean
<bean id="emp" class="com.hikaru.spring5.testDemo.emp">
<property name="name" value="luffer"></property>
<property name="d">
<bean id="d" class="com.hikaru.spring5.testDemo.dept">
<property name="deptName" value="jishubu"></property>
</bean>
</property>
</bean>
這裡不能使用單元測試,單元測試只允許使用一個構造器
4.級聯賦值
一種是外部bean注入屬性後再注入bean
另一種內部bean注入屬性
5.注入集合屬性
陣列:
<property name="courses">
<array>
<value>Java</value>
<value>資料庫</value>
</array>
</property>
list:
<property name="list">
<list>
<value>張三</value>
<value>法外狂徒</value>
</list>
</property>
set:
<property name="set">
<set>
<value>MySQL</value>
<value>Redis</value>
</set>
</property>
map:
<property name="map">
<map>
<entry key="Java" value="java">
</entry>
<entry key="PHP" value="php">
</entry>
</map>
</property>
工廠Bean
public class myBean implements FactoryBean<Course>{
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("C#");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
Course c= (Course) context.getBean("myBean", Course.class);
System.out.println(c);
}
}
工廠Bean區別於普通Bean,xml配置檔案定義的Bean型別與返回型別可以不同
Bean的作用域
1 單例模式與多例模式
<bean id="myBean" class="myBean" scope="prototype">
</bean>
<bean id="myBean" class="myBean" scope="singleton">
</bean>
Spring中預設情況為單例模式,每次返回的例項都是一個例項
myBean@3d921e20
myBean@36b4cef0
上為多例模式的例項地址
Bean的生命週期
(1)通過構造器建立bean例項(無引數構造,或通過p進行有參構造)
(2)為bean的 屬性 設定值和其它物件的引用(set方法)
(3)呼叫bean的初始化方法(需要進行配置初始化的方法)
(4)獲取到bean物件進行使用
(5)當容器關閉時,呼叫bean的銷燬方法(需要進行配置銷燬的方法)
演示Bean的生命週期
public class Order {
private String oname;
public Order() {
System.out.println("step1");
}
public String getOname() {
return oname;
}
public void setOname(String oname) {
this.oname = oname;
System.out.println("step2");
}
public void initMethod() {
System.out.println("step3:init Bean");
}
public void destroyMethod() {
System.out.println("step5:destroy Eean");
}
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
Order order = context.getBean("Order", Order.class);
System.out.println("step4:get bean");
((ClassPathXmlApplicationContext) context).close();
}
}
ApplicationContext作為介面沒有close方法,但是其實現類ClassPathXmlApplicationContext 有,所以這裡需要進行強轉或者開始使用ClassPathXmlApplicationContext生宣告
XML
<bean id="Order" class="Order" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="order1"></property>
</bean>
step1
step1
step2
step3:init Bean
step4:get bean
step5:destroy Eean
bean的後置處理器
在宣告週期的第三步之前和之後:
(1)通過構造器建立bean例項(無引數構造,或通過p進行有參構造)
(2)為bean的 屬性 設定值和其它物件的引用(set方法)
把bean例項傳遞給後置處理器的方法
(3)呼叫bean的初始化方法(需要進行配置初始化的方法)
把bean例項傳遞給後置處理器的方法
(4)獲取到bean物件進行使用
(5)當容器關閉時,呼叫bean的銷燬方法(需要進行配置銷燬的方法)
public class postBean implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("before init");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("after init");
return bean;
}
}
實現BeanPostProcessor介面的bean會被當做後置處理器
<bean id="Order" class="Order" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="order1"></property>
</bean>
<bean class="postBean"></bean>
後置bean會對xml中的所有bean生效
輸出結果:
step1
step1
step2
before init
step3:init Bean
after init
step4:get bean
step5:destroy Eean
自動裝配
根據指定的裝配規則(屬性名稱或者屬性型別),Spring自動將匹配的屬性值進行自動注入
根據屬性型別裝配
<bean id="Emp" class="Emp" autowire="byType"></bean>
<bean class="Dept">
<property name="dName" value="develop"></property>
</bean>
這種方式的外部bean不能宣告為相同的型別
根據屬性名稱裝配
<bean id="Emp" class="Emp" autowire="byName"></bean>
<bean id="dept" class="Dept">
<property name="dName" value="develop"></property>
</bean>
這種方式要求外部bean名稱與bean的屬性名相同
引入外部屬性檔案
首先新增context約束宣告
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:JDBC.properties"/>
<bean id="Druid" class="com.alibaba.druid.pool.DruidDataSource" >
<property name="driverClassName" value="${prop.driverClassName}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.username}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
隨後通過讀取外部properties檔案的方式建立德魯伊資料庫連線池
這裡的外部檔案獲取必須使用${prop.}的形式
②基於註解方式
註解是程式碼的特殊標記,格式:@註解名稱(屬性名稱:屬性值,屬性名稱:屬性值...),註解可以作用在類、方法、屬性上。註解的目的是簡化xml配置。
Spring針對Bean管理中建立物件提供的註解:
(1) @Componet
(2) @Service
(3) @Controller
(4) @Repository
上面四個註解功能是一樣的,都可以用來建立bean例項
步驟:
(1)引入jar包:spring-aop-5.2.6.RELEASE.jar
(2)配置檔案中新增context約束,並在配置檔案中進行元件掃描(告訴Spring哪個包、類中存在註解)
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
>
<context:component-scan base-package="service"></context:component-scan>
多個包可以用,隔開 或者使用上層包名
(3)在Bean類上添加註解
@Component
public class UserService {
public void add() {
System.out.println("service add...");
}
或者使用@Component(value="userService"),value值對應xml配置bean的id寫法,value省略時預設為第一個字母小寫的類名
元件過濾掃描
<context:component-scan base-package="service" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
不使用預設掃描,只掃描註解為Component的Bean
<context:component-scan base-package="service" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
使用預設掃描但不掃描註解為Component的Bean
註解實現屬性注入:只需要在配置檔案設定元件掃描,其他均通過類註解實現
①Autowired:根據屬性型別自動裝配
第一步 在service和dao上新增建立物件註解建立物件
第二步 在service類中的dao屬性上新增Autowired註解注入dao屬性
註解封裝了屬性的set方法,不需要再次建立
@Component
public class UserService {
@Autowired
private UserDao userDao;
public void add() {
userDao.add();
}
②Qualifier:根據屬性名稱自動裝配,需要配合Autowired使用
@Component
public class UserService {
@Autowired
@Qualifier(value = "userDaoImpl")
private UserDao userDao;
public void add() {
userDao.add();
}
比如UserDao有多個實現類,所以需要註解指名是哪一個,這裡的userDaoImpl是實現類建立註解預設生成的首字母小寫的類名
③Resource:
@Resource(name = "userDaoImpl")
private UserDao userDao;
等價於@Autowired
@Qualifier(value = "userDaoImpl")
④Value:
@Value("123")
private String name;
同樣不需要set方法
完全註解開發
配置註解另類為配置類,替代xml配置檔案
設定自動掃描元件註解
@Configuration
@ComponentScan(basePackages = {"service", "dao"})
public class SpringConfig {
}
載入配置類:AnnotationConfigApplicationContext
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);