4.Spring AOP

news2025/2/8 4:53:07

4.1  Spring AOP的基本概念

4.1.1  AOP的概念

    在业务处理代码中,通常都有日志记录、性能统计、安全控制、事务处理、异常处理等操作。尽管使用OOP可以通过封装或继承的方式达到代码的重用,但仍然存在同样的代码分散到各个方法中。因此,采用OOP处理日志记录等操作,不仅增加了开发者的工作量,而且提高了升级维护的困难。为了解决此类问题,AOP思想应运而生。AOP采取横向抽取机制,即将分散在各个方法中的重复代码提取出来,然后在程序编译或运行阶段,再将这些抽取出来的代码应用到需要执行的地方。这种横向抽取机制,采用传统的OOP是无法办到的,因为OOP实现的是父子关系的纵向重用。但是AOP不是OOP的替代品,而是OOP的补充,它们相辅相成。

4.1.2  AOP的术语

1.切面

    切面(Aspect)是指封装横切到系统功能(如事务处理)的类。

2.连接点

    连接点(Joinpoint)是指程序运行中的一些时间点,如方法的调用或异常的抛出。

3.切入点

    切入点(Pointcut)是指那些需要处理的连接点。在Spring AOP 中,所有的方法执行都是连接点,而切入点是一个描述信息,它修饰的是连接点,通过切入点确定哪些连接点需要被处理。

4.通知(增强处理)

    由切面添加到特定的连接点(满足切入点规则)的一段代码,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面开启后,切面的方法。因此,通知是切面的具体实现。

5.引入

    引入(Introduction)允许在现有的实现类中添加自定义的方法和属性。

6.目标对象

    目标对象(Target Object)是指所有被通知的对象。如果AOP 框架使用运行时代理的方式(动态的AOP)来实现切面,那么通知对象总是一个代理对象。

7.代理

    代理(Proxy)是通知应用到目标对象之后,被动态创建的对象。

8.组入

    组入(Weaving)是将切面代码插入到目标对象上,从而生成代理对象的过程。根据不同的实现技术,AOP织入有三种方式:编译器织入,需要有特殊的Java编译器;类装载期织入,需要有特殊的类装载器;动态代理织入,在运行期为目标类添加通知生成子类的方式。Spring AOP框架默认采用动态代理织入,而AspectJ(基于Java语言的AOP框架)采用编译器织入和类装载期织入。

 4.2  动态代理

目前,Spring AOP中常用JDKCGLIB两种动态代理技术。

4.2.1  JDK动态代理

    JDK动态代理是java.lang.reflect.*包提供的方式,它必须借助一个接口才能产生代理对象。因此,对于使用业务接口的类,Spring默认使用JDK动态代理实现AOP

下面通过一个实例演示使用JDK动态代理实现Spring AOP

1.创建应用

    创建一个名为SpringAOP的Web应用。

2.创建接口及实现类

    SpringAOP的java目录下,创建一个dynamic.jdk包,在该包中创建接口TestDao和接口实现类TestDaoImpl。该实现类作为目标类,在代理类中对其方法进行增强处理。

package dynamic.jdk;

public interface TestDao {
    public void save();

    public void modify();

    public void delete();
}
package dynamic.jdk;

public class TestDaoImpl implements TestDao {
    
    public void save() {
        System.out.println("保存");
    }


    public void modify() {
        System.out.println("修改");
    }


    public void delete() {
        System.out.println("删除");
    }
}

3.创建切面类

    SpringAOP的java目录下,创建一个aspect包,在该包中创建切面类MyAspect,在该类中可以定义多个通知(增强处理的功能方法)。

package aspect;

/**
 * 切面类,可以定义多个通知,即增强处理的方法
 */
public class MyAspect {
    public void check() {
        System.out.println("模拟权限控制");
    }

    public void except() {
        System.out.println("模拟异常处理");
    }

    public void log() {
        System.out.println("模拟日志记录");
    }

    public void monitor() {
        System.out.println("性能监测");
    }
}

4.创建代理类

    在dynamic.jdk包中,创建代理类JDKDynamicProxy。在JDK动态代理中,代理类必须实现java.lang.reflect.InvocationHandler接口,并编写代理方法。在代理方法中,需要通过Proxy实现动态代理。

package dynamic.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import aspect.MyAspect;

public class JDKDynamicProxy implements InvocationHandler {
    //声明目标类接口对象(真实对象)
    private TestDao testDao;

    /**
     * 创建代理的方法,建立代理对象和真实对象的代理关系,并返回代理对象
     **/
    public Object createProxy(TestDao testDao) {
        this.testDao = testDao;
        //1.类加载器
        ClassLoader cld = JDKDynamicProxy.class.getClassLoader();
        //2.被代理对象实现的所有接口
        Class[] clazz = testDao.getClass().getInterfaces();
        //3.使用代理类进行增强,返回代理后的对象
        return Proxy.newProxyInstance(cld, clazz, this);
    }

    /**
     * 代理的逻辑方法,所有动态代理类的方法调用,都交给该方法处理
     * proxy被代理对象
     * method将要被执行的方法信息
     * args执行方法时需要的参数
     * return代理结果
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //创建一个切面
        MyAspect myAspect = new MyAspect();
        //前增强
        myAspect.check();
        myAspect.except();
        //在目标类上调用方法,并传入参数,相当于调用testDao里的方法
        Object obj = method.invoke(testDao, args);
        //后增强
        myAspect.log();
        myAspect.monitor();
        return obj;
    }
}

5.创建测试类

    在dynamic.jdk包中,创建测试类JDKDynamicTest。在主方法中创建代理对象和目标对象,然后从代理对象中获取对目标对象增强后的对象,最后调用该对象的添加、修改和删除方法。

package dynamic.jdk;

public class JDKDynamicTest {
    public static void main(String[] args) {
        //创建代理对象
        JDKDynamicProxy jdkProxy = new JDKDynamicProxy();
        //创建目标对象
        TestDao testDao = new TestDaoImpl();
        /**从代理对象中获取增强后的目标对象,该对象是一个被代理的对象,它会进入代理的逻辑方法invoke里**/
        TestDao testDaoAdvice = (TestDao) jdkProxy.createProxy(testDao);
        //执行方法
        testDaoAdvice.save();
        System.out.println("==============");
        testDaoAdvice.modify();
        System.out.println("==============");
        testDaoAdvice.delete();
    }
}

4.2.2  CGLIB动态代理

    CGLIBCode Generation Library)是一个高性能开源的代码生成包,采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。在Spring Core包中已经集成了CGLIB所需要的JAR包,不需要另外导入JAR包。

1.创建目标类

    在SpringAOP的java目录下,创建一个dynamic.cglib包,在该包中创建目标类TestDao,该类不需要实现任何接口。

package dynamic.cglib;

public class TestDao {
    public void save() {
        System.out.println("保存");
    }

    public void modify() {
        System.out.println("修改");
    }

    public void delete() {
        System.out.println("删除");
    }
}

2.创建代理类

    在dynamic.cglib包中,创建代理类CglibDynamicProxy,该类实现MethodInterceptor接口。

package dynamic.cglib;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import aspect.MyAspect;

public class CglibDynamicProxy implements MethodInterceptor {
    /**
     * 创建代理的方法,生成CGLIB代理对象
     * target目标对象,需要增强的对象
     * 返回目标对象的CGLIB代理对象
     */
    public Object createProxy(Object target) {
        //创建一个动态类对象,即增强类对象
        Enhancer enhancer = new Enhancer();
        //确定需要增强的类,设置其父类
        enhancer.setSuperclass(target.getClass());
        //确定代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor的方法
        enhancer.setCallback(this);
        //返回创建的代理对象
        return enhancer.create();
    }

    /**
     * intercept方法会在程序执行目标方法时被调用
     * proxy CGLIB根据指定父类生成的代理对象
     * method拦截方法
     * args拦截方法的参数数组
     * methodProxy方法的代理对象,用于执行父类的方法
     * 返回代理结果
     */

    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //创建一个切面
        MyAspect myAspect = new MyAspect();
        //前增强
        myAspect.check();
        myAspect.except();
        //目标方法执行,返回代理结果
        Object obj = methodProxy.invokeSuper(proxy, args);
        //后增强
        myAspect.log();
        myAspect.monitor();
        return obj;
    }
}

3.创建测试类

    在dynamic.cglib包中,创建测试类CglibDynamicTest。在主方法中创建代理对象和目标对象,然后从代理对象中获取对目标对象增强后的对象,最后调用该对象的添加、修改和删除方法。

package dynamic.cglib;

public class CglibDynamicTest {
    public static void main(String[] args) {
        //创建代理对象
        CglibDynamicProxy cdp = new CglibDynamicProxy();
        //创建目标对象
        TestDao testDao = new TestDao();
        //获取增强后的目标对象
        TestDao testDaoAdvice = (TestDao) cdp.createProxy(testDao);
        //执行方法
        testDaoAdvice.save();
        System.out.println("==============");
        testDaoAdvice.modify();
        System.out.println("==============");
        testDaoAdvice.delete();
    }
}

 4.3  基于代理类的AOP实现

    4.2节可知,在Spring中默认使用JDK动态代理实现AOP编程。使用org.springframework.aop.framework.ProxyFactoryBean创建代理是Spring AOP实现的最基本方式。

4.3.1.通知类型

    在讲解ProxyFactoryBean之前,先了解一下Spring的通知类型。根据Spring中通知在目标类方法的连接点位置,可以分为6种如下类型:

(1)环绕通知

    环绕通知(org.aopalliance.intercept.MethodInterceptor)是在目标方法执行前和执行后实施增强,可以应用于日志记录、事务处理等功能。

(2)前置通知

    前置通知(org.springframework.aop.MethodBeforeAdvice)是在目标方法执行前实施增强,可应用于权限管理等功能。

(3)后置返回通知

    后置返回通知(org.springframework.aop.AfterReturningAdvice)是在目标方法成功执行后实施增强,可应用于关闭流、删除临时文件等功能。

(4)后置(最终)通知

    后置通知(org.springframework.aop.AfterAdvice)是在目标方法执行后实施增强,与后置返回通知不同的是,不管是否发生异常都要执行该通知,可应用于释放资源。

(5)异常通知

    异常通知(org.springframework.aop.ThrowsAdvice)是在方法抛出异常后实施增强,可以应用于处理异常、记录日志等功能。

(6)引入通知

    引入通知(org.springframework.aop.IntroductionInterceptor)是在目标类中添加一些新的方法和属性,可以应用于修改目标类(增强类)。

4.3.2ProxyFactoryBean

    ProxyFactoryBeanorg.springframework.beans.factory.FactoryBean接口的实现类,FactoryBean负责实例化一个Bean实例,ProxyFactoryBean负责为其他Bean实例创建代理实例。

下面通过一个实现环绕通知的实例演示Spring使用ProxyFactoryBean创建AOP代理的过程。

1)导入相关JAR

    在核心JAR包基础上,需要再向SpringAOP的外部库目录下导入JARspring-aop-5.0.2.RELEASE.jaraopalliance-1.0.jaraopalliance-1.0.jarAOP联盟提供的规范包,可以通过地址“http://mvnrepository.com/artifact/aopalliance/aopalliance/1.0”下载。

导入aopalliance-1.0.jar方法:

在 pom.xml 中的依赖中添加以下代码:

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.10.RELEASE</version><!--在项目中使用的 Spring 版本-->
    </dependency>
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>

2)创建切面类

    由于该实例实现环绕通知,所以切面类需要实现org.aopalliance.intercept.MethodInterceptor接口。在java目录下,创建一个spring.proxyfactorybean包,并在该包中创建切面类MyAspect

package spring.proxyfactorybean;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * 切面类
 */
public class MyAspect implements MethodInterceptor{
    public Object invoke(MethodInvocation arg0) throws Throwable {
        //增强方法
        check();
        except();
        //执行目标方法
        Object obj = arg0.proceed();
        //增强方法
        log();
        monitor();
        return obj;
    }
    public void check() {
        System.out.println("模拟权限控制");
    }
    public void except() {
        System.out.println("模拟异常处理");
    }
    public void log() {
        System.out.println("模拟日志记录");
    }
    public void monitor() {
        System.out.println("性能监测");
    }
}

3)配置切面并指定代理

    切面类需要配置为Bean实例,Spring容器才能识别为切面对象。在resources包中,创建配置文件applicationContext.xml,并在文件中配置切面和指定代理对象。 

<?xml version="1.0" encoding="UTF-8"?>
<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="testDao" class="dao.TestDaoImpl"/>
    <!-- 创建一个切面 -->
    <bean id="myAspect" class="spring.proxyfactorybean.MyAspect"/>
    <!-- 使用Spring代理工厂定义一个名为testDaoProxy的代理对象 -->
    <bean id="testDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 指定代理实现的接口 -->
        <property name="proxyInterfaces" value="dao.TestDao"/>
        <!-- 指定目标对象 -->
        <property name="target" ref="testDao"/>
        <!-- 指定切面,植入环绕通知 -->
        <property name="interceptorNames" value="myAspect"/>
        <!-- 指定代理方式,true指定CGLIB动态代理;默认false,指定JDK动态代理 -->
        <property name="proxyTargetClass" value="true"/>
    </bean>
</beans>

4)创建测试类

    spring.proxyfactorybean包中,创建测试类ProxyFactoryBeanTest,在主方法中使用Spring容器获取代理对象,并执行目标方法。 

package spring.proxyfactorybean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import dao.TestDao;

public class ProxyFactoryBeanTest {
    private static ApplicationContext appCon;

    public static void main(String[] args) {
        appCon = new ClassPathXmlApplicationContext("resources/applicationContext.xml");
        //从容器中,获取增强后的目标对象
        TestDao testDaoAdvice = (TestDao) appCon.getBean("testDaoProxy");
        //执行方法
        testDaoAdvice.save();
        System.out.println("================");
        testDaoAdvice.modify();
        System.out.println("================");
        testDaoAdvice.delete();
    }
}

4.4  基于XML配置开发AspectJ

    AspectJ是一个基于Java语言的AOP框架。从Spring 2.0以后引入了AspectJ的支持。目前的Spring框架,建议开发者使用AspectJ实现Spring AOP。使用AspectJ实现Spring AOP的方式有两种:一是基于XML配置开发AspectJ,一是基于注解开发AspectJ。

    基于XML配置开发AspectJ是指通过XML配置文件定义切面、切入点及通知,所有这些定义都必须在<aop:config>元素内。

下面通过一个实例演示基于XML配置开发AspectJ的过程。

1.导入AspectJ框架相关的JAR

    需要再向SpringAOP应用导入JARspring-aspects-5.0.2.RELEASE.jaraspectjweaver-1.8.13.jar

    spring-aspects-5.0.2.RELEASE.jarSpringAspectJ提供的实现

    aspectjweaver-1.8.13.jarAspectJ框架所提供的规范包,可以通过地址“http://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.8.13”下载。

pom文件中增加如下代码:

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>


    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.13</version>
    </dependency>

2.创建切面类

    SpringAOP应用的java目录下,创建aspectj.xml包,在该包中创建切面类MyAspect,并在类中编写各种类型通知。

package aspectj.xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
 * 切面类,在此类中编写各种类型通知
 */
public class MyAspect {
	/**
	 * 前置通知,使用Joinpoint接口作为参数获得目标对象信息
	 */
	public void before(JoinPoint jp) {
		System.out.print("前置通知:模拟权限控制");
		System.out.println(",目标类对象:" + jp.getTarget() 
		+ ",被增强处理的方法:" + jp.getSignature().getName());
	} 
	/**
	 * 后置返回通知
	 */
	public void afterReturning(JoinPoint jp) {
		System.out.print("后置返回通知:" + "模拟删除临时文件");
		System.out.println(",被增强处理的方法:" + jp.getSignature().getName());
	}
	/**
	 * 环绕通知
	 * ProceedingJoinPoint是JoinPoint子接口,代表可以执行的目标方法
	 * 返回值类型必须是Object
	 * 必须一个参数是ProceedingJoinPoint类型
	 * 必须throws Throwable
	 */
	public Object around(ProceedingJoinPoint pjp) throws Throwable{
		//开始
		System.out.println("环绕开始:执行目标方法前,模拟开启事务");
		//执行当前目标方法
		Object obj = pjp.proceed();
		//结束
		System.out.println("环绕结束:执行目标方法后,模拟关闭事务");
		return obj;
	}
	/**
	 * 异常通知
	 */
	public void except(Throwable e) {
		System.out.println("异常通知:" + "程序执行异常" + e.getMessage());
	}
	/**
	 * 后置(最终)通知
	 */
	public void after() {
		System.out.println("最终通知:模拟释放资源");
	}
}

3.创建配置文件,并编写相关配置

    在java包中,创建配置文件applicationContext.xml,并<aop:config>元素及其子元素编写相关配置。

<?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:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 定义目标对象-->
    <bean id="testDao" class="dao.TestDaoImpl"/>
    <!-- 定义切面 -->
    <bean id="myAspect" class="aspectj.xml.MyAspect"/>
    <!-- AOP配置 -->
    <aop:config>
    	<!-- 配置切面 -->
    	<aop:aspect ref="myAspect">
    		<!-- 配置切入点,通知增强哪些方法 -->
    		<aop:pointcut expression="execution(* dao.*.*(..))" id="myPointCut"/>
    		<!-- 将通知与切入点关联 -->
    		<!-- 关联前置通知 -->
    		<aop:before method="before" pointcut-ref="myPointCut"/>
    		<!-- 关联后置返回通知,在目标方法成功执行后执行 -->
    		<aop:after-returning method="afterReturning" pointcut-ref="myPointCut"/>
    		<!-- 关联环绕通知 -->
    		<aop:around method="around" pointcut-ref="myPointCut"/>
    		<!-- 关联异常通知,没有异常发生时,将不会执行增强,throwing属性设置通知的第二个参数名称 -->
    		<aop:after-throwing method="except" pointcut-ref="myPointCut" throwing="e"/>
    		<!-- 关联后置(最终)通知,不管目标方法是否成功,都要执行 -->
    		<aop:after method="after" pointcut-ref="myPointCut"/>
    	</aop:aspect>
    </aop:config>
</beans>

4.创建测试类

    aspectj.xml包中,创建测试类XMLAspectJTest,在主方法中使用Spring容器获取代理对象,并执行目标方法。

package aspectj.xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import dao.TestDao;
public class XMLAspectJTest {
	private static ApplicationContext appCon;
	public static void main(String[] args) {
		appCon = new ClassPathXmlApplicationContext("/aspectj/xml/applicationContext.xml");
		//从容器中,获取增强后的目标对象
		TestDao testDaoAdvice = (TestDao)appCon.getBean("testDao");
		//执行方法
		testDaoAdvice.save();
	}
}

4.5  基于注解开发AspectJ 

    基于注解开发AspectJ要比基于XML配置开发AspectJ便捷许多,所以在实际开发中推荐使用注解方式。

下面通过一个实例讲解基于注解开发AspectJ的过程。 

1.创建切面类,并进行注解

    SpringAOP应用的java目录下,创建aspectj.annotation包,在该包中创建切面类MyAspect。在该类中,首先使用@Aspect注解定义一个切面类,由于该类在Spring中是作为组件使用的,所以还需要使用@Component注解。然后,使用@Pointcut注解切入点表达式,并通过定义方法来表示切入点名称。最后在每个通知方法上添加相应的注解,并将切入点名称作为参数传递给需要执行增强的通知方法。

package aspectj.annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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;
/**
 * 切面类,在此类中编写各种类型通知
 */
@Aspect//对应<aop:aspect ref="myAspect">
@Component//对应<bean id="myAspect" class="aspectj.xml.MyAspect"/>
public class MyAspect {
	/**
	 * 定义切入点
	 */
	@Pointcut("execution(* dao.*.*(..))")//也可以直接注解到通知方法上
	private void myPointCut() {
		//对应<aop:pointcut expression="execution(* dao.*.*(..))" id="myPointCut"/>
	}
	/**
	 * 前置通知,使用Joinpoint接口作为参数获得目标对象信息
	 */
	@Before("myPointCut()")//对应<aop:before method="before" pointcut-ref="myPointCut"/>
	//@Pointcut("execution(* dao.*.*(..))")
	public void before(JoinPoint jp) {
		System.out.print("前置通知:模拟权限控制");
		System.out.println(",目标类对象:" + jp.getTarget() 
		+ ",被增强处理的方法:" + jp.getSignature().getName());
	} 
	/**
	 * 后置返回通知
	 */
	@AfterReturning("myPointCut()")
	public void afterReturning(JoinPoint jp) {
		System.out.print("后置返回通知:" + "模拟删除临时文件");
		System.out.println(",被增强处理的方法:" + jp.getSignature().getName());
	}
	/**
	 * 环绕通知
	 * ProceedingJoinPoint是JoinPoint子接口,代表可以执行的目标方法
	 * 返回值类型必须是Object
	 * 必须一个参数是ProceedingJoinPoint类型
	 * 必须throws Throwable
	 */
	@Around("myPointCut()")
	public Object around(ProceedingJoinPoint pjp) throws Throwable{
		//开始
		System.out.println("环绕开始:执行目标方法前,模拟开启事务");
		//执行当前目标方法
		Object obj = pjp.proceed();
		//结束
		System.out.println("环绕结束:执行目标方法后,模拟关闭事务");
		return obj;
	}
	/**
	 * 异常通知
	 */
	@AfterThrowing(value="myPointCut()",throwing="e")
	public void except(Throwable e) {
		System.out.println("异常通知:" + "程序执行异常" + e.getMessage());
	}
	/**
	 * 后置(最终)通知
	 */
	@After("myPointCut()")
	public void after() {
		System.out.println("最终通知:模拟释放资源");
	}
}

2.注解目标类

    使用注解@Repository将目标类dynamic.jdk.TestDaoImpl注解为目标对象,注解代码如下:

    @Repository("testDao")

package dao;
import org.springframework.stereotype.Repository;
@Repository("testDao")
public class TestDaoImpl implements TestDao{
	@Override
	public void save() {
		//int n = 100/0;
		System.out.println("保存");
	}
	@Override
	public void modify() {
		System.out.println("修改");
	}
	@Override
	public void delete() {
		System.out.println("删除");
	}
}

3.创建配置文件

    resources包中,创建配置文件applicationContext.xml,并在配置文件中指定需要扫描的包,使注解生效。同时,需要启动基于注解的AspectJ支持。

<?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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
   <!-- 指定需要扫描的包,使注解生效 -->
   <context:component-scan base-package="aspectj.annotation"/>
   <context:component-scan base-package="dao"/>
   <!-- 启动基于注解的AspectJ支持 -->
   <aop:aspectj-autoproxy/>
</beans>

4.创建测试类

    测试类与运行结果与4.4相同,不再赘述。

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

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

相关文章

文件上传App,H5,小程序多端兼容

插件地址&#xff1a;https://ext.dcloud.net.cn/plugin?id5459 下载lsj-upload插件 代码如下 结构 <lsj-upload :option"option" :size"size" :formats"formats" :debug"debug":instantly"instantly" change"…

网络编程(现在不重要)

目录 网络编程三要素与InetAddress类的使用 软件架构 面临的主要问题 网络编程三要素&#xff08;对应三个问题&#xff09; InetAddress的使用 TCP与UDP协议剖析与TCP编程案例&#xff08;了解&#xff09; TCP协议 UDP协议 例子 UDP、URL网络编程 URL&#xff1a;&…

Android多线程:Handler runOnUiThread 异步消息处理机制

目录 一&#xff0c;Android中的多线程问题 1.模拟耗时工作 2.Android开启子线程 二&#xff0c;在子线程中更新UI 1.异步消息处理机制 Handler 2.使用runOnUiThread更新UI 一&#xff0c;Android中的多线程问题 Android用户界面是与用户交互的接口&#xff0c;对于用户的…

偏微分方程算法之混合边界差分

目录 一、研究对象 二、差分格式 2.1 向前欧拉格式 1. 中心差商 1.1.1 理论推导 1.1.2 算例实现 2. x0处向前差商&#xff0c;x1处向后差商 1.2.1 理论推导 1.2.2 算例实现 2.2 Crank-Nicolson格式 2.2.1 理论推导 2.2.2 算例实现 一、研究对象 这里我们以混合边界…

科技云报道:AI大模型疯长,存储扛住了吗?

科技云报道原创。 AI大模型正在倒逼数字基础设施产业加速升级。 过去一年半&#xff0c;AI大模型标志性的应用相继出现&#xff0c;从ChatGPT到Sora一次次刷新人们的认知。震撼的背后&#xff0c;是大模型参数指数级的增长。 这种数据暴涨的压力&#xff0c;快速传导到了大模…

Efficient Multimodal learning from data-centric perspective

[MLLM-小模型推荐-2024.3.18] Bunny 以数据的眼光看问题 - 知乎近期几天会梳理下多模态小模型相关的论文&#xff0c;做个汇总。为了能够每天更新点啥&#xff0c;先穿插一些小模型算法。等到全部算法都梳理完成后&#xff0c;再发布一篇最终汇总版本的。 3.15 号 BAAI 发布了 …

关于机器学习/深度学习的一些事-答知乎问(五)

嵌入学习方法在解决小样本学习问题时面临的挑战是什么&#xff1f; &#xff08;1&#xff09;过度依赖于辅助数据&#xff0c;预训练的模式违背了小样本学习的本质定义。几乎所有的嵌入学习方法都需要通过大量辅助样本来预训练特征嵌入函数&#xff0c;但在实际应用场景中&am…

葡萄书--图理论基础

图的定义 G{V,E} 节点和边的信息可以是类别型的&#xff0c;类别型数据的取值只能是哪一类别。一般称类别型的信息为标签。 节点和边的信息可以是数值型的&#xff0c;数值型数据的取值范围为实数。一般称数值型的信息为属性。 在图的计算任务中&#xff0c;我们认为&#x…

不消除存储瓶颈,AIGC就是梦幻泡影

大数据产业创新服务媒体 ——聚焦数据 改变商业 在大模型和AIGC的新纪元&#xff0c;我们正见证一个前所未有的技术革命。从更自然的人机对话&#xff0c;到图片、视频生成&#xff0c;AIGC技术正在彻底改变我们创造、学习和交流的方式。 然而&#xff0c;这一切进步的背后&am…

【数据结构|C语言版】顺序表应用

前言1. 基于动态顺序表实现通讯录1.1 通讯录功能1.2 代码实现1.2.1 SeqList.h1.2.2 SeqList.c1.2.3 Contact.h1.2.4 Contact.c1.2.5 test.c 1.3 控制台测试1.3.1 添加联系人1.3.2 删除联系人1.3.3 修改联系人1.3.4 查找联系人1.3.5 清空通讯录1.3.6 通讯录读档和存档 2. 好题测…

如何打开一个fbx模型

步骤 ① 下载fbx viewer &#xff0c;以下是管网链接 FBX Review | Cross-platform 3D model viewer | Autodesk ②需要用邮箱进行注册&#xff0b;确认&#xff0c;这一步完成之后 ③下载之后吧fbx文件导入到这个里面就可以了

NL2SQL进阶系列(4):ConvAI、DIN-SQL、C3-浙大、DAIL-SQL-阿里等16个业界开源应用实践详解[Text2SQL]

NL2SQL进阶系列(4)&#xff1a;ConvAI、DIN-SQL等16个业界开源应用实践详解[Text2SQL] NL2SQL基础系列(1)&#xff1a;业界顶尖排行榜、权威测评数据集及LLM大模型&#xff08;Spider vs BIRD&#xff09;全面对比优劣分析[Text2SQL、Text2DSL] NL2SQL基础系列(2)&#xff1a…

前端导出excel 接口处理和导出处理

如果按照一般的请求方式&#xff0c;接口会返回如下乱码 此时&#xff0c;接口其实已经请求成功了&#xff0c;只需要对乱码进行一下处理就行 1.请求方式处理 1.1 如果是直接使用axios进行请求 axios({method: get,url: url,params: params,//需要添加responseType: blob }…

采用分治法求含n个实数序列中的最大元素和次大元素(C语言)

目录 实验内容&#xff1a; 实验过程&#xff1a; 1.算法设计 2.程序清单 3.复杂度分析 4.运行结果 实验内容&#xff1a; 设计一个程序&#xff0c;采用分治法求含n个实数序列中的最大元素和次大元素&#xff0c;并分析算法的时间复杂度。 实验过程&#xff1a; 1.算法…

深度学习之PyTorch实现卷积神经网络(CNN)

在深度学习领域&#xff0c;卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;CNN&#xff09;是一种非常强大的模型&#xff0c;专门用于处理图像数据。CNN通过卷积操作和池化操作来提取图像中的特征&#xff0c;具有较好的特征学习能力&#xff0c;特别适用…

华为OD机试 - 连续天数的最高利润额(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

AI音乐,8大变现方式——Suno:音乐版的ChatGPT - 第505篇

悟纤之歌 这是利用AI为自己制作的一首歌&#xff0c;如果你也感兴趣&#xff0c;可以花点时间阅读下本篇文章。 ​ 导读 随着新一代AI音乐创作工具Suno V3、Stable audio2.0、天工SkyMusic的发布&#xff0c;大家玩自创音乐歌曲&#xff0c;玩的不亦乐乎。而有创业头脑的朋友…

一些实用的工具网站

200 css渐变底色 https://webgradients.com/ 200动画效果复制 https://css-loaders.com/classic/ 二次贝塞尔曲线 https://blogs.sitepointstatic.com/examples/tech/canvas-curves/bezier-curve.html 三次贝塞尔曲线 https://blogs.sitepointstatic.com/examples/tech/c…

百货商场用户画像描绘and价值分析(下)

目录 内容概述数据说明技术点主要内容4 会员用户画像和特征字段创造4.1 构建会员用户基本特征标签4.2 会员用户词云分析 5 会员用户细分和营销方案制定5.1 会员用户的聚类分析及可视化5.2 对会员用户进行精细划分并分析不同群体带来的价值差异 内容概述 本项目内容主要是基于P…

环形链表II

给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 …