Spring开发核心之AOP的实现与切入点持久化
什么是AOP
将一个大的功能划分为小的功能单元,然后将这些小的功能进行组合,就构成了一个完整的大功能。在划分功能单元的时候,要考虑到它们的通用性。这种技术称为模块化设计,也称为面向切面编程(AOP)
AOP的实现
Spring中AOP的实现主要是通过动态代理的方式来实现的。Spring可以为普通的类以及接口生成动态代理,以增强类的功能。
代理样例
以下面代码为例:
package com.example.demo;
public interface TestService {
void doSomething();
}
完成代理:
package com.example.demo;
public class TestServiceProxy implements InvocationHandler {
private Object target;
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行前");
Object result = method.invoke(target, args);
System.out.println("方法执行后");
return result;
}
}
在这个代理中,我们使用了代理对象和被代理的原始对象。利用这种方式,增强了原始对象的功能。
Spring AOP实现
Spring中使用AOP代理主要有两种方式:
- 基于proxy:使用JDK自带的Proxy类实现代理,这种方式只能代理接口,不能代理类;
- 基于aspect:基于Aspectj框架实现织入,可以代理接口和类。
使用proxy示例:
在Spring AOP中,我们可以通过实现Interceptor接口来实现增强方法,Interceptor有两个主要的执行方法——preHandle和afterCompletion。
假设我们有一个类:
@Service
public class TestService {
public void doSomething() {
System.out.println("do something!");
}
}
为这个类添加一个拦截器:
@Aspect
@Component
public class TestAspect {
@Pointcut("execution(* com.example.demo.TestService.*(..))")
public void testMethod() {}
@Before("testMethod()")
public void doBefore() {
System.out.println("方法执行前...");
}
@After("testMethod()")
public void doAfter() {
System.out.println("方法执行后...");
}
}
在这个例子中,我们使用了@Aspect和@Pointcut两个注解。@Aspect表示这个类为切面类,@Pointcut表示定义一个切入点,拦截TestService类的所有方法。
使用aspect示例:
定义切面类:
@Aspect
@Component
public class AspectTest {
@Pointcut("execution(* com.example.demo.Test.*(..))")
public void testMethod() {}
@Around("testMethod()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("方法执行前...");
Object result = pjp.proceed();
System.out.println("方法执行后...");
return result;
}
}
当我们使用基于aspect的方式时,我们需要定义切面类,并且使用@Aspect注解,@Pointcut注解定义需要织入的切入点,@Around注解则定义需要增强的方法。
AOP切入点持久化
在AOP中,我们经常需要使用切入点。但是,如果我们有多个切面类,而切入点又是一样的,这就会带来不必要的重复工作。所以,我们可以将一些切入点持久化,方便以后的使用。
Spring提供了两种方式来实现切入点的持久化:使用@Aspect标记或是在XML配置文件中进行定义。
@Aspect标记
- 定义切面接口:
package com.example.demo.advice;
public interface ICommonAdvice {
}
- 定义基础切面类,需要实现切面接口:
package com.example.demo.advice;
@Aspect
@Component
public abstract class BasicAdvice implements ICommonAdvice {
}
使用@Aspect标记这个类,加上@Component注解,表示这是一个切面类,并且需要实现切面接口。
- 在子切面类中继承并声明切入点:
package com.example.demo.advice;
@Aspect
@Component
public class TestAdvice extends BasicAdvice {
@Pointcut("execution(* com.example.demo.Test.*(..))")
public void testMethod() {}
@Around("testMethod()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("方法执行前...");
Object result = pjp.proceed();
System.out.println("方法执行后...");
return result;
}
}
在子切面类中,我们可以继承父切面类,并声明自己的切入点。
XML配置
在XML配置文件中进行定义
- 在XML配置文件中引入AOP的命名空间
<bean xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
- 定义切入点:
<aop:pointcut expression="execution(* com.example.demo.Test.*(..))" id="testMethod" />
- 定义增强方法:
<aop:aspect ref="testAspect">
<aop:around method="doAround" pointcut-ref="testMethod" />
</aop:aspect>
在这个例子中,我们定义了一个切入点,拦截Test类的所有方法。同时,我们定义了在Test类中应用切面的类TestAspect。我们使用
总结
本文介绍了AOP的实现方式及切入点的持久化方式。AOP实现主要基于动态代理,Spring中实现AOP一般有两种方式:使用@Aspect标记或是在XML文件中进行定义。切入点的持久化可以避免重复定义相同的切入点,减轻工作量,提高效率。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring开发核心之AOP的实现与切入点持久化 - Python技术站