Spring AOP面向切面
Spring AOP
- Spring AOP -Aspect Oriented Programming 面向切面编程
- AOP的做法是将 普通、与业务无关 的功能抽象封装为 切面类
- 切面 可 配置 在 目标方法 的执行前、执行后,真正做到即插即用
AOP概念
Spring AOP与AspectJ的关系
- Eclipse AspectJ,一种基于Java平台的面向切面编程的语言
- Spring AOP使用AspectJWeaver实现类与方法匹配
- Spring AOP利用代理模式实现对象运行时功能扩展
关键概念
AOP配置过程
- 依赖AspectJ
<!--Aspectjweaver是Spring AOP的底层依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
- 实现切面类/方法
public class MethodAspect {
//切面方法,用于扩展额外功能
//JoinPoint 连接点,通过连接点可以获取目标类/方法信息
public void printExecutionTime(JoinPoint joinPoint) {
SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm:ss SSS");
String now = sdf.format(new Date());
String className = joinPoint.getTarget().getClass().getName(); //获取目标类的名称
String methodName = joinPoint.getSignature().getName(); //获取目标方法名称
System.out.println("----->" + now + ":" + className + "." + methodName);
}
}
- 配置Aspect Bean
<bean id="methodAspect" class="com.hua.spring.aop.aspect.MethodAspect"></bean>
- 定义切点(PointCut)
- 配置通知(Adivce)
<aop:config>
<aop:pointcut id="pointcut" expression="execution(public * com.hua..*.*(..))"></aop:pointcut>
<!-- 定义切面类 -->
<aop:aspect ref="methodAspect">
<!-- before通知(Advice),代表在目标方法运行前先执行methodAspect.printExecutionTime() -->
<aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
JoinPoint对象
注解 | 说明 |
---|---|
Object getTarget() |
获取IoC容器内目标对象 |
Signature getSignature() |
获取目标方法 |
Object[] getArgs() |
获取目标方法参数 |
PointCut切点表达式
AOP通知
特殊的“通知” -引介增强
- 引介增强(IntroductionInterceptor)是对类的增强,而非方法
- 允许在运行时为目标类增加新属性或方法
- 允许在运行时改变类的行为,让类随运行环境动态变更
环绕通知
**ProceedingJoinPoint **是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
基于注解开发AOP
@Component //标记当前类为IoC组件
@Aspect //说明当前类是切面类
public class MethodCheker {
//环绕通知(切点表达式)
@Around("execution(* com.hua..*Service.*(..))")
public Object check(ProceedingJoinPoint pjp) throws Throwable {
try {
Long startTime = new Date().getTime();
Object proceed = pjp.proceed();
Long endTime = new Date().getTime();
Long duration = endTime - startTime;
if (duration >= 1000) {
String className = pjp.getTarget().getClass().getName();
String methodName = pjp.getSignature().getName();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String now = sdf.format(new Date());
System.out.println("---->" + now + ":" + className + "." + methodName + "(" + duration + "ms)<----");
}
return proceed;
} catch (Throwable e) {
throw e;
}
}
}
AOP代理模式应用
Spring基于代理模式实现功能动态扩展,包含两种形式:
- 目标拥有接口,通过JDK动态代理实现功能扩展
- 目标没有接口,通过CGLib组件实现功能扩展
代理模式-静态代理
- 代理模式通过代理对象对原对象实现功能扩展
代理模式-JDK动态代理
- 根据接口动态的生成代理类
- 必须实现接口才可以运行
/**
* InvocationHandler是JDK提供的反射类,用于在JDK动态代理中对目标方法进行增强
* InvocationHandler实现类与切面类的环绕通知类似
*/
public class ProxyInvocationHandler implements InvocationHandler {
private Object target; //目标对象
public ProxyInvocationHandler(Object target) {
this.target = target;
}
/**
* 在 invoke() 方法对目标方法进行增强
*
* @param proxy 代理类对象
* @param method 目标方法
* @param args 目标方法参数
* @return 目标方法实参
* @throws Throwable 目标方法抛出异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) + "----");
Object ret = method.invoke(target, args);//调用目标方法
return ret;
}
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler(userService);
// 动态创建代理类
UserService userServiceProxy = (UserService)Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
invocationHandler);
userServiceProxy.createUser();
}
}
CGLib实现代理类
- CGLib是运行时字节码增强技术
- Spring AOP扩展无接口类使用CGLib
- AOP会运行时生成目标继承类字节码的方式进行行为扩展