什麼是 AOP?前端
AOP 即面向切面編程,利用 AOP 能夠對業務進行解耦,提升重用性,提升開發效率java
應用場景:日誌記錄,性能統計,安全控制,事務處理,異常處理apache
AOP 底層實現原理是採用代理實現的編程
基本特性:安全
事務控制分類:服務器
編程式事務:手動控制事務操做多線程
聲明式事務:經過 AOP 控制事務mvc
@Component @Scope("prototype") public class TransactionUtils { // 獲取事務源 @Autowired private DataSourceTransactionManager dataSourceTransactionManager; // 開啓事務 public TransactionStatus begin() { TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute()); return transaction; } // 提交事務 public void commit(TransactionStatus transaction) { dataSourceTransactionManager.commit(transaction); } // 回滾事務 public void rollback(TransactionStatus transaction) { dataSourceTransactionManager.rollback(transaction); } }
@Component @Aspect public class TransactionAop { @Autowired private TransactionUtils transactionUtils; @Around("execution(* com.kernel.service.UserService.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) { try { // 調用方法以前執行 System.out.println("開啓事務"); TransactionStatus transactionStatus = transactionUtils.begin(); proceedingJoinPoint.proceed(); System.out.println("提交事務"); transactionUtils.commit(transactionStatus); } catch (Throwable throwable) { System.out.println("回滾事務"); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } } }
事務注意事項:app
必定不要將代碼經過 try 包裹起來,若是程序發生異常,事務接收不到異常,就會認爲程序正常執行,就不會進行回滾,必須手動回滾jsp
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
經過 AOP 實現,對方法進行攔截,在方法執行以前開啓事務,結束後提交事務,發生異常回滾事務
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ExtTransaction { }
@Component @Aspect public class TransactionAop { @Autowired private TransactionUtils transactionUtils; private TransactionStatus transactionStatus = null; /** * AOP實現事務管理 * * @param proceedingJoinPoint 切面通知對象 */ @Around("execution(* com.kernel.service.*.* (..))") public void around(ProceedingJoinPoint proceedingJoinPoint) { try { // 獲取註解對象 ExtTransaction extTransaction = getExtTransaction(proceedingJoinPoint); begin(extTransaction); // 執行目標方法 proceedingJoinPoint.proceed(); // 提交事務 commit(); } catch (Throwable throwable) { transactionUtils.rollback(); } } /** * 獲取註解對象 * * @param proceedingJoinPoint 切面通知對象 * @return 註解對象 * @throws NoSuchMethodException */ public ExtTransaction getExtTransaction(ProceedingJoinPoint proceedingJoinPoint) throws NoSuchMethodException { // 獲取方法名稱 String method = proceedingJoinPoint.getSignature().getName(); // 獲取目標方法 Class<?> classTarget = proceedingJoinPoint.getTarget().getClass(); // 獲取目標對象類型 Class[] parameterTypes = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterTypes(); // 獲取目標對象方法 Method objMethod = classTarget.getMethod(method, parameterTypes); // 獲取註解 ExtTransaction declaredAnnotation = objMethod.getDeclaredAnnotation(ExtTransaction.class); return declaredAnnotation; } /** * 開啓事務 * @param extTransaction 註解對象 * @return 事務對象 */ TransactionStatus begin(ExtTransaction extTransaction) { if (extTransaction != null) transactionStatus = transactionUtils.begin(); return transactionStatus; } /** * 提交事務 */ void commit() { if (transactionStatus != null) transactionUtils.commit(transactionStatus); } /** * 回滾事務 */ void rollback() { transactionUtils.rollback(); } }
Spring IOC 指的是控制反轉,IOC 容器負責實例化、定位、配置應用程序中的對象及創建這些對象間的依賴,交由Spring來管理這些,實現解耦
實現步驟:
掃包
將標註了註解的類,經過反射建立實例並添加的 bean 容器中
當用戶向容器要 bean 時,經過 beanId 在 bean 容器中查找並返回實例
package com.kernel.ext; import com.kernel.ext.annotation.ExtAutoWired; import com.kernel.ext.annotation.ExtService; import com.kernel.utils.ClassUtil; import org.apache.commons.lang.StringUtils; import java.lang.reflect.Field; import java.util.List; import java.util.concurrent.ConcurrentHashMap; /** * IOC 註解版本 */ public class ExtClassPathXmlApplicationContext { // 包名 private String packageName; // bean容器 private ConcurrentHashMap<String, Object> beans = null; /** * 構造函數 * * @param packageName 包名 * @throws InstantiationException * @throws IllegalAccessException */ public ExtClassPathXmlApplicationContext(String packageName) throws InstantiationException, IllegalAccessException { this.packageName = packageName; init(); } /** * 初始化對象 * * @throws IllegalAccessException * @throws InstantiationException */ private void init() throws IllegalAccessException, InstantiationException { // 遍歷全部類 List<Class<?>> classes = ClassUtil.getClasses(packageName); // 將全部標註ExtService註解的類加入到容器中 findAnnotationByClasses(classes); } /** * 過濾標註ExtService註解的類 * * @param classes * @throws InstantiationException * @throws IllegalAccessException */ private void findAnnotationByClasses(List<Class<?>> classes) throws InstantiationException, IllegalAccessException { for (Class classInfo : classes) { ExtService extService = (ExtService) classInfo.getAnnotation(ExtService.class); if (extService != null) { Object newInstance = newInstance(classInfo); beans.put(toLowerCaseFirstOne(classInfo.getSimpleName()), newInstance); } } } /** * 經過反射構建對象 * * @param classInfo * @return * @throws InstantiationException * @throws IllegalAccessException */ private Object newInstance(Class classInfo) throws InstantiationException, IllegalAccessException { return classInfo.getClass().newInstance(); } /** * 經過beanId查找對應的實例 * * @param beanId * @return */ public Object getBean(String beanId) throws IllegalAccessException { Object object = null; if (StringUtils.isEmpty(beanId)) return null; for (String id : beans.keySet()) if (beanId.equals(id)) { object = beans.get(beanId); attrAssign(object); break; } return object; } /** * 依賴注入 */ void attrAssign(Object object) throws IllegalAccessException { Class<?> aClass = object.getClass(); Field[] declaredFields = aClass.getDeclaredFields(); for (Field field : declaredFields) { ExtAutoWired extAutoWired = field.getAnnotation(ExtAutoWired.class); if (extAutoWired != null) { field.setAccessible(true); Object bean = getBean(field.getName()); field.set(field.getName(), object); } } } /** * 首字母變小寫 * * @param s * @return */ public static String toLowerCaseFirstOne(String s) { if (Character.isLowerCase(s.charAt(0))) return s; else { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append(Character.toLowerCase(s.charAt(0))); stringBuffer.append(s.substring(1)); return stringBuffer.toString(); } } }
執行流程:
用戶請求 url 至前端控制器 DispatcherServlet
DispatcherServlet 調用處理器映射器 HandlerMapping
HandlerMapping 根據 url 找到具體的處理器生成處理器執行鏈,並將執行鏈返回給 DispatcherServlet
DispatcherServlet 根據處理器 Handler 獲取處理器適配器 HandlerAdapter 執行
執行 Handler
返回 ModelAndView 返回給 DispatcherServlet
DispatcherServlet 將 ModelAnd view 傳遞給視圖解析器 ViewResolver
ViewResolver 解析成具體 View
渲染視圖
init:在 Servlet 生命週期中,該方法僅執行一次,它是在將服務器裝入 Servlet 時執行的,負責初始化 Servlet 對象,Servlet 是單例多線程的
service:負責響應請求,每當一個客戶請求一個 HttpServlet 對象,該對象的 Service 方法就要被調用,傳遞一個 ServletRequest 和 ServletResponse 對象
destroy:在服務器中止卸載 Servlet 時調用
實現步驟:
建立一個 ExtDispatcherServlet 繼承 HttpServlet
掃包
將標註了 @ExtController 註解的類,經過反射建立對象添加到容器中,將 beanId 和控制器關聯
將標註了 @ExtRequestMapping 註解的類,將請求url 和控制器對象關聯,將 url 和 方法關聯
當用戶請求 url 時,查找和 url 對應的對象,而後查找和 url 對應的方法,執行方法,解析並渲染
package com.kernel.ext.servlet; import com.kernel.controller.ExtIndexController; import com.kernel.ext.annotation.ExtController; import com.kernel.ext.annotation.ExtRequestMapping; import com.kernel.utils.ClassUtil; import org.apache.commons.lang.StringUtils; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import java.util.concurrent.ConcurrentHashMap; /** * 手寫SpringMVC */ public class ExtDispatcherServlet extends HttpServlet { // 關聯beanId和Object private ConcurrentHashMap<String, Object> mvcBeans = new ConcurrentHashMap<>(); // 關聯url和控制器對象 private ConcurrentHashMap<String, Object> mvcBeanUrl = new ConcurrentHashMap<>(); // 關聯url和methodName private ConcurrentHashMap<String, String> mvcMethodUrl = new ConcurrentHashMap<>(); /** * 初始化Servlet */ public void init() { try { List<Class<?>> classes = ClassUtil.getClasses("com.kernel.controller"); findClassMVCBeans(classes); handlerMapping(mvcBeans); } catch (Exception e) { e.printStackTrace(); } } /** * 關聯url和控制器對象、url和methoName * @param mvcBeans */ private void handlerMapping(ConcurrentHashMap<String, Object> mvcBeans) { for (Object classInfo : mvcBeans.values()) { ExtRequestMapping extCla***equestMapping = classInfo.getClass().getDeclaredAnnotation(ExtRequestMapping.class); String requestBaseUrl = null; if (extCla***equestMapping != null) { requestBaseUrl = extCla***equestMapping.value(); } Method[] methods = classInfo.getClass().getDeclaredMethods(); for (Method method : methods) { ExtRequestMapping extMthodRequestMapping = method.getDeclaredAnnotation(ExtRequestMapping.class); if (extCla***equestMapping != null){ String httpRequestUrl = extMthodRequestMapping.value(); mvcBeanUrl.put(requestBaseUrl + httpRequestUrl, classInfo); mvcMethodUrl.put(requestBaseUrl + httpRequestUrl, method.getName()); } } } } /** * 將全部控制器添加到mvcBeans中 * @param classes 包內全部類 * @throws InstantiationException * @throws IllegalAccessException * @throws ClassNotFoundException */ private void findClassMVCBeans(List<Class<?>> classes) throws InstantiationException, IllegalAccessException, ClassNotFoundException { for (Class classInfo : classes) { ExtController extController = (ExtController) classInfo.getDeclaredAnnotation(ExtController.class); if (extController != null){ mvcBeans.put(classInfo.getName(), ClassUtil.newInstance(classInfo)); } } } /** * get請求 * @param req * @param resp * @throws IOException * @throws ServletException */ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { try { doPost(req, resp); } catch (Exception e) { e.printStackTrace(); } } /** * post請求 * @param req * @param resp */ protected void doPost(HttpServletRequest req, HttpServletResponse resp) { try { doDispatch(req, resp); } catch (Exception e) { e.printStackTrace(); } } /** * 路由分發 * @param req * @param resp * @throws Exception */ private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception { String requestUrl = req.getServletPath(); Object object = mvcBeanUrl.get(requestUrl); if (object == null) object = ExtIndexController.class.newInstance(); String methodName = mvcMethodUrl.get(requestUrl); if (StringUtils.isEmpty(methodName)) methodName = "error"; Class<?> classInfo = object.getClass(); String resultPage = (String) methodInvoke(classInfo, object, methodName); viewDisplay(resultPage, req, resp); } /** * 視圖渲染 * @param resultPage * @param req * @param resp * @throws ServletException * @throws IOException */ private void viewDisplay(String resultPage, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String suffix = ".jsp"; String prefix = "/"; req.getRequestDispatcher(prefix + resultPage + suffix).forward(req, resp); } /** * 反射執行方法 * @param classInfo 控制器 * @param object 控制器對象 * @param methodName 方法名稱 * @return * @throws InvocationTargetException * @throws IllegalAccessException * @throws NoSuchMethodException */ private Object methodInvoke(Class<?> classInfo, Object object, String methodName) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { Method method = null; try { method = classInfo.getDeclaredMethod(methodName); } catch (NoSuchMethodException e) { e.printStackTrace(); } finally { return method.invoke(object); } } }