學習springAOP

第三章 Spring AOP

第一節 AOP 簡介

1. 概念

AOP全稱為Aspect Oriented Programming,表示面向切面編程。何為切面呢?

由此可以得出,切面是一種將那些與業務無關,但業務模塊都需要使用的功能封裝起來的技術。這樣便于減少系統的重復代碼,降低模塊之間的耦合度。

2. AOP 基本術語

  • 連接點( Joinpoint ):

    連接點就是被攔截到的程序執行點,因為Spring只支持方法類型的連接點,所以在Spring中連接點就是被攔截到的方法。連接點由兩個信息確定:

    • 方法( 表示程序執行點,即在哪個目標方法)
    • 相對點(表示方位,即目標方法的什么位置,比如調用前,后等)
  • 切入點(Pointcut):

    切入點是對連接點進行攔截的條件定義。切入點表達式如何和連接點匹配是AOP的核心,Spring缺省使用AspectJ切入點語法。 一般認為,所有的方法都可以認為是連接點,但是我們并不希望在所有的方法上都添加通知,而切入點的作用就是提供一組規則來匹配連接點,給滿足規則的連接點添加通知。

  • 通知、增強(Advice):

    可以為切入點添加額外功能,分為:前置通知、后置通知、異常通知、環繞通知、最終通知等。

  • 目標對象(Target):

    目標對象指將要被增強的對象,即包含主業務邏輯的類對象。或者說是被一個或者多個切面所通知的對象。

  • 織入(Weaving):

    織入是將切面和業務邏輯對象連接起來, 并創建通知代理的過程。織入可以在編譯時,類加載時和運行時完成。在編譯時進行織入就是靜態代理,而在運行時進行織入則是動態代理

  • 代理(Proxy):

    被AOP織入通知后,產生的結果類。

  • 切面(Aspect):

    切面是一個橫切關注點的模塊化,一個切面能夠包含同一個類型的不同增強方法,比如說事務處理和日志處理可以理解為兩個切面。切面由切入點和通知組成,它既包含了橫切邏輯的定義,也包括了切入點的定義。 Spring AOP就是負責實施切面的框架,它將切面所定義的橫切邏輯織入到切面所指定的連接點中。

第二節 AOP 應用

AOP應用場景有許多,最典型的應用場景就是日志和事務。這里以事務實現為例進行講解。

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.11</version>
</dependency>
<!-- 切面相關的包 -->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.9</version>
</dependency>

1. 編寫業務層

public interface UserService {int saveUser(Map<String,Object> params);
}public class UserServiceImpl implements UserService {@Overridepublic int saveUser(Map<String, Object> params) {System.out.println("保存用戶信息" + params);return 0;}
}

2. 配置業務層

AOP 功能的實現是基于 IOC 的,因此,業務層對象應該納入 IOC 容器來管理。

<?xml version="1.0" encoding="UTF-8"?>
<!-- xmlns = xml namespace-->
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 業務層對象--><bean id="userService" class="com.qf.spring.aop.service.impl.UserServiceImpl" />
</beans>

3. 編寫通知類

通知分為前置通知、后置通知、異常拋出通知、環繞通知、最終通知五種。首先實現前置通知。

前置通知接口

public interface MethodBeforeAdvice extends BeforeAdvice {/*** Callback before a given method is invoked.*/void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}
public class BeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {String methodName = method.getName();String className = method.getDeclaringClass().getName();System.out.println("準備執行方法:" + className + "." + methodName + ",參數:" + Arrays.toString(args));}
}

4. 配置通知

通知的實現也是基于 IOC 容器的,因此需要將通知對象納入 IOC 容器管理。

<?xml version="1.0" encoding="UTF-8"?>
<!-- xmlns = xml namespace-->
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 業務層對象--><bean id="userService" class="com.qf.spring.aop.service.impl.UserServiceImpl" /><!--配置通知對象--><bean id="before" class="com.qf.spring.aop.advice.BeforeAdvice" />
</beans>

5. AOP 配置

當通知對象和業務層對象都納入 IOC 容器管理之后,需要將通知對象作用在業務層對象上。Spring 提供了 aop 標簽來完成這一功能。

<aop:config><!--pointcut表示切點,也就是通知會在哪些位置觸發expression表示切點表達式,切點表達式必須是execution(), execution()方法中的參數必須配置到方法上比如 * com.qf.spring.aop.service..*(..)第一個 * 表示任意訪問修飾符com.qf.spring.aop.service.. 最后的兩個..表示service包下面的所有子包中的類*(..) 表示任意方法, 如果()中沒有..,則表示不帶參數的方法;有,就表示帶任意參數--><aop:pointcut id="切點ID" expression="切點表達式"/><aop:advisor advice-ref="通知ID" pointcut-ref="切點ID" />
</aop:config>

要使用 aop 標簽,必須要在 beans 標簽上添加 aop 命名空間

xmlns:aop="http://www.springframework.org/schema/aop"

然后在 xsi:schemaLocation 屬性值中添加 aop 命名空間和約束文檔

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
<aop:config><aop:pointcut id="pc" expression="execution(* com.qf.spring.aop.service..*(..))"/><aop:advisor advice-ref="before" pointcut-ref="pc" />
</aop:config>

6. 測試

public class AopTest {@Testpublic void saveUserTest(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = context.getBean("userService", UserService.class);Map<String,Object> params = new HashMap<>();params.put("name", "劉德華");params.put("sex", "男");userService.saveUser(params);}
}

7. 后置通知

后置通知接口

public interface AfterReturningAdvice extends AfterAdvice {/*** Callback after a given method successfully returned.*/void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
}
public class AfterAdvice implements AfterReturningAdvice {@Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {String methodName = method.getName();String className = method.getDeclaringClass().getName();System.out.println("執行完方法:" + className + "." + methodName + ",參數:" + Arrays.toString(args) + ",得到返回值:" + returnValue);}
}
<bean id="after" class="com.qf.spring.aop.advice.AfterAdvice" />
<aop:config><!--pointcut表示切點,也就是通知會在哪些位置觸發expression表示切點表達式,切點表達式必須是execution(), execution()方法中的參數必須配置到方法上比如 * com.qf.spring.aop.service..*(..)第一個 * 表示任意訪問修飾符com.qf.spring.aop.service.. 最后的兩個..表示service包下面的所有子包中的類*(..) 表示任意方法, 如果()中沒有..,則表示不帶參數的方法;有,就表示帶任意參數--><aop:pointcut id="pc" expression="execution(* com.qf.spring.aop.service..*(..))"/><aop:advisor advice-ref="before" pointcut-ref="pc" /><aop:advisor advice-ref="after" pointcut-ref="pc" />
</aop:config>

8. 異常拋出通知

異常拋出通知接口

/*** There are not any methods on this interface, as methods are invoked by* reflection. Implementing classes must implement methods of the form:* void afterThrowing([Method, args, target], ThrowableSubclass);* public void afterThrowing(Exception ex)</pre>* public void afterThrowing(RemoteException)</pre>* public void afterThrowing(Method method, Object[] args, Object target, Exception ex)* public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)*/
public interface ThrowsAdvice extends AfterAdvice {
}
public class ExceptionAdvice implements ThrowsAdvice {public void afterThrowing(Method method, Object[] args, Object target, Exception ex){String methodName = method.getName();String className = method.getDeclaringClass().getName();System.out.println("執行方法時:" + className + "." + methodName + ",參數:" + Arrays.toString(args) + ",發生了異常:" + ex.getMessage());}
}public class UserServiceImpl implements UserService {@Overridepublic int saveUser(Map<String, Object> params) {System.out.println("保存用戶信息" + params);throw new RuntimeException("異常拋出演示");}
}
<bean id="exception" class="com.qf.spring.aop.advice.ExceptionAdvice" />
<aop:config><!--pointcut表示切點,也就是通知會在哪些位置觸發expression表示切點表達式,切點表達式必須是execution(), execution()方法中的參數必須配置到方法上比如 * com.qf.spring.aop.service..*(..)第一個 * 表示任意訪問修飾符com.qf.spring.aop.service.. 最后的兩個..表示service包下面的所有子包中的類*(..) 表示任意方法, 如果()中沒有..,則表示不帶參數的方法;有,就表示帶任意參數--><aop:pointcut id="pc" expression="execution(* com.qf.spring.aop.service..*(..))"/><aop:advisor advice-ref="before" pointcut-ref="pc" /><aop:advisor advice-ref="after" pointcut-ref="pc" /><aop:advisor advice-ref="exception" pointcut-ref="pc" />
</aop:config>

9. 環繞通知

環繞通知接口

@FunctionalInterface
public interface MethodInterceptor extends Interceptor {/*** Implement this method to perform extra treatments before and* after the invocation.*/@NullableObject invoke(@Nonnull MethodInvocation invocation) throws Throwable;
}
public class AroundAdvice implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod(); //獲取被攔截的方法對象Object[] args = invocation.getArguments();//獲取方法的參數Object target = invocation.getThis(); //獲取代理對象String methodName = method.getName();String className = method.getDeclaringClass().getName();try {System.out.println("準備執行方法:" + className + "." + methodName + ",參數:" + Arrays.toString(args));Object returnValue = method.invoke(target, args);System.out.println("執行完方法:" + className + "." + methodName + ",參數:" + Arrays.toString(args) + ",得到返回值:" + returnValue);return returnValue;} catch (Throwable t){System.out.println("執行方法時:" + className + "." + methodName + ",參數:" + Arrays.toString(args) + ",發生了異常:" + t.getMessage());throw t;}}
}
<!--配置通知對象-->
<!--  <bean id="before" class="com.qf.spring.aop.advice.BeforeAdvice" /><bean id="after" class="com.qf.spring.aop.advice.AfterAdvice" /><bean id="exception" class="com.qf.spring.aop.advice.ExceptionAdvice" />-->
<bean id="around" class="com.qf.spring.aop.advice.AroundAdvice" />
<aop:config><!--pointcut表示切點,也就是通知會在哪些位置觸發expression表示切點表達式,切點表達式必須是execution(), execution()方法中的參數必須配置到方法上比如 * com.qf.spring.aop.service..*(..)第一個 * 表示任意訪問修飾符com.qf.spring.aop.service.. 最后的兩個..表示service包下面的所有子包中的類*(..) 表示任意方法, 如果()中沒有..,則表示不帶參數的方法;有,就表示帶任意參數--><aop:pointcut id="pc" expression="execution(* com.qf.spring.aop.service..*(..))"/><!--   <aop:advisor advice-ref="before" pointcut-ref="pc" /><aop:advisor advice-ref="after" pointcut-ref="pc" /><aop:advisor advice-ref="exception" pointcut-ref="pc" />--><aop:advisor advice-ref="around" pointcut-ref="pc" />
</aop:config>

第三節 AspectJ

1. AspectJ 簡介

AspectJ是一個面向切面的框架,它擴展了Java語言,定義了AOP 語法,能夠在編譯期提供代碼的織入。Spring通過集成AspectJ實現了以注解的方式定義增強類,大大減少了配置文件中的工作量

2. AspectJ 注解

  • @Aspect 切面標識
  • @Pointcut 切入點
  • @Before 前置通知
  • @AfterReturning 后置通知
  • @Around 環繞通知
  • @AfterThrowing 異常拋出通知

3. AspectJ 應用

3.1 通知類編寫
@Aspect
public class AspectJAdvice {@Before(value = "execution(* com.qf.spring.aop.service..*(..))")public void before(JoinPoint jp){Object[] args = jp.getArgs(); //獲取方法參數Signature signature = jp.getSignature(); //獲取簽名if(signature instanceof MethodSignature){ //如果簽名是方法簽名Method method = ((MethodSignature) signature).getMethod(); //獲取方法String methodName = method.getName();String className = method.getDeclaringClass().getName();System.out.println("準備執行方法:" + className + "." + methodName + ",參數:" + Arrays.toString(args));}}@AfterReturning(value = "execution(* com.qf.spring.aop.service..*(..))", returning = "returnValue")public void after(JoinPoint jp, Object returnValue){Object[] args = jp.getArgs(); //獲取方法參數Signature signature = jp.getSignature(); //獲取簽名if(signature instanceof MethodSignature){ //如果簽名是方法簽名Method method = ((MethodSignature) signature).getMethod(); //獲取方法String methodName = method.getName();String className = method.getDeclaringClass().getName();System.out.println("執行完方法:" + className + "." + methodName + ",參數:" + Arrays.toString(args) + ",得到返回值:" + returnValue);}}@AfterThrowing(value = "execution(* com.qf.spring.aop.service..*(..))", throwing = "t")public void exception(JoinPoint jp, Throwable t){Object[] args = jp.getArgs(); //獲取方法參數Signature signature = jp.getSignature(); //獲取簽名if(signature instanceof MethodSignature){ //如果簽名是方法簽名Method method = ((MethodSignature) signature).getMethod(); //獲取方法String methodName = method.getName();String className = method.getDeclaringClass().getName();System.out.println("執行方法時:" + className + "." + methodName + ",參數:" + Arrays.toString(args) + ",發生了異常:" + t.getMessage());}}@Around("execution(* com.qf.spring.aop.service..*(..))")public Object around(ProceedingJoinPoint pjp) throws Throwable {Object[] args = pjp.getArgs();//獲取方法的參數Object target = pjp.getTarget(); //獲取代理對象Signature signature = pjp.getSignature(); //獲取簽名if(signature instanceof MethodSignature) { //如果簽名是方法簽名Method method = ((MethodSignature) signature).getMethod(); //獲取被攔截的方法對象String methodName = method.getName();String className = method.getDeclaringClass().getName();try {System.out.println("準備執行方法:" + className + "." + methodName + ",參數:" + Arrays.toString(args));Object returnValue = method.invoke(target, args);System.out.println("執行完方法:" + className + "." + methodName + ",參數:" + Arrays.toString(args) + ",得到返回值:" + returnValue);return returnValue;} catch (Throwable t){System.out.println("執行方法時:" + className + "." + methodName + ",參數:" + Arrays.toString(args) + ",發生了異常:" + t.getMessage());throw t;}}return null;}
}
3.2 啟用注解支持
<bean  class="com.qf.spring.aop.advice.AspectJAdvice" />
<!--配置通知對象-->
<!--  <bean id="before" class="com.qf.spring.aop.advice.BeforeAdvice" /><bean id="after" class="com.qf.spring.aop.advice.AfterAdvice" /><bean id="exception" class="com.qf.spring.aop.advice.ExceptionAdvice" />-->
<!--    <bean id="around" class="com.qf.spring.aop.advice.AroundAdvice" />-->
<!--<aop:config>&lt;!&ndash;pointcut表示切點,也就是通知會在哪些位置觸發expression表示切點表達式,切點表達式必須是execution(), execution()方法中的參數必須配置到方法上比如 * com.qf.spring.aop.service..*(..)第一個 * 表示任意訪問修飾符com.qf.spring.aop.service.. 最后的兩個..表示service包下面的所有子包中的類*(..) 表示任意方法, 如果()中沒有..,則表示不帶參數的方法;有,就表示帶任意參數&ndash;&gt;<aop:pointcut id="pc" expression="execution(* com.qf.spring.aop.service..*(..))"/>&lt;!&ndash;   <aop:advisor advice-ref="before" pointcut-ref="pc" /><aop:advisor advice-ref="after" pointcut-ref="pc" /><aop:advisor advice-ref="exception" pointcut-ref="pc" />&ndash;&gt;<aop:advisor advice-ref="around" pointcut-ref="pc" /></aop:config>--><!-- 啟動AspectJ 注解  自動為類生成代理-->
<aop:aspectj-autoproxy proxy-target-class="true" />

第四節 代理模式

代理模式一共分為兩種: 靜態代理和動態代理

1. 靜態代理

靜態代理模式由三個部分構成:

  • 一個公共的接口

  • 一個代理角色

  • 一個被代理角色

其中代理角色和被代理角色均需要實現公共接口。

/*** 人類接口,描述人類購物*/
public interface Person {//購物void shopping();
}
/*** 被代理的角色*/
public class ZhangSan implements Person{@Overridepublic void shopping() {System.out.println("購物");}
}
/*** 代理角色*/
public class PersonProxy implements Person{private ZhangSan zhangSan; //維護一個被代理的角色public PersonProxy(ZhangSan zhangSan) {this.zhangSan = zhangSan;}@Overridepublic void shopping() {System.out.println("代理人準備代理購物");zhangSan.shopping();System.out.println("代理人代理購物完畢");}
}
/*** 靜態代理測試*/
public class StaticProxyTest {public static void main(String[] args) {Person p = new PersonProxy(new ZhangSan());p.shopping();}
}

思考:如果有多人想要代理購物,那么像PersonProxy這樣的類就需要寫多次;從編程的角度出發,這顯然不合理。應該如何解決?

使用泛型進行解決。

public class StaticProxy<T> implements Person {private T t;public StaticProxy(T t) {this.t = t;}@Overridepublic void shopping() {//判斷當前對象是否繼承或者實現了Person接口if(!Person.class.isAssignableFrom(t.getClass()))throw new RuntimeException(t.getClass().getName() + "沒有實現Person接口");System.out.println("代理人準備代理購物");((Person)t).shopping();System.out.println("代理人代理購物完畢");}
}/*** 靜態代理測試*/
public class StaticProxyTest {public static void main(String[] args) {
//        Person p = new PersonProxy(new ZhangSan());
//        p.shopping();Person p = new StaticProxy<>(new ZhangSan());p.shopping();}
}

思考:如果有多個接口想要代理,應該如何解決?

使用動態代理。

2. 動態代理

動態代理也分為兩種:JDK 動態代理 和 CGLIB 動態代理

2.1 JDK 動態代理

JDK 動態代理是基于接口實現的,因此只能為實現了接口的類做代理。其實現原理是:在內存中生成一個代理類繼承與 Proxy, 同時實現代理接口,然后在內存中進行編譯,編譯后使用類加載器將該代理類加載進來,從而創建一個代理對象。

public class DynamicProxyTest {public static void main(String[] args) {Person p = new ZhangSan();Class<?> clazz = Person.class;Class[] interfaces = { clazz };ClassLoader loader = clazz.getClassLoader();InvocationHandler handler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("準備進行代理購物");return method.invoke(p, args);}};Person proxy = (Person) Proxy.newProxyInstance(loader, interfaces, handler);proxy.shopping();}
}
2.2 CGLIB 動態代理

CGLIB 動態代理是基于繼承實現的,因此可以為任何類做代理。如果一個類實現了接口,通常會使用 JDK 動態代理,但也可以強制使用 CGLIB 動態代理。

public class CgLibProxy {public static void main(String[] args) {final Person p = new ZhangSan();//創建字節碼增強對象Enhancer enhancer = new Enhancer();//設置父類,需要繼承enhancer.setSuperclass(p.getClass());//需要注意:這里的InvocationHandler是CGLIB提供的net.sf.cglib.proxy.InvocationHandlerenhancer.setCallback(new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] params) throws Throwable {System.out.println("準備進行代理購物");return method.invoke(p, params);}});//創建動態代理實例Person proxy = (Person) enhancer.create();proxy.shopping();}
}

Spring CGLIB

public class SpringCgLibProxy {public static void main(String[] args) {final Person p = new ZhangSan();//創建字節碼曾強對象Enhancer enhancer = new Enhancer();//設置父類,需要繼承enhancer.setSuperclass(p.getClass());enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {System.out.println("準備進行代理購物");return method.invoke(p, params);}});//創建動態代理實例Person proxy = (Person) enhancer.create();proxy.shopping();}
}
2.3 區別

JDK 動態代理只能為實現了接口的類做代理, CGLIB 動態代理能夠為所有的類做代理。JDK 動態代理的創建代理從效率上來說要比 CGLIB 動態代理快。Cglib不能對聲明final的方法進行代理,因為final關鍵字修飾的方法不可被重寫。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/40392.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/40392.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/40392.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

昇思25天學習打卡營第4天|應用實踐

昇思25天學習打卡營第4天 文章目錄 昇思25天學習打卡營第4天基于 MindSpore 實現 BERT 對話情緒識別模型簡介環境配置數據集數據加載和數據預處理input_idsattention_mask 模型構建模型驗證模型推理自定義推理數據集 打卡記錄 基于 MindSpore 實現 BERT 對話情緒識別 模型簡介…

奧比中光astra_pro相機使用記錄

一、信息獲取 1、官網 用于了解產品信息 http://www.orbbec.com.cn/sys/37.html 2、開發者社區 咨詢問題下載開發部https://developer.orbbec.com.cn/ 二 、windowvs19 1、相機型號 orbbec_astro_pro 根據對應的型號找到需要的包工具 踩坑1&#xff0c;因為這個相機型號…

第20章 Mac+VSCode配置C++環境

1. 下載VSCode VSCode下載地址在mac終端里輸入xcode- select --install命令&#xff0c;根據提示安裝xcode工具。 2. 安裝插件&#xff08;4個&#xff09; 打開VScode&#xff0c;點擊應用右側菜單欄 C/C&#xff08;必裝&#xff09; Code Runner&#xff08;必裝&#xf…

UCOS-III 任務調度與就緒列表管理

01. 就緒優先級位圖 在實時操作系統中&#xff0c;任務調度的效率至關重要。UCOS-III通過就緒優先級位圖來快速查找最高優先級的就緒任務&#xff0c;從而實現高效調度。就緒優先級位圖是一個按位表示的結構&#xff0c;每個位代表一個優先級&#xff0c;當某個優先級上有任務就…

高效管理 TensorFlow 2 GPU 顯存的實用指南

前言 在使用 TensorFlow 2 進行訓練或預測時&#xff0c;合理管理 GPU 顯存至關重要。未能有效管理和釋放 GPU 顯存可能導致顯存泄漏&#xff0c;進而影響后續的計算任務。在這篇文章中&#xff0c;我們將探討幾種方法來有效釋放 GPU 顯存&#xff0c;包括常規方法和強制終止任…

【FFmpeg】avcodec_open2函數

目錄 1. avcodec_open21.1 編解碼器的預初始化&#xff08;ff_encode_preinit & ff_decode_preinit&#xff09;1.2 編解碼器的初始化&#xff08;init&#xff09;1.3 釋放編解碼器&#xff08;ff_codec_close&#xff09; FFmpeg相關記錄&#xff1a; 示例工程&#xff…

Windows編程之多線程事件對象(Event Object)用法詳解

目錄 一、前言 二、基礎用法 三、API詳解 1.創建事件對象 2控制事件狀態 3.等待事件對象&#xff1a; 四、實戰案例 1.案例描述 2.代碼設計 3.總設計代碼 4.運行結果 一、前言 事件對象&#xff08;Event Object&#xff09;是我們在大型項目中&#xff0c;進行多線…

競賽選題 醫學大數據分析 - 心血管疾病分析

文章目錄 1 前言1 課題背景2 數據處理3 數據可視化4 最后 1 前言 &#x1f525; 優質競賽項目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于大數據的心血管疾病分析 該項目較為新穎&#xff0c;適合作為競賽課題方向&#xff0c;學長非常推薦&#xff01; &#x1f9…

AI繪畫Stable Diffusion 解鎖精美壁紙創作:利用SD與LLM定制你的專屬壁紙,AI副業變現指南!

大家好&#xff0c;我是畫畫的小強 今天給大家分享一下用AI繪畫Stable Diffusion 制作精美手機壁紙&#xff0c;這也可能是當前最快AIGC變現的一種途徑。雖然本文的主題為手機壁紙&#xff0c;當調整不同的比例的分辨率寬高比例&#xff0c;就可以直接復用到手機、電腦和平板、…

旋轉和鏡像的關系

旋轉矩陣行列式與 在E(3)三維空間中&#xff0c;旋轉矩陣的行列式可以用來判斷該旋轉是否包含鏡像變換。 行列式為正&#xff1a; 表示純旋轉&#xff0c;不包含鏡像。 旋轉矩陣保持向量的長度和角度不變&#xff0c;只是改變向量的方向。 行列式為負&#xff1a; 表示旋轉…

機器學習原理之 -- 支持向量機分類:由來及原理詳解

支持向量機&#xff08;Support Vector Machine, SVM&#xff09;是統計學習理論的一個重要成果&#xff0c;廣泛應用于分類和回歸問題。SVM以其高效的分類性能和良好的泛化能力在機器學習領域中占據重要地位。本文將詳細介紹支持向量機的由來、基本原理、構建過程及其優缺點。…

LVS負載均衡群集部署之——DR模式的介紹及搭建步驟

一、LVS-DR集群介紹1.1 LVS-DR 工作原理1.2 數據包流向分析1.3 LVS-DR 模式的特點1.4 LVS-DR中的ARP問題1.4.1 問題一1.4.2 問題二二、構建LVS-DR集群2.1 構建LVS-DR集群的步驟&#xff08;理論&#xff09;1.配置負載調度器&#xff08;192.168.80.30&#xff09;&#xff08;…

5分鐘教你用AI把老照片動起來,別再去花49塊9的冤枉錢了

文章目錄 需要的工具 最近&#xff0c;AI視頻在各大平臺上&#xff0c;又火了。 只是火的形式&#xff0c;變成了將老照片動起來&#xff0c;打情感牌&#xff0c;或者做很多經典電視劇的再整活。 直接把可靈的生成時間&#xff0c;從以前的4分鐘&#xff0c;生生的干成了20分鐘…

鴻蒙應用筆記

安裝就跳過了&#xff0c;一直點點就可以了 配置跳過&#xff0c;就自動下了點東西。 鴻蒙那個下載要12g個內存&#xff0c;大的有點嚇人。 里面跟idea沒區別 模擬器或者真機運行 真機要鴻蒙4.0&#xff0c;就可以實機調試 直接在手機里面跑&#xff0c;這個牛逼&#xf…

國標GB/T 28181詳解:國標GBT28181-2022 SIP服務器發起廣播的命令流程

目錄 一、定義 二、作用 1、實現信息的集中管理和分發 &#xff08;1&#xff09;信息集中 &#xff08;2&#xff09;信息分發 2、提高信息傳輸的可靠性和效率 &#xff08;1&#xff09;可靠性 &#xff08;2&#xff09;提高效率 3、支持多種設備和系統的互通 &am…

mongdb學習與使用

1. 基礎概念 MongoDB簡介&#xff1a; MongoDB是一個基于文檔的NoSQL數據庫&#xff0c;具有高性能、高可用性和易擴展性。數據存儲在類似JSON的BSON格式中。 基本術語&#xff1a; Database&#xff08;數據庫&#xff09;&#xff1a; 集合的容器。Collection&#xff08;集合…

國產強大免費WAF, 社區版雷池動態防護介紹

雷池WAF&#xff0c;基于智能語義分析的下一代 Web 應用防火墻 使用情況 我司于2023年4月23日對雷池進行測試&#xff0c;測試一個月后&#xff0c;于2023年5月24日對雷池進行正式切換&#xff0c;此時版本為1.5.1。 里程碑紀念 后續一直跟隨雷池進行版本升級&#xff0c;當前…

QT_GUI

1、QT安裝 一個跨平臺的應用程序和用戶界面框架&#xff0c;用于開發圖形用戶界面&#xff08;GUI&#xff09;應用程序以及命令行工具。QT有商業版額免費開源版&#xff0c;一般使用免費開源版即可&#xff0c;下面安裝的是QT5&#xff0c;因為出來較早&#xff0c;使用較多&…

Python特征工程 — 1.4 特征歸一化方法詳解

目錄 1 Min-Max歸一化 方法1&#xff1a;自定義的Min-Max歸一化封裝函數 方法2&#xff1a; scikit-learn庫中的MinMaxScaler 2 Z-score歸一化 方法1&#xff1a;自定義的Z-score歸一化封裝函數 方法2&#xff1a; scikit-learn庫中的StandardScaler 3 最大值歸一化 4 L…

考研生活day1--王道課后習題2.2.1、2.2.2、2.2.3

2.2.1 題目描述&#xff1a; 解題思路&#xff1a; 這是最基礎的操作&#xff0c;思路大家應該都有&#xff0c;缺少的應該是如何下筆&#xff0c;很多同學都是有思路但是不知道如何下筆&#xff0c;這時候看思路的意義不大&#xff0c;可以直接看答案怎么寫&#xff0c;最好…