Spring使用指南 ~ 5、Spring AOP 使用简介

news2024/11/25 10:27:28

Spring AOP 使用简介

一、通知(Advice)

公共使用类 Agent.java

package com.luo.spring.guides.aop.simple.domain;

public class Agent {
    public void speak() {
        System.out.println("Bond");
    }
}

1、前置通知

package com.luo.spring.guides.aop.simple.beforeadvice;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class SimpleBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        System.out.println("Before method: " + method);
    }
}

测试

package com.luo.spring.guides.aop.simple.beforeadvice;

import com.luo.spring.guides.aop.simple.domain.Agent;
import org.springframework.aop.framework.ProxyFactory;

/**
 * @author : archer
 * @date : Created in 2022/12/12 14:55
 * @description :
 */
public class Main {

    public static void main(String... args) {
        Agent target = new Agent();

        ProxyFactory pf = new ProxyFactory();
        pf.addAdvice(new SimpleBeforeAdvice());
        pf.setTarget(target);

        Agent proxy = (Agent) pf.getProxy();

        System.out.println("");
        proxy.speak();
    }
}

输出

Before method: public void com.luo.spring.guides.aop.simple.domain.Agent.speak()
Bond

2、后置返回通知

package com.luo.spring.guides.aop.simple.afterreturningadvice;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class SimpleAfterReturningAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, 
             Object[] args, Object target) throws Throwable {
        System.out.println("After return:" + method.getName());
    }
}

测试

package com.luo.spring.guides.aop.simple.afterreturningadvice;

import com.luo.spring.guides.aop.simple.domain.Agent;
import org.springframework.aop.framework.ProxyFactory;

/**
 * @author : archer
 * @date : Created in 2022/12/12 14:55
 * @description :
 */
public class Main {

    public static void main(String... args) {
        Agent target = new Agent();

        ProxyFactory pf = new ProxyFactory();
        pf.addAdvice(new SimpleAfterReturningAdvice());
        pf.setTarget(target);

        Agent proxy = (Agent) pf.getProxy();
        proxy.speak();
    }
}

输出

Bond
After return:speak

3、环绕通知

package com.luo.spring.guides.aop.simple.aroundadvice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class SimpleAroundAdvice implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("before");

        Object proceed = invocation.proceed();

        System.out.println("after");

        return proceed;
    }
}

测试

package com.luo.spring.guides.aop.simple.aroundadvice;

import com.luo.spring.guides.aop.simple.domain.Agent;
import org.springframework.aop.framework.ProxyFactory;

/**
 * @author : archer
 * @date : Created in 2022/12/12 14:55
 * @description :
 */
public class Main {

    public static void main(String... args) {
        Agent target = new Agent();

        ProxyFactory pf = new ProxyFactory();
        pf.addAdvice(new SimpleAroundAdvice());
        pf.setTarget(target);

        Agent proxy = (Agent) pf.getProxy();

        proxy.speak();
    }
}

输出

before
Bond
after

4、异常通知

  • 如果异常类型相同,Spring 优先使用参数多个那个
package com.luo.spring.guides.aop.simple.domain;

public class ErrorBean {
    public void errorMethod() throws Exception {
        throw new Exception("Generic Exception");
    }

    public void otherErrorMethod() throws IllegalArgumentException {
        throw new IllegalArgumentException("IllegalArgument Exception");
    }
}
package com.luo.spring.guides.aop.simple.throwsadvice;

import org.springframework.aop.ThrowsAdvice;

import java.lang.reflect.Method;

public class SimpleThrowsAdvice implements ThrowsAdvice {


    public void afterThrowing(Exception ex) throws Throwable {
        System.out.println("***");
        System.out.println("Generic Exception Capture");
        System.out.println("Caught: " + ex.getClass().getName());
        System.out.println("***\n");
    }

    //如果异常类型相同,Spring 优先使用参数多个那个
    public void afterThrowing(Method method, Object[] args, Object target, IllegalArgumentException ex) throws Throwable {
        System.out.println("***");
        System.out.println("IllegalArgumentException Capture");
        System.out.println("Caught: " + ex.getClass().getName());
        System.out.println("Method: " + method.getName());
        System.out.println("***\n");
    }
}

测试

package com.luo.spring.guides.aop.simple.throwsadvice;

import com.luo.spring.guides.aop.simple.domain.ErrorBean;
import org.springframework.aop.framework.ProxyFactory;

/**
 * @author : archer
 * @date : Created in 2022/12/12 15:23
 * @description :
 */
public class Main {
    public static void main(String... args) throws Exception {
        ErrorBean errorBean = new ErrorBean();

        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(errorBean);
        pf.addAdvice(new SimpleThrowsAdvice());

        ErrorBean proxy = (ErrorBean) pf.getProxy();

        try {
            proxy.errorMethod();
        } catch (Exception ignored) {

        }

        try {
            proxy.otherErrorMethod();
        } catch (Exception ignored) {

        }
    }
}

输出

******
Generic Exception Capture
Caught: java.lang.Exception
******

******
IllegalArgumentException Capture
Caught: java.lang.IllegalArgumentException
Method: otherErrorMethod
******

二、切入点(Pointcut)

切入点:可以看成判断是否触发通知代理类的条件规则

通用接口

package com.luo.spring.guides.aop.pointcut.statics.domain;

/**
 * Created by iuliana.cosmina on 4/2/17.
 */
public interface Singer {
	void sing();
}

1、静态切入点

  • 静态切入点只能把被代理类的的一些静态信息(如类名称,方法名称等),作为判断条件

被代理类

package com.luo.spring.guides.aop.pointcut.statics.domain;


/**
 * Created by iuliana.cosmina on 4/2/17.
 */
public class GoodGuitarist implements Singer {

	@Override public void sing() {
		System.out.println("Who says I can't be free \n" +
				"From all of the things that I used to be");
	}
}
package com.luo.spring.guides.aop.pointcut.statics.domain;


/**
 * Created by iuliana.cosmina on 4/2/17.
 */
public class GreatGuitarist implements Singer {

	@Override public void sing() {
		System.out.println("I shot the sheriff, \n" +
				"But I did not shoot the deputy");
	}
}

通知类

package com.luo.spring.guides.aop.pointcut.statics.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class SimpleAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println(">> Invoking " + invocation.getMethod().getName());
        Object retVal = invocation.proceed();
        System.out.println(">> Done\n");
        return retVal;
    }
}

切入点

  • 有很多判断条件,具体参考可重写父类方法
package com.luo.spring.guides.aop.pointcut.statics;

import com.luo.spring.guides.aop.pointcut.statics.domain.GoodGuitarist;
import org.springframework.aop.support.StaticMethodMatcherPointcut;

import java.lang.reflect.Method;
//有很多判断条件,具体参考可重写父类方法
public class SimpleStaticPointcut extends StaticMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> cls) {
        return ("sing".equals(method.getName())) && cls == GoodGuitarist.class;
    }

//    @Override
//    public ClassFilter getClassFilter() {
//        ClassFilter classFilter = cls -> (cls == GoodGuitarist.class);
//        return classFilter;
//    }
}

测试

package com.luo.spring.guides.aop.pointcut.statics;

import com.luo.spring.guides.aop.pointcut.statics.advice.SimpleAdvice;
import com.luo.spring.guides.aop.pointcut.statics.domain.GoodGuitarist;
import com.luo.spring.guides.aop.pointcut.statics.domain.GreatGuitarist;
import com.luo.spring.guides.aop.pointcut.statics.domain.Singer;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

public class Main {
    public static void main(String... args) {
        GoodGuitarist goodGuitarist = new GoodGuitarist();
        GreatGuitarist greatGuitarist = new GreatGuitarist();

        Singer proxyOne;
        Singer proxyTwo;

        Pointcut pc = new SimpleStaticPointcut();
        Advice advice = new SimpleAdvice();
        Advisor advisor = new DefaultPointcutAdvisor(pc, advice);

        ProxyFactory pf = new ProxyFactory();
        pf.addAdvisor(advisor);
        pf.setTarget(goodGuitarist);
        proxyOne = (Singer)pf.getProxy();

        pf = new ProxyFactory();
        pf.addAdvisor(advisor);
        pf.setTarget(greatGuitarist);
        proxyTwo = (Singer)pf.getProxy();

        proxyOne.sing();
        proxyTwo.sing();
    }
}

输出

>> Invoking sing
Who says I can’t be free
From all of the things that I used to be
>> Done

I shot the sheriff,
But I did not shoot the deputy

2、动态切入点

  • 动态切入点相对于静态切入点,增加了对参数内容的条件判断
  • 增加了灵活性,降低了性能

被代理类

package com.luo.spring.guides.aop.pointcut.dyanmic;

public class SampleBean {
    public void foo(int x) {
        System.out.println("Invoked foo() with: " + x);
    }

    public void bar() {
        System.out.println("Invoked bar()");
    }
}

通知类

package com.luo.spring.guides.aop.pointcut.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class SimpleAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println(">> Invoking " + invocation.getMethod().getName());
        Object retVal = invocation.proceed();
        System.out.println(">> Done\n");
        return retVal;
    }
}

切入点

package com.luo.spring.guides.aop.pointcut.dyanmic;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.DynamicMethodMatcherPointcut;

import java.lang.reflect.Method;

public class SimpleDynamicPointcut extends DynamicMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> cls) {
        System.out.println("Static check for " + method.getName());
        return ("foo".equals(method.getName()));
    }

    @Override
    public boolean matches(Method method, Class<?> cls, Object... args) {
        System.out.println("Dynamic check for " + method.getName());

        int x = ((Integer) args[0]).intValue();

        return (x != 100);
    }

    @Override
    public ClassFilter getClassFilter() {
        return cls -> (cls == SampleBean.class);
    }
}

测试

package com.luo.spring.guides.aop.pointcut.dyanmic;

import com.luo.spring.guides.aop.pointcut.advice.SimpleAdvice;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

public class Main {
    public static void main(String... args) {
        SampleBean target = new SampleBean();

        Advisor advisor = new DefaultPointcutAdvisor(
            new SimpleDynamicPointcut(), new SimpleAdvice());

        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);
        SampleBean proxy = (SampleBean)pf.getProxy();

        proxy.foo(1);
        proxy.foo(10);
        proxy.foo(100);

        proxy.bar();
        proxy.bar();
        proxy.bar();
    }
}

输出

Static check for foo
Static check for bar
Static check for toString
Static check for clone
Static check for foo
Dynamic check for foo
>> Invoking foo
Invoked foo() with: 1
>> Done

Dynamic check for foo
>> Invoking foo
Invoked foo() with: 10
>> Done

Dynamic check for foo
Invoked foo() with: 100
Static check for bar
Invoked bar()
Invoked bar()
Invoked bar()

3、常用的使用技巧

通用类

package com.luo.spring.guides.aop.pointcut.namematching;

/**
 * Created by iuliana.cosmina on 4/2/17.
 */
public class Guitar {
	private String brand =" Martin";

	public String play(){
		return "G C G C Am D7";
	}

	public String getBrand() {
		return brand;
	}

	public void setBrand(String brand) {
		this.brand = brand;
	}
}

1)、使用简单名称匹配

被代理类及其相关类

package com.luo.spring.guides.aop.pointcut.namematching;

import com.luo.spring.guides.aop.pointcut.Singer;

/**
 * Created by iuliana.cosmina on 4/2/17.
 */
public class GrammyGuitarist implements Singer {

	@Override public void sing() {
		System.out.println("sing: Gravity is working against me\n" +
				"And gravity wants to bring me down");
	}

	public void sing(Guitar guitar) {
		System.out.println("play: " + guitar.play());
	}

	public void rest(){
		System.out.println("zzz");
	}

	public void talk(){
		System.out.println("talk");
	}
}

a、使用 NameMatchMethodPointcut

测试类

package com.luo.spring.guides.aop.pointcut.namematching;

import com.luo.spring.guides.aop.pointcut.advice.SimpleAdvice;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.NameMatchMethodPointcut;

/**
 * @author : archer
 * @date : Created in 2022/12/13 11:01
 * @description :
 */
public class NamePointcutMain {

    public static void main(String... args) {
        GrammyGuitarist johnMayer = new GrammyGuitarist();

        NameMatchMethodPointcut pc = new NameMatchMethodPointcut();
        pc.addMethodName("sing");
        pc.addMethodName("rest");

        Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());
        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(johnMayer);
        pf.addAdvisor(advisor);

        GrammyGuitarist proxy = (GrammyGuitarist) pf.getProxy();
        proxy.sing();
        proxy.sing(new Guitar());
        proxy.rest();
        proxy.talk();
    }
}

输出

>> Invoking sing
sing: Gravity is working against me
And gravity wants to bring me down
>> Done

>> Invoking sing
play: G C G C Am D7
>> Done

>> Invoking rest
zzz
>> Done

talk

b、使用 NameMatchMethodPointcutAdvisor

测试

package com.luo.spring.guides.aop.pointcut.namematching;

import com.luo.spring.guides.aop.pointcut.advice.SimpleAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;

/**
 * Created by iuliana.cosmina on 4/2/17.
 */
public class NamePointcutUsingAdvisorMain {
	public static void main(String... args) {
		GrammyGuitarist johnMayer = new GrammyGuitarist();

		NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(new SimpleAdvice());
		advisor.setMappedNames("sing", "rest");		

		ProxyFactory pf = new ProxyFactory();
		pf.setTarget(johnMayer);
		pf.addAdvisor(advisor);

		GrammyGuitarist proxy = (GrammyGuitarist) pf.getProxy();
		proxy.sing();
		proxy.sing(new Guitar());
		proxy.rest();
		proxy.talk();
	}
}

输出

>> Invoking sing
sing: Gravity is working against me
And gravity wants to bring me down
>> Done

>> Invoking sing
play: G C G C Am D7
>> Done

>> Invoking rest
zzz
>> Done

talk

2)、使用正则表达式创建切入点

  • 代理所有包含 sing 单词的方法

被代理类

package com.luo.spring.guides.aop.pointcut.regex;


import com.luo.spring.guides.aop.pointcut.Singer;

/**
 * Created by iuliana.cosmina on 4/2/17.
 */
public class Guitarist implements Singer {

	@Override public void sing() {
		System.out.println("Just keep me where the light is");
	}

	public void sing2() {
		System.out.println("Oh gravity, stay the hell away from me");
	}

	public void rest() {
		System.out.println("zzz");
	}

}

测试类

package com.luo.spring.guides.aop.pointcut.regex;

import com.luo.spring.guides.aop.pointcut.advice.SimpleAdvice;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;

public class Main {
    public static void main(String... args) {
        Guitarist johnMayer = new Guitarist();

        JdkRegexpMethodPointcut pc = new JdkRegexpMethodPointcut();
        //代理所有包含 sing 单词的方法
        pc.setPattern(".*sing.*");
        Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());

        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(johnMayer);
        pf.addAdvisor(advisor);
        Guitarist proxy = (Guitarist) pf.getProxy();

        proxy.sing();
        proxy.sing2();
        proxy.rest();
    }
}

输出

>> Invoking sing
Just keep me where the light is
>> Done

>> Invoking sing2
Oh gravity, stay the hell away from me
>> Done

zzz

3)、使用 AspectJ 切入点表达式

a、编程式
package com.luo.spring.guides.aop.pointcut.aspectj;

import com.luo.spring.guides.aop.pointcut.advice.SimpleAdvice;
import com.luo.spring.guides.aop.pointcut.regex.Guitarist;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

public class Main {
    public static void main(String... args) {
        Guitarist guitarist = new Guitarist();

        AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
        //代理所有以 sing 单词开头的的方法,参数和返回值都任意
        pc.setExpression("execution(* sing*(..))");
        Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());

        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(guitarist);
        pf.addAdvisor(advisor);
        Guitarist proxy = (Guitarist) pf.getProxy();

        proxy.sing();
        proxy.sing2();
        proxy.rest();
    }
}

输出

>> Invoking sing
Just keep me where the light is
>> Done

>> Invoking sing2
Oh gravity, stay the hell away from me
>> Done

zzz

b、注解式

配置类

package com.luo.spring.guides.aop.pointcut.aspectj.annotation.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * Created by iuliana.cosmina on 4/9/17.
 */
@Configuration
@ComponentScan(basePackages = {"com.luo.spring.guides.aop.pointcut.aspectj.annotation"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {}

切入点和通知

package com.luo.spring.guides.aop.pointcut.aspectj.annotation.advise;

import com.luo.spring.guides.aop.pointcut.domain.Guitar;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AnnotatedAdvice {
    
    //&& args(value) 给通知注解方法传参
    @Pointcut("execution(* com.luo.spring.guides.aop.pointcut.aspectj.annotation..sing*(com.luo.spring.guides.aop.pointcut.domain.Guitar)) && args(value)")
    public void singExecution(Guitar value) {
    }

    @Pointcut("bean(john*)")
    public void isJohn() {
    }

    //@Pointcut 中有 && args(value) 才能加入参数(Guitar value)
    @Before("singExecution(value) && isJohn()")
    public void simpleBeforeAdvice(JoinPoint joinPoint, Guitar value) {
        if(value.getBrand().equals("Gibson")) {
        System.out.println("Executing: " + 
            joinPoint.getSignature().getDeclaringTypeName() + " "
            + joinPoint.getSignature().getName() + " argument: " + value.getBrand());
        }
    }

    @Around("singExecution(value) && isJohn()")
    public Object simpleAroundAdvice(ProceedingJoinPoint pjp, Guitar value) throws Throwable {
        System.out.println("Before execution: " +      
            pjp.getSignature().getDeclaringTypeName() + " "
            + pjp.getSignature().getName()
            + " argument: " + value.getBrand());

        Object retVal = pjp.proceed();

        System.out.println("After execution: " +   
            pjp.getSignature().getDeclaringTypeName() + " "
            + pjp.getSignature().getName()
            + " argument: " + value.getBrand());

        return retVal;
    }
}

被代理相关类

package com.luo.spring.guides.aop.pointcut.aspectj.annotation;

import com.luo.spring.guides.aop.pointcut.Singer;
import com.luo.spring.guides.aop.pointcut.domain.Guitar;
import org.springframework.stereotype.Component;

/**
 * Created by iuliana.cosmina on 4/9/17.
 */
@Component("johnMayer")
public class GrammyGuitarist implements Singer {

	@Override public void sing() {
		System.out.println("sing: Gravity is working against me\n" +
				"And gravity wants to bring me down");
	}

	public void sing(Guitar guitar) {
		System.out.println("play: " + guitar.play());
	}

	public void rest(){
		System.out.println("zzz");
	}

	public void talk(){
		System.out.println("talk");
	}
}
package com.luo.spring.guides.aop.pointcut.aspectj.annotation;

import com.luo.spring.guides.aop.pointcut.domain.Guitar;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

/**
 * Created by iuliana.cosmina on 4/9/17.
 */
@Component("documentarist")
public class NewDocumentarist {

	protected GrammyGuitarist guitarist;

	public void execute() {
		guitarist.sing();
		Guitar guitar = new Guitar();
		guitar.setBrand("Gibson");
		guitarist.sing(guitar);
		guitarist.talk();
	}

	@Autowired
	@Qualifier("johnMayer")
	public void setGuitarist(GrammyGuitarist guitarist) {
		this.guitarist = guitarist;
	}
}

测试

package com.luo.spring.guides.aop.pointcut.aspectj.annotation;

import com.luo.spring.guides.aop.pointcut.aspectj.annotation.config.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;

/**
 * @author : archer
 * @date : Created in 2022/12/13 14:38
 * @description :
 */
public class Main {

    public static void main(String[] args) {
        GenericApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);

        NewDocumentarist documentarist = ctx.getBean("documentarist", NewDocumentarist.class);
        documentarist.execute();

        ctx.close();
    }
}

输出

sing: Gravity is working against me
And gravity wants to bring me down
Before execution: com.luo.spring.guides.aop.pointcut.aspectj.annotation.GrammyGuitarist sing argument: Gibson
Executing: com.luo.spring.guides.aop.pointcut.aspectj.annotation.GrammyGuitarist sing argument: Gibson
play: G C G C Am D7
After execution: com.luo.spring.guides.aop.pointcut.aspectj.annotation.GrammyGuitarist sing argument: Gibson
talk

4)、使用 AnnotationMatchingPointcut

  • @AdviceRequired 注解标识的方法会被代理增强
package com.luo.spring.guides.aop.pointcut.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface AdviceRequired {
}
package com.luo.spring.guides.aop.pointcut.annotation;

import com.luo.spring.guides.aop.pointcut.Singer;
import com.luo.spring.guides.aop.pointcut.domain.Guitar;

/**
 * Created by iuliana.cosmina on 4/2/17.
 */
public class Guitarist implements Singer {

	@Override public void sing() {
		System.out.println("Dream of ways to throw it all away");
	}

	@AdviceRequired
	public void sing(Guitar guitar) {
		System.out.println("play: " + guitar.play());
	}

	public void rest(){
		System.out.println("zzz");
	}

}

测试

package com.luo.spring.guides.aop.pointcut.annotation;

import com.luo.spring.guides.aop.pointcut.advice.SimpleAdvice;
import com.luo.spring.guides.aop.pointcut.domain.Guitar;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;

public class Main {
    public static void main(String... args) {
        Guitarist johnMayer = new Guitarist();

        //用 `@AdviceRequired` 注解标识的方法会被代理增强
        AnnotationMatchingPointcut pc = AnnotationMatchingPointcut
            .forMethodAnnotation(AdviceRequired.class);
        Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());

        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(johnMayer);
        pf.addAdvisor(advisor);
        Guitarist proxy = (Guitarist) pf.getProxy();

        proxy.sing(new Guitar());
        proxy.rest();
    }
}

输出

>> Invoking sing
play: G C G C Am D7
>> Done

zzz

4、切入点的高级使用

1)、使用控制流切入点

  • 可以设置只能某一特别方法才触发通知
package com.apress.prospring5.ch5;

public class TestBean {
    public void foo() {
        System.out.println("foo()");
    }
}

测试

package com.luo.spring.guides.aop.pointcut.cflow;

import com.luo.spring.guides.aop.simple.beforeadvice.SimpleBeforeAdvice;
import org.springframework.aop.Advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.ControlFlowPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;

public class Main {
    public static void main(String... args) {
        Main ex = new Main();
        ex.run();
    }

    public void run() {
        TestBean target = new TestBean();

        //方法名为 test 才触发通知
        Pointcut pc = new ControlFlowPointcut(Main.class, "test");
        Advisor advisor = new DefaultPointcutAdvisor(pc, 
            new SimpleBeforeAdvice());

        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);

        TestBean proxy = (TestBean) pf.getProxy();
 
        System.out.println("Trying normal invoke");
        proxy.foo();
        System.out.println("Trying under ControlFlowDemo.test()");
        test(proxy);
    }

    private void test(TestBean bean) {
        bean.foo();
    }
}

输出

Trying normal invoke
foo()
Trying under ControlFlowDemo.test()
Before method: public void com.luo.spring.guides.aop.pointcut.cflow.TestBean.foo()
foo()

2)、使用组合切入点

被代理类

package com.luo.spring.guides.aop.pointcut.namematching;

import com.luo.spring.guides.aop.pointcut.Singer;
import com.luo.spring.guides.aop.pointcut.domain.Guitar;

/**
 * Created by iuliana.cosmina on 4/2/17.
 */
public class GrammyGuitarist implements Singer {

	@Override public void sing() {
		System.out.println("sing: Gravity is working against me\n" +
				"And gravity wants to bring me down");
	}

	public void sing(Guitar guitar) {
		System.out.println("play: " + guitar.play());
	}

	public void rest(){
		System.out.println("zzz");
	}

	public void talk(){
		System.out.println("talk");
	}
}

通知类

package com.luo.spring.guides.aop.simple.beforeadvice;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class SimpleBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        System.out.println("Before method: " + method);
    }
}

切入点

package com.luo.spring.guides.aop.pointcut.composable;

import org.springframework.aop.support.StaticMethodMatcher;

import java.lang.reflect.Method;

/**
 * @author : archer
 * @date : Created in 2022/12/13 17:09
 * @description :
 */
public class SingMethodMatcher  extends StaticMethodMatcher {
    @Override
    public boolean matches(Method method, Class<?> cls) {
        return (method.getName().startsWith("si"));
    }
}
package com.luo.spring.guides.aop.pointcut.composable;

import org.springframework.aop.support.StaticMethodMatcher;

import java.lang.reflect.Method;

/**
 * @author : archer
 * @date : Created in 2022/12/13 17:09
 * @description :
 */
public class TalkMethodMatcher extends StaticMethodMatcher {
    @Override
    public boolean matches(Method method, Class<?> cls) {
        return "talk".equals(method.getName());
    }
}
package com.luo.spring.guides.aop.pointcut.composable;

import org.springframework.aop.support.StaticMethodMatcher;

import java.lang.reflect.Method;

/**
 * @author : archer
 * @date : Created in 2022/12/13 17:09
 * @description :
 */
public class RestMethodMatcher extends StaticMethodMatcher {
    @Override
    public boolean matches(Method method, Class<?> cls) {
        return (method.getName().endsWith("st"));
    }
}

测试

package com.luo.spring.guides.aop.pointcut.composable;

import com.luo.spring.guides.aop.pointcut.domain.Guitar;
import com.luo.spring.guides.aop.pointcut.namematching.GrammyGuitarist;
import com.luo.spring.guides.aop.simple.beforeadvice.SimpleBeforeAdvice;
import org.springframework.aop.Advisor;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.StaticMethodMatcher;

import java.lang.reflect.Method;

public class Main {
    public static void main(String... args) {
        GrammyGuitarist johnMayer = new GrammyGuitarist();

        ComposablePointcut pc = new ComposablePointcut(ClassFilter.TRUE, 
            new SingMethodMatcher());

        System.out.println("Test 1 >> ");
        GrammyGuitarist proxy = getProxy(pc, johnMayer);
        testInvoke(proxy);
        System.out.println();

        System.out.println("Test 2 >> ");
        //与前面取并集
        pc.union(new TalkMethodMatcher());
        proxy = getProxy(pc, johnMayer);
        testInvoke(proxy);
        System.out.println();

        System.out.println("Test 3 >> ");
        //与前面取交集
        pc.intersection(new RestMethodMatcher());
        proxy = getProxy(pc, johnMayer);
        testInvoke(proxy);
    }

    private static GrammyGuitarist getProxy(ComposablePointcut pc,
            GrammyGuitarist target) {
        Advisor advisor = new DefaultPointcutAdvisor(pc, 
            new SimpleBeforeAdvice());

        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);
        return (GrammyGuitarist) pf.getProxy();
    }

    private static void testInvoke(GrammyGuitarist proxy) {
        proxy.sing();
        proxy.sing(new Guitar());
        proxy.talk();
        proxy.rest();
    }
}

输出

Test 1 >>
Before method: public void com.luo.spring.guides.aop.pointcut.namematching.GrammyGuitarist.sing()
sing: Gravity is working against me
And gravity wants to bring me down
Before method: public void com.luo.spring.guides.aop.pointcut.namematching.GrammyGuitarist.sing(com.luo.spring.guides.aop.pointcut.domain.Guitar)
play: G C G C Am D7
talk
zzz

Test 2 >>
Before method: public void com.luo.spring.guides.aop.pointcut.namematching.GrammyGuitarist.sing()
sing: Gravity is working against me
And gravity wants to bring me down
Before method: public void com.luo.spring.guides.aop.pointcut.namematching.GrammyGuitarist.sing(com.luo.spring.guides.aop.pointcut.domain.Guitar)
play: G C G C Am D7
Before method: public void com.luo.spring.guides.aop.pointcut.namematching.GrammyGuitarist.talk()
talk
zzz

Test 3 >>
sing: Gravity is working against me
And gravity wants to bring me down
play: G C G C Am D7
talk
zzz

三、动态代理

目前动态代理实现有 JDK 动态代理和 CGLIB 动态代理两种方案,两种方案的详情介绍,请读者自行阅读资料,这里就不介绍了。

这里是只对两者的性能做下简单的对比。

被代理类相关

package com.luo.spring.guides.aop.pointcut.dyanmicproxy;

public interface SimpleBean {
    //被代理增强的通知方法,即被通知方法
    void advised();
    //不被被代理增强的通知方法,即未被通知方法
    void unadvised();
}
package com.apress.prospring5.ch5;

public class DefaultSimpleBean implements SimpleBean {
    private long dummy = 0;

    //被代理增强的通知方法,即被通知方法
    @Override
    public void advised() {
        dummy = System.currentTimeMillis();
    }

    //不被被代理增强的通知方法,即未被通知方法
    @Override
    public void unadvised() {
        dummy = System.currentTimeMillis();
    }
}

切入点

package com.luo.spring.guides.aop.pointcut.dyanmicproxy;

import org.springframework.aop.support.StaticMethodMatcherPointcut;

import java.lang.reflect.Method;

public class TestPointcut extends StaticMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class cls) {
        return ("advise".equals(method.getName()));
    } 
}

通知类

package com.luo.spring.guides.aop.pointcut.dyanmicproxy;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class NoOpBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target)
        throws Throwable {
        // no-op
    }
}

测试

package com.luo.spring.guides.aop.pointcut.dyanmicproxy;

import org.springframework.aop.Advisor;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

public class ProxyPerfTest {
    public static void main(String... args) {
        SimpleBean target = new DefaultSimpleBean();

        Advisor advisor = new DefaultPointcutAdvisor(new TestPointcut(),
                new NoOpBeforeAdvice());

        //标准 CGLIB 代理
        runCglibTests(advisor, target);
        //冻结通知链 CGLIB 代理
        runCglibFrozenTests(advisor, target);
        //JDK 代理
        runJdkTests(advisor, target);
    }

    private static void runCglibTests(Advisor advisor, SimpleBean target) {
        ProxyFactory pf = new ProxyFactory();
        pf.setProxyTargetClass(true);
        pf.setTarget(target);
        pf.addAdvisor(advisor);

        SimpleBean proxy = (SimpleBean)pf.getProxy();
        System.out.println("Running CGLIB (Standard) Tests");
        test(proxy);
    }

    private static void runCglibFrozenTests(Advisor advisor, SimpleBean target) {
        ProxyFactory pf = new ProxyFactory();
        pf.setProxyTargetClass(true);
        pf.setTarget(target);
        pf.addAdvisor(advisor);
        //冻结通知链
        pf.setFrozen(true);

        SimpleBean proxy = (SimpleBean) pf.getProxy();
        System.out.println("Running CGLIB (Frozen) Tests");
        test(proxy);
    }

    private static void runJdkTests(Advisor advisor, SimpleBean target) {
        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);
        pf.setInterfaces(new Class[]{SimpleBean.class});

        SimpleBean proxy = (SimpleBean)pf.getProxy();
        System.out.println("Running JDK Tests");
        test(proxy);
    }

    private static void test(SimpleBean bean) {
        long before = 0;
        long after = 0;

        System.out.println("Testing Advised Method");
        before = System.nanoTime();
        for(int x = 0; x < 500000; x++) {
            bean.advised();
        }
        after = System.nanoTime();

        System.out.println("Took " + (after - before) / 1000000 + " ms");

        System.out.println("Testing Unadvised Method");
        before = System.nanoTime();
        for(int x = 0; x < 500000; x++) {
            bean.unadvised();
        }
        after = System.nanoTime();

        System.out.println("Took " + (after - before) / 1000000 + " ms");

        System.out.println("Testing equals() Method");
        before = System.nanoTime();
        for(int x = 0; x < 500000; x++) {
            bean.equals(bean);
        }
        after = System.nanoTime();

        System.out.println("Took " + (after - before) / 1000000 + " ms");

        System.out.println("Testing hashCode() Method");
        before = System.nanoTime();
        for(int x = 0; x < 500000; x++) {
            bean.hashCode();
        }
        after = System.nanoTime();

        System.out.println("Took " + (after - before) / 1000000 + " ms");

        Advised advised = (Advised)bean;

        System.out.println("Testing Advised.getProxyTargetClass() Method");
        before = System.nanoTime();
        for(int x = 0; x < 500000; x++) {
            advised.getTargetClass();
        }
        after = System.nanoTime();

        System.out.println("Took " + (after - before) / 1000000 + " ms");

        System.out.println(">>>\n");
    }
}

输出图解

在这里插入图片描述

小结

  • 标准 CGLIB 和 JDK 动态代理,在被通知方法和未被通知方法之间的性能差异不大。但是,当使用具有冻结通知链的 CGLIB 代理时,性能存在显著差距。
  • 对于 equals() 和 hashCode() 方法,在使用 CGLIB 代理时,明显更快。
  • 对于 Advised 接口上的方法,CGLIB 冻结代理上运行的更快,原因是被通知方法早就在 intercept() 方法中进行了处理,从而避免了其他方法所需的大部分逻辑。

四、引入

引入是 Spring 中可用的 AOP 功能集的重要组成部分,通过使用引入,可以动态的向现有对象引入新功能。可以将引入视为一种特殊类型的通知。

被代理类

package com.luo.spring.guides.aop.pointcut.introduction;

/**
 * Created by iuliana.cosmina on 4/9/17.
 */
public class Contact {

	private String name;
	private String phoneNumber;
	private String email;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPhoneNumber() {
		return phoneNumber;
	}

	public void setPhoneNumber(String phoneNumber) {
		this.phoneNumber = phoneNumber;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}
}

引入及其相关类

package com.luo.spring.guides.aop.pointcut.introduction;

public interface IsModified {
    boolean isModified();
}
package com.luo.spring.guides.aop.pointcut.introduction;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

//对象修改检测技术,可用于检测是否有值被更改过,改过就入库,否则不入库,减少与数据库的链接次数
@SuppressWarnings("all")
public class IsModifiedMixin extends DelegatingIntroductionInterceptor 
        implements IsModified {
    private boolean isModified = false;

    private Map<Method, Method> methodCache = new HashMap<>();

    @Override
    public boolean isModified() {
        return isModified;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        if (!isModified) {
            if ((invocation.getMethod().getName().startsWith("set"))
                && (invocation.getArguments().length == 1)) {

                Method getter = getGetter(invocation.getMethod());

                if (getter != null) {
                    Object newVal = invocation.getArguments()[0];
                    Object oldVal = getter.invoke(invocation.getThis(),null);

                    if((newVal == null) && (oldVal == null)) {
                        isModified = false;
                    } else if((newVal == null) && (oldVal != null)) {
                        isModified = true;
                    } else if((newVal != null) && (oldVal == null)) {
                        isModified = true;
                    } else {
                        isModified = !newVal.equals(oldVal);
                    }
                }
            }
        }

        return super.invoke(invocation);
    }


    private Method getGetter(Method setter) {
        Method  getter = methodCache.get(setter);

        if (getter != null) {
            return getter;
        }

        String getterName = setter.getName().replaceFirst("set", "get");
        try {
            getter = setter.getDeclaringClass().getMethod(getterName, null);
            synchronized (methodCache) {
                methodCache.put(setter, getter);
            }
            return getter;
        } catch (NoSuchMethodException ex) {
            return null;
        }
    }
}
package com.luo.spring.guides.aop.pointcut.introduction;

import org.springframework.aop.support.DefaultIntroductionAdvisor;

public class IsModifiedAdvisor extends DefaultIntroductionAdvisor {
    public IsModifiedAdvisor() {
        super(new IsModifiedMixin());
    }
}

1、引入简介

下面示例是对象修改检测技术的实现。

测试

package com.luo.spring.guides.aop.pointcut.introduction;

import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.framework.ProxyFactory;

@SuppressWarnings("all")
public class Main {
    public static void main(String... args) {
        Contact target = new Contact();
        target.setName("John Mayer");

        IntroductionAdvisor advisor = new IsModifiedAdvisor();

        ProxyFactory pf = new ProxyFactory();
        pf.setTarget(target);
        pf.addAdvisor(advisor);
        //使用cglib代理方式,默认jdk代理
        pf.setOptimize(true);

        Object proxy = pf.getProxy();

        //使用 jdk 代理时,生成的代理对象并不是对象类(Contact)的实例。
        System.out.println("Is Contact?: " + (proxy instanceof Contact));
        System.out.println("Is IsModified?: " + (proxy instanceof IsModified));

        Contact contact = (Contact) pf.getProxy();
        IsModified proxyInterface = (IsModified)proxy;
        System.out.println("Has been modified?: " + 
            proxyInterface.isModified());

        contact.setName("John Mayer");

        System.out.println("Has been modified?: " + 
            proxyInterface.isModified());

        contact.setName("Eric Clapton");

        System.out.println("Has been modified?: " + 
            proxyInterface.isModified());
    }
}

输出

Is Contact?: true
Is IsModified?: true
Has been modified?: false
Has been modified?: false
Has been modified?: true

2、使用 ProxyFactoryBean

ProxyFactoryBcan类是FactoryBean 的一个实现,它允许指定一个bean 作为目标,并且为该 bean 提供一组通知和顾问(Advisor)(这些通知和顾问最终被合并到一个 AOP 代理中)。

配置类

package com.luo.spring.guides.aop.proxyfactorybean.introduction;

import com.luo.spring.guides.aop.proxyfactorybean.introduction.domain.Contact;
import com.luo.spring.guides.aop.proxyfactorybean.introduction.domain.IsModifiedAdvisor;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by iuliana.cosmina on 4/9/17.
 */
@Configuration
public class AppConfig {

	@Bean
	public Contact guitarist() {
		Contact guitarist = new Contact();
		guitarist.setName("John Mayer");
		return guitarist;
	}

	@Bean
	public Advisor advisor() {
		return new IsModifiedAdvisor();
	}

	@Bean
	ProxyFactoryBean bean() {
		ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
		proxyFactoryBean.setTarget(guitarist());
		//cglib代理 设置是否直接代理目标类,false表示直接代理特定接口
		proxyFactoryBean.setProxyTargetClass(true);
		proxyFactoryBean.addAdvisor(advisor());
		return proxyFactoryBean;
	}
}

测试

package com.luo.spring.guides.aop.proxyfactorybean.introduction;

import com.luo.spring.guides.aop.proxyfactorybean.introduction.domain.Contact;
import com.luo.spring.guides.aop.proxyfactorybean.introduction.domain.IsModified;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;

@SuppressWarnings("all")
public class Main {
    public static void main(String... args) {
        GenericApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);

        Contact bean = (Contact) ctx.getBean("bean");
        IsModified mod = (IsModified) bean;

        System.out.println("Is Contact?: " + (bean instanceof Contact));
        System.out.println("Is IsModified?: " + (bean instanceof IsModified));

        System.out.println("Has been modified?: " + mod.isModified());
        bean.setName("John Mayer");

        System.out.println("Has been modified?: " + mod.isModified());
        bean.setName("Eric Clapton");

        System.out.println("Has been modified?: " + mod.isModified());
    }
}

输出

Is Contact?: true
Is IsModified?: true
Has been modified?: false
Has been modified?: false
Has been modified?: true

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

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

相关文章

算法拾遗二十二之Dijstra算法优化+认识一些经典的递归过程

Dijstra算法优化 public static class NodeRecord {public Node node;public int distance;public NodeRecord(Node node, int distance) {this.node node;this.distance distance;}}public static class NodeHeap {private Node[] nodes; // 实际的堆结构// key 某一个node&…

VS2019封装C++接口至可执行动态链接库.dll

VS2019封装C接口至可执行动态链接库.dll 本文主要总结如何对已实现的C函数、类进行封装&#xff0c;以便在其他平台或者语言上进行便捷使用或者二次开发&#xff01; 重点参考&#xff1a; 1.https://blog.csdn.net/hai_fellow_Z/article/details/117290517 2.https://blog.csd…

打工人的发展困境

近期在公司内部QE社区举办了一场研讨会&#xff0c;主题是《QA角色职业发展机遇讨论》。收集到以下观点&#xff0c;觉得挺有意思&#xff0c;又不局限于特定角色&#xff0c;于是用XX代替QA。 无论是在公司内部还是外部&#xff0c;XX都是单独作战居多&#xff0c;从他人身上…

都在说00后是躺平的一代,公司刚来的00后却把我卷哭了

上个月公司来了许多新面孔&#xff0c;其中居然有一个是00年的&#xff0c;虽然也给公司带来了一些新的血液&#xff0c;但也让我意识到&#xff0c;自己年级确实不小了。这小老弟是去年毕业的&#xff0c;在某软件公司干了半年&#xff0c;现在跳槽到我们公司起薪18K&#xff…

通过数据分析提升客流量和销售额

只有把每一位来光临的顾客都当作最后一位顾客&#xff0c;我们才可能真正实现营销和服务管理的精细化&#xff0c;正如只有把自己的每一天都当作生命的最后一天来对待的人才可能真正珍惜时间和生命一样。 如何提高门店的销售额可以说是大部分店长都十分困惑的问题&#xff0c;…

12月13日 OpenCV 实战基础学习笔记——Harris、SIFT

文章目录前言一、Harris 角点检测1、公式推导2、找到 E(u,v)E(u, v)E(u,v) 的最大值二、SIFT1、关键点定位 keypoint localisation2、特征描述 feature description前言 本文为12月13日 OpenCV 实战基础学习笔记&#xff0c;分为两个章节&#xff1a; Harri 角点检测&#xf…

docker安装Gitlab、修改密码、创建组、创建项目、创建用户、Git拉取代码

安装 建议内存大于2G 开放防火墙端口80和配置映射文件夹 firewall-cmd --zonepublic --add-port80/tcp --permanent firewall-cmd --reload mkdir -p /docker_data/gitlab/{data,logs,config}启动Gitlab容器&#xff08;启动容器之前确保22&#xff0c;80&#xff0c;443端口没…

图解外包验收流程V2.0及详细说明实例

详细说明 01、【开发计划内容】-标准模板{文档} 1.包含二级模块开发计划&#xff0c;标注里程碑验收节点&#xff1b; 2.包含架构设计、数据库设计输出时间&#xff1b; 3.包含接口开发计划及其接口明细&#xff1b; 4.所有节点分配到具体的人和具体的开始结束时间&#xff1…

Python读取Word文档中的Excel嵌入文件

今天群友提出一个问题&#xff1a; 给出Word示例如下&#xff1a; 对于这种嵌入文件在Word中都属于ole文件。 下面我们假设需要读取每个嵌入的Excel文件中的python工作表中的A1单元格。 python调用宏实现 首先我们看看如何调用com接口的宏代码实现这个效果&#xff0c;最终完…

SpringBoot多模块项目初始化搭建

&#x1f3b6; 文章简介&#xff1a;SpringBoot多模块项目初始化搭建 &#x1f4a1; 创作目的&#xff1a;详细介绍SpringBoot多模块项目的搭建 ☀️ 今日天气&#xff1a;阳光明媚 &#x1f4dd; 每日一言&#xff1a;不求事事圆满&#xff0c;但求事事甘心。 文章目录1、新建…

数据结构与算法——Java实现单链表、双向链表、环型链表、约瑟夫

目录 一、单链表 1.1 单链表基本介绍 1.2 分析与实现带head头结点的单向链表 1.2.1第一种方式&#xff1a; 尾部添加元素示意图 1.2.2 尾部添加元素的代码实现以及遍历链表的实现 1.2.3 第二种方式&#xff1a; 根据排名将节点插入到指定位置的示意图 1.2.4 根据排名将节…

MySQL入门到精通经典50题,看这一篇就够了

MySQL入门到精通经典50题学习笔记 pdf获取方式&#xff0c;公众号&#xff1a;每天都要努力coding回复&#xff1a;mysql经典50题 文章目录MySQL入门到精通经典50题学习笔记[toc]MySQL安装教程详解数据预览sql建表语句1.查询" 01 “课程比” 02 "课程成绩高的学生的…

基于鸽群算法优化的lssvm回归预测-附代码

基于鸽群算法优化的lssvm回归预测 - 附代码 文章目录基于鸽群算法优化的lssvm回归预测 - 附代码1.数据集2.lssvm模型3.基于鸽群算法优化的LSSVM4.测试结果5.Matlab代码摘要&#xff1a;为了提高最小二乘支持向量机&#xff08;lssvm&#xff09;的回归预测准确率&#xff0c;对…

你的项目需要自动化测试吗?看看这篇文章再说吧

什么是自动化测试&#xff1f; 通过代码的方式&#xff0c;实现测试用例的自动运行&#xff0c;评估运行结果&#xff0c;并对测试结果及异常情况进行记录。 为什么进行自动化测试&#xff1f; 纯手工测试会有好多重复性的操作&#xff0c;浪费时间&#xff0c;而且频繁的回…

央企招聘:中国大唐集团2023年度公开招聘公告

中国大唐集团科学技术研究总院有限公司&#xff08;以下简称科研总院&#xff09;是世界500强企业——中国大唐集团有限公司&#xff08;以下简称集团公司&#xff09;的全资子公司&#xff0c;是集团公司的科技创新中心、战略参谋本部、成果孵化中心、技术服务保障基地和科技人…

Qt+VS+VTK综合开发环境配置

说明 本文旨在介绍一种个人常用的开发环境&#xff0c;主要解决在VS中开发Qt项目的部分问题&#xff0c;以及解决使用基于Qt的VTK库开发时可能遇到的部分问题&#xff0c;并通过合理的配置提升新项目搭建时间 该教程使用版本为VS2017&#xff0c;Qt5.14.2以及VTK8.2&#xff…

git_No.1_版本管控的全流程

文章目录1.获取Git仓库1.1 已存在目录中初始化仓库1.2 克隆一个仓库2. 将变更提交到仓库2.1 查看当前文件状态2.2 跟踪新文件2.3 暂存已修改的文件2.4 忽略文件2.5 查看已暂存和未暂存的修改2.6 提交更新2.7 跳过使用暂存区2.8 移除文件3. 查看提交历史4.远程仓库4.1 查看远程仓…

HTTP介绍

目录 1.什么是HTTP&#xff1f; 2.HTTP的特点 3.HTTP的优点和缺点 4.HTTP请求数据格式 5.HTTP响应数据格式 6.GET请求和POST请求的区别 7.状态码分类说明 8.查看发送的请求数据包方式 1.什么是HTTP&#xff1f; Hyper Text Transfer Protocol,超文本传输协议&#xff…

[036]基于Vue+Nodejs的网上作业批改系统(含源码、数据库、万字课设报告)

文章目录一、项目介绍二、源码获取一、项目介绍 网络作业提交与批改系统&#xff1a;超强的作业批改项目&#xff0c;技术栈是VueNodeMysql&#xff0c;最重要的是有超详细的万字报告&#xff0c;一劳永逸&#xff0c;可冲~ 主要功能介绍&#xff1a; 1.管理员功能模块 管理员…

分享几个宝藏微信小程序

1.有数鸟&#xff1a;了解会员在各平台的消费明细 平时我们看视频听音乐&#xff0c;我们用的很多vip&#xff0c;你还记得你注册了哪些应用吗&#xff1f;有了这个小程序&#xff0c;就可以帮你记录每个平台的付款详情&#xff0c;以及总支出。 当我们添加会员项目时&#xff…