Spring AOP

Spring AOP简介

什么是AOP

AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。
在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。
为了解决这一问题,AOP思想随之产生。AOP采用横向抽取机制,将分散在各处的重复代码提取出来,然后在程序编译或允许时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。
AOP的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多地关注于其他业务逻辑地实现,这不但提高了开发效率,而且增强了代码的可维护性。
目前最流行的AOP框架有两个,分别为Spring AOP和AspectJ。Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强的代码。AspectJ是一个基于Java语言的AOP框架,从Spring2.0开始,Spring AOP引入了对AspectJ的支持,AspectJ拓展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。

AOP术语

1、Aspect(切面):在实际应用中,切面通常是指封装的用于横向插入系统功能(如事务,日志等)类,该类要被Spring容器识别为切面,需要在配置文件中通过<bean>元素指定。
2、Joinpoint(连接点):在程序执行过程中的某个阶段点,它实际上是对象的一个操作,例如,方法的调用或异常的抛出。在Spring AOP中,连接点就是指方法的调用。
3、Pointcut(切入点):是指切面与程序流程的交叉点,即那些需要处理的连接点,通常在程序中,切入点指的是类或者方法名,如某个通知要应用到所有以add开头的方法中,那么所有满足这一规则的方法都是切入点。
4、Advice(通知/增强处理):AOP框架在特定的切入点执行的增强处理,即在定义好的切点处所要执行的程序代码。可以将其理解为切面类中的方法,它是切面的具体实现。
5、Target Object(目标对象):是指所有被通知的对象,也称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象。
6、Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。
7、Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。

动态代理

我们已经知道AOP中的代理就是由AOP框架动态生成的一个对象,该对象可以作为目标对象使用。Spring中的AOP代理,可以是JDK动态代理,也可以是CGLIB代理。

JDK动态代理

JDK动态代理是通过java.lang.reflect.Proxy类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。
1)创建一个chapter03的项目
2)创建一个jdk包,在该包下创建接口UserDao

package jdk;
public interface UserDao {
	public void addUser();
	public void deleteUser();
}

3)在包中,创建UserDao接口实现类UserDaoImpl

package jdk;
public class UserDaoImpl implements UserDao{
	public void addUser() {
		System.out.println("添加用户");	
	}
	public void deleteUser() {
		System.out.println("删除用户");
	}
}

本案例中会将实现类UserDaoImpl作为目标类,对其中的方法进行增强处理。
4)在包中创建切面类MyAspect,该类中定义一个模拟权限检查的方法和一个模拟记录日志的方法,这两个方法就表示切面中的通知。

package jdk;

public class MyAspect {
	public void check_Permissions(){
		System.out.println("模拟检查权限...");
	}
	public void log(){
		System.out.println("模拟记录日志...");
	}
}

5)在包中创建代理类JdkProxy,该类需要实现InvocationHandler接口,并编写代理方法。

package jdk;
import java.lang.reflect.*;
public class JdkProxy implements InvocationHandler{
	private UserDao userDao;
	public Object createProxy(UserDao userDao){
		this.userDao=userDao;
		ClassLoader classLoader=JdkProxy.class.getClassLoader();
		Class[] clazz=userDao.getClass().getInterfaces();
		return Proxy.newProxyInstance(classLoader, clazz, this);
	}
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		MyAspect myAspect=new MyAspect();
		myAspect.check_Permissions();
		Object obj=method.invoke(userDao, args);
		myAspect.log();
		return obj;
	}
}

JdkProxy类实现了InvocationHandler接口,并实现了接口中的invoke()方法,所有动态代理类所调用的方法都会交由该方法处理。在创建的代理方法createProxy()中,使用了Proxy类的newProxyInstance()方法来创建代理对象。newProxyInstance()方法中包含3个参数,其中第1个参数是当前类的类加载器,第2个参数表示的是被代理对象实现的所有接口,第3个参数this表示的就是代理类JdkProxy本身。在invoke()方法中,目标类方法执行的前后,会分别执行切面类中的check_Permissions()方法和log()方法。
6)在jdk包中,创建测试类JdkTest。在该类中的main()方法中创建代理对象和目标对象,然后从代理对象中获得对目标对象userDao增强后的对象,最后调用该对象中的添加和删除方法。

package jdk;

public class JdkTest {
	public static void main(String[]args){
		JdkProxy jdkProxy=new JdkProxy();
		UserDao userDao= new UserDaoImpl();
		UserDao userDao1=(UserDao)jdkProxy.createProxy(userDao);
		userDao1.addUser();
		userDao1.deleteUser();
	}
}

在这里插入图片描述

CGLIB代理

JDK动态代理的使用非常简单,但它还有一定的局限性——使用动态代理的对象必须实现一个或多个接口。如果要对没有实现接口的类进行代理,那么可以使用CGLB代理。
CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。在Spring的核心包中已经集中了CGLIB所需要的包,所以开发中不需要另外导入JAR包。
1)创建一个cglib包,创建一个目标类UserDao,UserDao不需要实现任何接口

package cglib;

public class UserDao {
	public void addUser(){
		System.out.println("添加用户");
	}
	public void deleteUser(){
		System.out.println("删除用户");
	}
}

2)包中,创建代理类CglibProxy,该代理类需要实现MethodInterceptor接口,并实现接口中的intercept()方法

package cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.*;
import jdk.MyAspect;
public class CglibProxy implements MethodInterceptor{
	public Object createProxy(Object target){
		Enhancer enhancer=new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}

	@Override
	public Object intercept(Object proxy, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		MyAspect myAspect=new MyAspect();
		myAspect.check_Permissions();
		Object obj=methodProxy.invokeSuper(proxy, args);
		myAspect.log();
		return obj;
	}
}

首先创建一个动态类对象Enhancer,它是CGLIB的核心类;然后调用了Enhancer类的setSuperclass()方法来确定目标对象;接下来调用了setCallback()方法添加回调函数,其中的this代表的就是代理类CglibProxy本身;最后通过return语句将创建的代理类对象返回。intercept()方法会在程序执行目标方法时被调用,方法运行时将会执行切面类中的增强方法。
3)包中创建测试类CglibTest

package cglib;

public class CglibTest {
	public static void main(String[]args){
		CglibProxy cglibProxy = new CglibProxy();
		UserDao userDao=new UserDao();
		UserDao userDao1=(UserDao)cglibProxy.createProxy(userDao);
		userDao1.addUser();
		userDao1.deleteUser();
	}
}

在这里插入图片描述
这种没有实现接口的代理方式,就是CGLIB代理。

基于代理类的AOP实现

通过学习,我们已经对Spring中的两种代理模式有了一定了解。实际上Spring中的AOP代理默认就是使用AOPJDK动态代理的方式来实现的。在Spring中,使用ProxyFactoryBean是创建AOP代理的最基本方式。

Spring的通知类型

Spring中的通知按照在目标类方法的连接点位置,可以分为以下5种
1、org.aopalliance.intercept.MethodInterceptor(环绕通知)
在目标方法执行前后实施增强,可以应用于日志、事务管理等功能。
2、org.springframework.aop.MethodBeforeAdvice(前置通知)
在目标方法执行前实施增强,可以应用于权限管理等功能。
3、org.springframework.aop.AfterReturningAdvice(后置通知)
在目标方法执行后实施增强,可以应用于关闭流、上传文件、删除临时文件等功能。
4、org.springframework.aop.ThrowsAdvice(异常通知)
在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能。
5、org.springframework.aop.IntroductionInterceptor(引介通知)
在目标类中添加一些新的方法和属性,可以应用于修改老版本程序(增强类)

ProxyFactoryBean

ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。在Spring中,使用ProxyFactoryBean是创建AOP代理的基本方式。

属性名称描述
target代理的目标对象
proxyInterfaces代理要实现的接口,如果是多个接口,可以使用<list>标签。
proxyTargetClass是否对类代理而不是接口,设置为true时,使用CGLIB代理。
interceptorNames需要织入目标的Advice
singleton返回的代理是否为单实例,默认为true(即返回单实例)
optimize当设置为true时,强制使用CGLIB

1)在核心JAR包的基础上,再向项目中导入spring-aop-4.3.6.RELEASE.jar和aopalliance-1.0.jar
spring-aop-4.3.6.RELEASE.jar:是Spring为AOP提供的实现包,Spring的包中已经提供
aopalliance-1.0.jar:是AOP联盟提供的规范包,该JAR包可以通过https://repo1.maven.org/maven2/aopalliance/aopalliance/1.0/aopalliance-1.0.jar下载
在这里插入图片描述
2)创建一个factorybean包,创建切面类MyAspect。

package factorybean;
import org.aopalliance.intercept.*;
public class MyAspect implements MethodInterceptor{

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		check_Permissions();
		Object obj=mi.proceed();
		log();
		return obj;
	}
	public void check_Permissions(){
		System.out.println("模拟检查权限...");
	}
	public void log(){
		System.out.println("模拟记录日志...");
	}
}

3)在包中,创建配置文件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="userDao" class="jdk.UserDaoImpl"/>
    <bean id="myAspect" class="factorybean.MyAspect"/>
    <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<property name="proxyInterfaces" value="jdk.UserDao"/>
    	<property name="target" ref="userDao"/>
    	<property name="interceptorNames" value="myAspect"/>
    	<property name="proxyTargetClass" value="true"/>
    </bean>
</beans>

4)在包中,创建测试类ProxyFactoryBeanTest

package factorybean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import jdk.UserDao;
public class ProxyFactoryBeanTest {
	public static void main(String[] args){
		String xmlPath="factorybean/applicationContext.xml";
		ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlPath);
		UserDao userDao=(UserDao)applicationContext.getBean("userDaoProxy");
		userDao.addUser();
		userDao.deleteUser();
	}
}

在这里插入图片描述

AspectJ开发

AspectJ是一个基于Java语言的AOP框架,它提供了强大的AOP功能。Spring2.0之后,Spring AOP也引入了对AspectJ的支持,并允许直接使用AspectJ进行编程,而Spring自身的AOP API也尽量与AspectJ保持一致。新版本的Spring框架,也建议使用AspectJ来开发AOP。
使用AspectJ实现AOP有两种方式:一种是基于XML的声明式AspectJ,另一种是基于注解的声明式AspectJ。

基于XML的声明式AspectJ

基于XML的声明式AspectJ是指通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在<aop:config>元素内。
Spring配置文件中的<beans>元素下可以包括多个<aop:config>元素,一个<aop:config>元素有可以配置多个属性和子元素,其子元素包括<aop:pointcut><aop:advisor><aop:aspect>。在配置时,这3个子元素必须按照此顺序来定义。在<aop:aspect>元素下,同样包含了属性和多个子元素,通过使用<aop:aspect>元素及其子元素可以在XML文件中配置切面、切入点和通知。
1、配置切面
在Spring配置文件中,配置切面使用的是<aop:aspect>元素,该元素会将一个已经定义好的Spring Bean转换为切面Bean,所有要在配置文件中先定义一个普通的Spring Bean。定义完成后通过<aop:aspect>的ref属性即可引用该Bean。

属性名称描述
id用于定义该切面的唯一标识符
ref用于引用普通的Spring Bean

2、配置切入点
在Spring配置文件中,切入点是通过<aop:pointcut>元素来定义的。当<aop:pointcut>作为<aop:config>子元素定义时,表示该切入点时全局切入点,它可被多个切面所共享。<aop:pointcut>作为<aop:aspect>子元素定义时,表示该切入点只对当前切面有效。

属性名称描述
id用于定义该切面的唯一标识符
expression用于指定切入点关联的切入点表达式

例如:expression(* com.ex.jdk.*.*(..))就是定义的切入点表达式,该切入点表达式的意思是匹配com.ex.jdk包中任意类的任意方法的执行。其中execution()是表达式的主体,第一个*表示的是返回类型,使用*表示所有类型;com.ex.jdk是所需拦截的包名,后面第2个*表示的是类名,使用*表示所有类;第三个*表示方法名,使用*表示所有方法;后面(…)表示方法的参数,其中…表示任意参数。需要注意的是,第一个*与包名之间有一个空格。

expression(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

modifiers-pattern:表示定义的目标方法的访问修饰符,如public、private等。
ret-type-pattern:表示定义的目标方法的返回值类型,如void、String等。
declaring-type-pattern:表示定义的目标方法的类路径
name-pattern:表示具体需要被代理的目标方法
param-pattern:表示需要被代理的目标方法包含的参数
throws-pattern:需要被代理的目标方法抛出的异常类型。
其中带?的部分表示看配置项;其他部分属于必须配置项。
3、配置通知
在配置代码中,分别使用<aop:aspect>的子元素配置了5种常用通知

属性名称描述
pointcut该属性用于指定一个切入点表达式,Spring将在匹配该表达式的连接点时织入该通知
pointcut-ref该属性指定一个已经存在的切入点名称。通常pointcut和pointcut-ref两个属性只需要使用其中一个。
method该属性指定一个方法名,指定将切面Bean中的该方法转换为增强处理。
throwing该属性只对<after-throwing>元素有效,它用于指定一个形参名,异常通知方法可以通过该形参访问目标方法所抛出的异常。
returning该属性只对<after-returning>元素有效,它用于指定一个形参名,后置通知方法可以通过该形参访问目标方法的返回值。

1)导入AspectJ框架相关JAR包
1、spring-aspects-4.3.6.RELEASE.jar:Spring为AspectJ提供了实现,Spring的包中已经提供。
2、aspectjweaver-1.8.10.jar:是AspectJ框架所提供的规范,可以通过https://repo1.maven.org/maven2/org/aspectj/aspectjweaver/1.8.10/aspectjweaver-1.8.10.jar下载在这里插入图片描述
2)创建一个aspectj包,创建切面类MyAspect

package aspectj;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
	public void myBefore(JoinPoint joinPoint){
		System.out.println("前置通知:模拟执行权限检查");
		System.out.println("目标类是:"+joinPoint.getTarget());
		System.out.println(",被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	public void myAfterReturning(JoinPoint joinPoint){
		System.out.println("后置通知:模拟记录日志...,");
		System.out.println(",被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
		System.out.println("环绕开始:执行目标方法之前,模拟开启事务...");
		Object obj=proceedingJoinPoint.proceed();
		System.out.println("环绕结束:执行目标方法之后,模拟关闭事务...");
		return obj;
	}
	public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
		System.out.println("异常处理:"+"出错了"+e.getMessage());
	}
	public void myAfter(){
		System.out.println("最终处理:模拟方法结束后的释放资源...");
	}
}

注:环绕通知必须接收一个类型为ProceedingJoinPoint类型的参数,返回值必须是Object,且必须抛出异常。异常通知中可以传入Throwable类型的参数来输出异常信息。
3)在包中,创建配置文件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"
    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="userDao" class="jdk.UserDao"/>
        <bean id="myAspect" class="aspectj.MyAspect"/>
        <aop:config>
        	<aop:aspect ref="myAspect">
        		<aop:pointcut expression="execution(* jdk.*.*(..))" id="myPointCut"/>
        		<aop:before method="myBefore" pointcut-ref="myPointCut"/>
        		<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut"/>
        		<aop:around method="myAround" pointcut-ref="myPointCut"/>
        		<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
        		<aop:after method="myAfter" pointcut-ref="myPointCut"/>
        	</aop:aspect>
        </aop:config>
</beans>

注:aop:after-returning配置的后置通知只有目标方法成功执行后才会被织入,而aop:after最终通知不论目标方法如何结束,都会被织入。
4)创建测试类TestXmlAspectj

package aspectj;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import jdk.UserDao;
public class TestXmlAspectj {
	public static void main(String[]args){
		String xmlPath="aspectj/applicationContext.xml";
		ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlPath);
		UserDao userDao=(UserDao)applicationContext.getBean("userDao");
		userDao.addUser();
		userDao.deleteUser();
	}
}

在这里插入图片描述
在这里插入图片描述

基于注解的声明式AspectJ

与基于代理类的AOP实现相比,基于XML的声明式ApectJ要便捷得多,但是它也存在着一些缺点,那就是要在Spring文件中配置大量的代码信息。为了解决这个问题,AspectJ框架为AOP的实现提供了一套注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿代码。

注解名称描述
@Aspect用于定义一个切面
@Pointcut用于定义切入点表达式。在使用时和还需定义一个包含名称和任意参数的方法签名来表示切入点名称。实际上,这个方法签名就是一个返回值为void,且方法体为空的普通方法。
@Before用于定义前置通知,相当于BeforeAdvice。在使用时,通常需要指定一个value属性值,该属性值用于指定一个切入点表达式(可以是已有的切入点,也可以直接定义切入点表达式)
@AfterReturning用于定义后置通知,相当于AfterReturnAdvice。在使用时可以指定pointcut/value和returning属性,其中pointcut/value这两个属性作用一样,都用于切入点表达式。returning属性值用于表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法的返回值。
@Around用于定义环绕通知,相当于MethodInterceptor。在使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点。
@AfterThrowing用于定义异常通知来处理程序中未处理的异常,相当于ThrowAdvice。在使用时可指定pointcut/value和throwing属性。其中pointcut/value这两个属性作用一样,都用于切入点表达式。throwing属性用于指定一个形参名来表示Advice方法中可定义于此同名的形参,该形参可用于访问目标方法抛出的异常。
@After用于定义最终final通知,不管是否异常,该通知都会执行。使用时需要指定一个value属性,该属性用于指定通知的切入点。
@DeclareParents用于定义引介通知,相当于IntroductionInterceptor

1)创建annotation包,创建切面类MyAspect

package annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
	@Pointcut("execution(* jdk.*.*(..))")
	private void myPointCut(){}
	@Before("myPointCut()")
	public void myBefore(JoinPoint joinPoint){
		System.out.print("前置通知:模拟执行权限检查");
		System.out.print(",目标类是:"+joinPoint.getTarget());
		System.out.println(",被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	@AfterReturning("myPointCut()")
	public void myAfterReturning(JoinPoint joinPoint){
		System.out.print("后置通知:模拟记录日志...");
		System.out.println(",被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	@Around("myPointCut()")
	public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
		System.out.println("环绕开始:执行目标方法之前,模拟开启事务...");
		Object obj=proceedingJoinPoint.proceed();
		System.out.println("环绕结束:执行目标方法之后,模拟关闭事务...");
		return obj;
	}
	@AfterThrowing(value="myPointCut()",throwing="e")
	public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
		System.out.println("异常处理:"+"出错了"+e.getMessage());
	}
	@After("myPointCut()")
	public void myAfter(){
		System.out.println("最终处理:模拟方法结束后的释放资源...");
	}
}

首先使用@Aspect注解定义了切面,由于该类在Spring中是作为组件使用的,所有还需要添加@Component注解才能生效。然后使用@Pointcut注解来配置切入点表达式,并通过定义方法来表示切入点名称。接下来在每个通知相应的方法上添加了相应的注解,将切入点名称作为参数传递给需要执行增强的通知方法。
2)在UserDaoImpl中,添加注解@Repository(“userDao”)
3)在包中,创建配置文件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"
    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="annotation"/>
        <context:component-scan base-package="jdk"/>
        <aop:aspectj-autoproxy/>
</beans>

引入context约束信息,然后使用<context>元素设置了扫描路径,是注解生效,使用<aop:aspectj-autoproxy>来启动Spring对基于注解的声明式AspectJ的支持。
base-package属性可以使用统配符*,但是不能仅单独一个*表示扫描所有包,这样会报错。
4)创建测试类TestAnnotation

package annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import jdk.UserDao;
public class TestAnnotation {
	public static void main(String[]args){
		String xmlPath="annotation/applicationContext.xml";
		ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlPath);
		UserDao userDao=(UserDao)applicationContext.getBean("userDao");
		userDao.addUser();
		userDao.deleteUser();
	}
}

在这里插入图片描述
注:如果在同一连接点有多个通知需要执行,那么同一切面中,通知的执行顺序是未知的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值