轉(zhuǎn)載請(qǐng)注明來(lái)源 賴賴的博客
導(dǎo)語(yǔ)
你知道的越多援雇,越容易接受新的知識(shí)吮铭。
Spring IoC是有應(yīng)用場(chǎng)景的时迫,其特定決定了應(yīng)用的結(jié)構(gòu);也就是說(shuō)谓晌,一旦你用了Spring掠拳,也就受到了框架的限制,只能使用一些特定的設(shè)計(jì)模式纸肉。
實(shí)例
項(xiàng)目工程目錄結(jié)構(gòu)和代碼獲取地址
獲取地址(版本Log將會(huì)注明每一個(gè)版本對(duì)應(yīng)的課程)
https://github.com/laiyijie/SpringLearning
目錄結(jié)構(gòu)
乍一看溺欧,工程似乎打出上一次不少,多了好多柏肪;然而程序員做事姐刁,向來(lái)不會(huì)毫無(wú)章法,自有其中的規(guī)律烦味,而其中的規(guī)律了解了聂使,文件再多,也只是重復(fù)的工作量谬俄;
這個(gè)所謂的章法柏靶,就是今天要說(shuō)的重點(diǎn)之一,應(yīng)用的分層溃论;
Spring應(yīng)用結(jié)構(gòu)
現(xiàn)在工程有三個(gè)包屎蜓,包之間的關(guān)系如下:
- 應(yīng)用層:
me.laiyijie.demo
, 只調(diào)用服務(wù)層 - 服務(wù)層:
me.laiyijie.demo.service
,只調(diào)用數(shù)據(jù)層 - 數(shù)據(jù)層:
me.laiyijie.demo.dataaccess
钥勋,最底層梆靖,直接操作持久化數(shù)據(jù)(文件控汉、數(shù)據(jù)庫(kù)等)
具體含義如下:
me.laiyijie.demo
最上層的應(yīng)用層
- 向上:對(duì)應(yīng)的是整個(gè)應(yīng)用的出入口,現(xiàn)在的表現(xiàn)就是返吻,可以打印到控制臺(tái)姑子!
-
向下: 只調(diào)用了
me.laiyijie.demo.service
層,也就是說(shuō)Service層對(duì)這一層提供了服務(wù)测僵! - 職責(zé):負(fù)責(zé)處理入口數(shù)據(jù)和出口數(shù)據(jù)(如輸出輸入的格式化等)
me.laiyijie.demo.service
業(yè)務(wù)邏輯層街佑,所有的業(yè)務(wù)邏輯應(yīng)該在這一層實(shí)現(xiàn);
-
向上: 向應(yīng)用層提供服務(wù)捍靠,也就是提供接口給
me.laiyijie.demo
這一層的函數(shù)調(diào)用 -
向下: 只調(diào)用了
me.laiyijie.demo.dataaccess
層 - 職責(zé):通過(guò)調(diào)用dataaccess層實(shí)現(xiàn)業(yè)務(wù)邏輯沐旨,并且向應(yīng)用層提供服務(wù)。
me.laiyijie.demo.dataaccess
數(shù)據(jù)連接層榨婆,屏蔽底層于文件磁携、數(shù)據(jù)庫(kù)等持久化操作;
- 向上:向service層提供服務(wù)良风,使得服務(wù)層無(wú)需直接操作文件等持久化數(shù)據(jù)
- 向下: SQL谊迄,文件讀寫的
- 職責(zé):通過(guò)調(diào)用JDBC等直接操作數(shù)據(jù)庫(kù)和文件等持久化數(shù)據(jù)
小結(jié)
分層滿足兩個(gè)原則:
- 各層職責(zé)明確
- 僅調(diào)用自己的下一層,不會(huì)越層調(diào)用
運(yùn)行工程
運(yùn)行具有Main函數(shù)的 App.java
得到如下輸出
false
詳細(xì)解讀
程序入口為App.java從這里入口讀代碼
App.java
package me.laiyijie.demo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import me.laiyijie.demo.service.UserService;
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("root-context.xml");
UserService userService = context.getBean(UserService.class);
System.out.println(userService.login("lailai", "laiyijie","127.0.0.1"));
context.close();
}
}
Main函數(shù)中依舊只有四行代碼烟央,這里引用到了UserService
统诺,并且調(diào)用了UserService
的login
方法。
我們來(lái)看下UserService
UserService.java
package me.laiyijie.demo.service;
public interface UserService{
boolean login(String username,String password,String ip);
}
OK疑俭,非常簡(jiǎn)單粮呢,只是一個(gè)接口,定義了有這么一個(gè)方法钞艇,具體實(shí)現(xiàn)是在同一個(gè)包下面的另一個(gè)文件啄寡,UserServiceImpl.java中
UserServiceImpl.java
package me.laiyijie.demo.service;
import me.laiyijie.demo.dataaccess.AccountAccess;
import me.laiyijie.demo.dataaccess.LoginLogAccess;
public class UserServiceImpl implements UserService {
private AccountAccess accountAccess;
private LoginLogAccess loginLogAccess;
public boolean login(String username, String password,String ip) {
if (!accountAccess.isAccountExist(username)) {
return false;
}
if (accountAccess.isPasswordRight(username, password)) {
accountAccess.updateLastLoginTime(username);
loginLogAccess.addLoginLog(username, ip);
return true;
}
return false;
}
public AccountAccess getAccountAccess() {
return accountAccess;
}
public void setAccountAccess(AccountAccess accountAccess) {
this.accountAccess = accountAccess;
}
public LoginLogAccess getLoginLogAccess() {
return loginLogAccess;
}
public void setLoginLogAccess(LoginLogAccess loginLogAccess) {
this.loginLogAccess = loginLogAccess;
}
}
乍一看這個(gè)代碼,好長(zhǎng)哩照,函數(shù)好多挺物。其實(shí)并不復(fù)雜,
如果你對(duì)Java有一些了解的話應(yīng)該知道葡秒,后面的四個(gè)函數(shù)是getter和setter函數(shù),是向外提供了操作私有變量的方法嵌溢;
而最前面一個(gè)函數(shù)正是實(shí)現(xiàn)了UserService這個(gè)接口眯牧。
在這個(gè)類中,我們可以看到赖草,其通過(guò)私有變量的方式引用了dataaccess
層的兩個(gè)對(duì)象学少!
private AccountAccess accountAccess;
private LoginLogAccess loginLogAccess;
那么,在沒(méi)有使用Spring的情況下秧骑,我們的main函數(shù)應(yīng)該這樣調(diào)用login方法:
public static void main(String[] args) {
AccountAccess accountAccess = new AccountAccessImpl();
LoginLogAccess loginLogAccess = new LoginLogAccessImpl();
UserServiceImpl userServiceImpl = new UserServiceImpl();
userServiceImpl.setAccountAccess(accountAccess);
userServiceImpl.setLoginLogAccess(loginLogAccess);
userServiceImpl.login("lailai", "laiyijie", "127.0.0.1");
}
然而在App.java中版确,我們直接
UserService userService = context.getBean(UserService.class);
System.out.println(userService.login("lailai", "laiyijie","127.0.0.1"));
也就是說(shuō)扣囊,對(duì)象創(chuàng)建,以及對(duì)象之間的依賴關(guān)聯(lián)
通過(guò)root-context.xml來(lái)完成绒疗!
root-context.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" 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-4.3.xsd">
<bean id="accountAccessImpl" class="me.laiyijie.demo.dataaccess.AccountAccessImpl" ></bean>
<bean id="loginLogAccessImpl" class="me.laiyijie.demo.dataaccess.LoginLogAccessImpl"></bean>
<bean class="me.laiyijie.demo.service.UserServiceImpl">
<property ref="accountAccessImpl" name="accountAccess"></property>
<property ref="loginLogAccessImpl" name="loginLogAccess"></property>
</bean>
</beans>
經(jīng)過(guò)前兩節(jié)課的基礎(chǔ)侵歇,你應(yīng)該可以輕易看懂這些配置,無(wú)非就是完成了對(duì)象的創(chuàng)建和屬性的設(shè)置操作吓蘑;也就是與這段代碼的功能一樣:
AccountAccess accountAccess = new AccountAccessImpl();
LoginLogAccess loginLogAccess = new LoginLogAccessImpl();
UserServiceImpl userServiceImpl = new UserServiceImpl();
userServiceImpl.setAccountAccess(accountAccess);
userServiceImpl.setLoginLogAccess(loginLogAccess);
而這種配置方法惕虑,也就被稱為屬性注入
小結(jié)
- 屬性注入就是通過(guò)Spring的配置文件可以完成對(duì)對(duì)象
屬性
的配置,從而使得不從層次的對(duì)象之間產(chǎn)生依賴關(guān)系
磨镶,換句話講溃蔫,就是屬性注入提供了層與層之間的調(diào)用方式
- 然而恰恰就是這種依賴注入的方式,使得層次的劃分是水平劃分琳猫,是一層一層的向上提供服務(wù)伟叛!
- Spring提供的注入方式還有一種就是構(gòu)造注入,也就是在構(gòu)造方法的時(shí)候注入屬性脐嫂,如果理解了屬性注入想必不難理解構(gòu)造注入统刮。此處不展開,因?yàn)閷傩宰⑷肟梢詽M足絕大部分的應(yīng)用場(chǎng)景
附:
dataaccess層均為模擬雹锣,未實(shí)際接入持久層网沾,待后續(xù)添加。
AccountAccess.java
package me.laiyijie.demo.dataaccess;
public interface AccountAccess {
boolean isAccountExist(String username);
boolean isPasswordRight(String username, String password);
void updateLastLoginTime(String username);
}
AccountAccessImpl.java
package me.laiyijie.demo.dataaccess;
public class AccountAccessImpl implements AccountAccess {
public boolean isAccountExist(String username) {
if ("laiyijie".equals(username)) {
return true;
}
return false;
}
public void updateLastLoginTime(String username) {
}
public boolean isPasswordRight(String username, String password) {
if ("laiyijie".equals(username)&&"123456".equals(password)) {
return true;
}
return false;
}
}
LoginLogAccess.java
package me.laiyijie.demo.dataaccess;
public interface LoginLogAccess {
void addLoginLog(String username,String ip);
}
LoginLogAccessImpl.java
package me.laiyijie.demo.dataaccess;
public class LoginLogAccessImpl implements LoginLogAccess {
public void addLoginLog(String username, String ip) {
}
}
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>me.laiyijie</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
</dependencies>
</project>