QT-Qt設定背景圖片
1 簡介
1.1 歷史
-
Spring框架以interface21框架為基礎,經過重新設計,並不斷豐富其內涵,與2004年3月24日釋出了1.0正式版
-
作者為Rod Johnson
1.2 理念
- 使現有的技術更加容易使用
- 本身包括很多內容,並整合了現有的技術框架
1.3 優點
- Spring是一個開源的免費的框架(容器)
- Spring是一個輕量級的、非入侵式的框架
- 控制反轉(IOC)
- 面向切面程式設計(AOP)
- 支援事務的處理
1.4 組成
1.5 其他
- Spring Boot
- 一個快速開發的框架
- 基於Spring Boot可以快速開發單個微服務
- Spring Cloud
- 基於Spring Boot實現
2 IoC
以前的JavaWeb專案開發流程,比如
- 寫UserDao介面
public interface UserDao {
//方法
}
- 寫UserDaoImpl實現類
public class UserDaoImpl implements UserDao {
//方法實現
}
- 寫UserService業務介面
public interface UserService {
//方法
}
- 寫UserServiceImpl業務實現類
public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl(); //方法實現 }
- 測試
@Test
public void test() {
UserService userService = new UserServiceImpl();
//呼叫userService的方法
}
這種方法的弊端在於使用者的需求可能會影響程式碼,需要根據使用者需求去修改程式碼,代價很大
那麼,如果在UserServiceImpl中使用set介面,程式變為
public class UserServiceImpl implements UserService { private UserDao userDao; //利用set進行動態注入 public void setUserDao(UserDao userDao) { this.userDao = userDao; } //方法實現 }
測試的時候程式碼變為
@Test
public void test() {
UserService userService = new UserServiceImpl();
(UserServiceImpl) userService.setUserDao(new UserDaoImpl());
//呼叫userService的方法
}
使用set注入後,程式不再具有主動性,變成了被動的接收物件。主動權轉到了使用者,使用者選擇實現類物件
這種思想從本質上解決了問題,程式設計師不用管理物件的建立,降低了系統的耦合,從而專注業務的實現,這就是IoC的原型
2.1 IoC本質
控制反轉(IoC, Inversion of Control)是一種設計思想。在沒有IoC的程式中,使用面向物件程式設計,物件的建立和物件間的依賴關係完全硬編碼在程式中,物件的建立由程式自己控制。而控制反轉則是將物件的建立轉移給第三方
控制反轉是一種通過描述(XML或註解)並通過第三方去生產或獲取特定物件的方式,在Spring中實現控制反轉的是IoC容器,其實現方法是依賴注入(DI, Dependency Injection)
3 HelloSpring
- 編寫實體類
public class Hello {
private String str;
public void setStr(String str) {
this.str = str;
}
public String getStr() {
return str;
}
}
- 配置xml檔案
<bean id="hello" class="com.hjc.pojo.Hello">
<property name="str" value="Hello, world"/>
</bean>
- 測試
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello);
}
要實現不同的操作,我們只需要在xml檔案中修改。物件由Spring來建立,管理,裝配
4 IoC建立物件的方式
4.1 無參建構函式建立物件
要用無參建構函式建立物件,那麼類中就要保留無參建構函式,要麼顯示寫出無參建構函式,要麼就不寫建構函式
public class User {
private int id;
private String name;
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
//其他函式
}
此時,在配置xml時就如上文那樣,資料注入是由set注入的,那麼在類中就要寫出屬性的set方法
<bean id="user" class="com.hjc.pojo.User">
<property name="id" value="1"/>
<property name="name" value="admin"/>
</bean>
用這種方式配置,那麼物件是先由類的無參建構函式建立,再由類的set方法進行資料的注入,要注意這個先後順序
4.2 有參建構函式建立物件
如果要使用有參建構函式建立物件,那麼在類中就要寫出有參建構函式。此時,資料是由有參建構函式注入
public class User {
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
}
在配置xml檔案時就和上文不太一樣
<bean id="user" class="com.hjc.pojo.User">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="admin"/>
</bean>
<bean id="user" class="com.hjc.pojo.User">
<constructor-arg type="int" value="1"/>
<constructor-arg type="java.lang.String" value="admin"/>
</bean>
<bean id="User" class="com.hjc.pojo.User">
<constructor-arg name="id" value="1"/>
<constructor-arg name="name" value="admin"/>
</bean>
以上三種方法都可以實現相同的效果。以這種方式配置,物件就由類的有參建構函式建立,而資料也是由有參建構函式注入
不管物件是由無參建構函式還是有參建構函式建立的,在配置檔案載入的時候,容器中的物件就已經被初始化了
5 Spring配置
5.1 別名
Spring中別名的使用和MyBatis中類似,使用別名之後可以省略類的包路徑
5.2 bean的配置
<!--
id: bean的唯一識別符號
class: bean物件對應的全限定名
name: 別名,可以取多個別名
scope: bean為單例還是多例
-->
<bean id="user" class="com.hjc.pojo.User" name="user2" scope="singleton">
<property name="id" value="1"/>
<property name="name" value="admin"/>
</bean>
5.3 使用import
將多個配置檔案匯入合併為一個配置檔案。比如,在applicationContext.xml檔案中匯入service.xml和dao.xml配置檔案,將這兩個配置檔案合併為一個applicationContext.xml配置檔案
<import resource="service.xml"/>
<import resource="dao.xml"/>
6 依賴注入
6.1 構造器注入
參考4.2
6.2 set方式注入
- 依賴注入
- 依賴:bean物件的建立依賴於容器
- 注入:bean物件的所有屬性由容器注入
例項
- 編寫實體類
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbies;
private Map<String, String> scores;
private Set<String> games;
private String nullPoint;
private Properties info;
//一定要寫各屬性的set方法,在此省略
}
public class Address {
private String address;
public setAddress(String address) {
this.address = address;
}
}
- 配置xml檔案,使用set方法注入
<bean id="address" class="com.hjc.pojo.Address">
<property name="address" value="testAddress"/>
</bean>
<bean id="student" class="com.hjc.pojo.Student">
<!--基本型別(包括String)注入,value-->
<property name="name" value="test"/>
<!--bean注入,ref-->
<property name="address" ref="address"/>
<!--陣列注入,-->
<property name="books">
<array>
<value>testBook1</value>
<value>testBook2</value>
<value>testBook3</value>
</array>
</property>
<!--list注入-->
<property name="hobbies">
<list>
<value>testHobby1</value>
<value>testHobby2</value>
</list>
</property>
<!--map注入-->
<property name="scores">
<map>
<entry key="Math" value="100"/>
<entry key="Physics" value="100"/>
</map>
</property>
<!--set注入-->
<property name="games">
<set>
<value>testGame1</value>
<value>testGame2</value>
</set>
</property>
<!--null注入-->
<property name="nullPoint">
<null/>
</property>
<!--properties注入-->
<property name="info">
<props>
<prop key="testInfo1">test1</prop>
<prop key="testInfo2">test2</prop>
</props>
</property>
</bean>
- 測試
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student);
}
6.3 其他方式注入
p名稱空間,對應set注入,參考官方文件
c名稱空間,物件構造器注入,參考官方文件
7 bean的作用域
7.1 singleton
單例模式,也是Spring的預設模式,不顯示寫出就表示單例
單例模式表示在容器中物件只會建立一個
7.2 prototype
原型模式,表示每次從容器中獲得物件的時候,容器會建立一個新的物件返回
7.3 request、session、application
這幾個在web開發中使用
8 bean的自動裝配
之前的xml配置都是手動配置的,那麼自動裝配是Spring會在上下文中自動尋找,自動裝配屬性
在Spring中有三種裝配的方式
- 在xml中顯示配置
- 在java類中顯示配置
- 隱式自動裝配
8.1 byName自動裝配
public class Student {
private String name;
private Computer computer;
private KeyBoard keyBoard;
//set方法
}
public class Computer {
}
public class KeyBoard {
}
有三個實體類,用byName自動裝配配置xml
<bean id="computer" class="com.hjc.pojo.Computer"/>
<bean id="keyBoard" class="com.hjc.pojo.KeyBoard"/>
<!--
byName: 自動在容器上下文中查詢和自己物件set方法後面的值對應的bean id
如:類中setComputer方法,Computer對應bean id為computer
-->
<bean id="student" class="com.hjc.pojo.Student" autowire="byName">
<property name="name" value="test"/>
</bean>
這種方法要求bean的id不能隨便選擇,要和自動注入的屬性的set方法的值一致,除了首字母大寫變成小寫
8.2 byType自動裝配
還是上面三個實體類,用byType實現自動裝配
<bean id="computer" class="com.hjc.pojo.Computer"/>
<bean id="keyBoard" class="com.hjc.pojo.KeyBoard"/>
<!--
byType: 會自動在容器上下文中查詢和自己物件屬性型別相同的bean
-->
<bean id="student" class="com.hjc.pojo.Student" autowire="byType">
<property name="name" value="test"/>
</bean>
這種方法要求物件中屬性型別要唯一,如果有幾個相同的屬性型別,就不能自動裝配
8.3 註解自動裝配
jdk1.5支援註解,spring2.5支援註解
要使用註解
- 匯入約束,context約束
- 配置註解的支援
<context:annotation-config/>
使用@Autowired註解,直接在屬性上使用,或者在set方法上使用。另外,使用@Autowired註解可以不寫set方法,前提是屬性符合byType的要求
實體類
public class Student {
private String name;
@Autowired
private Computer computer;
@Autowired
private KeyBoard keyBoard;
//set方法
}
xml檔案
<bean id="computer" class="com.hjc.pojo.Computer"/>
<bean id="keyBoard" class="com.hjc.pojo.KeyBoard"/>
<bean id="student" class="com.hjc.pojo.Student"/>
如果使用@Autowired實現自動裝配的環境比較複雜,無法通過一個註解完成的時候,可以配合使用@Qualifier(value="xxx")來指定唯一的一個bean
9 使用註解開發
在Spring4之後,要使用註解開發,必須要匯入aop的包。另外,需要在xml檔案中匯入context約束,配置註解的支援
<context:annotation-config/>
首先,要配置掃描的包,也就是說,在類中配置了註解還要被掃描到才有效
<context:component-scan base-package="com.hjc"/>
9.1 @Component
這個註解放在類上,表示這個類被Spring管理了,比如
@Component
public class User {
//省略
}
相當於在xml檔案中配置bean
<bean id="user" class="com.hjc.pojo.User"/>
9.2 @Value
這個註解放在類中的屬性上,表示注入資料,比如
@Component
public class User {
@Value("test")
private String name;
}
相當於在xml檔案中配置bean,並進行依賴注入
<bean id="user" class="com.hjc.pojo.User">
<property name="name" value="test"/>
</bean>
@Value註解也可以放在屬性的對應set方法上,但這個註解只能進行簡單的配置,複雜的配置還是使用xml檔案
9.3 @Component的衍生註解
web專案一般有三層,不同層中的註解名稱不同,但作用相同
- Dao層:@Repository
- Service層:@Service
- Controller層:@Controller
這幾個註解和@Component是一樣的,都表示將類註冊到Spring中,裝配bean
9.4 @Autowired
這個註解實現自動裝配,參考8.3
9.5 @Scope
這個註解配置類的作用域,是單例模式還是原型模式,比如
@Component
@Scope("singleton")
public class User {
@Value("test")
private String name;
}
總結:
- xml檔案配置更加萬能,適用於任何場合,維護簡單
- 註解適用於簡單的配置,維護相對複雜
- 專案中一般使用xml管理bean,使用註解完成屬性注入
10 使用JavaConfig配置
不使用xml檔案配置,都由Java程式碼配置
JavaConfig是Spring的一個子專案,在Spring4之後,稱為一個核心功能
10.1 @Configuration、@Bean
要用Java程式碼進行配置,就要用到@Configuration和@Bean這兩個註解,比如
@Configuration
public class AppConfig {
@Bean
public User getUser() {
return new User();
}
}
這就相當於在xml中配置
<bean id="getUser" class="com.hjc.pojo.User"/>
測試
@Test
public void test() {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
User user = context.getBean("getUser");
}
注意和xml檔案配置例項化物件的不同
10.2 @ComponentScan
在配置類中使用這個註解表示設定掃描類包
@Configuration
@ComponentScan("com.hjc")
public class AppConfig {
@Bean
public User getUser() {
return new User();
}
}
11 代理模式
代理模式是SpringAOP的底層實現
代理模式有兩種
- 靜態代理
- 動態代理
11.1 靜態代理
角色分析
- 抽象角色:一般會使用介面或者抽象類來解決
- 真實角色:被代理角色
- 代理角色:代理真實角色,代理真實角色後,一般會做附加操作
- 客戶:訪問代理物件的人
使用靜態代理的例子
- 編寫介面(抽象角色)
//租房介面
public interface Rent {
void rent();
}
- 編寫被代理類(真實角色)
//房東
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房東出租房子");
}
}
- 編寫代理類(代理角色)
public class Proxy implements Rent {
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
findHouse();
host.rent();
signContract();
}
public void findHouse() {
System.out.println("找到房子");
}
public void signContract() {
System.out.println("籤合同");
}
}
- 編寫客戶訪問代理角色
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy(new Host());
proxy.rent();
}
}
靜態代理的好處:
- 可以使真實角色的操作更加純粹,不用關注一些公共的業務
- 公共業務交給代理角色,實現了業務的分工
- 公共業務發生擴充套件的時候,方便集中管理
缺點:
- 一個真實角色就會產生一個代理角色,增加程式碼量,開發效率低
在web開發中,如果要對類中的方法進行增強,直接改類中對應方法的程式碼比較繁瑣,而且實際開發不允許直接改程式碼,我們可以使用靜態代理實現,舉個例子加深理解
- 編寫service介面
public interface UserService {
void add();
void delete();
void update();
void query();
}
- 編寫service實現類
public class UserServiceImpl implements UserService {
//實現對應方法
}
- 編寫代理類
public class UserServiceProxy implements UserService {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.find();
}
public void log(String msg) {
System.out.println("[debug] 使用了" + msg + "方法");
}
}
- 客戶訪問
public class Client {
public static void main(String[] args) {
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(new UserServiceImpl);
proxy.add();
proxy.delete();
proxy.update();
proxy.query();
}
}
這樣子就避免修改了原有的service程式碼,而是通過代理類對service類的方法進行增強
11.2 動態代理
- 動態代理和靜態代理的角色一樣
- 動態代理的代理類是動態生成的,不是直接寫好的
- 動態代理分為兩大類
- 基於介面的動態代理:jdk動態代理
- 基於類的動態代理:cglib
- 還有基於java位元組碼實現:Javassist
需要了解兩個類:Proxy,InvocationHandler
動態代理的好處
- 動態代理有靜態代理的優點
- 另外,一個動態代理類代理的是一個介面,一般對應一類業務,解決了靜態代理的缺點
12 AOP
12.1AOP定義
AOP(Aspect Oriented Programming),面向切面程式設計,通過預編譯方式和執行期動態代理實現程式功能統一維護的技術。AOP是OOP的延續,是函數語言程式設計的一種衍生範型
12.2 AOP在Spring中的作用
提供宣告式事務,允許使用者自定義切面
- 橫切關注點:跨越應用程式多個模組的方法或功能,如日誌,安全,快取,事務等等
- 切面(Aspect):橫切關注點被模組化的特殊物件,即是一個類
- 通知(Advice):切面必須要完成的工作,即是類中的一個方法
- 目標(Target):被通知物件
- 代理(Proxy):向目標物件應用通知之後建立的物件
- 切入點(PointCut):切面通知執行的“地點”
- 連線點(JointPoint):與切入點匹配的執行點
SpringAOP中,通過Advice定義橫切邏輯,Spring支援5種類型的Advice
- 前置通知
- 連線點:方法前
- 實現介面:org.springframework.aop.MethodBeforeAdvice
- 後置通知
- 連線點:方法後
- 實現介面:org.springframework.aop.AfterReturningAdvice
- 環繞通知
- 連線點:方法前後
- 實現介面:org.aopalliance.intercept.MethodInterceptor
- 異常丟擲通知
- 連線點:方法丟擲異常
- 實現介面:org.springframework.aop.ThrowsAdvice
- 引介通知
- 連線點:類中增加新的方法屬性
- 實現介面:org.springframework.aop.IntroductionInterceptor
AOP在不改變原有程式碼的情況下, 增加新的功能
11.3 使用Spring實現AOP
要使用AOP織入,需要匯入包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
假設有service介面和實現類
public interface UserService {
void add();
void delete();
void update();
void query();
}
public class UserServiceImpl implements UserService {
//實現對應方法
}
那麼,要對這幾個方法使用AOP進行方法增強
方式一:使用Spring的API實現
- 首先要定義切面和通知,定義類實現對應介面
public class BeforeLog implements MethodBeforeAdvice {
//Method: 被代理的目標物件的方法
//args: 方法引數
//target: 被代理的目標物件
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("執行" + target.getClass().getName() + "的" + method.getName());
}
}
public class AfterLog implements AfterReturningAdvice {
//returnValue: 被代理的方法的返回值
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("執行" + method.getName() + "返回結果" + returnValue);
}
}
- 在xml中配置,把這些類註冊到Spring中
<!--註冊bean-->
<bean id="userService" class="com.hjc.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.hjc.log.BeforeLog"/>
<bean id="afterLog" class="com.hjc.log.AfterLog"/>
<!--配置aop,匯入aop約束-->
<aop:config>
<!--切入點,expression表示式-->
<aop:pointcut id="pointcut" expression="execution(* com.hjc.service.UserServiceImpl.*(..))"/>
<!--執行增強-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
- 測試
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//動態代理的是介面
UserService userService = (UserService) context.getBean("userService");
userService.add();
userService.delete();
}
方式二:自定義實現AOP
不通過實現Spring的介面來實現AOP,而是自己定義切面類和通知
public class MyLog {
public void beforeLog() {
//具體實現
}
public void AfterLog() {
//具體實現
}
}
同樣,在xml中配置
<bean id="myLog" class="com.hjc.log.MyLog"/>
<aop:config>
<!--自定義切面-->
<aop:aspect ref="myLog">
<!--切入點-->
<aop:pointcut id="pointcut" expression="execution(* com.hjc.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="beforeLog" pointcut-ref="pointcut"/>
<aop:after method="afterLog" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
方式三:註解實現AOP
使用註解實現AOP,主要是在定義切面類的時候使用註解
@Aspect
public class MyLog {
@Before("execution(* com.hjc.service.UserServiceImpl.*(..))")
public void before() {
//具體實現
}
@After("execution(* com.hjc.service.UserServiceImpl.*(..))")
public void after() {
//具體實現
}
}
在xml中只需註冊bean,當然也可以使用註解自動裝配。還要開啟註解支援
<bean id="myLog" class="com.hjc.log.MyLog"/>
<aop:aspectj-autoproxy/>
13 整合MyBatis
步驟
- 匯入相關jar包
- junit
- mybatis
- mysql
- spring
- aop織入
- mybatis-spring
- 編寫配置檔案
13.1 MyBatis
13.2 MyBatis-Spring
參考官方文件
- 編寫資料來源配置
- sqlSessionFactory
- sqlSessionTemplate
- 給Dao介面編寫實現類
- 註冊實現類到Spring中
- 測試
14 宣告式事務
14.1 事務
- 事務涉及資料的完整性和一致性問題
- 事務ACID原則
- 原子性
- 一致性
- 隔離性
- 永續性
14.2 Spring中的事務管理
- 宣告式事務:AOP
- 程式設計式事務:在程式碼中進行事務管理
一般使用宣告式事務,而不是用程式設計式事務,因為程式設計式事務會改變程式碼
- 在xml中配置宣告式事務
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" value="dataSource"/>
</bean>
- 結合AOP實現事務織入
<!--配置事務通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--給具體方法配置事務-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<!--配置所有方法-->
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事務切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.hjc.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>