Spring 框架的一个关键组件是面向方面的编程(AOP)框架。面向方面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样的常见的很好的方面的例子,如日志记录、审计、声明式事务、安全性和缓存等。
AOP实现原理:Spring AOP是基于动态代理机制实现的,通过动态代理机制生成目标对象的代理对象,当外部调用目标对象的相关方法时,Spring注入的其实是代理对象Proxy,通过调用代理对象的方法执行AOP增强处理,然后回调目标对象的方法。
AOP中的相关概念
Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
Pointcut(切点):表示所有连接点中的实际要执行的 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
Target(目标对象):织入 Advice 的目标对象.。
Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。
AOP有两种实现方式:
1.基于xml配置实现 2通过注解方式实现
方式一:基于xml配置实现
引入依赖的jar包:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency>
编写目标类:
public class Count { public void add() { System.out.println("add ..."); } }
编写增强类和增强方法:
import org.aspectj.lang.ProceedingJoinPoint; public class CountProxy { public void beforeCall(){ System.out.println("前置增强。。。"); } public void afterCall(){ System.out.println("后置增强。。。"); } public void aroundCall(ProceedingJoinPoint point) throws Throwable { System.out.println("环形增强,执行方法前。。。"); long start = System.currentTimeMillis(); point.proceed(); long end=System.currentTimeMillis(); System.out.println("环形增强,执行方法后。。。"); System.out.println("方法耗时:"+(end-start)+"ms"); } }
xml配置文件中引入aop约束,并配置切入点和切面:
<?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="count" class="com.aopxml.Count"/> <bean id="countProxy" class="com.aopxml.CountProxy"/> <!-- 配置AOP的操作 --> <aop:config> <!-- 配置切入点,对类Count里面的所有方法都增强 --> <aop:pointcut expression="execution(* com.aopxml.Count.*(..))" id="pointcut1"></aop:pointcut> <!-- 配置切面 aop:aspect标签里面使用属性ref,ref属性值写增强类的bean的id值 --> <aop:aspect ref="countProxy"> <!-- 增强类型 method属性:增强类的方法名称 pointcut-ref属性:切入点的id值 --> <!-- 前置通知 --> <aop:before method="beforeCall" pointcut-ref="pointcut1"></aop:before> <!-- 后置通知 --> <aop:after method="afterCall" pointcut-ref="pointcut1"></aop:after> <!-- 环形通知 --> <aop:around method="aroundCall" pointcut-ref="pointcut1"></aop:around> </aop:aspect> </aop:config> </beans>
编写测试类:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("bean3.xml"); Count count= (Count) context.getBean("count"); count.add(); } }
运行结果:
前置增强。。。
环形增强,执行方法前。。。
add ...
环形增强,执行方法后。。。
方法耗时:45ms
后置增强。。。
方式二:基于 @AspectJ注解的实现方式
导入依赖jar包:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.13</version> </dependency>
配置xml文件开启@Aspectj支持,需要引入aop约束:
<?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:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 启动@AspectJ支持,自动搜索切面类 --> <aop:aspectj-autoproxy/> <!-- component-scan只能识别@Compent、@Controller、@Service、@Repository注解--> <context:component-scan base-package="com.aopanno"> <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect" /> </context:component-scan> </beans>
如果不打算使用XML Schema的配置方式,则应该在Spring配置文件中增加如下片段来启用@AspectJ支持(即上面的<aop:aspectj-autoproxy />和下面创建Bean的方式选择一种即可启用@AspectJ支持):
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />
编写目标类:
public @interface MyTransaction { }
import org.springframework.stereotype.Component; @Component public class Count { public void add() { System.out.println("add ..."); } @MyTransaction//自定义注解 public void delete(){ System.out.println("delete ..."); } }
编写增强类:
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class CountProxy { @Before(value = "execution(* com.aopanno.Count.add(..))") public void beforeCall(){ System.out.println("注解方式add方法前置增强。。。"); } @After("execution(* com.aopanno.Count.add(..))") public void afterCall(){ System.out.println("注解方式add方法后置增强。。。"); } @Around("execution(* com.aopanno.Count.add(..))") public void aroundCall(ProceedingJoinPoint point) throws Throwable { System.out.println("注解方式环形增强,执行add方法前。。。"); long start = System.currentTimeMillis(); point.proceed(); long end=System.currentTimeMillis(); System.out.println("注解方式环形增强,执行add方法后。。。"); System.out.println("方法耗时:"+(end-start)+"ms"); } @Around("@annotation(MyTransaction)")//通过自定义注解标签匹配 public void myAnnoAdvice(ProceedingJoinPoint point) throws Throwable { System.out.println("自定义注解方式环形增强,执行delete方法前。。。"); point.proceed(); System.out.println("自定义注解方式环形增强,执行delete方法后。。。"); } }
编写测试类:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("bean4.xml"); //根据类型获取bean对象 Count count= context.getBean(Count.class); count.add();
count.delete(); } }
运行结果:
注解方式环形增强,执行add方法前。。。
注解方式add方法前置增强。。。
add ...
注解方式环形增强,执行add方法后。。。
方法耗时:48ms
注解方式add方法后置增强。。。
自定义注解方式环形增强,执行delete方法前。。。
delete ...
自定义注解方式环形增强,执行delete方法后。。。