《羊了個羊》11月29日通關技巧
Spring IOC
介紹
介紹引用於 https://baijiahao.baidu.com/s?id=1712887849553755889&wfr=spider&for=pc
-
IOC
全稱是Inversion of Control
,控制反轉。它是一種設計思想,由容器將設計好的物件交給容器控制,而非物件內部直接new。 -
傳統的Java設計中,直接會在物件的內部通過new進行物件的建立,是程式主動建立以來物件;對
IOC
來說,有一個專門的容器專門管理這些物件的生命週期,控制物件的建立;所以在IOC
中,是通過IOC
容器控制物件,由IOC
容器控制外部資源的獲取。 -
傳統應用中是由我們自己在物件中主動控制去直接獲取以來物件;反轉則是通過容器來進行物件的依賴和建立,這裡,物件只是被動的接收依賴物件,因此稱為反轉,而反轉的則是依賴物件的獲取被反轉了。
-
IOC
不是一種具體的技術,而是一種設計思想,IOC
的目的是為了如何指導我們編寫出更加鬆耦合,更加優雅的程式。傳統的應用程式使我們在類的內部顯式的建立依賴的物件。從而導致類於類之間耦合度過高。而使用了IOC
的設計思想,將物件的建立,查詢依賴,以及生命週期的控制權交給了IOC
容器。物件之間耦合較鬆,更加靈活。
IOC 底層原理
xml 解析、工廠模式、反射
-
xml配置檔案,配置建立的物件(bean)
-
建立工廠類,將xml解析、通過反射建立物件
IOC(BeanFactory 介面)
-
IOC 思想基於 IOC 容器完成,IOC 容器底層就是物件工廠
-
Spring 提供 IOC 容器實現兩種方式:(兩個介面)
(1)BeanFactory:IOC 容器基本實現,是 Spring 內部的使用介面,不提供開發人員進行使用。載入配置檔案時候不會建立物件,在獲取物件(使用)才去建立物件
(2)ApplicationContext:BeanFactory 介面的子介面,提供更多更強大的功能,一般由開發人
員進行使用。載入配置檔案時候就會把在配置檔案物件進行建立 -
ApplicationContext 介面
FileSystemXmlApplicationContext
:在磁碟中去查詢ClassPathXmlApplicationContext
:在src目錄下查詢 -
BeanFactory 介面,可以看到 ApplicationContext 是他的子介面
ConfigurableApplicationContext
:擴充套件介面
IOC 操作 Bean 管理
Bean 管理指的是兩個操作
(1)Spring 建立物件
(2)Spirng 注入屬性
Bean 管理操作有兩種方式
(1)基於 xml 配置檔案方式實現
(2)基於註解方式實現
基於 xml 方式
基於 xml 方式建立物件
<!--配置 User 物件建立-->
<bean id="user" class="com.zjh.spring.User"/>
-
在 spring 配置檔案中,使用 bean 標籤,標籤裡面新增對應屬性,就可以實現物件建立
-
在 bean 標籤有很多屬性,介紹常用的屬性
-
id
屬性:唯一標識 -
class
屬性:類全路徑(包類路徑)
-
-
建立物件時候,預設也是執行無引數構造方法完成物件建立
基於 xml 方式注入屬性
(1)DI:依賴注入,就是注入屬性
第一種注入方式:使用 set 方法進行注入
(1)建立類,定義屬性和對應的 set 方法
public class Book {
private String bookName;
private String bookAuthor;
public void setBookName(String bookName) {
this.bookName = bookName;
}
public void setBookAuthor(String bookAuthor) {
this.bookAuthor = bookAuthor;
}
public void testBook() {
System.out.println(bookName + ", 作者是:" + bookAuthor);
}
}
(2)在 spring 配置檔案配置物件建立,配置屬性注入
<!-- set 方法注入屬性 -->
<bean id="book" class="com.zjh.spring.Book">
<!-- 使用 property 完成屬性注入
name:類裡面屬性名稱
value:向屬性注入的值
-->
<property name="bookName" value="西遊記"/>
<property name="bookAuthor" value="吳承恩"/>
</bean>
(3)測試方法
@Test
public void testSet() {
// 載入 spring 配置檔案
ApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
// 獲取配置建立的物件
Book book = context.getBean("book", Book.class);
System.out.println(book);
book.testBook();
}
(4)結果
第二種注入方式:使用有引數構造進行注入
(1)建立類,定義屬性,建立屬性對應有引數構造方法
public class Order {
private String name;
private String address;
/**
* 有參建構函式
* @param name 商品名稱
* @param address 地址
*/
public Order(String name, String address) {
this.name = name;
this.address = address;
}
public void testOrder() {
System.out.println("某某某在" + address + "購買了一臺" + name);
}
}
(2)在 spring 配置檔案中進行配置
<!-- 有引數構造注入屬性-->
<bean id="order" class="com.zjh.spring.Order">
<!-- 使用 constructor-arg 完成屬性注入
name:類裡面屬性名稱
value:向屬性注入的值
-->
<constructor-arg name="name" value="電腦"/>
<constructor-arg name="address" value="合肥"/>
</bean>
(3)測試方法
@Test
public void testConstructor() {
// 載入 spring 配置檔案
ApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
// 獲取配置建立的物件
Order order = context.getBean("order", Order.class);
System.out.println(order);
order.testOrder();
}
(4)結果
p 名稱空間注入(瞭解)
(1)使用 p 名稱空間注入,可以簡化基於 xml 配置方式。第一步:新增 p 名稱空間在配置檔案中xmlns:p="http://www.springframework.org/schema/p"
(2)第二步:進行屬性注入,在 bean 標籤裡面進行操作
<!-- 使用p名稱空間 set 方法注入屬性 -->
<bean id="pBook" class="com.zjh.spring.Book" p:bookName="西遊記" p:bookAuthor="吳承恩"/>
測試方法,跟上方一樣照貓畫虎!!! 我就不寫測試方法和結果了
xml 注入其他型別屬性
字面量
(1)null 值
<!--null 值-->
<property name="address">
<null/>
</property>
(2)屬性值包含特殊符號
<!--屬性值包含特殊符號
1 把<>進行轉義 < >
2 把帶特殊符號內容寫到 CDATA
-->
<property name="address">
<value><![CDATA[<<南京>>]]></value>
</property>
注入屬性-外部 bean
(1)建立兩個類 service 類和 dao 類,在 service 呼叫 dao 裡面的方法
public interface UserDao {
/**
* 修改方法
*/
void update();
}
public class UserDaoImpl implements UserDao{
@Override
public void update() {
System.out.println("dao update......");
}
}
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
public void add() {
System.out.println("service add......");
userDao.update();
}
}
(3)在 spring 配置檔案中進行配置
<!-- service 和 dao 物件建立-->
<bean id="userService" class="com.zjh.spring.service.UserService">
<!--注入 userDao 物件
name 屬性:類裡面屬性名稱
ref 屬性:建立 userDao 物件 bean 標籤 id 值
-->
<property name="userDao" ref="userDaoImpl"/>
</bean>
<bean id="userDaoImpl" class="com.zjh.spring.dao.UserDaoImpl"/>
注入屬性-內部 bean
(1)一對多關係:部門和員工。一個部門有多個員工,一個員工屬於一個部門。部門是一,員工是多
(2)在實體類之間表示一對多關係,員工表示所屬部門,使用物件型別屬性進行表示
public class Dept {
private String deptName;
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
}
public class Emp {
private String name;
private String gender;
/**
* 員工屬於某一個部門,使用物件形式表示
*/
private Dept dept;
public void setName(String name) {
this.name = name;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public void show() {
System.out.println("我叫:" + name + ", 性別:" + gender + ", 屬於:" + dept.getDeptName());
}
}
(3)在 spring 配置檔案中進行配置
<!--內部 bean-->
<bean id="emp" class="com.zjh.spring.bean.Emp">
<!--設定兩個普通屬性-->
<property name="name" value="zjh"/>
<property name="gender" value="男"/>
<!--設定物件型別屬性-->
<property name="dept">
<bean class="com.zjh.spring.bean.Dept">
<property name="deptName" value="技術部"/>
</bean>
</property>
</bean>
注入屬性-級聯賦值
(1)第一種寫法
<!--級聯賦值-->
<bean id="emp" class="com.zjh.spring.bean.Emp">
<!--設定兩個普通屬性-->
<property name="name" value="lucy"/>
<property name="gender" value="女"/>
<!--級聯賦值-->
<property name="dept" ref="dept"/>
</bean>
<bean id="dept" class="com.zjh.spring.bean.Dept">
<property name="deptName" value="財務部"/>
</bean>
(2)第二種寫法,需要給 dept 加上get方法,要不然獲取不到屬性
public Dept getDept() {
return dept;
}
<!--級聯賦值-->
<bean id="emp" class="com.zjh.spring.bean.Emp">
<!--設定兩個普通屬性-->
<property name="name" value="lucy"/>
<property name="gender" value="女"/>
<!--級聯賦值-->
<property name="dept" ref="dept"/>
<property name="dept.deptName" value="技術部" />
</bean>
<bean id="dept" class="com.zjh.spring.bean.Dept" />
xml 注入集合屬性
注入陣列型別屬性
注入 List 集合型別屬性
注入 Map 集合型別屬性
(1)建立類,定義陣列、list、map、set 型別屬性,生成對應 set 方法
public class Stu {
/**
* 陣列型別屬性
*/
private String[] courses;
/**
* list 集合型別屬性
*/
private List<String> list;
/**
* map 集合型別屬性
*/
private Map<String,String> maps;
/**
* set 集合型別屬性
*/
private Set<String> sets;
public void setSets(Set<String> sets) {
this.sets = sets;
}
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void show() {
System.out.println("陣列型別屬性:" + Arrays.toString(courses));
System.out.println("list集合型別屬性:" + list.toString());
System.out.println("map集合型別屬性:" + maps.toString());
System.out.println("set集合型別屬性:" + sets.toString());
}
}
(2)在 spring 配置檔案進行配置
<!-- 集合型別屬性注入-->
<bean id="stu" class="com.zjh.spring.collection.Stu">
<!--陣列型別屬性注入-->
<property name="courses">
<array>
<value>java 課程</value>
<value>資料庫課程</value>
</array>
</property>
<!--list 型別屬性注入-->
<property name="list">
<list>
<value>張三</value>
<value>小三</value>
</list>
</property>
<!--map 型別屬性注入-->
<property name="maps">
<map>
<entry key="JAVA" value="java"/>
<entry key="PHP" value="php"/>
</map>
</property>
<!--set 型別屬性注入-->
<property name="sets">
<set>
<value>MySQL</value>
<value>Redis</value>
</set>
</property>
</bean>
在集合裡面設定物件型別值
public class Course {
private String courseName;
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
}
/**
* list 集合設定物件型別值
*/
private List<Course> courseList;
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
<!--注入 list 集合型別,值是物件-->
<property name="courseList">
<list>
<ref bean="courseOne"/>
<ref bean="courseTwo"/>
</list>
</property>
<!--建立多個 course 物件-->
<bean id="courseOne" class="com.zjh.spring.collection.Course">
<property name="courseName" value="Spring5 框架"/>
</bean>
<bean id="courseTwo" class="com.zjh.spring.collection.Course">
<property name="courseName" value="MyBatis 框架"/>
</bean>
把集合注入部分提取出來
(1)在 spring 配置檔案中引入名稱空間 utilxmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
(2)建立 List屬性
public class Book {
private List<String> list;
public void setList(List<String> list) {
this.list = list;
}
public void show() {
System.out.println("util:list注入的值:" + list.toString());
}
}
(3)使用 util 標籤完成 list 集合注入提取
<!-- 提取 list 集合型別屬性注入-->
<util:list id="bookUtilList">
<value>進擊的巨人</value>
<value>死神</value>
<value>JoJo的奇妙冒險</value>
</util:list>
<!-- 提取 list 集合型別屬性注入使用-->
<bean id="bookList" class="com.zjh.spring.collection.Book">
<property name="list" ref="bookUtilList"/>
</bean>
FactoryBean
Spring 有兩種型別 bean,一種普通 bean,另外一種工廠 bean(FactoryBean)
-
普通 bean:在配置檔案中定義 bean 型別就是返回型別
-
工廠 bean:在配置檔案定義 bean 型別可以和返回型別不一樣
第一步 建立類,讓這個類作為工廠 bean,實現介面 FactoryBean
第二步 實現接口裡面的方法,在實現的方法中定義返回的 bean 型別
public class MyBean implements FactoryBean<Course> {
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCourseName("java");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
第三步 注入bean
<bean id="myBean" class="com.zjh.spring.factorybean.MyBean"/>
第四步 測試方法
@Test
public void testFactoryBean() {
// 載入 spring 配置檔案
ApplicationContext context =
new ClassPathXmlApplicationContext("beanFactory.xml");
// 獲取配置建立的物件
Course course = (Course) context.getBean("myBean");
System.out.println(course.getCourseName());
}
bean 作用域
在 Spring 裡面,設定建立 bean 例項是單例項還是多例項。在 Spring 裡面,預設情況下,bean 是單例項物件,如下圖所示:
如何設定單例項還是多例項
- 在 spring 配置檔案 bean 標籤裡面有屬性(scope)用於設定單例項還是多例項
- scope 屬性值
-
singleton
:表示是單例項物件 -
prototype
:表示是多例項物件
-
-
singleton
和prototype
區別
(1)singleton 單例項,prototype 多例項
(2)設定 scope 值是 singleton 時候,載入 spring 配置檔案時候就會建立單例項物件設定 scope 值是 prototype 時候,不是在載入 spring 配置檔案時候建立物件,在呼叫 getBean 方法時候建立多例項物件
bean 生命週期
生命週期
(1)通過構造器建立 bean 例項(無引數構造)
(2)為 bean 的屬性設定值和對其他 bean 引用(呼叫 set 方法)
(3)呼叫 bean 的初始化的方法(需要進行配置初始化的方法)
(4)bean 可以使用了(物件獲取到了)
(5)當容器關閉時候,呼叫 bean 的銷燬的方法(需要進行配置銷燬的方法)
程式碼演示上述步驟
(1)建立 Orders 類,實現上述步驟方法
public class Orders {
private String orderName;
public Orders(){
System.out.println("第一步 執行無引數構造建立 bean 例項");
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
System.out.println("第二步 呼叫 set 方法設定屬性值");
}
/**
* 建立執行初始化的方法
*/
public void initMethod() {
System.out.println("第三步 執行初始化的方法");
}
/**
* 建立執行的銷燬的方法
*/
public void destroyMethod() {
System.out.println("第五步 執行銷燬的方法");
}
}
(2)配置 初始化方法和銷燬方法
<!--配置 User 物件建立
init-method 初始化載入方法
destroy-method 銷燬載入方法
-->
<bean id="orders" class="com.zjh.spring.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="orderName" value="手機"/>
</bean>
(3)測試方法
@Test
public void testSMZQ() {
// 載入 spring 配置檔案
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("beanSNZQ.xml");
// 獲取配置建立的物件
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步 獲取建立 bean 例項物件值:" + orders.getOrderName());
// 手動讓 bean 例項銷燬
context.close();
}
(4)結果
bean 的前後置處理器,bean 生命週期有七步
(1)通過構造器建立 bean 例項(無引數構造)
(2)為 bean 的屬性設定值和對其他 bean 引用(呼叫 set 方法)
(3)把 bean 例項傳遞 bean 後置處理器的方法 postProcessBeforeInitialization
(4)呼叫 bean 的初始化的方法(需要進行配置初始化的方法)
(5)把 bean 例項傳遞 bean 後置處理器的方法 postProcessAfterInitialization
(6)bean 可以使用了(物件獲取到了)
(7)當容器關閉時候,呼叫 bean 的銷燬的方法(需要進行配置銷燬的方法)
程式碼演示 前後置處理器效果
(1)建立類,實現介面 BeanPostProcessor,建立後置處理器。重寫postProcessBeforeInitialization、postProcessAfterInitialization方法
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之前執行的方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之後執行的方法");
return bean;
}
}
(2)配置前後置處理器
<!-- 配置後置處理器-->
<bean id="myBeanPost" class="com.zjh.spring.bean.MyBeanPost" />
(3)測試方法跟上面一樣
(4)結果
xml 自動裝配
根據指定裝配規則(屬性名稱或者屬性型別),Spring 自動將匹配的屬性值進行注入
根據屬性名稱裝配
<!--實現自動裝配
bean 標籤屬性 autowire,配置自動裝配
autowire 屬性常用兩個值:
byName 根據屬性名稱注入 ,注入值 bean 的 id 值和類屬性名稱一樣
byType 根據屬性型別注入
-->
<bean id="autowireEmp" class="com.zjh.spring.autowire.AutowireEmp" autowire="byName" />
<bean id="autowireDept" class="com.zjh.spring.autowire.AutowireDept" />
根據屬性型別裝配
<bean id="autowireEmp" class="com.zjh.spring.autowire.AutowireEmp" autowire="byType" />
<bean id="autowireDept" class="com.zjh.spring.autowire.AutowireDept" />
外部屬性檔案
配置資料庫資訊
(1)配置德魯伊連線池
(2)下載德魯伊jar包並引入
<!--直接配置連線池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/userDb"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
引入外部屬性檔案配置資料庫連線池
(1)建立外部屬性檔案,properties 格式檔案,寫資料庫資訊
prop.driverClass=com.mysql.jdbc.Driver
prop.url=com.mysql.jdbc.Driver
prop.userName=root
prop.password=root
(2)先引入 context 名稱空間,在把外部 properties 屬性檔案引入到 spring 配置檔案中xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<!--引入外部屬性檔案-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置連線池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"/>
<property name="url" value="${prop.url}"/>
<property name="username" value="${prop.userName}"/>
<property name="password" value="${prop.password}"/>
</bean>
基於註解方式
什麼是註解
(1)註解是程式碼特殊標記,格式:@註解名稱(屬性名稱=屬性值, 屬性名稱=屬性值..)
(2)使用註解,註解作用在類上面,方法上面,屬性上面
(3)使用註解目的:簡化 xml 配置
Spring 針對 Bean 管理中建立物件提供註解
(1)@Component:使用於元件上
(2)@Service:使用於業務邏輯層上
(3)@Controller:使用於控制層上
(4)@Repository:使用於持久層上
- 上面四個註解功能是一樣的,都可以用來建立 bean 例項
基於註解方式實現物件建立
(1)引入 jar 包
(2)開啟元件掃描
<!--開啟元件掃描
如果掃描多個包有以下兩種方法
1 多個包使用逗號隔開
2 掃描包上層目錄
-->
<context:component-scan base-package="com.zjh.spring"/>
(3)建立類,在類上面新增建立物件註解
/**
* @author zjh
*
* <bean id="userService" class=".."/> 同下方註解一個意思
*/
@Service(value = "userService")
public class UserService {
public void add() {
System.out.println("service add......");
}
}
(4)測試方法
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
(5)結果
開啟元件掃描細節配置
<!--示例 1
use-default-filters="false" 表示現在不使用預設 filter,自己配置 filter
context:include-filter 設定掃描哪些內容
-->
<context:component-scan base-package="com.zjh.spring" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例 2
下面配置掃描包所有內容
context:exclude-filter: 設定哪些內容不進行掃描
-->
<context:component-scan base-package="com.zjh.spring">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
基於註解方式實現屬性注入
(1)@Autowired:根據屬性型別進行自動裝配
第一步 把 service 和 dao 物件建立,在 service 和 dao 類新增建立物件註解
第二步 在 service 注入 dao 物件,在 service 類新增 dao 型別屬性,在屬性上面使用註解
@Service(value = "userService")
public class UserService {
/**
* 注入 userDao
*/
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add......");
userDao.update();
}
}
(2)@Qualifier:根據名稱進行注入
@Qualifier 註解的使用,和上面@Autowired 一起使用
@Service(value = "userService")
public class UserService {
/**
* 注入 userDao
*
* Autowired註解根據型別進行注入,如果有兩個同類型的bean就會出錯,就可以使用
* 到Qualifier註解根據名稱注入。
*/
@Autowired
@Qualifier(value = "userDaoImpl")
private UserDao userDao;
public void add() {
System.out.println("service add......");
userDao.update();
}
}
(3)@Resource:預設是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean才會按照型別來注入
@Service(value = "userService")
public class UserService {
@Resource
private UserDao userDao;
public void add() {
System.out.println("service add......");
userDao.update();
}
}
(4)@Value:注入普通型別屬性
/**
* 會將 zjh 注入到 name 中
*/
@Value(value = "zjh")
private String name;
完全註解開發
(1)建立配置類,替代 xml 配置檔案
/**
* @author zjh
*/
@Configuration
@ComponentScan(basePackages = {"com.zjh.spring"})
public class SpringConfig {
}
(2)編寫測試類
@Test
public void testAllAnnotations() {
// AnnotationConfigApplicationContext 載入配置類
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}