在前面的文章中已經和你們分享過關於spring IOC的知識,已經經過他的實現機制。咱們都知道spring的兩大核心:AOP(面向切面)和IOC(控制反轉),本篇咱們就一塊兒學習一下AOP的知識的。html
這裏分享一個問題?當咱們軟件開發完成後,須要給每個方法添加操做日誌,咱們怎麼操做呢?我想最簡單的方法就是在每個方法的開始前將咱們的日誌邏輯加入,固然這是最直接的一種方法,可是他的缺點也是很明細,若是咱們的方法有不少,添加這個日誌邏輯就須要不少的工做量,顯然這是一種不可取的方式。如何更好的解決這個問題呢?spring很好的幫咱們處理了這個難點,經過切面編程,咱們能夠在咱們須要的切面添加相應的業務邏輯已達到咱們須要的效果。java
接下來開始咱們的內容,AOP的實現藉助於JAVA的動態代理知識,我先經過動態代理的方式爲你們介紹一下AOP的實現原理,以便你們更好的理解。git
每個動態代理類都必需要實現InvocationHandler這個接口,而且每一個代理類的實例都關聯到了一個handler,當咱們經過代理對象調用一個方法的時候,這個方法的調用就會被轉發爲由InvocationHandler這個接口的 invoke 方法來進行調用。咱們來看看InvocationHandler這個接口的惟一一個方法 invoke 方法:github
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
咱們看到這個方法一共接受三個參數,那麼這三個參數分別表明什麼呢?web
proxy: 指代咱們所代理的那個真實對象
method: 指代的是咱們所要調用真實對象的某個方法的Method對象
args: 指代的是調用真實對象某個方法時接受的參數
這裏我以一個添加日誌需求爲因,實現一個這個過程:spring
public class LogInterception implements InvocationHandler { private Object target; public void setTarget(Object target) { this.target = target; } private void beforeMethod(){ System.out.println("切面添加日誌開始"); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub beforeMethod(); Object result = method.invoke(target, args); afterMethod(); return result; } private void afterMethod(){ System.out.println("切面添加日誌結束"); } }
咱們寫好了代理,下面咱們看一下如何將其加入到方法中使用:express
//添加日誌管理切面 LogInterception log = new LogInterception(); log.setTarget(userService); IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), new Class[]{IUserService.class}, log); // IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), log); boolean flag = userServiceProxy.add();
這裏簡單爲你們解釋一下,首先咱們聲明動態代理的類,經過咱們對外提供的setTarget方法將咱們的代理類注入,而後通Proxy.newProxyInstance獲得咱們代理方法的代理對象,而後經過調用咱們的代理對象實現咱們的動態代理。apache
public class UserAction extends ActionSupport implements ServletRequestAware, ServletResponseAware{ private IUserService userService; private HttpServletRequest request; private HttpSession session; private HttpServletResponse response; /** * @Description 添加用戶測試 * @throws IOException * * @author 高尚 * @version 1.0 * @date 建立時間:2017年12月13日 上午10:56:30 */ public void addJdkInterception(){ Map<String, Object> result = new HashMap<String, Object>(); result.put("status", 1); result.put("msg", "操做成功"); System.out.println("action操做開始"); //添加日誌管理切面 LogInterception log = new LogInterception(); log.setTarget(userService); IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), new Class[]{IUserService.class}, log); // IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), log); boolean flag = userServiceProxy.add(); result.put("data", flag); System.out.println("action操做結束"); //輸出到客戶端 PrintWriter writer = null; try { if(null != response){ response.setContentType("text/html;charset=UTF-8");// 解決中文亂碼 response.setCharacterEncoding("UTF-8"); writer = response.getWriter(); writer.write(JSONObject.toJSONString(result)); }else{ System.out.println(JSONObject.toJSONString(result)); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { if(null != writer){ writer.flush(); writer.close(); } } } public void setUserService(IUserService userService) { this.userService = userService; } @Override public void setServletResponse(HttpServletResponse response) { // TODO Auto-generated method stub this.response = response; } @Override public void setServletRequest(HttpServletRequest request) { // TODO Auto-generated method stub this.request = request; this.session = this.request.getSession(); } }
啓動咱們的項目試一把,經過控制檯的日誌信息,我想你必定能理解java動態代理的機制。編程
經過上面的內容,相信對於你理解spring aop的知識必定頗有幫助。下面咱們來看一下srping aop的知識:瀏覽器
首先咱們搭建一個簡單的spring開發環境:
一、dao接口和實現類
public interface IUserDao { boolean add(); void delete(); } public class UserDaoImpl implements IUserDao { @Override public boolean add() { System.out.println("dao添加操做成功"); return false; } @Override public void delete() { System.out.println("dao刪除操做成功"); } }
二、service接口和實現類
public interface IUserService { boolean add(); void delete(); } public class UserServiceImpl implements IUserService { private IUserDao userDao; @Override public boolean add() { System.out.println("service添加操做開始"); userDao.add(); System.out.println("service添加操做結束"); return false; } @Override public void delete() { System.out.println("service刪除操做開始"); userDao.delete(); System.out.println("service刪除操做結束"); } public void setUserDao(IUserDao userDao) { this.userDao = userDao; } }
三、action實現類
public class UserAction extends ActionSupport implements ServletRequestAware, ServletResponseAware{ private IUserService userService; private HttpServletRequest request; private HttpSession session; private HttpServletResponse response; /** * @Description 添加用戶測試 * @throws IOException * * @author 高尚 * @version 1.0 * @date 建立時間:2017年12月13日 上午10:56:30 */ public void add(){ Map<String, Object> result = new HashMap<String, Object>(); result.put("status", 1); result.put("msg", "操做成功"); System.out.println("action操做開始"); boolean flag = userService.add(); result.put("data", flag); System.out.println("action操做結束"); //輸出到客戶端 PrintWriter writer = null; try { if(null != response){ response.setContentType("text/html;charset=UTF-8");// 解決中文亂碼 response.setCharacterEncoding("UTF-8"); writer = response.getWriter(); writer.write(JSONObject.toJSONString(result)); }else{ System.out.println(JSONObject.toJSONString(result)); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { if(null != writer){ writer.flush(); writer.close(); } } } /** * @Description 刪除用戶測試 * @throws IOException * * @author 高尚 * @version 1.0 * @date 建立時間:2017年12月13日 上午10:56:30 */ public void delete(){ Map<String, Object> result = new HashMap<String, Object>(); result.put("status", 1); result.put("msg", "操做成功"); System.out.println("action操做開始"); userService.delete(); result.put("data", true); System.out.println("action操做結束"); //輸出到客戶端 PrintWriter writer = null; try { if(null != response){ response.setContentType("text/html;charset=UTF-8");// 解決中文亂碼 response.setCharacterEncoding("UTF-8"); writer = response.getWriter(); writer.write(JSONObject.toJSONString(result)); }else{ System.out.println(JSONObject.toJSONString(result)); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { if(null != writer){ writer.flush(); writer.close(); } } } public void setUserService(IUserService userService) { this.userService = userService; } @Override public void setServletResponse(HttpServletResponse response) { // TODO Auto-generated method stub this.response = response; } @Override public void setServletRequest(HttpServletRequest request) { // TODO Auto-generated method stub this.request = request; this.session = this.request.getSession(); } }
四、切面邏輯
public class TimeIntercepation { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); public void before(){ System.out.println("執行開始時間:" + sdf.format(new Date())); } public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("around 執行前"); Object result = pjp.proceed();//得到方法執行後的返回參數 System.out.println("around 執行後"); return result; } public void after(){ System.out.println("執行開始時間:" + sdf.format(new Date())); } public void afterReturning(){ System.out.println("方法正常執行完畢"); } public void throwing(){ System.out.println("方法執行出現異常"); } }
五、spring配置文件
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <bean name="userDao" class="com.hpugs.spring.one.dao.UserDaoImpl" lazy-init="true"></bean> <bean name="userService" class="com.hpugs.spring.one.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean name="userAction" class="com.hpugs.spring.one.action.UserAction" scope="prototype"> <property name="userService" ref="userService"></property> </bean> <bean name="timeIntercepation" class="com.hpugs.spring.one.interception.TimeIntercepation" lazy-init="true"></bean> <aop:config> <aop:pointcut expression="execution(* com.hpugs.spring.one.service.*.*(..))" id="serviceIntercepation"/> <aop:aspect id="timeAspect" ref="timeIntercepation"> <aop:before method="before" pointcut-ref="serviceIntercepation"/> <aop:around method="around" pointcut-ref="serviceIntercepation"/> <aop:after-returning method="afterReturning" pointcut-ref="serviceIntercepation"/> <aop:after method="after" pointcut-ref="serviceIntercepation"/> <aop:after-throwing method="throwing" pointcut-ref="serviceIntercepation"/> </aop:aspect> </aop:config> </beans>
這裏簡單解釋一下,aop:config:設置spring切面,添加切面操做;aop:pointcut:聲明切面;aop:aspect:添加切面操做
六、status配置
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <!-- 取消 struts2 動態方法調用 --> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <!-- 是否啓用開發模式 --> <constant name="struts.devMode" value="false" /> <!-- 全部匹配 *.action 的請求都由 struts2 處理 --> <constant name="struts.action.extension" value="action" /> <!-- struts 配置文件改動後,是否從新加載 --> <constant name="struts.configuration.xml.reload" value="true" /> <!-- 設置瀏覽器是否緩存靜態內容 --> <constant name="struts.serve.static.browserCache" value="false" /> <!-- 自動從新加載映射加載 --> <constant name="struts.convention.classes.reload" value="true"/> <!-- action 對象是由Spring負責建立 --> <constant name="struts.objectFactory" value="spring" /> <package name="user" namespace="/" extends="struts-default"> <action name="user_add" class="userAction" method="add"></action> <action name="user_delete" class="userAction" method="delete"></action> </package> </struts>
七、web.xml配置
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> <web:welcome-file-list> <web:welcome-file>index.jsp</web:welcome-file> </web:welcome-file-list>
八、junit單元測試
@Test public void addTest(){ //注入Spring Bean ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); //獲取UserAction對象 UserAction userAction = (UserAction) applicationContext.getBean("userAction"); //都有添加操做 userAction.add(); }
九、單元測試結果:
action操做開始
執行開始時間:2018-01-18 15:34:14.412
around 執行前
service添加操做開始
dao添加操做成功
service添加操做結束
around 執行後
方法正常執行完畢
執行開始時間:2018-01-18 15:34:14.412
action操做結束
{"msg":"操做成功","data":false,"status":1}
到這裏咱們經過xml的方式就實現了方法操做時間切面方法的注入。
最後在爲你們介紹一下關於spring annotation添加切面的實現:
首先使咱們的spring配置文件,添加註解事務
<!-- <bean name="timeIntercepation" class="com.hpugs.spring.one.interception.TimeIntercepation" lazy-init="true"></bean> --> <context:annotation-config></context:annotation-config> <context:component-scan base-package="com.hpugs.spring.one.interception"></context:component-scan> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
這裏解釋一下,咱們能夠經過bean的方式將咱們的代理方法注入,可是不推薦你們使用,這裏推薦你們使用包掃描的方式進行代理方法注入。
經過註解的方式進行切面聲明:
@Aspect @Component public class TimeIntercepation { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Pointcut("execution(* com.hpugs.spring.one.service.*.*(..))") public void myMethod(){} @Before("execution(* com.hpugs.spring.one.service.*.*(..))") public void before(){ System.out.println("執行開始時間:" + sdf.format(new Date())); } @Around("myMethod()") public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("around 執行前"); Object result = pjp.proceed();//得到方法執行後的返回參數 System.out.println("around 執行後"); return result; } @After("execution(* com.hpugs.spring.one.service.*.*(..))") public void after(){ System.out.println("執行開始時間:" + sdf.format(new Date())); } @AfterReturning("myMethod()") public void afterReturning(){ System.out.println("方法正常執行完畢"); } @AfterThrowing("myMethod()") public void throwing(){ System.out.println("方法執行出現異常"); } }
這裏解釋一下myMethod方法,若是咱們的方法共用切面時,咱們能夠經過這種方式將面聲明進行復用。
關於Spring AOP的內容就和你們探討到這裏,以上源代碼下載地址:https://github.com/hpugs/Spring-aop