Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
項目介紹
Java項目開發用的公用基礎模塊,包括:Spring容器初始化、配置文件讀取工具類、分頁對象、Protobuf工具類、反射工具類等
安裝教程
使用說明
SystemConfig繼承了spring默認加載配置文件類org.springframework.beans.factory.config.PropertyPlaceholderConfigurer,并對其進行了增強,增加了一些常用的方法,所以只需要將原來配置文件中的PropertyPlaceholderConfigurer換成SystemConfig類即可,如下,在spring配置文件中加入bean定義:
<bean class="com.cnsugar.common.config.SystemConfig"> <property name="fileEncoding" value="UTF-8" /> <property name="locations"> <list> <value>classpath:application.properties</value> </list> </property> </bean>
在任何類中只需要用SystemConfig中的方法即可讀取application.properties中的所有配置,如:
String str = SystemConfig.getString("key"); int i = SystemConfig.getInt("key");
通過該工具類,可以使普通的java應用程序集成spring來管理項目的各種對象。
在類中可以通過以下兩種方式獲取Spring容器中的所有bean對象:
UserService userService = AppContext.getBean("userService"); UserService userService = AppContext.getBean(UserService.class);
注意:如果在SpringMVC項目中使用該工具類,需要在listener初始化方法中調用AppContext.initWebApplicationContext()方法來同步SpringMVC容器,否會在日志中看到spring會初始化兩次。如新建一個WebContextListener類:
public class WebContextListener extends org.springframework.web.context.ContextLoaderListener { @Override public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { return super.initWebApplicationContext(servletContext); } @Override public void contextInitialized(ServletContextEvent event) { super.contextInitialized(event); initialize(event.getServletContext()); StringBuilder sb = new StringBuilder(); sb.append("\r\n======================================================================\r\n"); sb.append("\r\n 歡迎使用 ").append(SystemConfig.getConfig("app.name")).append("\r\n"); sb.append("\r\n Version ").append(SystemConfig.getConfig("api.version")).append("\r\n"); sb.append("\r\n======================================================================\r\n"); System.out.println(sb.toString()); } /** * 系統初始化 * @param servletContext */ private void initialize(ServletContext servletContext) { AppContext.initApplicationContext(WebApplicationContextUtils.getWebApplicationContext(servletContext)); //初始化配置屬性 servletContext.setAttribute("conf", SystemConfig.getConfigMap()); } @Override public void contextDestroyed(ServletContextEvent event) { super.contextDestroyed(event); } }
在web.xml中加入:
<listener> <listener-class>com.cnsugar.web.listener.WebContextListener</listener-class> </listener>
ReflectUtils工具類中的主要方法介紹:
/** * 獲取類中非final和static的屬性(包括父類中的屬性),帶Field屬性和對應的setter和getter方法Method對象 */ List<BeanField> getBeanFields(Class clazz); /** * 獲取類或所有父類中非final和static的屬性 */ List<Field> getFields(Class clazz); /** * 獲取類或所有父類中setter方法 */ List<Method> getSetterMethods(Class clazz); /** * 獲取類或所有父類中getter方法 */ List<Method> getGetterMethods(Class clazz); /** * 直接讀取對象屬性值, 無視private/protected修飾符, 不經過getter方法 */ Object getFieldValue(final Object obj, final String fieldName); /** * 直接設置對象屬性值, 無視private/protected修飾符, 不經過setter方法 */ void setFieldValue(Object obj, String fieldName, Object value);
一般結合common-jdbc或基于mybatis的PageInterceptor使用,也可直接傳一個數據List進去進行分頁,toString()方法會生成基于bootstrap的分頁操作欄的html代碼,可直接在web頁面顯示。(后面會詳細說明)。
CamelCaseUtils: 駝峰命名法(CamelCase)和下劃線風格(UnderScoreCase)字符串之間的轉換工具類;
ProtostuffUtil: protostuff工具類;
Utils: 包含常用的獲取系統時間、日期轉換、日期比較、時間格式化、普通java類型轉換等方法工具類;
Bytes: 各種java類型與byte[]互轉工具類;
Base64: Base64編碼和解碼工具類;
大有學問#
今天實際操作上手有一點小變更,就是將top中的nav-bottom模塊重劃分到main部分中。
這樣剛好是一個完整的main,不用重新切割測量大小。
今天寫一個大概的行模塊布局,詳細部分下次再寫。
/* 清除瀏覽器默認設置 */
* {
margin: 0;
padding: 0;
}
/* 背景顏色 */
body {
background-color: #f3f6f6;
}
.bGW {
background-color: white;
}
/* 添加浮動 */
.fL {
float: left;
}
.fR {
float: right;
}
/* 清除浮動 */
.clearF {
overflow: hidden;
}
/* 版心寬度1200px,水平居中 */
.bX {
width: 1200px;
margin: auto;
}
.top {
height: 101px;
<!-- 臨時背景顏色,方便看清楚盒子位置,填充實際內容時會刪除掉該屬性 -->
background-color: antiquewhite;
}
<div class="top bX"></div>
top部分
.nav {
height: 420px;
<!-- 這是吸取的真實背景顏色 -->
background-color: #1c056c;
}
<div class="nav"></div>
nav
.mB42 {
margin-bottom: 42px;
}
.menu {
height: 60px;
/* 盒子陰影 */
box-shadow: 1px 2px rgba(118, 118, 118, 0.2);
margin-top: 8px;
margin-bottom: 37px;
}
/* 初階段用于顯示模塊位置,實際布局需刪除該選擇器 */
.main-top,
.main-center div,
.main-bottom div {
background-color: white;
}
.main-top {
height: 603px;
}
.main-center {
height: 923px;
}
.main-center-top,
.main-center-bottom {
height: 440px;
}
.main-bottom-top,
.main-bottom-bottom {
height: 318px;
}
<div class="main bX">
<!-- main可劃分為四個大的行模塊 -->
<!-- menu行模塊 -->
<div class="menu bGW">main-menu</div>
<!-- main-top行模塊 -->
<div class="main-top mB42">
精品推薦
</div>
<!-- main-center行模塊 -->
<div class="main-center mB42">
<div class="main-center-top mB42">
編程入門
</div>
<div class="main-center-bottom mB42">
數據分析師
</div>
</div>
<!-- main-bottom行模塊 -->
<div class="main-bottom mB42">
<div class="main-bottom-top mB42">
機器學習工程師
</div>
<div class="main-bottom-bottom">
前端開發工程師
</div>
</div>
</div>
menu行模塊和main-top行模塊
main-center行模塊
main-bottom行模塊
.footer {
height: 417px;
}
<div class="footer bGW">
<div class="bX">
footer
</div>
</div>
footer部分
下次更新完整代碼。拜拜
文地址:Understanding Design Patterns in JavaScript
原文作者:Sukhjinder Arora
譯者:HelloGitHub-Robert
當啟動一個新的項目時候,我們不應該馬上開始編程。而是首先應該定義項目的目的和范圍,然后列出其功能或規格。如果你已經開始編程或者正在從事一個復雜的項目,則應該選擇一個最適合你項目的設計模式。
在軟件工程中,設計模式是針對軟件設計中常見問題的可重用解決方案。設計模式也是經驗豐富的開發人員針對特定問題的最佳實踐。它可以被當作編程的模板。
許多工程師要么認為設計模式浪費時間,要么不知道如何恰當的使用設計模式。但如果能正確使用設計模式,則可以幫助你寫出更好的可讀性更高的代碼,并且代碼更容易被維護和理解。
最重要的是,設計模式為軟件開發人員提供了通用的詞匯表。它們能讓學習你代碼的人很快了解代碼的意圖。例如,如果你的項目中使用了裝飾器模式,那么新的開發可以很快就知道這段代碼的作用,從而他們可以將更多精力放在解決業務問題上,而不是試圖理解代碼在做什么。
我們已經知道了什么是設計模式和它的重要性,下面我們深入研究一下 JavaScript 中的 7 種設計模式。
模塊是一段獨立的代碼,因此我們可以更新模塊而不會影響代碼的其它部分。模塊還允許我們通過為變量創建單獨的作用域來避免命名空間污染。當它們與其它代碼解耦時,我們還可以在其它項目中重用模塊。
模塊是任何現代 JavaScript 應用程序不可或缺的一部分,有助于保持代碼干凈,獨立和有條理。在 JavaScript 中有許多方法可以創建模塊,其中一種是模塊模式。
與其它編程語言不同,JavaScript 沒有訪問修飾符,也就是說,你不能將變量聲明為私有的或公共的。因此,模塊模式也可用來模擬封裝的概念。
模塊模式使用 IIFE(立即調用的函數表達式),閉包和函數作用域來模擬封裝的概念。例如:
const myModule = (function() {
const privateVariable = 'Hello World';
function privateMethod() {
console.log(privateVariable);
}
return {
publicMethod: function() {
privateMethod();
}
}
})();
myModule.publicMethod();
由于是 IIFE 因此代碼會被立即執行,并將返回對象賦值給了 myModule 變量。由于閉包,即使在 IIFE 完成后,返回的對象仍可以訪問 IIFE 內部定義的函數和變量。
因此,IIFE 內部定義的變量和函數對外部是看不見的,從而使其成為 myModule 模塊的私有成員。
執行代碼后,myModule 變量看起來像下面所示:
const myModule = {
publicMethod: function() {
privateMethod();
}};
因此當我們調用 publicMethod() 時候,它將調用 privateMethod() 例如:
// Prints 'Hello World'
module.publicMethod();
揭示模塊模式是 Christian Heilmann 對模塊模式的略微改進。模塊模式的問題在于,我們必須創建新的公共函數才能調用私有函數和變量。
在這種模式下,我們將返回的對象的屬性映射到要公開暴露的私有函數上。這就是為什么將其稱為揭示模塊模式。例如:
const myRevealingModule = (function() {
let privateVar = 'Peter';
const publicVar = 'Hello World';
function privateFunction() {
console.log('Name: '+ privateVar);
}
function publicSetName(name) {
privateVar = name;
}
function publicGetName() {
privateFunction();
}
/** reveal methods and variables by assigning them to object properties */
return {
setName: publicSetName,
greeting: publicVar,
getName: publicGetName
};
})();
myRevealingModule.setName('Mark');
// prints Name: Mark
myRevealingModule.getName();
這種模式讓我們更容易知道哪些函數和變量是公共的,無形中提高了代碼的可讀性。執行代碼后 myRevealingModule 看起來像下所示:
const myRevealingModule = {
setName: publicSetName,
greeting: publicVar,
getName: publicGetName
};
當我們調用 myRevealingModule.setName('Mark') 時,實際調用了內部的 publicSetName。當調用 myRevealingModule.getName() 時,實際調用了內部的 publicGetName 例如:
myRevealingModule.setName('Mark');
// prints Name: Mark
myRevealingModule.getName();
與模塊模式相比,揭示模塊模式的優勢有:
在 ES6 之前,JavaScript 沒有內置模塊,因此開發人員必須依靠第三方庫或模塊模式來實現模塊。但是自從 ES6,JavaScript 內置了模塊。
ES6 的模塊是以文件形式存儲的。每個文件只能有一個模塊。默認情況下,模塊內的所有內容都是私有的。通過使用 export 關鍵字來暴露函數、變量和類。模塊內的代碼始終在嚴格模式下運行。
有兩種方法可以導出函數和變量聲明:
// utils.js
export const greeting = 'Hello World';
export function sum(num1, num2) {
console.log('Sum:', num1, num2);
return num1 + num2;
}
export function subtract(num1, num2) {
console.log('Subtract:', num1, num2);
return num1 - num2;
}
// This is a private function
function privateLog() {
console.log('Private Function');
}
// utils.js
function multiply(num1, num2) {
console.log('Multiply:', num1, num2);
return num1 * num2;
}
function divide(num1, num2) {
console.log('Divide:', num1, num2);
return num1 / num2;
}
// This is a private function
function privateLog() {
console.log('Private Function');
}
export {multiply, divide};
與導出模塊相似,有兩種使用 import 關鍵字導入模塊的方法。例如:
// main.js
// importing multiple items
import { sum, multiply } from './utils.js';
console.log(sum(3, 7));
console.log(multiply(3, 7));
// main.js
// importing all of module
import * as utils from './utils.js';
console.log(utils.sum(3, 7));
console.log(utils.multiply(3, 7));
// utils.js
function sum(num1, num2) {
console.log('Sum:', num1, num2);
return num1 + num2;
}
function multiply(num1, num2) {
console.log('Multiply:', num1, num2);
return num1 * num2;
}
export {sum as add, multiply};
// main.js
import { add, multiply as mult } from './utils.js';
console.log(add(3, 7));
console.log(mult(3, 7));
一個單例對象是只能實例化一次的對象。如果不存在,則單例模式將創建類的新實例。如果存在實例,則僅返回對該對象的引用。重復調用構造函數將始終獲取同一對象。
JavaScript 是一直內置單例的語言。我們只是不稱它們為單例,我們稱它們為對象字面量。例如:
const user = {
name: 'Peter',
age: 25,
job: 'Teacher',
greet: function() {
console.log('Hello!');
}
};
因為 JavaScript 中的每個對象都占用一個唯一的內存位置,并且當我們調用該 user 對象時,實際上是在返回該對象的引用。
如果我們嘗試將 user 變量復制到另一個變量并修改該變量。例如:
const user1 = user;
user1.name = 'Mark';
我們將看到兩個對象都被修改,因為 JavaScript 中的對象是通過引用而不是通過值傳遞的。因此,內存中只有一個對象。例如:
// prints 'Mark'
console.log(user.name);
// prints 'Mark'
console.log(user1.name);
// prints true
console.log(user === user1);
可以使用構造函數來實現單例模式。例如:
let instance = null;
function User() {
if(instance) {
return instance;
}
instance = this;
this.name = 'Peter';
this.age = 25;
return instance;
}
const user1 = new User();
const user2 = new User();
// prints true
console.log(user1 === user2);
調用此構造函數時,它將檢查 instance 對象是否存在。如果對象不存在,則將 this 變量分配給 instance 變量。如果該對象存在,則只返回該對象。
單例也可以使用模塊模式來實現。例如:
const singleton = (function() {
let instance;
function init() {
return {
name: 'Peter',
age: 24,
};
}
return {
getInstance: function() {
if(!instance) {
instance = init();
}
return instance;
}
}
})();
const instanceA = singleton.getInstance();
const instanceB = singleton.getInstance();
// prints true
console.log(instanceA === instanceB);
在上面的代碼中,我們通過調用 singleton.getInstance 方法來創建一個新實例。如果實例已經存在,則此方法僅返回該實例。如果該實例不存在,則通過調用該 init() 函數創建一個新實例。
工廠模式使用工廠方法創建對象而不需要指定具體的類或構造函數的模式。
工廠模式用于創建對象而不需要暴露實例化的邏輯。當我們需要根據特定條件生成不同的對象時,可以使用此模式。例如:
class Car{
constructor(options) {
this.doors = options.doors || 4;
this.state = options.state || 'brand new';
this.color = options.color || 'white';
}
}
class Truck {
constructor(options) {
this.doors = options.doors || 4;
this.state = options.state || 'used';
this.color = options.color || 'black';
}
}
class VehicleFactory {
createVehicle(options) {
if(options.vehicleType === 'car') {
return new Car(options);
} else if(options.vehicleType === 'truck') {
return new Truck(options);
}
}
}
這里,創建了一個 Car 和一個 Truck 類(具有一些默認值),該類用于創建新的 car 和 truck對象。而且定義了一個VehicleFactory 類,用來根據 options 對象中的 vehicleType 屬性來創建和返回新的對象。
const factory = new VehicleFactory();
const car = factory.createVehicle({
vehicleType: 'car',
doors: 4,
color: 'silver',
state: 'Brand New'
});
const truck= factory.createVehicle({
vehicleType: 'truck',
doors: 2,
color: 'white',
state: 'used'
});
// Prints Car {doors: 4, state: "Brand New", color: "silver"}
console.log(car);
// Prints Truck {doors: 2, state: "used", color: "white"}
console.log(truck);
我為類 VehicleFactory 創建了一個新的 factory 對象。然后,我們通過調用 factory.createVehicle 方法并且傳遞 options 對象,其 vehicleType 屬性可能為 car 或者 truck 來創建新 Car 或 Truck 對象。
裝飾器模式用于擴展對象的功能,而無需修改現有的類或構造函數。此模式可用于將特征添加到對象中,而無需修改底層的代碼。
此模式的一個簡單示例為:
function Car(name) {
this.name = name;
// Default values
this.color = 'White';
}
// Creating a new Object to decorate
const tesla= new Car('Tesla Model 3');
// Decorating the object with new functionality
tesla.setColor = function(color) {
this.color = color;
}
tesla.setPrice = function(price) {
this.price = price;
}
tesla.setColor('black');
tesla.setPrice(49000);
// prints black
console.log(tesla.color);
這種模式的一個更實際的例子是:
假設汽車的成本取決于其功能的數量。如果沒有裝飾器模式,我們將不得不為不同的功能組合創建不同的類,每個類都有一個 cost 方法來計算成本。例如:
class Car() {
}
class CarWithAC() {
}
class CarWithAutoTransmission {
}
class CarWithPowerLocks {
}
class CarWithACandPowerLocks {
}
但是,通過裝飾器模式,我們可以創建一個基類 car 并且通過裝飾器函數給不同的對象添加對應的成本邏輯。
class Car {
constructor() {
// Default Cost
this.cost = function() {
return 20000;
}
}
}
// Decorator function
function carWithAC(car) {
car.hasAC = true;
const prevCost = car.cost();
car.cost = function() {
return prevCost + 500;
}
}
// Decorator function
function carWithAutoTransmission(car) {
car.hasAutoTransmission = true;
const prevCost = car.cost();
car.cost = function() {
return prevCost + 2000;
}
}
// Decorator function
function carWithPowerLocks(car) {
car.hasPowerLocks = true;
const prevCost = car.cost();
car.cost = function() {
return prevCost + 500;
}
}
首先,我們創建了小轎車的基類 Car。然后針對要添加的特性創建了裝飾器并且此裝飾器以 Car 對象為參數。然后通過返回更新后的小汽車成本來覆蓋對象的成本函數,且添加了一個用來標識某個特性是否已經被添加的屬性。
要添加新的功能,我們只需要像下面一樣就可以:
const car = new Car();
console.log(car.cost());
carWithAC(car);
carWithAutoTransmission(car);
carWithPowerLocks(car);
最后,我們可以像這樣計算汽車的成本:
// Calculating total cost of the car
console.log(car.cost());
我們已經了解了 JavaScript 中使用的各種設計模式,但是這里沒有涉及到可以用 JavaScript 實現的設計模式。
盡管了解各種設計模式很重要,但不要過度使用它們也同樣重要。在使用設計模式之前,你應該仔細考慮你的問題是否適合該設計模式。要知道某個模式是否適合你的問題,應該好好研究該設計模式以及它的應用。
最后,歡迎優秀的你加入 HelloGitHub 的「譯文亦舞」系列,讓你的才華舞動起來!把優秀的文章分享給更多的人。要求:
關注 HelloGitHub 頭條號第一時間收到推送
*請認真填寫需求信息,我們會在24小時內與您取得聯系。