1趟大、IOC與DI
IOC和DI分別是控制反轉(zhuǎn)和依賴(lài)注入门粪。所謂的控制反轉(zhuǎn)就是將對(duì)象的創(chuàng)建權(quán)交給框架,而依賴(lài)注入則是利用框架設(shè)置屬性毛雇。在MCV框架中嫉称,有兩種方式進(jìn)行IOC和DI:
1、xml的bean配置
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/page"/>
<property name="suffix" value=".jsp"/>
</bean>
bean在之前已經(jīng)解釋過(guò)灵疮,class后面是完整類(lèi)名织阅,name是屬性名,value是屬性值震捣。
2荔棉、包掃描和四類(lèi)注解
在開(kāi)始使用mvc框架的時(shí)候,我配置了包掃描路徑為controller包蒿赢,現(xiàn)在將這個(gè)掃描路徑擴(kuò)大
<!--包掃描-->
<context:component-scan base-package="com.fan.*"/>
在mvc中润樱,有四種注解可以幫助我們創(chuàng)建對(duì)象
//dao層用注解
@Repository
//service層注解
@Service
//controller層注解
@Controller
//不是經(jīng)典三層的其它類(lèi)注解
@Component
目前來(lái)說(shuō),這四種注解的作用是一樣的羡棵,但是不保證以后mvc框架針對(duì)不同注解有另外的優(yōu)化壹若,所以這里遵循約定,嚴(yán)格按照所屬層級(jí)使用注解晾腔。
3舌稀、使用框架創(chuàng)建的實(shí)例
為了驗(yàn)證1和2方法是否真的創(chuàng)建了一個(gè)實(shí)例,編寫(xiě)一個(gè)類(lèi):
package com.fan.dao;
import cn.hutool.db.Db;
import com.fan.entity.ForumUser;
import org.springframework.stereotype.Repository;
import java.sql.SQLException;
import java.util.List;
@Repository
public class UserDao {
public UserService(){
System.out.println("UserDao正在實(shí)例化灼擂!");
}
//根據(jù)賬戶(hù)和密碼獲取用戶(hù)(登錄驗(yàn)證)
public ForumUser selectUserByAccountAndPassword(String account, String password) {
String sql = "select * from forumUser where account= ? and password=?;" ;
try {
List<ForumUser> list = Db.use().query(sql, ForumUser.class,account,password);
if (list.size() > 0) {
return list.get(0);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
當(dāng)服務(wù)器啟動(dòng)用戶(hù)登錄時(shí)壁查,控制臺(tái)會(huì)輸出UserDao正在實(shí)例化!利用bean配置會(huì)得到同樣的結(jié)果剔应。那么如何將框架實(shí)例化出來(lái)的對(duì)象取出來(lái)使用呢睡腿?這里有一個(gè)注解@Autowiredprivate语御,將這個(gè)注解寫(xiě)在對(duì)象上,這個(gè)對(duì)象的引用就會(huì)指向框架創(chuàng)建出來(lái)的對(duì)象的引用席怪。
//此時(shí)应闯,這個(gè)userDao已經(jīng)不是空引用了,可以調(diào)用方法和屬性
@Autowired
private UserDao userDao;
2挂捻、經(jīng)典三層結(jié)構(gòu)
在mvc框架中碉纺,有一個(gè)經(jīng)典的三層結(jié)構(gòu):控制層->業(yè)務(wù)層->數(shù)據(jù)層,這三層結(jié)構(gòu)理論上是一層一層往下調(diào)用的刻撒,不允許跨層調(diào)用骨田。也就是說(shuō),控制層調(diào)用業(yè)務(wù)層的對(duì)象声怔,業(yè)務(wù)層調(diào)用數(shù)據(jù)層态贤,但是控制層不能夠直接調(diào)用數(shù)據(jù)層。而且控制層不能夠出現(xiàn)數(shù)據(jù)相關(guān)的操作醋火,這里主要是指與數(shù)據(jù)庫(kù)相關(guān)的語(yǔ)句悠汽,這些操作應(yīng)該被放在數(shù)據(jù)層。就拿用戶(hù)登錄驗(yàn)證舉例:
//dao層-數(shù)據(jù)層
package com.fan.dao;
import cn.hutool.db.Db;
import com.fan.entity.ForumUser;
import org.springframework.stereotype.Repository;
import java.sql.SQLException;
import java.util.List;
//IOC
@Repository
public class UserDao {
//根據(jù)賬戶(hù)和密碼獲取用戶(hù)(登錄驗(yàn)證)
public ForumUser selectUserByAccountAndPassword(String account, String password) {
String sql = "select * from forumUser where account= ? and password=?;" ;
try {
List<ForumUser> list = Db.use().query(sql, ForumUser.class,account,password);
if (list.size() > 0) {
return list.get(0);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
//service層-服務(wù)層
package com.fan.service;
import com.fan.dao.UserDao;
import com.fan.entity.ForumUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
//IOC
@Service
public class UserService {
//取出框架里的UserDao實(shí)例
@Autowired
private UserDao userDao;
//根據(jù)賬戶(hù)和密碼獲取用戶(hù)(登錄驗(yàn)證)
public ForumUser selectUserByAccountAndPassword(String account, String password) {
//service層調(diào)用dao層方法
return userDao.selectUserByAccountAndPassword(account, password);
}
}
//控制層-controller層
package com.fan.controller;
import com.fan.entity.ForumUser;
import com.fan.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
//IOC
@Controller
@RequestMapping("/login")
public class LoginController {
//取出實(shí)例
@Autowired
private UserService userService;
@RequestMapping("/verify")
public ModelAndView login(String account, String password, HttpServletRequest req) {
ModelAndView mav = new ModelAndView("/error");
//調(diào)用service層方法
ForumUser forumUser = userService.selectUserByAccountAndPassword(account, password);
try {
if (forumUser != null) {
req.getSession().setAttribute("ForumUser", account);
mav = new ModelAndView("/loginsuccess");
}
} catch (Exception e) {
e.printStackTrace();
}
return mav;
}
}
當(dāng)然芥驳,三層分層并不是必須遵守的柿冲,逐層調(diào)用也不是強(qiáng)制規(guī)范,一切設(shè)計(jì)都要根據(jù)實(shí)際情況來(lái)晚树。
3姻采、線(xiàn)程安全
關(guān)于多線(xiàn)程的安全問(wèn)題,之前已經(jīng)討論過(guò)爵憎,這里簡(jiǎn)單說(shuō)一下慨亲。在mvc框架中,IOC創(chuàng)建對(duì)象使用的是單例模式宝鼓,所以打上注解或者bean配置的類(lèi)刑棵,最終只會(huì)生成一個(gè)實(shí)例。對(duì)于非靜態(tài)的方法來(lái)說(shuō)愚铡,如果每個(gè)線(xiàn)程都能生成各自的實(shí)例蛉签,那么線(xiàn)程就是安全的,如果是共用一個(gè)實(shí)例沥寥,也就是框架生成的單例碍舍,那么就有可能出現(xiàn)線(xiàn)程安全問(wèn)題。對(duì)于這種情況可以使用棧封閉邑雅、鎖和ThreadLocale封閉片橡。靜態(tài)方法本身也是線(xiàn)程安全的,只要不去操作外部的靜態(tài)變量淮野,那么就可以在多線(xiàn)程中直接使用捧书。
4吹泡、Service層與Dao層的分工
現(xiàn)有一論壇,要求用戶(hù)可以刪除自己的帖子经瓷,并在刪除帖子時(shí)要將對(duì)應(yīng)跟帖一并刪除爆哑。帖子和跟帖分別在兩張表中,跟帖表中有用戶(hù)uid和帖子pid舆吮。在PostDao和CommentDao分別寫(xiě)根據(jù)帖子pid刪除內(nèi)容的方法
@Autowired
private JdbcTemplate jdbcTemplate;
//CommentDao層根據(jù)帖子id刪除對(duì)應(yīng)評(píng)論
public void deleteCommentByPid(int pid) {
String sql = "delete from cmt where pid=?";
jdbcTemplate.update(sql, pid);
}
//PostDao層根據(jù)帖子id刪除用戶(hù)帖子
public void deletePostById(int id) {
String sql="delete from post where id=?";
jdbcTemplate.update(sql,id);
}
刪除帖子并附帶刪除其回帖屬于帖子的業(yè)務(wù)揭朝,所以在PostService對(duì)這兩個(gè)Dao進(jìn)行調(diào)用
@Autowired
private PostDao postDao;
@Autowired
private CommentDao commentDao;
//事務(wù)管理
@Autowired
private TransactionTemplate trans;
//根據(jù)帖子pid刪除帖子和該帖子的評(píng)論
public int deletePostById(int pid) {
int success = 0;
trans.execute(new TransactionCallback<>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
postDao.deletePostById(pid);
commentDao.deleteCommentByPid(pid);
return 1;
}
});
success = 1;
return success;
}
在這里即體現(xiàn)出框架分層的作用,對(duì)于不同的數(shù)據(jù)操作色冀,只需要組合調(diào)用dao層方法就可以實(shí)現(xiàn)業(yè)務(wù)萝勤,業(yè)務(wù)越復(fù)雜就越能體現(xiàn)出分層的好處,與此同時(shí)在controller層呐伞,對(duì)service的調(diào)用還可以進(jìn)一步優(yōu)化
@Autowired private PostService postService;
@RequestMapping("/deletePost")
public void deletePostById(int pid, int uid, HttpServletResponse resp, HttpSession session) throws IOException {
//從session域中取出論壇用戶(hù)數(shù)據(jù),將登陸用戶(hù)id與發(fā)帖喲用戶(hù)id比對(duì)慎式,只有兩者一直才可以刪帖
int aUid = session.getAttribute("ForumUser")).getId();
int success = 0;
if (aUid == uid) {
success = postService.deletePostById(pid);
}
resp.getWriter().print(success);
}
}