学习springAOP

news2024/10/5 19:19:14

第三章 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 {

    @Override
    public 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 {

    @Override
    public 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 {

    @Test
    public 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 {
    @Override
    public 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 {

    @Override
    public 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.
	 */
	@Nullable
	Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;
}
public class AroundAdvice implements MethodInterceptor {
    @Override
    public 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{

    @Override
    public void shopping() {
        System.out.println("购物");
    }
}
/**
 * 代理角色
 */
public class PersonProxy implements Person{

    private ZhangSan zhangSan; //维护一个被代理的角色

    public PersonProxy(ZhangSan zhangSan) {
        this.zhangSan = zhangSan;
    }

    @Override
    public 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;
    }

    @Override
    public 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() {
            @Override
            public 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.InvocationHandler
        enhancer.setCallback(new InvocationHandler() {
            @Override
            public 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() {
            @Override
            public 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.coloradmin.cn/o/1888267.html

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

相关文章

Linux 64位系统运行32位程序

1、原理分析 Linux 64位系统运行32位程序的原理主要涉及到系统架构的兼容性、库文件的支持以及特定的运行环境设置。 1.1 系统架构兼容性 x64与x86的区别&#xff1a; x86是Intel于1985年推出的32位指令集架构&#xff0c;后被AMD、VIA等厂商广泛采用。x64&#xff08;也称为…

AI需求强劲推动韩国六月芯片出口创历史新高

据路透社7月1日报道&#xff0c;韩国产业通商资源部最新数据显示&#xff0c;韩国出口连续第九个月增长&#xff0c;六月份海外对芯片的持续且增强的需求将芯片出口额推至历史高位。这一出口增长势头同时带动韩国制造业采购经理指数&#xff08;PMI&#xff09;攀升至两年多以来…

SpringBoot实战(二十八)集成 Collabora Online 实现在线编辑

目录 一、什么是 Collabora Online?二、Docker 下载并启动 CODE2.1 拉取镜像2.2 启动镜像2.3 访问界面2.4 补充:nextcloud 的镜像启动三、SpringBoot 实现 WOPI 服务3.1 什么是WOPI?3.2 Spring Boot 简单实现3.3 另一种实现方式3.4 总结四、补充:coolwsd.xml 核心配置介绍c…

类型“{}”上不存在属性“xxxx”。ts(2339)

解决&#xff1a;类型“{}”上不存在属性“xxxx”和非类型化函数调用不能接受类型参数等问题。 问题发现 今天一个学生&#xff0c;发我一张图&#xff08;如下&#xff09;。 他从远端拉取到本地&#xff08;自家电脑&#xff09;后打开的代码视图&#xff0c;一大堆抛红。问…

s5.日志服务

日志服务 1. rsyslog 系统日志服务2.常见日志文件3. 启用网络日志服务4. 利用 MySQL 存储日志信息5. 日志管理工具 journalctl6. Logrotate 日志转储 1. rsyslog 系统日志服务 rsyslog是CentOS 6 以后版本的系统管理服务.它提供了高性能&#xff0c;出色的安全性和模块化设计。…

vue为啥监听不了@scroll

哈喽 大家好 我在vue中写了一个滚动scroll监听事件 然后滚动鼠标 发现进不来我的方法断点 原因&#xff1a; 事件绑定错误&#xff1a;确保你使用scroll正确绑定到了可滚动容器上。 事件冒泡&#xff1a;滚动事件可能被封装在某些组件内部&#xff0c;导致不会冒泡到父元素上…

背景图的动效,非常的炫酷,非一般的感觉。

我们都知道在一些展示型项目中&#xff0c;背景图加上动效后&#xff0c;可以立马让整个设计档次提升了&#xff0c;这次带来了一批背景图的动效图&#xff0c;大家看一下。

Web 基础与 HTTP 协议

Web 基础与 HTTP 协议 一、Web 基础1.1域名和 DNS域名的概念Hosts 文件DNS&#xff08;Domain Name System 域名系统&#xff09;域名注册 1.2网页与 HTML网页概述HTML 概述网站和主页Web1.0 与 Web2.0 1.3静态网页与动态网页静态网页动态网页 二、HTTP 协议1.1HTTP 协议概述1.…

3115.力扣每日一题7/2 Java

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;算法练习关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 目录 思路 解题方法 时间复杂度 空间复杂度 Code 总结 思路 这道题的…

SpringBoot+mail 轻松实现各类邮件自动推送

一、简介 在实际的项目开发过程中&#xff0c;经常需要用到邮件通知功能。例如&#xff0c;通过邮箱注册&#xff0c;邮箱找回密码&#xff0c;邮箱推送报表等等&#xff0c;实际的应用场景非常的多。 早期的时候&#xff0c;为了能实现邮件的自动发送功能&#xff0c;通常会…

android iconfont带图标的图文并茂的一种实现

android实现图文并茂方法很多。 这里针对&#xff0c;仅本地图标&#xff0c;需要对齐&#xff0c;任意位置&#xff0c;兼容换行导致后面空白的问题做的一种方案。 www.iconfont.cn&#xff0c;注册&#xff1b; 上传svg的icon&#xff1b; 下载项目得到iconfont.ttf&#xf…

泰雷茲具有首个通过FIPS 140-3 三级认证的HSMs

泰雷兹LunaHsm是业界首款通过FIPS140-33级认证的解决方案&#xff0c;安策引进泰雷兹HSM产品可以帮助您满足您的数据安全合规性需求&#xff0c;阻力企业提高竞争力。 安策提供泰雷茲ThalesLunaHSMs成为首个通过FIPS140-3三级认证的硬件安全模块图 我们很高兴地宣布&#xff0c…

Java 汉诺塔问题 详细分析

汉诺塔 汉诺塔&#xff08;Tower of Hanoi&#xff09;&#xff0c;又称河内塔&#xff0c;是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子&#xff0c;在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小…

Ignis 应用: 社交 + 游戏 + 工业4.0,Ignis 构建Web3生态圈

引言 在数字经济快速发展的今天&#xff0c;Web3技术为我们带来了前所未有的变革。作为Ardor平台的主要子链&#xff0c;Ignis公链在推动Web3生态系统建设中扮演了重要角色。本文将通过介绍Vessel Chain、Mythical Beings和Bridge Champ等应用&#xff0c;探讨Ignis公链如何通…

Android原生与flutter模块交互

Flutter定义了三种不同类型的Channel&#xff1a; BasicMessageChannel&#xff1a;用于传递字符串和半结构化的信息&#xff0c;持续通信&#xff0c;收到消息后可以回复此次消息&#xff0c;如&#xff1a;Native将遍历到的文件信息陆续传递到Dart&#xff0c;在比如&#xf…

【Python机器学习系列】建立决策树模型预测小麦品种(案例+源码)

这是我的第314篇原创文章。 一、引言 对于表格数据&#xff0c;一套完整的机器学习建模流程如下&#xff1a; 针对不同的数据集&#xff0c;有些步骤不适用&#xff0c;其中橘红色框为必要步骤&#xff0c;欢迎大家关注翻看我之前的一些相关文章。前面我介绍了机器学习模型的二…

MySQL 9.0 发布了!

从昨晚开始&#xff0c;在DBA群里大家就在讨论MySQL 9.0发布的事情&#xff0c;但是Release Note和官方文档都没有更新&#xff0c;所以今天早上一上班就赶紧瞅了下具体更新了哪些内容&#xff1f; 整体看来&#xff0c;基本没什么创新。下面是9.0新增或废弃的一些特性。 &…

自动批量将阿里云盘文件发布成WordPress文章脚本源码(以RiPro主题为例含付费信息下载地址SEO等自动设置)源码

背景 很多资源下载站&#xff0c;付费资源下载站&#xff0c;付费内容查看等都可以用WordPress站点发布内容&#xff0c;这些站点一般会基于一个主题&#xff0c;付费信息作为文章附属的信息发布&#xff0c;底层存储在WP表里&#xff0c;比如日主题&#xff0c;子比主题等。 …

计算机是如何看到图像的

本节课为「计算机视觉 CV 核心知识」第 4 节&#xff1b; 「AI秘籍」系列课程&#xff1a; 人工智能应用数学基础人工智能Python基础人工智能基础核心知识人工智能BI核心知识人工智能CV核心知识 本文涉及代码&#xff1a;https://github.com/hivandu/AI_Cheats Hi, 大家好。我…

【MySQL备份】Percona XtraBackup压缩备份实战篇

目录 1.前言 2.准备工作 2.1.环境信息 2.2.配置/etc/my.cnf文件 2.3.授予root用户BACKUP_ADMIN权限 2.4.安装qpress 3. 压缩备份 3.1.创建压缩备份 3.2.创建全量备份 3.3.对比两个备份的大小 4.解压备份 5.准备备份 6.备份恢复 ​7.问题分析 8.总结 "实战…