AOP

代理模式

提出问题

现有缺陷

假设我们有一个计算类,里面有加减乘除四个方法,现在我们要为这四个方法添加日志,即在方法执行的前后分别输出一句话,这时我们会发现如下缺陷:

1.对核心业务有干扰。核心业务是加减乘除的实现,而我们现在还要额外编写日志的代码。

2.附加功能分散在各个业务功能方法中,不利于统一维护。

解决思路

解决这两个问题的核心就是解耦。我们需要把附加功能从业务功能代码中提取出来

困难

要抽取的代码在方法内部,无法像以前一样将子类中重复的代码抽取到父类来解决。因此要引入新的技术。

概念

代理模式:二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。

我们之前使用计算类的过程为:

创建计算类对象,调用对象的方法,获取返回值

现在我们创建了一个代理类,使用的过程为:

创建代理类对象,调用代理类的加减乘除方法,代理类调用计算类的对应方法并获取返回值,代理类将返回值返回给我们

即代理类中有着目标类的相同方法,且核心业务的代码是调用目标类来实现的,这样就可以在核心代码的前后加上附加代码

代理模式类似于Servlet的过滤器:在访问对应业务前进行处理,访问完业务后再进行处理

相关术语:

代理:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类、对象、方法。

目标:被代理“套用”非核心逻辑代码的类、对象、方法。

静态代理

静态代理的实现:

1.创建代理类

2.目标类若实现了接口,代理类也要实现对应接口并重写方法

3.在代理类中创建目标类的成员变量

4.代理类在对应方法中调用目标类的方法实现核心功能

5.代理类在对应方法的前后添加附加功能

注:一个代理类对应一个目标类

静态代理的缺点:静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。

提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理 类来实现。这就需要使用动态代理技术了。

动态代理

我们可以创建一个代理类工厂,然后在工厂创建时获取目标对象,然后使用 newProxyInstance 方法创建代理对象并返回

newProxyInstance():创建一个代理对象,其中有三个参数:

1.classLoader:加载动态生成的代理类的类加载器

2.interfaces:目标对象实现的所有接口的class对象所组成的数组

3.invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法

其中,classLoader一般是一个应用程序类加载器,我们自己编写的类一般都使用这个加载器,因此我们可以直接使用this.getClss().getClassLoader()获取

interfaces可以直接使用目标对象.getClss().getInterfaces()获取目标对象的所有接口

invocationHandler则是需要使用匿名内部类创建,其内部只有一个invoke方法

invoke():代理对象内部的方法的实现,有三个参数

proxy:代理对象

method:代理对象需要实现的方法,即其中需要重写的方法

args:method所对应方法的参数

注:由于代理对象和代理类都是自动生成的,我们不知道代理类的名称,但是我们知道代理类继承的接口,因此创建的代理对象我们都会使用接口类型的变量接,因此method的方法是接口中对应的方法,这个方式是可以使用目标类的对象调用的

在invoke内部核心业务的实现就是通过method.invoke(target,args)实现的

target为目标对象

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class ProxyFactory{private Object target;public ProxyFactory(Object target) {this.target = target;}public Object getProxy(){ClassLoader classLoader = this.getClass().getClassLoader();Class<?>[] interfaces = target.getClass().getInterfaces();InvocationHandler invocationHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = method.invoke(target, args);return result;}};return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);}}
public void test1(){ProxyFactory factory = new ProxyFactory(new CalculatorPureImpl());Calculator proxy = (Calculator) factory.getProxy();System.out.println(proxy.add(1, 1));}

动态代理有两种:

1.jdk动态代理,要求必须有接口,最终生成的代理类和目标类实现相同的接口,生成的代理类在com.sun.proxy包下,类名为$proxy+数字

2.cglib动态代理,最终生成的代理类会继承目标类,并且和目标类在相同的包下

AOP概念及相关术语

概念

AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程(oop)的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。

相关术语

横切关注点

从目标类中抽取出来的同一类的非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。

通知

每一个横切关注点的功能都需要写一个方法来实现,这样的方法就叫通知方法

前置通知:在被代理的目标方法前执行

返回通知:在被代理的目标方法成功结束后执行

异常通知:在被代理的目标方法异常结束后执行

后置通知:在被代理的目标方法最终结束后执行

环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

切面

封装通知方法的类

目标

被代理的目标对象

代理

向目标对象应用通知之后创建的代理对象

连接点

横切关注点在目标类的位置,即我们要从哪里抽取非核心业务代码,那里就是连接点

注:连接点是一个纯逻辑概念,从哪抽取,哪就是连接点

切入点

定位连接点的方式

连接点只是一个逻辑概念,为了找到连接点的具体位置,需要用到切入点

切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

基于注解的AOP

技术说明

动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因 为这个技术要求代理对象和目标对象实现同样的接口。

cglib:通过继承被代理的目标类实现代理,所以不需要目标类实现接口。

AspectJ:本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终效果是动态的。weaver就是织入器。Spring只是借用了AspectJ中的注解。

使用步骤

  1. 在IOC所需依赖的基础上加入下述依赖
<!-- spring-aspects会帮我们传递过来aspectjweaver -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>
  1. 创建切面类,将目标类和切面类交给IOC容器管理
  2. 使用@Aspect对切面类进行注解,在Spring中开启Aspectj的自动代理
<aop:aspectj-autoproxy />
  1. 配置切面类
    1. 在切面类内部创建通知方法,并编写内部代码
    2. 使用通知注解对通知方法进行标记,如@Before:前置通知
    3. 配置注解的value属性,指定要处理哪个类的哪个方法
@Before("execution(public int com.CalculatorPureImpl.add(int,int))")
  1. 在测试类中创建ApplicationContext对象,并获取目标类继承的接口类型的代理类对象,使用代理类对象调用方法

注:目标类在被代理过后,无法再通过IOC容器获取,因此获取代理类对象时可以直接使用根据类型获取bean的方式获取代理类对象,类型为接口的类型

各种通知

  • 前置通知:使用@Before注解标识,在被代理的目标方法前执行
  • 返回通知:使用@AfterReturning注解标识,在被代理的目标方法成功结束后执行
  • 异常通知:使用@AfterThrowing注解标识,在被代理的目标方法异常结束后执行
  • 后置通知:使用@After注解标识,在被代理的目标方法最终结束后执行
  • 环绕通知:使用@Around注解标识,使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

各种通知的执行顺序:

  • Spring版本5.3.x以前:
    • 前置通知
    • 目标操作
    • 后置通知
    • 返回通知或异常通知
  • Spring版本5.3.x以后:
    • 前置通知
    • 目标操作
    • 返回通知或异常通知
    • 后置通知

切入点表达式语法

我们在使用注解将一个方法标记为通知后,还需要通过注解的value属性设置该通知的生效范围

其具体语法如下:

基础结构:

execution(权限修饰符 方法返回值 方法所在类型的全类名.方法名(参数列表))

@Before("execution(public int com.CalculatorPureImpl.add(int,int))")

其中,"execution()"是固定格式,参数列表只填参数的类型,使用逗号分隔

我们也可以使用通配符*代表任意,如

@Before("execution(* com.CalculatorPureImpl.*(..))")

上述代码代表权限修饰符和方法返回值任意,com包下的CalculatorPureImpl类,方法随意,方法的参数随意

具体细节:

  • 用*号代替“权限修饰符”和“返回值”部分表示“权限修饰符”和“返回值”不限
  • 在包名的部分,一个“*”号只能代表包的层次结构中的一层,表示这一层是任意的。
    • 例如:*.Hello匹配com.Hello,不匹配com.atguigu.Hello
  • 在包名的部分,使用“*..”表示包名任意、包的层次深度任意
  • 在类名的部分,类名部分整体用*号代替,表示类名任意
  • 在类名的部分,可以使用*号代替类名的一部分
    • 例如:*Service匹配所有名称以Service结尾的类或接口
  • 在方法名部分,可以使用*号表示方法名任意
  • 在方法名部分,可以使用*号代替方法名的一部分
    • 例如:*Operation匹配所有方法名以Operation结尾的方法
  • 在方法参数列表部分,使用(..)表示参数列表任意
  • 在方法参数列表部分,使用(int,..)表示参数列表以一个int类型的参数开头
  • 在方法参数列表部分,基本数据类型和对应的包装类型是不一样的
    • 切入点表达式中使用 int 和实际方法中 Integer 是不匹配的
  • 在方法返回值部分,如果想要明确指定一个返回值类型,那么必须同时写明权限修饰符
    • 例如:execution(public int ..Service.*(.., int)) 正确
    • 例如:execution(* int ..Service.*(.., int)) 错误

重用切入点表达式

我们希望设置一个切入点表达式,然后在任意切面类中都可以使用,降低代码的重复性

声明:

@Pointcut(切面表达式)

public void xxx(){}

@Pointcut("execution(* com.CalculatorPureImpl.*(..))")
public void pointCut(){}

在同一个切面的使用:

使用xxx()来代表这里的切面表达式,例:

@Before("pointCut()")

在不同切面使用:
需要使用全类名得到切面表达式所在的类,然后使用.xxx()使用切面表达式。例:

@Before("com.CommonPointCut.pointCut()")

获取通知的相关信息

获取连接点信息

获取连接点信息可以在通知方法的参数位置设置 JoinPoint 类型的形参

@Before("pointCut()")
public void beforeMethod(JoinPoint joinPoint){//获取连接点的签名信息String methodName = joinPoint.getSignature().getName();//获取目标方法得到的实参信息String args = Arrays.toString(joinPoint.getArgs());System.out.println("Logger-->前置通知,方法名:" + methodName + ",参数:" + args);
}

获取目标方法的返回值

@AfterReturning中的属性 returning ,可以设置通知方法的某个形参来接收目标方法的返回值

@AfterReturning(value = "pointCut()",returning = "result")
public void afterReturningMethod(JoinPoint joinPoint,Object result){String methodName = joinPoint.getSignature().getName();System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
}

获取目标方法的异常

@AfterThrowing中的属性throwing,可以设置通知方法的某个形参来接收目标方法的异常

@AfterThrowing(value = "pointCut()",throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){String methodName = joinPoint.getSignature().getName();System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}

注:通知方法中接收异常的形参的类型也可以是Exception

环绕通知

环绕通知相当于另外四种通知的总和

使用环绕通知时需要设置通知方法的形参为 ProceedingJoinPoint 类型

该类型的对象有一个方法:proceed(),代表目标方法的执行

注:当我们使用环绕通知时,通知方法需要返回 目标方法的返回值

@Around("pointCut()")
public Object aroundMethod(ProceedingJoinPoint joinPoint){Object result = null;try {System.out.println("环绕通知-->目标对象方法执行之前");joinPoint.proceed();System.out.println("环绕通知-->目标对象方法返回值之后");} catch (Throwable throwable) {throwable.printStackTrace();System.out.println("环绕通知-->目标对象方法出现异常时");}finally {System.out.println("环绕通知-->目标对象方法执行完毕");}return result;
}

切面的优先级

当一个目标方法上同时存在多个切面时,优先级高的切面先执行

我们可以在切面上通过@Order注解的value属性设置切面的优先级

  • value属性为一个int类型的数字,数字越小,优先级越高,默认值为Integer类型的最大值

基于xml的AOP

注:一般使用注解的方式

<context:component-scan base-package="com.atguigu.aop.xml"></context:componentscan><aop:config><!--配置切面类--><aop:aspect ref="loggerAspect"><!--配置公共的切入点表达式--><aop:pointcut id="pointCut" expression="execution(*com.atguigu.aop.xml.CalculatorImpl.*(..))"/><aop:before method="beforeMethod" pointcut-ref="pointCut"></aop:before><aop:after method="afterMethod" pointcut-ref="pointCut"></aop:after><aop:after-returning method="afterReturningMethod" returning="result"pointcut-ref="pointCut"></aop:after-returning><aop:after-throwing method="afterThrowingMethod" throwing="ex" pointcut-ref="pointCut"></aop:after-throwing><aop:around method="aroundMethod" pointcut-ref="pointCut"></aop:around></aop:aspect><aop:aspect ref="validateAspect" order="1"><aop:before method="validateBeforeMethod" pointcut-ref="pointCut"></aop:before></aop:aspect></aop:config>

声明式事务

概念

编程式事务:事务功能的相关操作全部通过自己编写代码来实现

声明式事务:

事务控制的代码有规律可循,代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出来,进行相关的封装。

有以下两个概念:

编程式:自己写代码实现功能

声明式:通过配置让框架实现功能

基于注解的声明式事务

使用步骤

1.添加IOC容器的依赖和Spring持久化层支持jar包,以及mysql相关依赖

<!-- Spring 持久化层支持jar包 -->
<!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个
jar包 -->
<!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>5.3.1</version>
</dependency><!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.1</version>
</dependency><!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>

注:由于事务已经被封装为框架,不需要我们手动创建切面,因此可以不导入AOP的依赖

2.添加事务管理器

<!-- 导入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 配置数据源 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="${jdbc.url}"/><property name="driverClassName" value="${jdbc.driver}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/>
</bean>
<!-- 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="druidDataSource"></property>
</bean>

添加事务管理器时还需要配置数据源

3.开启事务驱动

<!-- 开启事务的注解驱动-->
<!-- transaction-manager属性的默认值是transactionManager,
如果事务管理器bean的id正好就是这个默认值,则可以省略这个属性 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

注:

1.开启事务的注解驱动时需要指定一个事务管理器的id,且默认值为 transactionManager

2.开启事务驱动时需要导入的名称空间为tx结尾的那个

4.添加事务注解

在需要被事务管理的方法上添加@Transactional注解

@Transactional注解的位置:

1.标识在需要被事务管理的方法上

2.标识在类上,则类中的所有方法都会被事务管理

事务属性

我们可以通过设置@Transactional注解的各个属性来更改事务属性

只读

对于一个查询操作来说,如果我们把它设置成只读,就能明确的告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化

@Transactional(readOnly = true)

readOnly的默认值为false

超时

事务在执行过程中,有可能会遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源,大概率时因为程序运行出现了问题。

此时这个很可能出现问题的程序应该被回滚,撤销它已做的操作,把资源让出来。

即超时回滚,释放资源

@Transactional(timeout = 3)

timeout的值代表超时多少秒就回滚并释放资源并抛出异常

回滚策略

声明式事务默认只针对运行时异常回滚,编译时异常不回滚

可以通过以下属性设置回滚策略

  • rollbackFor属性:需要设置一个Class类型的对象
  • rollbackForClassName属性:需要设置一个字符串类型的全类名
  • noRollbackFor属性:需要设置一个Class类型的对象
  • rollbackFor属性:需要设置一个字符串类型的全类名

注:前两个代表出现那些异常时回滚,后两个代表出现那些异常时不回滚

前两个很少使用,因为默认是出现任何运行时异常都进行回滚

@Transactional(noRollbackFor = ArithmeticException.class)
//@Transactional(noRollbackForClassName = "java.lang.ArithmeticException"

上述代码代表的是出现数学运算异常(ArithmeticException)时不进行回滚 的两种形式

事务隔离级别
@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化

事务传播行为

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。

例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行

可以通过@Transactional中的propagation属性设置事务传播行为

注:应该修改被调用的事务方法的propagation属性

@Transactional(propagation = Propagation.REQUIRED),默认情况,表示如果当前线程上有已经开启的事务可用,那么就在这个事务中运行。此时,只要大的事务中出现异常,即使调用的小的事务方法正常执行,也会被回滚。

@Transactional(propagation = Propagation.REQUIRES_NEW),表示不管当前线程上是否有已经开启 的事务,都要开启新事务。此时,如果大的事务中出现异常,但是调用的小的事务方法正常执行,那么小的事务方法的结果会被提交,而大的事务中其他操作正常回滚。

基于xml的声明式事务

使用步骤

1.相对于注解需要多加入下面的依赖

<!-- spring-aspects会帮我们传递过来aspectjweaver -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>

2.添加事务管理器

<!-- 导入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 配置数据源 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="${jdbc.url}"/><property name="driverClassName" value="${jdbc.driver}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/>
</bean>
<!-- 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="druidDataSource"></property>
</bean>

添加事务管理器时还需要配置数据源

3.配置事务通知和切入点表达式

<!-- 配置切入点表达式 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(*
com.atguigu.spring.tx.xml.service.impl.*.*(..))"></aop:advisor>
</aop:config><!-- tx:advice标签:配置事务通知 -->
<!-- id属性:给事务通知标签设置唯一标识,便于引用 -->
<!-- transaction-manager属性:关联事务管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- tx:method标签:配置具体的事务方法 -->
<!-- name属性:指定方法名,可以使用星号代表多个字符 -->
<tx:method name="get*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<!-- read-only属性:设置只读属性 -->
<!-- rollback-for属性:设置回滚的异常 -->
<!-- no-rollback-for属性:设置不回滚的异常 -->
<!-- isolation属性:设置事务的隔离级别 -->
<!-- timeout属性:设置事务的超时属性 -->
<!-- propagation属性:设置事务的传播行为 -->
<tx:method name="save*" read-only="false" rollbackfor="java.lang.Exception" propagation="REQUIRES_NEW"/>
<tx:method name="update*" read-only="false" rollbackfor="java.lang.Exception" propagation="REQUIRES_NEW"/>
<tx:method name="delete*" read-only="false" rollbackfor="java.lang.Exception" propagation="REQUIRES_NEW"/>
</tx:attributes>
</tx:advice>

注:配置事务通知时,需要通过 tx:method 标签的 name 属性指定通知方法名来表明那些方法需要开启事务,可以使用 * 通配符,如果方法未被包含在内,那么将表示不开启事务

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

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

相关文章

货拉拉0-1数据指标体系构建与应用

目录 一、背景 二、指标体系搭建 2.1 指标设计 2.2 指标体系搭建 2.3 指标维度拆解 三、指标标准化建设 四、指标元数据管理 五、指标应用&未来规划 原文大佬介绍的这篇指标体系构建有借鉴意义&#xff0c;现摘抄下来用作沉淀学习。如有侵权请告知~ 一、背景 指标…

汽车摄像头匿名化处理解决方案,保护信息的安全性和隐私性

随着智能交通和自动驾驶技术的迅猛发展&#xff0c;汽车摄像头已成为现代汽车不可或缺的一部分&#xff0c;摄像头所捕捉的图像信息也引发了日益严峻的信息安全问题。如何在充分利用摄像头功能的同时&#xff0c;保障个人隐私和信息安全&#xff0c;已成为企业亟待解决的问题。…

IP地址定位技术引发的个人隐私保护问题

IP地址定位技术对互联网的影响深远且多面&#xff0c;它不仅改变了网络管理与优化的方式&#xff0c;还极大地推动了在线广告营销、电子商务、地理信息服务等多个领域的发展。然而&#xff0c;与此同时&#xff0c;它也引发了一系列关于个人隐私保护的问题。 首先&#xff0c;I…

vue的学习之用vue写一个hello,vue

根据以下步骤下载vue.js 介绍 — Vue.js 创建一个damo.html &#xff0c;引入vue.js即可 <body><div id"app">{{ message }}</div><!-- Vue --><!-- 开发环境版本&#xff0c;包含了有帮助的命令行警告 --><script src"js/vu…

清华新突破,360°REA重塑多智能体系统:全方位提升复杂任务表现

引言&#xff1a;多智能体系统的新篇章——360REA框架 在多智能体系统的研究领域&#xff0c;最新的进展揭示了一种全新的框架——360REA&#xff08;Reusable Experience Accumulation with 360 Assessment&#xff09;。这一框架的提出&#xff0c;不仅是对现有系统的一次重大…

如何修改WordPress数据库表前缀以提高安全性

WordPress作为世界上最受欢迎的内容管理系统之一&#xff0c;吸引了数以百万计的用户。然而&#xff0c;正因为其广泛的使用&#xff0c;WordPress网站也成为了黑客攻击的目标之一。其中一个最常见的安全漏洞是使用默认的数据库表前缀wp_&#xff0c;使得黑客能够更轻松地进行大…

Oracle交换分区测试

1、用exchange分区表减少初始化过程中对业务中断的影响 2、创建分区表 create table t_p (id number,name varchar2(30),addr varchar2(50)) partition by range(id) (partition p1 values less than(10), partition p2 values less than(20), partition p3 values less …

sql(ctfhub)

一.整数型注入 输入1 输入2 输入2-1&#xff0c;回显为1的结果&#xff0c;说明是数字型&#xff0c;只有数字型才可加减 判断字段数为2 查询数据库 查表 查列 显示flag内容 二.字符型注入 输入1 输入2 输入2-1&#xff0c;说明为字符型&#xff0c;不是数字型 判断闭合方式为…

【数据分析面试】27. 计算广告评论比例 (SQL)

题目: 计算广告评论比例 假设你有一个ads表&#xff0c;包含ID和广告名称&#xff0c;比如“劳动节衬衫促销”。feed_comments表保存了不同用户在常规信息流中对广告的评论。moments_comments表保存了不同用户在moments中对广告的评论。 编写一个查询&#xff0c;获取广告在f…

问题总结笔记

1.向量旋转 问题&#xff1a; 将一个向量旋转90 方法&#xff1a;旋转矩阵 FVector FrontDir EndMousePoint - Point; FrontDir.Normalize(); FVector Left FVector(-FrontDir.Y, FrontDir.X, 0); Verties.Add(Point Left * (WallWedith / 2)); Verties.Add(FVector(Vertie…

BFS 专题 ——FloodFill算法:733.图像渲染

文章目录 前言FloodFill算法简介题目描述算法原理代码实现——BFSCJava 前言 大家好啊&#xff0c;今天就正式开始我们的BFS专题了&#xff0c;觉得有用的朋友给个三连呗。 FloodFill算法简介 中文&#xff1a;洪水灌溉 举个例子&#xff0c;正数为凸起的山峰&#xff0c;负…

攻防世界---misc---easycap

1.下载附件是一个流量包&#xff0c;拿到wireshark中分析 2.查看分级协议 3.过滤data 4.追踪tcp流 5.得到flag

OpenCV与AI深度学习 | OpenCV如何读取仪表中的指针刻度

本文来源公众号“OpenCV与AI深度学习”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;OpenCV如何读取仪表中的指针刻度 最近遇到一个问题&#xff0c;如何读取仪表中的指针指向的刻度。 解决方法有多种&#xff0c;比如&#xff…

Linux使用Docker部署DashDot访问本地服务器面板

文章目录 1. 本地环境检查1.1 安装docker1.2 下载Dashdot镜像 2. 部署DashDot应用 本篇文章我们将使用Docker在本地部署DashDot服务器仪表盘&#xff0c;并且结合cpolar内网穿透工具可以实现公网实时监测服务器系统、处理器、内存、存储、网络、显卡等&#xff0c;并且拥有API接…

【C++】双指针算法:复写零

1.题目 别看这是一道简单题&#xff0c;它的通过率低于一些中等甚至困难的题目&#xff01; 大大增加这道题目难度的是最后一句话&#xff1a;1.不可越界写入。2.就地修改。 如果可以再创建一个数组的话&#xff0c;那么这道题目就会非常简单&#xff0c;但这道题目必须要求在…

Linux的学习之路:18、进程间通信(2)

摘要 本章主要是说一下命名管道和共享内存 目录 摘要 一、命名管道 1、创建一个命名管道 2、匿名管道与命名管道的区别 3、命名管道的打开规则 4、代码实现 二、system V共享内存 1、共享内存 2、共享内存函数 三、代码 四、思维导图 一、命名管道 1、创建一个命…

13.Nacos简介,下载,安装,启动-windows

Nacos是阿里巴巴的产品&#xff0c;现在是SpringCloud的一个组件。 相比Eureka功能更加丰富&#xff0c;服务注册与发现和分布式配置。 Nacos下载地址&#xff1a; https://github.com/alibaba/nacos windows下载nacos-server-1.4.1.zip文件 nacos是基于java语言实现的&…

【系统架构师】-案例考点(三)

1、信息系统架构ISA设计 四种架构模型&#xff1a; 1&#xff09;单机应用 2&#xff09;客户机/服务器模式&#xff1a;两层、三层C/S、B/S模型、MVC模式等 3&#xff09;面向服务架构SOA 4&#xff09;企业数据交换总线&#xff1a;不同企业应用之间通过信息交换的公共频…

一招下载transformers真不用网上那些教程(我试了1*mol多次才知道)

pip很多是2 然而&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;…

element中file-upload组件的提示‘按delete键可删除’,怎么去掉

问题描述 element中file-upload组件会出现这种提示‘按delete键可删除’ 解决方案&#xff1a; 这是因为使用file-upload组件时自带的提示会盖住上传的文件名&#xff0c;修改一下自带的样式即可 ::v-deep .el-upload-list__item.is-success.focusing .el-icon-close-tip {d…