springmvc入门学习
2017.05.24:
spring控制器
Controller控制器,是MVC中的部分C,為什么是部分呢?因?yàn)榇颂幍目刂破髦饕?fù)責(zé)功能處理部分:
1、收集、驗(yàn)證請(qǐng)求參數(shù)并綁定到命令對(duì)象;
2、將命令對(duì)象交給業(yè)務(wù)對(duì)象,由業(yè)務(wù)對(duì)象處理并返回模型數(shù)據(jù);
3、返回ModelAndView(Model部分是業(yè)務(wù)對(duì)象返回的模型數(shù)據(jù),視圖部分為邏輯視圖名)。
還記得DispatcherServlet嗎?主要負(fù)責(zé)整體的控制流程的調(diào)度部分:
1、負(fù)責(zé)將請(qǐng)求委托給控制器進(jìn)行處理;
2、根據(jù)控制器返回的邏輯視圖名選擇具體的視圖進(jìn)行渲染(并把模型數(shù)據(jù)傳入)。
因此MVC中完整的C(包含控制邏輯+功能處理)由(DispatcherServlet + Controller)組成。
因此此處的控制器是Web MVC中部分,也可以稱為頁(yè)面控制器、動(dòng)作、處理器。
spring Web MVC支持多種類型的控制器,比如實(shí)現(xiàn)Controller接口,從Spring2.5開(kāi)始支持注解方式的控制器(如@Controller、@RequestMapping、@RequestParam、@ModelAttribute等),我們也可以自己實(shí)現(xiàn)相應(yīng)的控制器(只需要定義相應(yīng)的HandlerMapping和HandlerAdapter即可)。
三種常用控制器:
首先創(chuàng)建一個(gè)實(shí)體(model)
package cn.cfs.springmvc.domain;public class User {//主鍵private Integer id;//用戶名private String username;//密碼private String password;/*** 獲取 主鍵** @return 主鍵*/public Integer getId() {return id;}/*** 設(shè)置 主鍵** @param id 主鍵*/public void setId(Integer id) {this.id = id;}/*** 獲取 用戶名** @return 用戶名*/public String getUsername() {return username;}/*** 設(shè)置 用戶名** @param username 用戶名*/public void setUsername(String username) {this.username = username;}/*** 獲取 密碼** @return 密碼*/public String getPassword() {return password;}/*** 設(shè)置 密碼** @param password 密碼*/public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "User [id=" + id + ", username=" + username + ", password="+ password + "]";}}(一)CommandController命令控制器
package cn.cfs.springmvc.controller;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.validation.BindException; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractCommandController; import cn.cfs.springmvc.domain.User;public class UserCommandController extends AbstractCommandController {//綁定數(shù)據(jù)public UserCommandController() {this.setCommandClass(User.class);this.setCommandName("user");}@Overrideprotected ModelAndView handle(HttpServletRequest request,HttpServletResponse response, Object command, BindException errors)throws Exception {User user =(User)command;System.out.println(user.toString());return new ModelAndView("index");} }
action-servlet.xml配置文件中的代碼:
<!-- 采用className的方式去訪問(wèn) --> <bean id="userCommandController" class="cn.cfs.springmvc.controller.UserCommandController"></bean>
前臺(tái)通過(guò)訪問(wèn)url:http://192.168.1.253:8082/springmvc_01/usercommandcontroller.action?id=99&username=aaa&password=ssss?來(lái)測(cè)試
控制臺(tái)打印語(yǔ)句:User [id=99, username=aaa, password=ssss]
(二)SimpleFormController簡(jiǎn)單表單控制器
package cn.cfs.springmvc.controller;import org.springframework.web.servlet.mvc.SimpleFormController; import cn.cfs.springmvc.domain.User;public class UserFormController extends SimpleFormController {//在構(gòu)造方法里去綁定public UserFormController() {this.setCommandClass(User.class);this.setCommandName("user");}@Overrideprotected void doSubmitAction(Object command) throws Exception {User user=(User)command;System.out.println(user.toString());} }
action-servlet.xml配置文件中的代碼:
<!-- 采用className的方式去訪問(wèn) --><bean id="userFormController" class="cn.cfs.springmvc.controller.UserFormController"><property name="formView" value="login" /><property name="successView" value="index" /></bean>
formView 是通過(guò)get方法訪問(wèn)這個(gè)請(qǐng)求處理后跳轉(zhuǎn)的頁(yè)面,successView是通過(guò)post方法訪問(wèn)這個(gè)請(qǐng)求處理后跳轉(zhuǎn)的頁(yè)面。舉個(gè)例子,例如添加用戶,肯定先要跳轉(zhuǎn)到添加用戶的頁(yè)面上,然后在表單中填寫一些用戶信息,點(diǎn)擊保存提交按鈕跳轉(zhuǎn)到控制器中,處理完自己的業(yè)務(wù)代碼再跳到指定頁(yè)面,這種簡(jiǎn)單表單方式就是為了解決這種需求。
訪問(wèn)路徑:http://192.168.1.253:8082/springmvc_01/userformcontroller.action?跳轉(zhuǎn)到添加頁(yè)面的地址,因?yàn)槭侵苯釉跒g覽器敲的所以是get方式訪問(wèn)的,會(huì)跳轉(zhuǎn)到login.jsp頁(yè)面上
login.jsp代碼:
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>登錄</title> </head> <body><form action="${pageContext.request.contextPath}/userformcontroller.action" method="post"><table><tr><td>用戶名:</td><td><input type="text" name="username" /></td></tr><tr><td>密碼:</td><td><input type="password" name="password" /></td></tr><tr><td colspan="2"><input type="submit" value="保存提交" /></td></tr></table></form></body> </html>根據(jù)上面login.jsp中代碼所示,輸入完用戶名和密碼后,點(diǎn)擊保存提交按鈕,通過(guò)form中post的方式將表單內(nèi)容提交到userformcontroller.action這個(gè)請(qǐng)求上,回通過(guò)控制器找到doSubmitAction這個(gè)方法,在這個(gè)方法中處理自己業(yè)務(wù)邏輯,最后跳轉(zhuǎn)到index.jsp頁(yè)面上,最后總結(jié)一下,這種方式是通過(guò)get和post提交方式來(lái)區(qū)分跳轉(zhuǎn)的頁(yè)面的,只有post提交方式才會(huì)跳轉(zhuǎn)到帶有處理自己業(yè)務(wù)邏輯的后臺(tái)方法中。
控制臺(tái)打印語(yǔ)句:User [id=null, username=aaaa, password=123456]
(三) WizardFormController向?qū)Э刂破?/span> package cn.cfs.springmvc.controller;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.validation.BindException; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractWizardFormController; import cn.cfs.springmvc.domain.User;public class UserWizardController extends AbstractWizardFormController {//綁定數(shù)據(jù)public UserWizardController() {this.setCommandClass(User.class);this.setCommandName("user");}/*** 完成提交的函數(shù)*/@Overrideprotected ModelAndView processFinish(HttpServletRequest request,HttpServletResponse response, Object command, BindException errors)throws Exception {User user=(User)command;System.out.println(user.toString());return new ModelAndView("index");}/*** 取消方法,配置點(diǎn)擊取消后要跳轉(zhuǎn)的頁(yè)面*/@Overrideprotected ModelAndView processCancel(HttpServletRequest request,HttpServletResponse response, Object command, BindException errors)throws Exception {return new ModelAndView("index");} }action-servlet.xml配置文件中的代碼:
<!-- 采用className的方式去訪問(wèn) --><bean id="userwizardcontroller" class="cn.cfs.springmvc.controller.UserWizardController"><property name="pages"><list><value>/wizard/1</value><value>/wizard/2</value><value>/wizard/3</value></list></property></bean>
list下的value為跳轉(zhuǎn)的頁(yè)面,對(duì)應(yīng)的上下文中會(huì)創(chuàng)建一個(gè)叫wizard的文件夾,以及對(duì)應(yīng)的3個(gè)頁(yè)面文件
訪問(wèn)路徑:http://192.168.1.253:8082/springmvc_01/userwizardcontroller.action?會(huì)按照l(shuí)ist中value的先后順序去加載頁(yè)面
wizard/1.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>向?qū)П韱慰刂破?lt;/title> </head> <body><form action="${pageContext.request.contextPath}/userwizardcontroller.action" method="post"><table><tr><td>第一個(gè)頁(yè)面,id<input type="text" name="id" value="${user.id}" /> </td></tr><tr><td><input type="submit" name="_target1" value="下一步" /><input type="submit" name="_cancel" value="取消" /></td></tr></table></form></body> </html>wizard/2.jsp<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>向?qū)П韱慰刂破?lt;/title> </head> <body><form action="${pageContext.request.contextPath}/userwizardcontroller.action" method="post"><table><tr><td>第二個(gè)頁(yè)面,用戶名<input type="text" name="username" value="${user.username}"/> </td></tr><tr><td><input type="submit" name="_target0" value="上一步" /><input type="submit" name="_cancel" value="取消" /><input type="submit" name="_target2" value="下一步" /></td></tr></table></form></body> </html>
wizard/3.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>向?qū)П韱慰刂破?lt;/title> </head> <body><form action="${pageContext.request.contextPath}/userwizardcontroller.action" method="post"><table><tr><td>第三個(gè)頁(yè)面,密碼<input type="text" name="password" value="${user.password}" /> </td></tr><tr><td><input type="submit" name="_target1" value="上一步" /><input type="submit" name="_finish" value="完成" /><input type="submit" name="_cancel" value="取消" /></td></tr></table></form></body> </html>
上面的jsp文件中主要是通過(guò) 提交按鈕的 name名稱去控制上下頁(yè),取消,完成操作的,詳細(xì)請(qǐng)看下面的表格(1-1);
| 屬性 | 具體的的含義 |
| _targetN | N是可變化的,指具體的頁(yè)碼,從0開(kāi)始,例如當(dāng)前為第二頁(yè),上一頁(yè)則為_(kāi)target0,下一頁(yè)則為_(kāi)target1 |
| _cancel | 取消,點(diǎn)擊后請(qǐng)求發(fā)送到后臺(tái)的取消方法中,根據(jù)具體的配置跳轉(zhuǎn)到指定的取消頁(yè)面 |
| _finish | 完成,點(diǎn)擊后請(qǐng)求發(fā)送到后臺(tái)的完成方法中,會(huì)自動(dòng)將向?qū)ы?yè)的表單數(shù)據(jù)做拼接,根據(jù)具體的配置跳轉(zhuǎn)到指定的完成請(qǐng)求處理后的頁(yè)面 |
上述為當(dāng)前springmvc比較常用的控制器,當(dāng)然還有很多,例如默認(rèn)的AbstractController這是最簡(jiǎn)單控制器,很多參數(shù)都需要通過(guò)原始的方法去獲取,后續(xù)還待完善。
數(shù)據(jù)的交互(獲取頁(yè)面數(shù)據(jù)與將數(shù)據(jù)返回到頁(yè)面)
一、從頁(yè)面接收參數(shù)
Spring MVC接收請(qǐng)求提交的參數(shù)值的幾種方法
-
使用HttpServletRequest獲取
@RequestMapping("/login.do") public String login(HttpServletRequest request) { String name = request.getParameter("name") String pass = request.getParameter("pass") } -
使用@RequestParam注解,和表單的name屬性保持一致
@RequestMapping("/login.do") public String login(HttpServletRequest request, @RequestParam("pass") String name, @RequestParam("pass") String password) { // 表單屬性是pass,用變量password接收 syso(name); syso(password) } -
自動(dòng)注入Bean屬性
<form action="login.do"> 用戶名:<input name="name"/> 密碼:<input name="pass"/> <input type="submit" value="登陸"> </form> //封裝的User類 public class User{ private String name; private String pass; } @RequestMapping("/login.do") public String login(User user) { syso(user.getName()); syso(user.getPass()); }
二、向頁(yè)面?zhèn)髦?/span>
使用Map、Model和ModelMap
Java代碼:
JSP頁(yè)面:
1、time:${requestScope.time}<br/>2、names:${requestScope.names }<br/>3、city:${requestScope.city }<br/>4、gender:${requestScope.gender }Session存儲(chǔ):可以利用HttpServletReequest的getSession()方法
@RequestMapping("/login.do") public String login(String name,String pwd ModelMap model,HttpServletRequest request){ User user = serService.login(name,pwd); HttpSession session = request.getSession(); session.setAttribute("user",user); model.addAttribute("user",user); return "success"; }Spring MVC 默認(rèn)采用的是轉(zhuǎn)發(fā)來(lái)定位視圖,如果要使用重定向,可以如下操作
或者用如下方法,工作中常用的方法:
public String login(){ //TODO return "redirect:regirst.do"; }2017.05.26
spring DAO
Spring提供的DAO(數(shù)據(jù)訪問(wèn)對(duì)象)支持主要的目的是便于以標(biāo)準(zhǔn)的方式使用不同的數(shù)據(jù)訪問(wèn)技術(shù), 如JDBC,Hibernate或者JDO等。它不僅可以讓你方便地在這些持久化技術(shù)間切換, 而且讓你在編碼的時(shí)候不用考慮處理各種技術(shù)中特定的異常。
為了便于以一種一致的方式使用各種數(shù)據(jù)訪問(wèn)技術(shù),如JDBC、JDO和Hibernate, Spring提供了一套抽象DAO類供你擴(kuò)展。這些抽象類提供了一些方法,通過(guò)它們你可以 獲得與你當(dāng)前使用的數(shù)據(jù)訪問(wèn)技術(shù)相關(guān)的數(shù)據(jù)源和其他配置信息。
Dao支持類:
JdbcDaoSupport - JDBC數(shù)據(jù)訪問(wèn)對(duì)象的基類。 需要一個(gè)DataSource,同時(shí)為子類提供 JdbcTemplate。
HibernateDaoSupport - Hibernate數(shù)據(jù)訪問(wèn)對(duì)象的基類。 需要一個(gè)SessionFactory,同時(shí)為子類提供 HibernateTemplate。也可以選擇直接通過(guò) 提供一個(gè)HibernateTemplate來(lái)初始化, 這樣就可以重用后者的設(shè)置,例如SessionFactory, flush模式,異常翻譯器(exception translator)等等。
JdoDaoSupport - JDO數(shù)據(jù)訪問(wèn)對(duì)象的基類。 需要設(shè)置一個(gè)PersistenceManagerFactory, 同時(shí)為子類提供JdoTemplate。?
JpaDaoSupport - JPA數(shù)據(jù)訪問(wèn)對(duì)象的基類。 需要一個(gè)EntityManagerFactory,同時(shí) 為子類提供JpaTemplate。?
(1)Sping對(duì)JdbcDaoSupport的支持
數(shù)據(jù)庫(kù)建表:
drop table if exists user; /*==============================================================*/ /* Table: user */ /*==============================================================*/ create table user ( id bigint AUTO_INCREMENT not null, name varchar(24), age int, primary key (id) );public class User { private Integer id; private String name; private Integer age; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource" singleton="true"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/springdb</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>leizhimin</value> </property> </bean> <bean id="baseDAO" class="com.lavasoft.springnote.ch05_jdbc03_temp.BaseDAO" abstract="true"><property name="dataSource"> <ref bean="dataSource"/> </property> </bean> <bean id="userDAO"class="com.lavasoft.springnote.ch05_jdbc03_temp.UserDAO" parent="baseDAO"> </bean> </beans>
import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class SpringDAODemo { public static void main(String[] args) { ApplicationContext context = new FileSystemXmlApplicationContext("D:\\_spring\\src\\com\\lavasoft\\springnote\\ch05_jdbc03_temp\\bean-jdbc-temp.xml"); User user = new User(); user.setName("hahhahah"); user.setAge(new Integer(22)); IUserDAO userDAO = (IUserDAO) context.getBean("userDAO"); userDAO.insert(user); user = userDAO.find(new Integer(1)); System.out.println("name: " + user.getName()); } }
(2) Spring DAO之Hibernate
HibernateDaoSupport - Hibernate數(shù)據(jù)訪問(wèn)對(duì)象的基類。 需要一個(gè)SessionFactory,同時(shí)為子類提供 HibernateTemplate。也可以選擇直接通過(guò) 提供一個(gè)HibernateTemplate來(lái)初始化, 這樣就可以重用后者的設(shè)置,例如SessionFactory, flush模式,異常翻譯器(exception translator)等等。
drop table if exists user; /*==============================================================*/ /* Table: user */ /*==============================================================*/ create table user ( id bigint AUTO_INCREMENT not null, name varchar(24), age int, primary key (id) );
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.lavasoft.springnote.ch06_hbm_02deTx.User"table="user"> <id name="id" column="id"> <generator class="native"/> </id> <property name="name" column="name"/> <property name="age" column="age"/> </class> </hibernate-mapping>
public interface IUserDAO { public void insert(User user); public User find(Integer id); } import org.hibernate.SessionFactory; import org.springframework.orm.hibernate3.HibernateTemplate; /** * Created by IntelliJ IDEA.<br> * <b>User</b>: leizhimin<br> * <b>Date</b>: 2008-4-23 15:15:55<br> * <b>Note</b>: DAO實(shí)現(xiàn) */ public class UserDAO implements IUserDAO { private HibernateTemplate hibernateTemplate; public void setSessionFactory(SessionFactory sessionFactory) { this.hibernateTemplate =new HibernateTemplate(sessionFactory); } public void insert(User user) { hibernateTemplate.save(user); System.out.println("所保存的User對(duì)象的ID:"+user.getId()); } public User find(Integer id) { User user =(User) hibernateTemplate.get(User.class, id); return user; } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/springdb</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>leizhimin</value> </property> </bean> <bean id="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"destroy-method="close"> <property name="dataSource"> <ref bean="dataSource"/> </property> <property name="mappingResources"> <list> <value>com/lavasoft/springnote/ch06_hbm_02proTx/User.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </prop> </props> </property> </bean> <bean id="userDAO" class="com.lavasoft.springnote.ch06_hbm_02proTx.UserDAO"> <property name="sessionFactory"> <ref bean="sessionFactory"/> </property> </bean> </beans>
import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class SpringHibernateDemo { public static void main(String[] args) { ApplicationContext context =new FileSystemXmlApplicationContext("D:\\_spring\\src\\com\\lavasoft\\springnote\\ch06_hbm_02proTx\\bean-hbm_tx.xml"); // 建立DAO物件 IUserDAO userDAO = (IUserDAO) context.getBean("userDAO"); User user = new User(); user.setName("caterpillar"); user.setAge(new Integer(30)); userDAO.insert(user); user = userDAO.find(new Integer(1)); System.out.println("name: " + user.getName()); } }
2017.05.27
AOP
AOP(Aspect Oriented Programming),即面向切面編程,可以說(shuō)是OOP(Object Oriented Programming,面向?qū)ο缶幊?#xff09;的補(bǔ)充和完善。OOP引入封裝、繼承、多態(tài)等概念來(lái)建立一種對(duì)象層次結(jié)構(gòu),用于模擬公共行為的一個(gè)集合。不過(guò)OOP允許開(kāi)發(fā)者定義縱向的關(guān)系,但并不適合定義橫向的關(guān)系,例如日志功能。日志代碼往往橫向地散布在所有對(duì)象層次中,而與它對(duì)應(yīng)的對(duì)象的核心功能毫無(wú)關(guān)系對(duì)于其他類型的代碼,如安全性、異常處理和透明的持續(xù)性也都是如此,這種散布在各處的無(wú)關(guān)的代碼被稱為橫切(cross cutting),在OOP設(shè)計(jì)中,它導(dǎo)致了大量代碼的重復(fù),而不利于各個(gè)模塊的重用。
AOP技術(shù)恰恰相反,它利用一種稱為"橫切"的技術(shù),剖解開(kāi)封裝的對(duì)象內(nèi)部,并將那些影響了多個(gè)類的公共行為封裝到一個(gè)可重用模塊,并將其命名為"Aspect",即切面。所謂"切面",簡(jiǎn)單說(shuō)就是那些與業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼,降低模塊之間的耦合度,并有利于未來(lái)的可操作性和可維護(hù)性。
使用"橫切"技術(shù),AOP把軟件系統(tǒng)分為兩個(gè)部分:核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)。業(yè)務(wù)處理的主要流程是核心關(guān)注點(diǎn),與之關(guān)系不大的部分是橫切關(guān)注點(diǎn)。橫切關(guān)注點(diǎn)的一個(gè)特點(diǎn)是,他們經(jīng)常發(fā)生在核心關(guān)注點(diǎn)的多處,而各處基本相似,比如權(quán)限認(rèn)證、日志、事物。AOP的作用在于分離系統(tǒng)中的各種關(guān)注點(diǎn),將核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)分離開(kāi)來(lái)。
AOP核心概念
1、橫切關(guān)注點(diǎn)
對(duì)哪些方法進(jìn)行攔截,攔截后怎么處理,這些關(guān)注點(diǎn)稱之為橫切關(guān)注點(diǎn)
2、切面(aspect)
類是對(duì)物體特征的抽象,切面就是對(duì)橫切關(guān)注點(diǎn)的抽象
3、連接點(diǎn)(joinpoint)
被攔截到的點(diǎn),因?yàn)镾pring只支持方法類型的連接點(diǎn),所以在Spring中連接點(diǎn)指的就是被攔截到的方法,實(shí)際上連接點(diǎn)還可以是字段或者構(gòu)造器
4、切入點(diǎn)(pointcut)
對(duì)連接點(diǎn)進(jìn)行攔截的定義
5、通知(advice)
所謂通知指的就是指攔截到連接點(diǎn)之后要執(zhí)行的代碼,通知分為前置、后置、異常、最終、環(huán)繞通知五類
6、目標(biāo)對(duì)象
代理的目標(biāo)對(duì)象
7、織入(weave)
將切面應(yīng)用到目標(biāo)對(duì)象并導(dǎo)致代理對(duì)象創(chuàng)建的過(guò)程
8、引入(introduction)
在不修改代碼的前提下,引入可以在運(yùn)行期為類動(dòng)態(tài)地添加一些方法或字段
Spring對(duì)AOP的支持
Spring中AOP代理由Spring的IOC容器負(fù)責(zé)生成、管理,其依賴關(guān)系也由IOC容器負(fù)責(zé)管理。因此,AOP代理可以直接使用容器中的其它bean實(shí)例作為目標(biāo),這種關(guān)系可由IOC容器的依賴注入提供。Spring創(chuàng)建代理的規(guī)則為:
1、默認(rèn)使用Java動(dòng)態(tài)代理來(lái)創(chuàng)建AOP代理,這樣就可以為任何接口實(shí)例創(chuàng)建代理了
2、當(dāng)需要代理的類不是代理接口的時(shí)候,Spring會(huì)切換為使用CGLIB代理,也可強(qiáng)制使用CGLIB
AOP編程其實(shí)是很簡(jiǎn)單的事情,縱觀AOP編程,程序員只需要參與三個(gè)部分:
1、定義普通業(yè)務(wù)組件
2、定義切入點(diǎn),一個(gè)切入點(diǎn)可能橫切多個(gè)業(yè)務(wù)組件
3、定義增強(qiáng)處理,增強(qiáng)處理就是在AOP框架為普通業(yè)務(wù)組件織入的處理動(dòng)作
所以進(jìn)行AOP編程的關(guān)鍵就是定義切入點(diǎn)和定義增強(qiáng)處理,一旦定義了合適的切入點(diǎn)和增強(qiáng)處理,AOP框架將自動(dòng)生成AOP代理,即:代理對(duì)象的方法=增強(qiáng)處理+被代理對(duì)象的方法。
下面給出一個(gè)Spring AOP的.xml文件模板,名字叫做aop.xml,之后的內(nèi)容都在aop.xml上進(jìn)行擴(kuò)展:
<?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:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.2.xsd"></beans>基于Spring的AOP簡(jiǎn)單實(shí)現(xiàn)
注意一下,在講解之前,說(shuō)明一點(diǎn):使用Spring AOP,要成功運(yùn)行起代碼,只用Spring提供給開(kāi)發(fā)者的jar包是不夠的,請(qǐng)額外上網(wǎng)下載兩個(gè)jar包:
1、aopalliance.jar
2、aspectjweaver.jar
開(kāi)始講解用Spring AOP的XML實(shí)現(xiàn)方式,先定義一個(gè)接口:
public interface HelloWorld {void printHelloWorld();void doPrint(); }定義兩個(gè)接口實(shí)現(xiàn)類:
public class HelloWorldImpl1 implements HelloWorld {public void printHelloWorld(){System.out.println("Enter HelloWorldImpl1.printHelloWorld()");}public void doPrint(){System.out.println("Enter HelloWorldImpl1.doPrint()");return ;} } public class HelloWorldImpl2 implements HelloWorld {public void printHelloWorld(){System.out.println("Enter HelloWorldImpl2.printHelloWorld()");}public void doPrint(){System.out.println("Enter HelloWorldImpl2.doPrint()");return ;} }橫切關(guān)注點(diǎn),這里是打印時(shí)間:
public class TimeHandler {public void printTime(){System.out.println("CurrentTime = " + System.currentTimeMillis());} }有這三個(gè)類就可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Spring AOP了,看一下aop.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:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.2.xsd"><bean id="helloWorldImpl1" class="com.xrq.aop.HelloWorldImpl1" /><bean id="helloWorldImpl2" class="com.xrq.aop.HelloWorldImpl2" /><bean id="timeHandler" class="com.xrq.aop.TimeHandler" /><aop:config><aop:aspect id="time" ref="timeHandler"><aop:pointcut id="addAllMethod" expression="execution(* com.xrq.aop.HelloWorld.*(..))" /><aop:before method="printTime" pointcut-ref="addAllMethod" /><aop:after method="printTime" pointcut-ref="addAllMethod" /></aop:aspect></aop:config> </beans>寫一個(gè)main函數(shù)調(diào)用一下:
public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml");HelloWorld hw1 = (HelloWorld)ctx.getBean("helloWorldImpl1");HelloWorld hw2 = (HelloWorld)ctx.getBean("helloWorldImpl2");hw1.printHelloWorld();System.out.println();hw1.doPrint();System.out.println();hw2.printHelloWorld();System.out.println();hw2.doPrint(); }運(yùn)行結(jié)果為:
CurrentTime = 1446129611993 Enter HelloWorldImpl1.printHelloWorld() CurrentTime = 1446129611993CurrentTime = 1446129611994 Enter HelloWorldImpl1.doPrint() CurrentTime = 1446129611994CurrentTime = 1446129611994 Enter HelloWorldImpl2.printHelloWorld() CurrentTime = 1446129611994CurrentTime = 1446129611994 Enter HelloWorldImpl2.doPrint() CurrentTime = 1446129611994看到給HelloWorld接口的兩個(gè)實(shí)現(xiàn)類的所有方法都加上了代理,代理內(nèi)容就是打印時(shí)間
基于Spring的AOP使用其他細(xì)節(jié)
1、增加一個(gè)橫切關(guān)注點(diǎn),打印日志,Java類為:
public class LogHandler {public void LogBefore(){System.out.println("Log before method");}public void LogAfter(){System.out.println("Log after method");} } <?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:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.2.xsd"><bean id="helloWorldImpl1" class="com.xrq.aop.HelloWorldImpl1" /><bean id="helloWorldImpl2" class="com.xrq.aop.HelloWorldImpl2" /><bean id="timeHandler" class="com.xrq.aop.TimeHandler" /><bean id="logHandler" class="com.xrq.aop.LogHandler" /><aop:config><aop:aspect id="time" ref="timeHandler" order="1"><aop:pointcut id="addTime" expression="execution(* com.xrq.aop.HelloWorld.*(..))" /><aop:before method="printTime" pointcut-ref="addTime" /><aop:after method="printTime" pointcut-ref="addTime" /></aop:aspect><aop:aspect id="log" ref="logHandler" order="2"><aop:pointcut id="printLog" expression="execution(* com.xrq.aop.HelloWorld.*(..))" /><aop:before method="LogBefore" pointcut-ref="printLog" /><aop:after method="LogAfter" pointcut-ref="printLog" /></aop:aspect></aop:config> </beans>測(cè)試類不變,打印結(jié)果為:
CurrentTime = 1446130273734 Log before method Enter HelloWorldImpl1.printHelloWorld() Log after method CurrentTime = 1446130273735CurrentTime = 1446130273736 Log before method Enter HelloWorldImpl1.doPrint() Log after method CurrentTime = 1446130273736CurrentTime = 1446130273736 Log before method Enter HelloWorldImpl2.printHelloWorld() Log after method CurrentTime = 1446130273736CurrentTime = 1446130273737 Log before method Enter HelloWorldImpl2.doPrint() Log after method CurrentTime = 1446130273737要想讓logHandler在timeHandler前使用有兩個(gè)辦法:
(1)aspect里面有一個(gè)order屬性,order屬性的數(shù)字就是橫切關(guān)注點(diǎn)的順序
(2)把logHandler定義在timeHandler前面,Spring默認(rèn)以aspect的定義順序作為織入順序
2、我只想織入接口中的某些方法
修改一下pointcut的expression就好了:
<?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:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.2.xsd"><bean id="helloWorldImpl1" class="com.xrq.aop.HelloWorldImpl1" /><bean id="helloWorldImpl2" class="com.xrq.aop.HelloWorldImpl2" /><bean id="timeHandler" class="com.xrq.aop.TimeHandler" /><bean id="logHandler" class="com.xrq.aop.LogHandler" /><aop:config><aop:aspect id="time" ref="timeHandler" order="1"><aop:pointcut id="addTime" expression="execution(* com.xrq.aop.HelloWorld.print*(..))" /><aop:before method="printTime" pointcut-ref="addTime" /><aop:after method="printTime" pointcut-ref="addTime" /></aop:aspect><aop:aspect id="log" ref="logHandler" order="2"><aop:pointcut id="printLog" expression="execution(* com.xrq.aop.HelloWorld.do*(..))" /><aop:before method="LogBefore" pointcut-ref="printLog" /><aop:after method="LogAfter" pointcut-ref="printLog" /></aop:aspect></aop:config> </beans>表示timeHandler只會(huì)織入HelloWorld接口print開(kāi)頭的方法,logHandler只會(huì)織入HelloWorld接口do開(kāi)頭的方法
3、強(qiáng)制使用CGLIB生成代理
前面說(shuō)過(guò)Spring使用動(dòng)態(tài)代理或是CGLIB生成代理是有規(guī)則的,高版本的Spring會(huì)自動(dòng)選擇是使用動(dòng)態(tài)代理還是CGLIB生成代理內(nèi)容,當(dāng)然我們也可以強(qiáng)制使用CGLIB生成代理,那就是<aop:config>里面有一個(gè)"proxy-target-class"屬性,這個(gè)屬性值如果被設(shè)置為true,那么基于類的代理將起作用,如果proxy-target-class被設(shè)置為false或者這個(gè)屬性被省略,那么基于接口的代理將起作用
2017.06.02
aop 代碼實(shí)現(xiàn):
最基本的代理模式 ?實(shí)現(xiàn)代碼:
公共接口代碼:
1 public interface IHello { 2 /** 3 * 業(yè)務(wù)方法 4 * @param str 5 */ 6 void sayHello(String str); 7 }目標(biāo)類代碼:
1 public class Hello implements IHello{ 2 @Override 3 public void sayHello(String str) { 4 System.out.println("hello "+str); 5 } 6 7 }代理類代碼,我們給它添加日志記錄功能,在方法開(kāi)始前后執(zhí)行特定的方法,是不是和AOP特別像呢?
public class ProxyHello implements IHello{ private IHello hello; public ProxyHello(IHello hello) {super();this.hello = hello;}@Overridepublic void sayHello(String str) {Logger.start();//添加特定的方法 hello.sayHello(str);Logger.end();}}日志類代碼:
1 public class Logger { 2 public static void start(){ 3 System.out.println(new Date()+ " say hello start..."); 4 } 5 6 public static void end(){ 7 System.out.println(new Date()+ " say hello end"); 8 } 9 }測(cè)試代碼:
1 public class Test { 2 public static void main(String[] args) { 3 IHello hello = new ProxyHello(new Hello());//如果我們需要日志功能,則使用代理類 4 //IHello hello = new Hello();//如果我們不需要日志功能則使用目標(biāo)類 5 hello.sayHello("明天"); 6 } 7 }以?java.lang.reflect.InvocationHandler 實(shí)現(xiàn) spring aop jdk代理:
jdk代理實(shí)現(xiàn)主要是實(shí)現(xiàn)InvocationHandler,并且將目標(biāo)對(duì)象注入到代理對(duì)象中,利用反射機(jī)制來(lái)執(zhí)行目標(biāo)對(duì)象的方法。
接口實(shí)現(xiàn)與靜態(tài)代理相同,代理類代碼:
1 public class DynaProxyHello implements InvocationHandler{ 2 3 private Object target;//目標(biāo)對(duì)象 4 /** 5 * 通過(guò)反射來(lái)實(shí)例化目標(biāo)對(duì)象 6 * @param object 7 * @return 8 */ 9 public Object bind(Object object){ 10 this.target = object; 11 return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this); 12 } 13 14 @Override 15 public Object invoke(Object proxy, Method method, Object[] args) 16 throws Throwable { 17 Object result = null; 18 Logger.start();//添加額外的方法 19 //通過(guò)反射機(jī)制來(lái)運(yùn)行目標(biāo)對(duì)象的方法 20 result = method.invoke(this.target, args); 21 Logger.end(); 22 return result; 23 } 24 25 }測(cè)試類代碼:
1 public class DynaTest { 2 public static void main(String[] args) { 3 IHello hello = (IHello) new DynaProxyHello().bind(new Hello());//如果我們需要日志功能,則使用代理類 4 //IHello hello = new Hello();//如果我們不需要日志功能則使用目標(biāo)類 5 hello.sayHello("明天"); 6 } 7 }看完上面的代碼可能和Spring AOP相比有一個(gè)問(wèn)題,日志類只能在方法前后打印,但是AOP應(yīng)該是可以在滿足條件就可以執(zhí)行,所有是否可以將DynaPoxyHello對(duì)象和日志操作對(duì)象(Logger)解耦呢?
看下面代碼實(shí)現(xiàn),將將DynaPoxyHello對(duì)象和日志操作對(duì)象(Logger)解耦:
我們要在被代理對(duì)象的方法前面或者后面去加上日志操作代碼(或者是其它操作的代碼),那么,我們可以抽象出一個(gè)接口,這個(gè)接口里就只有兩個(gè)方法:一個(gè)是在被代理對(duì)象要執(zhí)行方法之前執(zhí)行的方法,我們?nèi)∶麨?/span>start,第二個(gè)方法就是在被代理對(duì)象執(zhí)行方法之后執(zhí)行的方法,我們?nèi)∶麨?/span>end。
Logger的接口:
1 public interface ILogger { 2 void start(Method method); 3 void end(Method method); 4 }Logger的接口實(shí)現(xiàn):
1 public class DLogger implements ILogger{ 2 @Override 3 public void start(Method method) { 4 System.out.println(new Date()+ method.getName() + " say hello start..."); 5 } 6 @Override 7 public void end(Method method) { 8 System.out.println(new Date()+ method.getName() + " say hello end"); 9 } 10 }動(dòng)態(tài)代理類:
1 public class DynaProxyHello1 implements InvocationHandler{ 2 //調(diào)用對(duì)象 3 private Object proxy; 4 //目標(biāo)對(duì)象 5 private Object target; 6 7 public Object bind(Object target,Object proxy){ 8 this.target=target; 9 this.proxy=proxy; 10 return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this); 11 } 12 13 14 @Override 15 public Object invoke(Object proxy, Method method, Object[] args) 16 throws Throwable { 17 Object result = null; 18 //反射得到操作者的實(shí)例 19 Class clazz = this.proxy.getClass(); 20 //反射得到操作者的Start方法 21 Method start = clazz.getDeclaredMethod("start", new Class[]{Method.class}); 22 //反射執(zhí)行start方法 23 start.invoke(this.proxy, new Object[]{this.proxy.getClass()}); 24 //執(zhí)行要處理對(duì)象的原本方法 25 method.invoke(this.target, args); 26 //反射得到操作者的end方法 27 Method end = clazz.getDeclaredMethod("end", new Class[]{Method.class}); 28 //反射執(zhí)行end方法 29 end.invoke(this.proxy, new Object[]{method}); 30 return result; 31 } 32 33 }測(cè)試代碼:
1 public class DynaTest1 { 2 public static void main(String[] args) { 3 IHello hello = (IHello) new DynaProxyHello1().bind(new Hello(),new DLogger());//如果我們需要日志功能,則使用代理類 4 //IHello hello = new Hello();//如果我們不需要日志功能則使用目標(biāo)類 5 hello.sayHello("明天"); 6 } 7 }通過(guò)上面例子,可以發(fā)現(xiàn)通過(guò)動(dòng)態(tài)代理和發(fā)射技術(shù),已經(jīng)基本實(shí)現(xiàn)了AOP的功能,如果我們只需要在方法執(zhí)行前打印日志,則可以不實(shí)現(xiàn)end()方法,這樣就可以控制打印的時(shí)機(jī)了。如果我們想讓指定的方法打印日志,我們只需要在invoke()方法中加一個(gè)對(duì)method名字的判斷,method的名字可以寫在xml文件中,這樣我們就可以實(shí)現(xiàn)以配置文件進(jìn)行解耦了,這樣我們就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的spring aop框架。
簡(jiǎn)單代碼:
package com.jeedev.demo.view;import javax.servlet.http.HttpServletRequest;import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping;/*** 多視圖解析器支持示例* @author huligong**/ @Controller @RequestMapping(value = "/demo") public class JeeDevViewResolverTestController {private static Log logger = LogFactory.getLog(JeeDevViewResolverTestController.class);@RequestMapping(value = "/test1")public String test1(HttpServletRequest request, ModelMap map) {logger.info("使用JSP視圖解析器");map.put("name", "hello world");return "jspTest.jsp";}@RequestMapping(value = "/test2")public String test2(HttpServletRequest request, ModelMap map) {logger.info("使用Freemarker視圖解析器");map.put("name", "hello world");return "hello.ftl";}@RequestMapping(value = "/test3")public String test3(HttpServletRequest request, ModelMap map) {logger.info("使用Velocity視圖解析器");map.put("name", "hello world");return "/html/demo.htm";} }Controller入?yún)⑹荋ttpServletRequest和Modelmap,在請(qǐng)求過(guò)來(lái)的時(shí)候,組件啟動(dòng)spring 容器,同時(shí)將請(qǐng)求對(duì)象和xml中配置的所有bean對(duì)象生成的map傳給controller。
注解@requestmap就是獲取請(qǐng)求的列表和具體的數(shù)值判斷。
總結(jié)
以上是生活随笔為你收集整理的springmvc入门学习的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 消息称特斯拉申请扩建上海工厂,计划首次生
- 下一篇: springMVC 与mybatis 整