時間:2017年09月03日星期日
說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com
教學源碼:https://github.com/zccodere/s...
學習源碼:https://github.com/zccodere/s...
課程章節
概覽 AOP使用 AOP原理 AOP開源運用 課程實戰 課程總結
面向切面編程是一種編程范式
編程范式概覽
面向過程編程 面向對象編程 面向函數編程(函數式編程) 事件驅動編程(GUI開發中比較常見) 面向切面編程
AOP是什么
是一種編程范式,不是編程語言 解決特定問題,不能解決所有問題 是OOP的補充,不是替代
AOP的初衷
DRY:Don’t Repeat Yourself代碼重復性問題 SOC:Separation of Concerns關注點分離 -水平分離:展示層->服務層->持久層 -垂直分離:模塊劃分(訂單、庫存等) -切面分離:分離功能性需求與非功能性需求
使用AOP的好處
集中處理某一關注點/橫切邏輯 可以很方便地添加/刪除關注點 侵入性少,增強代碼可讀性及可維護性
AOP的應用場景
權限控制 緩存控制 事務控制 審計日志 性能監控 分布式追蹤 異常處理
支持AOP的編程語言
Java .NET C/C++ Ruby Python PHP …1-2 簡單案例
案例背景
產品管理的服務 產品添加、刪除的操作只能管理員才能進行 普通實現VS AOP實現
創建一個名為springaopguide的maven項目pom如下
完成后的項目結構如下
代碼編寫
1.編寫Product類
package com.myimooc.springaopguide.domain; /** * @title 產品領域模型 * @describe 產品實體對象 * @author zc * @version 1.0 2017-09-03 */ public class Product { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
2.編寫CurrentUserHolder類
package com.myimooc.springaopguide.security; /** * @title 獲取用戶信息 * @describe 模擬用戶的切換,將用戶信息存入當前線程 * @author zc * @version 1.0 2017-09-03 */ public class CurrentUserHolder { private static final ThreadLocalholder = new ThreadLocal<>(); public static String get(){ return holder.get() == null ? "unkown" : holder.get(); } public static void set(String user){ holder.set(user); } }
3.編寫AdminOnly類
package com.myimooc.springaopguide.security; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @title 管理員權限注解 * @describe 被該注解聲明的方法需要管理員權限 * @author zc * @version 1.0 2017-09-03 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AdminOnly { }
4.編寫SecurityAspect類
package com.myimooc.springaopguide.security; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.myimooc.springaopguide.service.AuthService; /** * @title 權限校驗切面類 * @describe * @author zc * @version 1.0 2017-09-03 */ // 聲明為一個切面 @Aspect @Component public class SecurityAspect { @Autowired private AuthService authService; // 使用要攔截標注有AdminOnly的注解進行操作 @Pointcut("@annotation(AdminOnly)") public void adminOnly(){ } @Before("adminOnly()") public void check(){ authService.checkAccess(); } }
5.編寫AuthService類
package com.myimooc.springaopguide.service; import java.util.Objects; import org.springframework.stereotype.Service; import com.myimooc.springaopguide.security.CurrentUserHolder; /** * @title 權限校驗類 * @describe 對用戶權限進行校驗 * @author zc * @version 1.0 2017-09-03 */ @Service public class AuthService { public void checkAccess(){ String user = CurrentUserHolder.get(); if(!Objects.equals("admin", user)){ throw new RuntimeException("operation not allow"); } } }
6.編寫ProductService類
package com.myimooc.springaopguide.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.myimooc.springaopguide.domain.Product; /** * @title 產品服務類 * @describe 產品相關業務服務-傳統方式實現權限校驗 * @author zc * @version 1.0 2017-09-03 */ @Service public class ProductService { @Autowired private AuthService AuthService; public void insert(Product product){ AuthService.checkAccess(); System.out.println("insert product"); } public void delete(Long id){ AuthService.checkAccess(); System.out.println("delete product"); } }
7.編寫ProductServiceAop類
package com.myimooc.springaopguide.service; import org.springframework.stereotype.Service; import com.myimooc.springaopguide.domain.Product; import com.myimooc.springaopguide.security.AdminOnly; /** * @title 產品服務類 * @describe 產品相關業務服務-AOP方式實現權限校驗 * @author zc * @version 1.0 2017-09-03 */ @Service public class ProductServiceAop { @AdminOnly public void insert(Product product){ System.out.println("insert product"); } @AdminOnly public void delete(Long id){ System.out.println("delete product"); } }
8.編寫AopGuideApplicationTests類
package com.myimooc.springaopguide; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.myimooc.springaopguide.security.CurrentUserHolder; import com.myimooc.springaopguide.service.ProductService; import com.myimooc.springaopguide.service.ProductServiceAop; /** * @title 單元測試類 * @describe 測試權限校驗服務是否生效 * @author zc * @version 1.0 2017-09-03 */ @RunWith(SpringRunner.class) @SpringBootTest public class AopGuideApplicationTests { @Autowired private ProductService productService; @Test(expected = Exception.class) public void annoInsertTest(){ CurrentUserHolder.set("tom"); productService.delete(1L); } @Test public void adminInsertTest(){ CurrentUserHolder.set("admin"); productService.delete(1L); } @Autowired private ProductServiceAop productServiceAop; @Test(expected = Exception.class) public void annoInsertAopTest(){ CurrentUserHolder.set("tom"); productServiceAop.delete(1L); } @Test public void adminInsertAopTest(){ CurrentUserHolder.set("admin"); productServiceAop.delete(1L); } }第二章:使用詳解 2-1 本節內容
Spring AOP使用方式
XML配置+Pointcut expression【不推薦使用方式】 注解方式+ Pointcut expression【推薦使用該方式】
Aspectj注解
@Aspect:用于聲明當前類是一個切面 @Pointcut:用于描述在哪些類、哪些方法上執行切面的代碼 Advice:描述想要在這些方法執行的什么時機進行攔截
本章內容
Pointcut express:切面表達式 5種Advice:建言的五種細分怎么使用2-2 切面表達式
切面表達式
1.designators(指示器) execution() 描述通過什么樣的方式去匹配哪些類、哪些方法 2.wildcards(通配符) * .. + 使用通配符進行描述 3.operators(運算符) && || ! 使用運算符進行多條件的判斷
Designators(指示器)
匹配方法 execution() 匹配注解 @target() @args() @within() @annotation() 匹配包/類型 @within() 匹配對象 this() bean() target() 匹配參數 args()
Wildcards(通配符)
* 匹配任意數量的字符 + 匹配指定類及其子類 .. 一般用于匹配任意參數的子包或參數
Operators(運算符)
&& 與操作符 || 或操作符 ! 非操作符2-3 匹配包類
// 匹配 ProductServiceAop 類里面的所有方法 @Pointcut("within(com.myimooc.springaopguide.service.ProductServiceAop)") public void matchType(){} // 匹配 com.myimooc.springaopguide.service 包及子包下所有類的方法 @Pointcut("within(com.myimooc.springaopguide.service..*)") public void matchPackage(){}2-4 匹配對象
// 匹配AOP對象的目標對象為指定類型的方法,即DemoDao的aop代理對象的方法 @Pointcut("this(com.myimooc.springaopguide.dao.DemoDao)") public void testDemo(){} // 匹配實現IDao接口的目標對象(而不是aop代理后的對象)的方法,這里即DemoDao的方法 @Pointcut("target(com.myimooc.springaopguide.dao.IDao)") public void targetDemo(){} // 匹配所有以Service結尾的bean里面的方法 @Pointcut("bean(*Service)") public void beanDemo(){}2-5 匹配參數
// 匹配任何以find開頭而且只有一個Long參數的方法 @Pointcut("execution(* *..find*(Long))") public void argsDemo1(){} // 匹配任何只有一個Long參數的方法 @Pointcut("args(Long)") public void argsDemo2(){} // 匹配任何以find開頭而且第一個參數為Long型的方法 @Pointcut("execution(* *..find*(Long,..))") public void argsDemo3(){} // 匹配第一個參數為Long型的方法 @Pointcut("args(Long,..))") public void argsDemo4(){}2-6 匹配注解
// 匹配方法標注有AdminOnly的注解的方法 @Pointcut("@annotation(com.myimooc.springaopguide.security.AdminOnly)") public void annoDemo(){} // 匹配標注有Beta的類底下的方法,要求的annotation的RetentionPolicy級別為CLASS @Pointcut("@within(com.google.common.annotations.Beta)") public void annoWithDemo(){} // 匹配標注有Repository的類底下的方法,要求的RetentionPolicy級別為RUNTIME @Pointcut("@target(org.springframework.stereotype.Repository)") public void annoTargetDemo(){} // 匹配傳入的參數類標注有Repository注解的方法 @Pointcut("@args(org.springframework.stereotype.Repository)") public void annoArgsDemo(){}2-7 匹配方法
execution()格式
execution( modifier-pattern? // 修飾符匹配 ret-type-pattern // 返回值匹配 declaring-type-pattern? // 描述值包名 name-pattern(param-pattern) // 方法名匹配(參數匹配) throws-pattern?// 拋出異常匹配 )
execution()實例
// 匹配 使用public修飾符 任意返回值 在com.myimooc.springaopguide.service包及子下 // 以Service結尾的類 任意方法(任意參數) @Pointcut("execution(public * com.myimooc.springaopguide.service..*Service.*(..))") public void matchCondition(){}2-8 建言注解
5中Advice(建言)注解
@Before,前置通知 @After(finally),后置通知,方法執行完之后 @AfterReturning,返回通知,成功執行之后 @AfterThrowing,異常通知,拋出異常之后 @Around,環繞通知
5中Advice(建言)實例
// 定義切點,攔截使用NeedSecured注解修飾的方法 @Pointcut("@within(com.myimooc.demo.security.NeedSecured)") public void annoTargetVsWithinDemo(){} // 使用NeedSecured注解修飾 且 在com.myimooc包下的方法 @Before("annoTargetVsWithinDemo() && within(com.myimooc..*)") public void beforeDemo(){ System.out.println("被攔截方法執行之前執行"); } @After("annoTargetVsWithinDemo() && within(com.myimooc..*)") public void afterDemo(){ System.out.println("被攔截方法執行之后執行"); } @AfterReturning("annoTargetVsWithinDemo() && within(com.myimooc..*)") public void afterReturning(){ System.out.println("代碼成功之后執行"); } @AfterThrowing("annoTargetVsWithinDemo() && within(com.myimooc..*)") public void afterThrowing(){ System.out.println("代碼執行拋出異常之后執行"); } @Around("annoTargetVsWithinDemo() && within(com.myimooc..*)") public Object aroundDemo(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("相當于@Before"); try{ Object result = pjp.proceed(pjp.getArgs()); System.out.println("相當于@AfterReturning"); return result; }catch (Throwable throwable) { System.out.println("相當于@AfterThrowing"); throw throwable; }finally { System.out.println("相當于@After"); } }
Advice中的參數及結果綁定
@Before("annoTargetVsWithinDemo() && within(com.myimooc..*) && args(userId)") public void beforeWithArgs(JoinPoint joinPoint,Long userId){ System.out.println("被攔截方法執行之前執行,args:"+userId); } @AfterReturning(value="annoTargetVsWithinDemo() && within(com.myimooc..*)",returning="returnValue") public void getResult(Object returnValue){ if(returnValue != null){ System.out.println("代碼成功之后執行,result:"+returnValue); } }第三章:實現原理 3-1 本節內容
上節回顧
Pointcut expression的組成部分 各種designators的區別 5中advice及參數、結果綁定
實現原理
概述 設計:代理模式、責任鏈模式 實現:JDK實現、cglib實現3-2 原理概述
原理概述:植入的時機
1.編譯期(AspectJ) 2.類加載時(Aspectj 5+) 3.運行時(Spring AOP)【本節課講解內容】
運行時值入
運行時織入是怎么實現的 從靜態代理到動態代理 基于接口代理與基于繼承代理3-3 代理模式
代理AOP對象
Caller:調用方 Proxy:AOP代理對象 Target:目標對象
代理模式類圖
客戶端通過接口來引用目標對象 代理對象把真正的方法委托目標對象來執行,自己執行額外的邏輯
代碼編寫
1.編寫Subject類
package com.myimooc.myproxydemo.pattern; /** * @title 代理對象接口 * @describe * @author zc * @version 1.0 2017-09-13 */ public interface Subject { void request(); }
2.編寫RealSubject類
package com.myimooc.myproxydemo.pattern; /** * @title 目標對象 * @describe 實現了Subject接口 * @author zc * @version 1.0 2017-09-13 */ public class RealSubject implements Subject{ @Override public void request() { System.out.println("real subject execute request"); } }
3.編寫Proxy類
package com.myimooc.myproxydemo.pattern; /** * @title 代理對象 * @describe 同樣也實現了Subject接口 * @author zc * @version 1.0 2017-09-13 */ public class Proxy implements Subject{ // 需要引用目標對象 private RealSubject realSubject; // 強制必須傳入目標對象 public Proxy(RealSubject realSubject) { this.realSubject = realSubject; } @Override public void request() { // 在目標對象方法執行之前做一些額外的事情 System.out.println("before"); try{ // 代理對象不會做真實的業務邏輯,還是委托給真實的目標對象執行 realSubject.request(); }catch (Exception e) { System.out.println("ex:"+e.getMessage()); throw e; }finally { // 在目標對象方法執行之后做一些額外的事情 System.out.println("after"); } } }
4.編寫Client類
package com.myimooc.myproxydemo.pattern; /** * @title 客戶端 * @describe 測試代理模式 * @author zc * @version 1.0 2017-09-13 */ public class Client { public static void main(String[] args) { Subject subject = new Proxy(new RealSubject()); subject.request(); } }3-4 JDK代理
靜態代理與動態代理
靜態代理的缺點:每當需要代理的方法越多的時候,重復的邏輯就越多 動態代理的兩類實現:基于接口代理與基于繼承代理 兩類實現的代表技術:JDK代理與Cglib代理
JDK實現要點
類:java.lang.reflect.Proxy 接口:InvocationHandler 只能基于接口進行動態代理
代碼編寫
1.編寫JdkSubject類
package com.myimooc.myproxydemo.jdkimpl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import com.myimooc.myproxydemo.pattern.RealSubject; /** * @title 動態代理類 * @describe 相當于AOP的aspect * @author zc * @version 1.0 2017-09-13 */ public class JdkSubject implements InvocationHandler{ // 同樣需要引入目標對象 private RealSubject realSubject; public JdkSubject(RealSubject realSubject) { this.realSubject = realSubject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在目標對象方法執行之前做一些額外的事情 System.out.println("before"); Object result = null; try{ // 代理對象不會做真實的業務邏輯,還是委托給真實的目標對象執行 result = method.invoke(realSubject, args); }catch (Exception e) { System.out.println("ex:"+e.getMessage()); throw e; }finally { // 在目標對象方法執行之后做一些額外的事情 System.out.println("after"); } return result; } }
2.編寫Client類
package com.myimooc.myproxydemo.jdkimpl; import java.lang.reflect.Proxy; import com.myimooc.myproxydemo.pattern.RealSubject; import com.myimooc.myproxydemo.pattern.Subject; /** * @title 動態代理類 * @describe JDK實現動態代理測試類 * @author zc * @version 1.0 2017-09-13 */ public class Client { public static void main(String[] args) { Subject subject = (Subject) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{Subject.class}, new JdkSubject(new RealSubject())); subject.request(); } }3-5 JDK解析
JDK代理源碼解析
Proxy.newProxyInstance(首先,調用該方法) getProxyClass0、ProxyClassFactory、ProxyGenerator(然后,分別調用方法,生成字節碼) newInstance(最后,利用反射根據字節碼生成實例)3-6 Cglib代理
代碼編寫
1.編寫DemoMethodInterceptor類
package com.myimooc.myproxydemo.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * @title 需要植入的代碼類 * @describe 需要實現MethodInterceptorj接口 * @author zc * @version 1.0 2017-09-13 */ public class DemoMethodInterceptor implements MethodInterceptor{ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before in cglib"); Object result = null; try{ // 代理類調用父類的方法 proxy.invokeSuper(obj, args); }catch (Exception e) { System.out.println("ex:"+e.getMessage()); throw e; }finally { // 在目標對象方法執行之后做一些額外的事情 System.out.println("after in cglib"); } return result; } }
2.編寫Client類
package com.myimooc.myproxydemo.cglib; import com.myimooc.myproxydemo.pattern.RealSubject; import com.myimooc.myproxydemo.pattern.Subject; import net.sf.cglib.proxy.Enhancer; /** * @title 動態代理類 * @describe Cglib實現動態代理測試類 * @author zc * @version 1.0 2017-09-13 */ public class Client { public static void main(String[] args) { // 實例化Enhancer對象 Enhancer enhancer = new Enhancer(); // 設置需要代理的對象 enhancer.setSuperclass(RealSubject.class); // 設置需要植入的代碼 enhancer.setCallback(new DemoMethodInterceptor()); // 生成代理類 Subject subject = (Subject)enhancer.create(); subject.request(); } }
JDK與Cglib代理對比
JDK只能針對有接口的類的接口方法進行動態代理 Cglib基于繼承來實現代理,無法對static、final類進行代理 Cglib基于繼承來實現代理,無法對private、static方法進行代理3-7 Spring選擇
Spring創建代理bean時序圖
SpringAOP對兩種實現的選擇
如果目標對象實現了接口,則默認采用JDK動態代理 如果目標對象沒有實現接口,則采用Cglib進行動態代理 如果目標對象實現了接口,但設置強制cglib代理,則使用cglib代理 在SpringBoot中,通過@EnableAspectJAutoProxy(proxyTargetClass=true)設置3-8 鏈式調用
當多個AOP作用到同一個目標對象時,采用責任鏈模式
責任鏈模式類圖
代碼編寫
1.編寫Handler類
package com.myimooc.myproxydemo.chain; /** * @title 責任鏈模式 * @describe 抽象接口 * @author zc * @version 1.0 2017-09-13 */ public abstract class Handler { // 后繼Handler,是否有類進行處理 private Handler sucessor; // 對外暴露 public void execute(){ handleProcess(); if(sucessor != null){ sucessor.execute(); } } // 由子類實現 protected abstract void handleProcess(); public Handler getSucessor() { return sucessor; } public void setSucessor(Handler sucessor) { this.sucessor = sucessor; } }
2.編寫Client類
package com.myimooc.myproxydemo.chain; /** * @title 責任鏈模式 * @describe 測試類 * @author zc * @version 1.0 2017-09-13 */ public class Client { static class HandlerA extends Handler{ @Override protected void handleProcess() { System.out.println("handle by a"); } } static class HandlerB extends Handler{ @Override protected void handleProcess() { System.out.println("handle by b"); } } static class HandlerC extends Handler{ @Override protected void handleProcess() { System.out.println("handle by c"); } } public static void main(String[] args) { HandlerA handlerA = new HandlerA(); HandlerB HandlerB = new HandlerB(); HandlerC HandlerC = new HandlerC(); // 設置鏈接關系 handlerA.setSucessor(HandlerB); HandlerB.setSucessor(HandlerC); handlerA.execute(); } }
3.編寫Chain類
package com.myimooc.myproxydemo.chain; import java.util.List; /** * @title 責任鏈模式 * @describe 封裝鏈式關系 * @author zc * @version 1.0 2017-09-13 */ public class Chain { private Listhandlers; private int index = 0; public Chain(List handlers){ this.handlers = handlers; } public void proceed(){ if(index >= handlers.size()){ return; } handlers.get(index++).execute(this); } }
4.編寫ChainHandler類
package com.myimooc.myproxydemo.chain; /** * @title 責任鏈模式 * @describe 對Handler進行封裝 * @author zc * @version 1.0 2017-09-13 */ public abstract class ChainHandler { public void execute(Chain chain){ handleProcess(); chain.proceed(); } // 由子類實現 protected abstract void handleProcess(); }
5.編寫ChainClient類
package com.myimooc.myproxydemo.chain; import java.util.Arrays; import java.util.List; /** * @title 責任鏈模式 * @describe 有順序的鏈式調用測試類 * @author zc * @version 1.0 2017-09-13 */ public class ChainClient { static class ChainHandlerA extends ChainHandler{ @Override protected void handleProcess() { System.out.println("handle by a"); } } static class ChainHandlerB extends ChainHandler{ @Override protected void handleProcess() { System.out.println("handle by b"); } } static class ChainHandlerC extends ChainHandler{ @Override protected void handleProcess() { System.out.println("handle by c"); } } public static void main(String[] args) { // 聲明鏈式調用順序 List第四章:代碼解讀 4-1 本節內容handlers = Arrays.asList( new ChainHandlerA(), new ChainHandlerB(), new ChainHandlerC() ); Chain chain = new Chain(handlers); chain.proceed(); } }
上節回顧
靜態代理與動態代理 JDK代理與Cglib代理區別及局限 代理模式與責任鏈模式
Spring AOP在開源項目里面的應用:三個例子
事務:@Transactional:Spring如何利用Transaction進行事務控制 安全:@PreAuthorize:Spring Security如何利用PreAuthorize進行安全控制 緩存:@Cacheable:Spring Cache如何利用Cacheable進行緩存控制
通過案例來講解,源碼可到我的github地址查看
第五章:實戰案例 5-1 案例背景實戰案例背景
商家產品管理系統 記錄產品修改的操作記錄 什么人在什么時間修改了哪些產品的哪些字段修改為什么值
實現思路
利用aspect去攔截增刪改方法 利用反射獲取對象的新舊值 利用@Around的advice去記錄操作記錄5-2 案例實現
創建名為mydatalog的maven項目pom如下
4.0.0 com.myimooc mydatalog 0.0.1-SNAPSHOT jar mydatalog http://maven.apache.org org.springframework.boot spring-boot-starter-parent 1.5.2.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-data-jpa mysql mysql-connector-java org.springframework.boot spring-boot-starter-test test commons-beanutils commons-beanutils com.alibaba fastjson 1.2.36
完成后的項目結構圖如下
受篇幅限制,源碼請到我的github地址查看
第六章:課程總結 6-1 課程總結要點清單
AOP的適用范圍及優劣勢 AOP的概念及Spring切面表達式 AOP的實現原理及運用
使用SpringAOP的注意事項
不宜把重要的業務邏輯放到AOP中處理 無法攔截static、final、private方法 無法攔截內部方法調用
課程小結
合理利用面向切面編程提高代碼質量 掌握SpringAOP概念及實現原理 了解AOP的優缺點及SpringAOP的使用局限
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70475.html
摘要:入門篇學習總結時間年月日星期三說明本文部分內容均來自慕課網。主要的功能是日志記錄,性能統計,安全控制,事務處理,異常處理等等。 《Spring入門篇》學習總結 時間:2017年1月18日星期三說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com教學示例源碼:https://github.com/zccodere/s...個人學習源碼:https://git...
摘要:時間年月日星期日說明本文部分內容均來自慕課網。慕課網教學示例源碼個人學習源碼第一章課程介紹課程介紹本課程緊接著小時學會課程,請先看入門課。異常返回通知在連接點拋出異常后執行。 時間:2017年3月19日星期日說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com教學示例源碼:https://github.com/zccodere/s...個人學習源碼:htt...
摘要:事務管理學習總結時間年月日星期二說明本文部分內容均來自慕課網。一致性一致性指事務前后數據的完整性必須保持一致。聲明式事務管理基于的方式很少使用需要為每個進行事務管理的類,配置一個進行增強。 《Spring事務管理》學習總結 時間:2017年2月7日星期二說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com/教學示例源碼:https://github.com...
摘要:時間年月日星期六說明本文部分內容均來自慕課網。慕課網教學源碼學習源碼第一章課程介紹課程簡介是啥讀音是輕量級的依賴注入框架說明一個的框架需要有基礎什么是剝離注入輕量級代碼少易維護性能優異,跟比較。 時間:2017年10月14日星期六說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com 教學源碼:https://github.com/zccodere/s......
時間:2017年08月16日星期三說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com教學源碼:無學習源碼:https://github.com/zccodere/s... 第一章:課程介紹 1-1 課程介紹 課程目錄 1.ssh知識點回顧 2.搭建ssm開發環境 3.struts2整合spring 4.spring整合hibernate 5.案例:使用ssh框架開發...
閱讀 2474·2021-11-16 11:45
閱讀 2444·2021-10-11 10:59
閱讀 2251·2021-10-08 10:05
閱讀 3816·2021-09-23 11:30
閱讀 2370·2021-09-07 09:58
閱讀 790·2019-08-30 15:55
閱讀 773·2019-08-30 15:53
閱讀 1923·2019-08-29 17:00