springmvc入门学习
2017.05.24:
spring控制器
Controller控制器,是MVC中的部分C,為什么是部分呢?因為此處的控制器主要負責功能處理部分:
1、收集、驗證請求參數并綁定到命令對象;
2、將命令對象交給業務對象,由業務對象處理并返回模型數據;
3、返回ModelAndView(Model部分是業務對象返回的模型數據,視圖部分為邏輯視圖名)。
還記得DispatcherServlet嗎?主要負責整體的控制流程的調度部分:
1、負責將請求委托給控制器進行處理;
2、根據控制器返回的邏輯視圖名選擇具體的視圖進行渲染(并把模型數據傳入)。
因此MVC中完整的C(包含控制邏輯+功能處理)由(DispatcherServlet + Controller)組成。
因此此處的控制器是Web MVC中部分,也可以稱為頁面控制器、動作、處理器。
spring Web MVC支持多種類型的控制器,比如實現Controller接口,從Spring2.5開始支持注解方式的控制器(如@Controller、@RequestMapping、@RequestParam、@ModelAttribute等),我們也可以自己實現相應的控制器(只需要定義相應的HandlerMapping和HandlerAdapter即可)。
三種常用控制器:
首先創建一個實體(model)
package cn.cfs.springmvc.domain;public class User {//主鍵private Integer id;//用戶名private String username;//密碼private String password;/*** 獲取 主鍵** @return 主鍵*/public Integer getId() {return id;}/*** 設置 主鍵** @param id 主鍵*/public void setId(Integer id) {this.id = id;}/*** 獲取 用戶名** @return 用戶名*/public String getUsername() {return username;}/*** 設置 用戶名** @param username 用戶名*/public void setUsername(String username) {this.username = username;}/*** 獲取 密碼** @return 密碼*/public String getPassword() {return password;}/*** 設置 密碼** @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 {//綁定數據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的方式去訪問 --> <bean id="userCommandController" class="cn.cfs.springmvc.controller.UserCommandController"></bean>
前臺通過訪問url:http://192.168.1.253:8082/springmvc_01/usercommandcontroller.action?id=99&username=aaa&password=ssss?來測試
控制臺打印語句:User [id=99, username=aaa, password=ssss]
(二)SimpleFormController簡單表單控制器
package cn.cfs.springmvc.controller;import org.springframework.web.servlet.mvc.SimpleFormController; import cn.cfs.springmvc.domain.User;public class UserFormController extends SimpleFormController {//在構造方法里去綁定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的方式去訪問 --><bean id="userFormController" class="cn.cfs.springmvc.controller.UserFormController"><property name="formView" value="login" /><property name="successView" value="index" /></bean>
formView 是通過get方法訪問這個請求處理后跳轉的頁面,successView是通過post方法訪問這個請求處理后跳轉的頁面。舉個例子,例如添加用戶,肯定先要跳轉到添加用戶的頁面上,然后在表單中填寫一些用戶信息,點擊保存提交按鈕跳轉到控制器中,處理完自己的業務代碼再跳到指定頁面,這種簡單表單方式就是為了解決這種需求。
訪問路徑:http://192.168.1.253:8082/springmvc_01/userformcontroller.action?跳轉到添加頁面的地址,因為是直接在瀏覽器敲的所以是get方式訪問的,會跳轉到login.jsp頁面上
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>根據上面login.jsp中代碼所示,輸入完用戶名和密碼后,點擊保存提交按鈕,通過form中post的方式將表單內容提交到userformcontroller.action這個請求上,回通過控制器找到doSubmitAction這個方法,在這個方法中處理自己業務邏輯,最后跳轉到index.jsp頁面上,最后總結一下,這種方式是通過get和post提交方式來區分跳轉的頁面的,只有post提交方式才會跳轉到帶有處理自己業務邏輯的后臺方法中。
控制臺打印語句:User [id=null, username=aaaa, password=123456]
(三) WizardFormController向導控制器 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 {//綁定數據public UserWizardController() {this.setCommandClass(User.class);this.setCommandName("user");}/*** 完成提交的函數*/@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");}/*** 取消方法,配置點擊取消后要跳轉的頁面*/@Overrideprotected ModelAndView processCancel(HttpServletRequest request,HttpServletResponse response, Object command, BindException errors)throws Exception {return new ModelAndView("index");} }action-servlet.xml配置文件中的代碼:
<!-- 采用className的方式去訪問 --><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為跳轉的頁面,對應的上下文中會創建一個叫wizard的文件夾,以及對應的3個頁面文件
訪問路徑:http://192.168.1.253:8082/springmvc_01/userwizardcontroller.action?會按照list中value的先后順序去加載頁面
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>向導表單控制器</title> </head> <body><form action="${pageContext.request.contextPath}/userwizardcontroller.action" method="post"><table><tr><td>第一個頁面,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>向導表單控制器</title> </head> <body><form action="${pageContext.request.contextPath}/userwizardcontroller.action" method="post"><table><tr><td>第二個頁面,用戶名<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>向導表單控制器</title> </head> <body><form action="${pageContext.request.contextPath}/userwizardcontroller.action" method="post"><table><tr><td>第三個頁面,密碼<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文件中主要是通過 提交按鈕的 name名稱去控制上下頁,取消,完成操作的,詳細請看下面的表格(1-1);
| 屬性 | 具體的的含義 |
| _targetN | N是可變化的,指具體的頁碼,從0開始,例如當前為第二頁,上一頁則為_target0,下一頁則為_target1 |
| _cancel | 取消,點擊后請求發送到后臺的取消方法中,根據具體的配置跳轉到指定的取消頁面 |
| _finish | 完成,點擊后請求發送到后臺的完成方法中,會自動將向導頁的表單數據做拼接,根據具體的配置跳轉到指定的完成請求處理后的頁面 |
上述為當前springmvc比較常用的控制器,當然還有很多,例如默認的AbstractController這是最簡單控制器,很多參數都需要通過原始的方法去獲取,后續還待完善。
數據的交互(獲取頁面數據與將數據返回到頁面)
一、從頁面接收參數
Spring MVC接收請求提交的參數值的幾種方法
-
使用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) } -
自動注入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()); }
二、向頁面傳值
使用Map、Model和ModelMap
Java代碼:
JSP頁面:
1、time:${requestScope.time}<br/>2、names:${requestScope.names }<br/>3、city:${requestScope.city }<br/>4、gender:${requestScope.gender }Session存儲:可以利用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 默認采用的是轉發來定位視圖,如果要使用重定向,可以如下操作
或者用如下方法,工作中常用的方法:
public String login(){ //TODO return "redirect:regirst.do"; }2017.05.26
spring DAO
Spring提供的DAO(數據訪問對象)支持主要的目的是便于以標準的方式使用不同的數據訪問技術, 如JDBC,Hibernate或者JDO等。它不僅可以讓你方便地在這些持久化技術間切換, 而且讓你在編碼的時候不用考慮處理各種技術中特定的異常。
為了便于以一種一致的方式使用各種數據訪問技術,如JDBC、JDO和Hibernate, Spring提供了一套抽象DAO類供你擴展。這些抽象類提供了一些方法,通過它們你可以 獲得與你當前使用的數據訪問技術相關的數據源和其他配置信息。
Dao支持類:
JdbcDaoSupport - JDBC數據訪問對象的基類。 需要一個DataSource,同時為子類提供 JdbcTemplate。
HibernateDaoSupport - Hibernate數據訪問對象的基類。 需要一個SessionFactory,同時為子類提供 HibernateTemplate。也可以選擇直接通過 提供一個HibernateTemplate來初始化, 這樣就可以重用后者的設置,例如SessionFactory, flush模式,異常翻譯器(exception translator)等等。
JdoDaoSupport - JDO數據訪問對象的基類。 需要設置一個PersistenceManagerFactory, 同時為子類提供JdoTemplate。?
JpaDaoSupport - JPA數據訪問對象的基類。 需要一個EntityManagerFactory,同時 為子類提供JpaTemplate。?
(1)Sping對JdbcDaoSupport的支持
數據庫建表:
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數據訪問對象的基類。 需要一個SessionFactory,同時為子類提供 HibernateTemplate。也可以選擇直接通過 提供一個HibernateTemplate來初始化, 這樣就可以重用后者的設置,例如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實現 */ 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對象的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),即面向切面編程,可以說是OOP(Object Oriented Programming,面向對象編程)的補充和完善。OOP引入封裝、繼承、多態等概念來建立一種對象層次結構,用于模擬公共行為的一個集合。不過OOP允許開發者定義縱向的關系,但并不適合定義橫向的關系,例如日志功能。日志代碼往往橫向地散布在所有對象層次中,而與它對應的對象的核心功能毫無關系對于其他類型的代碼,如安全性、異常處理和透明的持續性也都是如此,這種散布在各處的無關的代碼被稱為橫切(cross cutting),在OOP設計中,它導致了大量代碼的重復,而不利于各個模塊的重用。
AOP技術恰恰相反,它利用一種稱為"橫切"的技術,剖解開封裝的對象內部,并將那些影響了多個類的公共行為封裝到一個可重用模塊,并將其命名為"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻為業務模塊所共同調用的邏輯或責任封裝起來,便于減少系統的重復代碼,降低模塊之間的耦合度,并有利于未來的可操作性和可維護性。
使用"橫切"技術,AOP把軟件系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關系不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處基本相似,比如權限認證、日志、事物。AOP的作用在于分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。
AOP核心概念
1、橫切關注點
對哪些方法進行攔截,攔截后怎么處理,這些關注點稱之為橫切關注點
2、切面(aspect)
類是對物體特征的抽象,切面就是對橫切關注點的抽象
3、連接點(joinpoint)
被攔截到的點,因為Spring只支持方法類型的連接點,所以在Spring中連接點指的就是被攔截到的方法,實際上連接點還可以是字段或者構造器
4、切入點(pointcut)
對連接點進行攔截的定義
5、通知(advice)
所謂通知指的就是指攔截到連接點之后要執行的代碼,通知分為前置、后置、異常、最終、環繞通知五類
6、目標對象
代理的目標對象
7、織入(weave)
將切面應用到目標對象并導致代理對象創建的過程
8、引入(introduction)
在不修改代碼的前提下,引入可以在運行期為類動態地添加一些方法或字段
Spring對AOP的支持
Spring中AOP代理由Spring的IOC容器負責生成、管理,其依賴關系也由IOC容器負責管理。因此,AOP代理可以直接使用容器中的其它bean實例作為目標,這種關系可由IOC容器的依賴注入提供。Spring創建代理的規則為:
1、默認使用Java動態代理來創建AOP代理,這樣就可以為任何接口實例創建代理了
2、當需要代理的類不是代理接口的時候,Spring會切換為使用CGLIB代理,也可強制使用CGLIB
AOP編程其實是很簡單的事情,縱觀AOP編程,程序員只需要參與三個部分:
1、定義普通業務組件
2、定義切入點,一個切入點可能橫切多個業務組件
3、定義增強處理,增強處理就是在AOP框架為普通業務組件織入的處理動作
所以進行AOP編程的關鍵就是定義切入點和定義增強處理,一旦定義了合適的切入點和增強處理,AOP框架將自動生成AOP代理,即:代理對象的方法=增強處理+被代理對象的方法。
下面給出一個Spring AOP的.xml文件模板,名字叫做aop.xml,之后的內容都在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"></beans>基于Spring的AOP簡單實現
注意一下,在講解之前,說明一點:使用Spring AOP,要成功運行起代碼,只用Spring提供給開發者的jar包是不夠的,請額外上網下載兩個jar包:
1、aopalliance.jar
2、aspectjweaver.jar
開始講解用Spring AOP的XML實現方式,先定義一個接口:
public interface HelloWorld {void printHelloWorld();void doPrint(); }定義兩個接口實現類:
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 ;} }橫切關注點,這里是打印時間:
public class TimeHandler {public void printTime(){System.out.println("CurrentTime = " + System.currentTimeMillis());} }有這三個類就可以實現一個簡單的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>寫一個main函數調用一下:
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(); }運行結果為:
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接口的兩個實現類的所有方法都加上了代理,代理內容就是打印時間
基于Spring的AOP使用其他細節
1、增加一個橫切關注點,打印日志,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>測試類不變,打印結果為:
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前使用有兩個辦法:
(1)aspect里面有一個order屬性,order屬性的數字就是橫切關注點的順序
(2)把logHandler定義在timeHandler前面,Spring默認以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只會織入HelloWorld接口print開頭的方法,logHandler只會織入HelloWorld接口do開頭的方法
3、強制使用CGLIB生成代理
前面說過Spring使用動態代理或是CGLIB生成代理是有規則的,高版本的Spring會自動選擇是使用動態代理還是CGLIB生成代理內容,當然我們也可以強制使用CGLIB生成代理,那就是<aop:config>里面有一個"proxy-target-class"屬性,這個屬性值如果被設置為true,那么基于類的代理將起作用,如果proxy-target-class被設置為false或者這個屬性被省略,那么基于接口的代理將起作用
2017.06.02
aop 代碼實現:
最基本的代理模式 ?實現代碼:
公共接口代碼:
1 public interface IHello { 2 /** 3 * 業務方法 4 * @param str 5 */ 6 void sayHello(String str); 7 }目標類代碼:
1 public class Hello implements IHello{ 2 @Override 3 public void sayHello(String str) { 4 System.out.println("hello "+str); 5 } 6 7 }代理類代碼,我們給它添加日志記錄功能,在方法開始前后執行特定的方法,是不是和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 }測試代碼:
1 public class Test { 2 public static void main(String[] args) { 3 IHello hello = new ProxyHello(new Hello());//如果我們需要日志功能,則使用代理類 4 //IHello hello = new Hello();//如果我們不需要日志功能則使用目標類 5 hello.sayHello("明天"); 6 } 7 }以?java.lang.reflect.InvocationHandler 實現 spring aop jdk代理:
jdk代理實現主要是實現InvocationHandler,并且將目標對象注入到代理對象中,利用反射機制來執行目標對象的方法。
接口實現與靜態代理相同,代理類代碼:
1 public class DynaProxyHello implements InvocationHandler{ 2 3 private Object target;//目標對象 4 /** 5 * 通過反射來實例化目標對象 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 //通過反射機制來運行目標對象的方法 20 result = method.invoke(this.target, args); 21 Logger.end(); 22 return result; 23 } 24 25 }測試類代碼:
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();//如果我們不需要日志功能則使用目標類 5 hello.sayHello("明天"); 6 } 7 }看完上面的代碼可能和Spring AOP相比有一個問題,日志類只能在方法前后打印,但是AOP應該是可以在滿足條件就可以執行,所有是否可以將DynaPoxyHello對象和日志操作對象(Logger)解耦呢?
看下面代碼實現,將將DynaPoxyHello對象和日志操作對象(Logger)解耦:
我們要在被代理對象的方法前面或者后面去加上日志操作代碼(或者是其它操作的代碼),那么,我們可以抽象出一個接口,這個接口里就只有兩個方法:一個是在被代理對象要執行方法之前執行的方法,我們取名為start,第二個方法就是在被代理對象執行方法之后執行的方法,我們取名為end。
Logger的接口:
1 public interface ILogger { 2 void start(Method method); 3 void end(Method method); 4 }Logger的接口實現:
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 }動態代理類:
1 public class DynaProxyHello1 implements InvocationHandler{ 2 //調用對象 3 private Object proxy; 4 //目標對象 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 //反射得到操作者的實例 19 Class clazz = this.proxy.getClass(); 20 //反射得到操作者的Start方法 21 Method start = clazz.getDeclaredMethod("start", new Class[]{Method.class}); 22 //反射執行start方法 23 start.invoke(this.proxy, new Object[]{this.proxy.getClass()}); 24 //執行要處理對象的原本方法 25 method.invoke(this.target, args); 26 //反射得到操作者的end方法 27 Method end = clazz.getDeclaredMethod("end", new Class[]{Method.class}); 28 //反射執行end方法 29 end.invoke(this.proxy, new Object[]{method}); 30 return result; 31 } 32 33 }測試代碼:
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();//如果我們不需要日志功能則使用目標類 5 hello.sayHello("明天"); 6 } 7 }通過上面例子,可以發現通過動態代理和發射技術,已經基本實現了AOP的功能,如果我們只需要在方法執行前打印日志,則可以不實現end()方法,這樣就可以控制打印的時機了。如果我們想讓指定的方法打印日志,我們只需要在invoke()方法中加一個對method名字的判斷,method的名字可以寫在xml文件中,這樣我們就可以實現以配置文件進行解耦了,這樣我們就實現了一個簡單的spring aop框架。
簡單代碼:
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入參是HttpServletRequest和Modelmap,在請求過來的時候,組件啟動spring 容器,同時將請求對象和xml中配置的所有bean對象生成的map傳給controller。
注解@requestmap就是獲取請求的列表和具體的數值判斷。
總結
以上是生活随笔為你收集整理的springmvc入门学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 消息称特斯拉申请扩建上海工厂,计划首次生
- 下一篇: 使用Wireshark查看HTTPS中T