Spring框架---AOP技术

AOP概念的引入

第一步创建普通Maven项目

导入依赖

<dependencies><!--spring的核心--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><!--日志--><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><!--Junit测试--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!--数据库连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version></dependency><!--Spring整合junit测试--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.2.RELEASE</version><scope>test</scope></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency></dependencies>

编写数据库数据实体类 

package com.qcby.model;public class Account {private Integer id;private String name;private Double money;public Account() {}public Account(Integer id, String name, Double money) {this.id = id;this.name = name;this.money = money;}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 Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}@Overridepublic String toString() {return "Account{" +"id=" + id +", name='" + name + '\'' +", money=" + money +'}';}
}

业务层接口

package com.qcby.service;import com.qcby.model.Account;public interface AccountService {public void updateSaveAll(Account account1, Account account2);
}

业务层实现类

package com.qcby.service.Impl;import com.qcby.dao.AccountDao;
import com.qcby.model.Account;
import com.qcby.service.AccountService;
import com.qcby.utils.TxUtils;public class AccountServiceImpl implements AccountService{private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}/*** 对saveAll方法进行增强* @param account1* @param account2* @throws SQLException*/@Overridepublic void updateSaveAll(Account account1, Account account2) {//try {//TxUtils.startTransaction();//保存账号1accountDao.updateSaveAll(account1);//显示除零错误int a=1/0;//保存账户2accountDao.updateSaveAll(account2);//TxUtils.commit();//}catch (Exception e){//e.printStackTrace();//TxUtils.rollback();//}}
}

持久层接口

package com.qcby.dao;import com.qcby.model.Account;public interface AccountDao {public void updateSaveAll(Account account);
}

持久层实现类 

package com.qcby.dao;import com.qcby.model.Account;
import com.qcby.utils.TxUtils;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;public class AccountDaoImpl implements AccountDao {//实现两个账户的转账@Overridepublic void updateSaveAll(Account account) {Connection connection = null;PreparedStatement stmt = null;try{connection = TxUtils.getConnection();String sql = "update account set money = money + ? where name = ?";// 预编译SQL语句stmt = connection.prepareStatement(sql);stmt.setDouble(1,account.getMoney());stmt.setString(2,account.getName());//查询int result = stmt.executeUpdate();System.out.println("修改影响了"+result+"行数据");}catch (Exception e){try{stmt.close();} catch (SQLException e1) {e1.printStackTrace();}}}
}

 使用事务工具类TxUtils获取连接,使两次对数据库的操作在同一个连接,使用线程存储这个连接,避免因错误等原因导致”钱转丢问题“

package com.qcby.utils;import java.sql.Connection;
import java.sql.SQLException;import javax.sql.DataSource;import com.alibaba.druid.pool.DruidDataSource;/*** 事务的工具类*/
public class TxUtils {private static DruidDataSource ds = null;// 使用ThreadLocal存储当前线程中的Connection对象private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();// 在静态代码块中创建数据库连接池static {try {// 通过代码创建Druid数据库连接池ds = new DruidDataSource();ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/spring_db");ds.setUsername("root");ds.setPassword("2020");} catch (Exception e) {throw new ExceptionInInitializerError(e);}}/*** @Method: getConnection* @Description: 从数据源中获取数据库连接* @Anthor:* @return Connection* @throws SQLException*/public static Connection getConnection() throws SQLException {// 从当前线程中获取ConnectionConnection conn = threadLocal.get();if (conn == null) {// 从数据源中获取数据库连接conn = getDataSource().getConnection();// 将conn绑定到当前线程threadLocal.set(conn);}return conn;}/*** @Method: startTransaction* @Description: 开启事务* @Anthor:**/public static void startTransaction() {try {Connection conn = threadLocal.get();if (conn == null) {conn = getConnection();// 把 conn绑定到当前线程上threadLocal.set(conn);}// 开启事务conn.setAutoCommit(false);} catch (Exception e) {throw new RuntimeException(e);}}/*** @Method: rollback* @Description:回滚事务* @Anthor:*/public static void rollback() {try {// 从当前线程中获取ConnectionConnection conn = threadLocal.get();if (conn != null) {// 回滚事务conn.rollback();}} catch (Exception e) {throw new RuntimeException(e);}}/*** @Method: commit* @Description:提交事务* @Anthor:*/public static void commit() {try {// 从当前线程中获取ConnectionConnection conn = threadLocal.get();if (conn != null) {// 提交事务conn.commit();}} catch (Exception e) {throw new RuntimeException(e);}}/*** @Method: close* @Description:关闭数据库连接(注意,并不是真的关闭,而是把连接还给数据库连接池)* @Anthor:**/public static void close() {try {// 从当前线程中获取ConnectionConnection conn = threadLocal.get();if (conn != null) {conn.close();// 解除当前线程上绑定connthreadLocal.remove();}} catch (Exception e) {throw new RuntimeException(e);}}/*** @Method: getDataSource* @Description: 获取数据源* @Anthor:* @return DataSource*/public static DataSource getDataSource() {// 从数据源中获取数据库连接return ds;}}

使用JDK动态代理

创建一个JdkProxy代理类,来对目标对象的方法进行增强,代理核心方法updateSaveAll()

package com.qcby.JdkProxy;import com.qcby.service.AccountService;
import com.qcby.utils.TxUtils;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** 生成代理对象* 传入目标对象,生成该对象的代理对象,返回,在对目标对象的方法进行增强*/
public class JdkProxy {/*** 获取代理对象的方法 返回代理对象  对目标对象的方法进行增强* @param accountService* @return*/public static Object getProxy(AccountService accountService){/*** 使用JDK动态代理生成代理对象* 第一个参数:类的加载器* 第二个参数:当前传入的对象实现了哪些接口要字节码对象* 第三个参数:回调函数*/Object proxy = Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {/*** 调用代理对象的方法invoke方法就会执行* @param proxy* @param method* @param args* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//System.out.println("你调用了代理对象的invoke方法了。。。");//对目标对象的方法进行增强Object result = null;try {//开启事务TxUtils.startTransaction();result = method.invoke(accountService, args);//提交事务TxUtils.commit();} catch (Exception e) {e.printStackTrace();//回滚事务TxUtils.rollback();} finally {TxUtils.close();}return result;}});return proxy;}
}

配置文件 

<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><bean id="accountService" class="com.qcby.service.Impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"/></bean><bean id="accountDao" class="com.qcby.dao.AccountDaoImpl"/>
</beans>

编写测试类

package com.qcby.springAopTest;import com.qcby.JdkProxy.JdkProxy;
import com.qcby.model.Account;
import com.qcby.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Test01 {@Autowiredprivate AccountService accountService;@Testpublic void run(){Account account1 = new Account(null,"aaa",-1000.00);Account account2 = new Account(null,"bbb",1000.00);//accountService.updateSaveAll(account1,account2);Object proxyobj = JdkProxy.getProxy(accountService);//强转成accountService因为生成代理对象时候是写死的AccountService proxy = (AccountService) proxyobj;//在调用增强后的代理对象的savaAll方法//accountService.updateSaveAll(account1,account2);proxy.updateSaveAll(account1,account2);}}

AOP相关的概念

AOP的概述

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程

  • AOP是一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构
  • 通过预编译方式或者运行期动态代理实现程序功能的统一维护的一种技术
  • AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型
  • 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)

可以在不修改源代码的前提下,对程序进行增强!!

减少重复的代码,提供开发的xiaolv,维护方便

AOP的底层原理

AOP是基于动态代理实现的,动态分为两种

JDK的动态代理技术        基于接口

​ 1、为接口创建代理类的字节码文件

​ 2、使用ClassLoader将字节码文件加载到JVM

​ 3、创建代理类实例对象,执行对象的目标方法

cglib代理技术        基于类

AOP相关的术语

名词

解释

Joinpoint(连接点)

连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。

Pointcut(切入点)

切入点是指我们要对哪些Joinpoint进行拦截的定义

Advice(通知/增强)

通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

Target(目标对象)

代理的目标对象

Weaving(织入)

把增强应用到目标对象来创建新的代理对象的过程

Proxy(代理)

一个类被AOP织入增强后,就产生一个结果代理类

Aspect(切面)

切入点和通知的结合,以后咱们自己来编写和配置的

AOP配置文件方式

创建maven项目,引入依赖

<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!-- AOP联盟 --><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><!-- Spring Aspects --><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.0.2.RELEASE</version></dependency><!-- aspectj --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.3</version></dependency></dependencies>

创建applicationContext_demo2.xml配置文件,引入具体的AOP的schema约束

<?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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"></beans>

创建包结构,编写具体的接口和实现类

接口 

package com.qcby.demo2;public interface UserService {public void save();
}

实现类 

package com.qcby.demo2;public class UserServiceImpl implements UserService{@Overridepublic void save() {System.out.println("业务层:保存用户...");}
}

将目标类配置到Spring中

<!--管理bean对象--><bean id="userService" class="com.qcby.demo2.UserServiceImpl"/>

定义切面类

package com.qcby.demo2;/*** 自定义切面类 = 切入点(表达式) + 通知(增强的代码)*/
public class MyXmlAspect {/*** 通知*/public void log(){//发送手机端行//发送邮件/记录日志/事务管理System.out.println("增强的方法执行了。。。");}
}

在配置文件中定义切面类

<!--配置切面类,把该类交给IOC容器管理--><bean id="myXmlAspect" class="com.qcby.demo2.MyXmlAspect"/>

在配置文件中完成aop的配置

<!--配置AOP的增强--><aop:config><!--配置切面 = 切入点 + 通知组成--><aop:aspect ref="myXmlAspect"><!--前置通知:UserServiceImpl的save方法执行前,会增强--><aop:before method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())"/></aop:aspect></aop:config>

完成测试

package com.qcby.demo2Test;import com.qcby.demo2.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext_demo2.xml")
public class demo2Test {@Autowiredprivate UserService userService;/*** 测试*/@Testpublic void run1(){userService.save();}
}

 测试结果

切入点的表达式

再配置切入点的时候,需要定义表达式,具体展开如下:

切入点表达式的格式如下:

  • execution([修饰符] 返回值类型 包名.类名.方法名(参数))
  • 修饰符可以省略不写,不是必须要出现的。
  • 返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。
  • 包名例如:com.tx.demo3.BookDaoImpl
    • 首先com是不能省略不写的,但是可以使用 * 代替
    • 中间的包名可以使用 * 号代替
    • 如果想省略中间的包名可以使用 ..
  • 类名也可以使用 * 号代替,也有类似的写法:*DaoImpl
  • 方法也可以使用 * 号代替
  • 参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..

<?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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd">
​<!--管理bean对象--><bean id="userService" class="com.qcby.demo2.UserServiceImpl"/>
​<!--配置切面类,把该类交给IOC容器管理--><bean id="myXmlAspect" class="com.qcby.demo2.MyXmlAspect"/>
​<!--配置AOP的增强--><aop:config><!--配置切面 = 切入点 + 通知组成--><aop:aspect ref="myXmlAspect"><!--前置通知:UserServiceImpl的save方法执行前,会增强<aop:before method="log" pointcut="execution(public void com.qcby.demo2.UserServiceImpl.save())" />-->
​<!--切入点的表达式execution() 固定的写法public          是可以省略不写的方法的返回值      int String 通用的写法,可以编写 * 不能省略不写的包名+类名        不能省略不写的,编写 *  UserServiceImpl AccountServiceImpl方法名称         save() 可以写 *参数列表         (..) 表示任意类型和个数的参数比较通用的表达式:execution(public * com.qcby.*.*ServiceImpl.*(..))--><aop:before method="log" pointcut="execution(* com.qcby.*.*ServiceImpl.save*(..))" />
​</aop:aspect></aop:config>
​
</beans>

AOP的通知类型

通知类型类型说明

before

前置通知 目标方法执行前,进行增强。

afte

最终通知 目标方法执行成功或者失败,进行增强。

after-returning

后置通知 目标方法执行成功后,进行增强。

after-throwing

异常通知 目标方法执行失败后,进行增强。

around

环绕通知 目标方法执行前后,都可以进行增强。目标对象的方法需要手动执行。

<?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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd">
​<!--管理bean对象--><bean id="userService" class="com.qcby.demo2.UserServiceImpl"/>
​<!--配置切面类,把该类交给IOC容器管理--><bean id="myXmlAspect" class="com.qcby.demo2.MyXmlAspect"/>
​<!--配置AOP的增强--><aop:config><!--配置切面 = 切入点 + 通知组成--><aop:aspect ref="myXmlAspect"><!--AOP的通知类型前置通知:目标方法执行前,进行增强。<aop:before method="log" pointcut="execution(* com.qcby.*.*ServiceImpl.save*(..))" />最终通知:目标方法执行成功或者失败,进行增强。<aop:after method="log" pointcut="execution(* com.qcby.*.*ServiceImpl.save*(..))" />后置通知:目标方法执行成功后,进行增强。<aop:after-returning method="log" pointcut="execution(* com.qcby.*.*ServiceImpl.save*(..))" />异常通知:目标方法执行失败后,进行增强。<aop:after-throwing method="log" pointcut="execution(* com.qcby.*.*ServiceImpl.save*(..))" />环绕通知:目标方法执行前后,都可以进行增强。目标对象的方法需要手动执行。--><aop:around method="aroundLog" pointcut="execution(* com.qcby.*.*ServiceImpl.save*(..))" />
​</aop:aspect></aop:config>
​
</beans>

Spring的AOP技术-注解方式

AOP注解方式入门程序(半注解)

给切面类添加注解 @Aspect,编写增强的方法,使用通知类型注解声明

package com.qcby.demo3;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Component  // 把该类交给IOC去管理
@Aspect  // 声明是切面类  == <aop:aspect ref="myXmlAspect">
public class MyAnnoAspect {/*** 通知的方法*///@Before(value = "切入点表达式")@Before(value = "execution(public * com.qcby.demo3.OrderServiceImpl.save(..))")public void log(){System.out.println("增强了。。。");}
}

配置文件中开启自动代理

<aop:aspectj-autoproxy/>

编写测试

/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext_demo3.xml")
public class Demo3 {
​@Autowiredprivate OrderService orderService;
​/*** 测试*/@Testpublic void run1(){orderService.save();}
​
}

通知类型的注解

通知类型注解

注解

@Before

前置通知

@AfterReturing

后置通知

@Around

环绕通知(目标对象方法默认不执行的,需要手动执行)

@After

最终通知

@AfterThrowing

异常抛出通知

纯注解的方式

​
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
​@Configuration      // 配置类
@ComponentScan(value = "com.qcby.demo3")   // 扫描包
@EnableAspectJAutoProxy     // 开启自动代理 == <aop:aspectj-autoproxy />
public class SpringConfig {}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/464762.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

双指针算法篇——一快一慢须臾之间解决问题的飘逸与灵动(2)

前言&#xff1a; 上篇我们讲解了双指针算法的含义以及相关题型讲解&#xff0c;本次则趁热打铁&#xff0c;通过进阶题目的分析与讲解&#xff0c;促使我们更深入和灵活的理解运用双指针算法。 相关题目及讲解 一. 盛最多水的容器 题目链接&#xff1a;11. 盛最多水的容器 -…

koa项目实战 == 实现注册登录鉴权

一. 项目的初始化 1 npm 初始化 npm init -y生成package.json文件: 记录项目的依赖 2 git 初始化 git init生成’.git’隐藏文件夹, git 的本地仓库 3 创建 ReadMe 文件 二. 搭建项目 1 安装 Koa 框架 npm install koa2 编写最基本的 app 创建src/main.js const Koa…

ONLYOFFICE 文档8.2更新评测:PDF 协作编辑、性能优化及更多新功能体验

文章目录 &#x1f340;引言&#x1f340;ONLYOFFICE 产品简介&#x1f340;功能与特点&#x1f340;体验与测评ONLYOFFICE 8.2&#x1f340;邀请用户使用&#x1f340; ONLYOFFICE 项目介绍&#x1f340;总结 &#x1f340;引言 在日常办公软件的选择中&#xff0c;WPS 和微软…

MATLAB下的四个模型的IMM例程(CV、CT左转、CT右转、CA四个模型),附下载链接

基于IMM算法的目标跟踪。利用卡尔曼滤波和多模型融合技术&#xff0c;能够在含噪声的环境中提高估计精度&#xff0c;带图像输出 文章目录 概述源代码运行结果代码结构与功能1. 初始化2. 仿真参数设置3. 模型参数设置4. 生成量测数据5. IMM算法初始化6. IMM迭代7. 绘图8. 辅助函…

Segmentation fault 问题解决

问题描述 执行有import torch代码的py 文件报Segmentation fault 原因分析&#xff1a; 查了网上说的几种可能性 import torch 时出现 “Segmentation fault” 错误&#xff0c;通常表示 PyTorch 的安装或配置存在问题 可能的原因 不兼容的库版本: PyTorch、CUDA 或其他依赖…

如何搭建汽车行业AI知识库:定义+好处+方法步骤

在汽车行业&#xff0c;大型车企面临着员工众多、价值链长、技术密集和知识传播难等挑战。如何通过有效的知识沉淀与应用&#xff0c;提升各部门协同效率&#xff0c;快速响应客户咨询&#xff0c;降低销售成本&#xff0c;并开启体系化、可持续性的知识管理建设&#xff0c;成…

QGIS:HCMGIS插件

插件GitHub地址&#xff1a;https://github.com/thangqd/HCMGIS。 以下对HCMGIS插件进行简单介绍&#xff0c;并演示如何进行地图数据下载。 插件简介 HCMGIS - Basemaps, Download OpenData, Batch Converter, VN-2000 Projections, and Field Calculation Utilities for QGI…

SpringBoot集成Shiro+Jwt+Redis

1. 概述 首先需要知道为什么使用 ShiroJwtRedis 进行登录认证和权限控制。 1. 为什么用Shiro&#xff1f; 主要用的是 shiro 的登录认证和权限控制功能。 Shiro 参见本栏目文章 &#x1f343;《Shiro实战》 2. 为什么用Jwt&#xff1f; Shiro 默认的 Session 机制来帮助实现…

jenkins 构建报错 Cannot run program “sh”

原因 在 windows 操作系统 jenkins 自动化部署的时候, 由于自动化构建的命令是 shell 执行的,而默认windows 从 path 路径拿到的 shell 没有 sh.exe &#xff0c;因此报错。 解决方法 前提是已经安装过 git WINR 输入cmd 打开命令行, 然后输入where git 获取 git 的路径, …

Springboot——对接支付宝实现扫码支付

文章目录 前言官方文档以及说明1、申请沙箱2、进入沙箱获取对应的关键信息3、拿到系统生成的公钥和密钥 注意事项创建springboot项目1、引入依赖2、配置连接参数3、创建配置类&#xff0c;用于接收这些参数4、中间类的定义(订单类)5、编写测试接口场景一、pc端请求后端后&#…

【云备份项目】json以及jsoncpp库的使用

目录 1.JSON 2.什么是 JSON&#xff1f; 3.JSON 发展史 4.为什么要使用 JSON&#xff1f; 5.JSON 的不足 6.JSON 应该如何存储&#xff1f; 7.什么时候会使用 JSON 7.1.定义接口 7.2.序列化 7.3.生成 Token 7.4.配置文件 8.JSON的语法规则 8.1.对象和数组 8.2.JS…

【C++篇】在秩序与混沌的交响乐中: STL之map容器的哲学探寻

文章目录 C map 容器详解&#xff1a;高效存储与快速查找前言第一章&#xff1a;C map 的概念1.1 map 的定义1.2 map 的特点 第二章&#xff1a;map 的构造方法2.1 常见构造函数2.1.1 示例&#xff1a;不同构造方法 2.2 相关文档 第三章&#xff1a;map 的常用操作3.1 插入操作…

HOT100_最大子数组和

class Solution {public int maxSubArray(int[] nums) {int[] dp new int[nums.length];int res nums[0];dp[0] nums[0];for(int i 1; i< nums.length; i){dp[i] Math.max(nums[i] ,dp[i-1] nums[i]);res Math.max(res, dp[i]);}return res;} }

contenteditable实现需要一个像文本域一样的可编辑框

我这里是因为左上和右下有一个固定的模板&#xff0c;所有用textarea有点不方便&#xff0c;查了下还有一个方法可以解决就是在需要编辑的元素上加上 :contenteditable"true" 完整代码如下&#xff0c;因为这个弹窗是两用的&#xff0c;所以用messageType做了一下判…

SpringBoot源码解析(一)

SpringBoot自动装配原理 SpringBootApplication注解 我们在使用SpringBoot时&#xff0c;通常使用的是SpringBootApplication这个注解&#xff0c;比如&#xff1a; 而这个注解的定义为下图&#xff0c;可以发现这个注解上有另外三个注解&#xff1a;SpringBootConfiguration…

WPF+MVVM案例实战与特效(二十四)- 粒子字体效果实现

文章目录 1、案例效果2、案例实现1、文件创建2.代码实现3、界面与功能代码3、总结1、案例效果 提示:这里可以添加本文要记录的大概内容: 2、案例实现 1、文件创建 打开 Wpf_Examples 项目,在 Views 文件夹下创建窗体界面 ParticleWindow.xaml,在 Models 文件夹下创建粒子…

js中怎么把excel和pdf文件转换成图片打包下载

index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>文件转图片工具</title><!-- 本…

盘点 2024 十大免费/开源 WAF

WAF 是 Web Application Firewall 的缩写&#xff0c;也被称为 Web 应用防火墙。区别于传统防火墙&#xff0c;WAF 工作在应用层&#xff0c;对基于 HTTP/HTTPS 协议的 Web 系统有着更好的防护效果&#xff0c;使其免于受到黑客的攻击。 近几年经济增速开始放缓&#xff0c;科…

蓝牙资讯|苹果AirPods Pro 2推出听力测试、助听器和听力保护等功能

苹果推送iOS 18.1 系统版本更新&#xff0c;AirPods Pro 2 用户也在 iOS 18.1 中获得了强大的新功能。 运行固件 7B19 的 AirPods Pro 2 用户&#xff0c;搭配 iOS 18.1 系统的 iPhone&#xff0c;将获得三项强大的听力健康功能&#xff1a;听力测试、助听器和听力保护。 听力…

如何检查雷池社区版 WAF 是否安装成功?

容器运行状态检查&#xff1a; 使用命令行检查&#xff1a;打开终端&#xff0c;连接到安装雷池的服务器。运行 docker ps 命令&#xff0c;查看是否有与雷池相关的容器正在运行。 如果能看到类似 safeline-mgt、safeline-tengine 等相关容器&#xff0c;并且状态为 Up&#x…