目录
序
本文主要讲解,AOP的几种不同实现方式,以及几种实现方式之间的关联,最后在补充AOP实现机制和原理
AOP术语,知识点
切点 Poincut
指具体的切口 , 譬如指定的具体包、层级、类、方法 execution(* cn.burcher.service.UserServiceImpl.*(..));
或者是 指定的注解@annotation(log)
通知 Advice
切面的具体实现(增强),通知有五中不同的形式,决定了在不同的时间段做具体的实现,又或者说增强
切面 Aspect
通知和切点的结合,通知和切点共同定义了切面的全部内容,它是干什么的,什么时候在哪执行
织入(Weaving)
把切面加入程序代码的过程。切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里有多个点可以进行织入:
- 编译期:切面在目标类编译时被织入,这种方式需要特殊的编译器
- 类加载期:切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码
- 运行期:切面在应用运行的某个时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP就是以这种方式织入切面的。
实现方式
通用环境
业务接口
package cn.burcher.service;
public interface UserService {
void add();
void delete();
void update();
void query();
}
业务实现类
package cn.burcher.service;
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("添加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
}
1.使用spring提供的接口
前置通知:MethodBeforeAdvice
后置通知:AfterReturningAdvice
前置通知
package cn.burcher.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class LogBefore implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getSimpleName()+"的"+method.getName()+"方法执行了");
}
}
后置通知
package cn.burcher.log;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class LogAfter implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getSimpleName()+"的"+method.getName()+"方法执行了");
}
}
在beans.xml注册(注意:使用aop需要导入配置)
<bean id="logBefore" class="cn.burcher.log.LogBefore"/>
<bean id="logAfter" class="cn.burcher.log.LogAfter"/>
<bean id="userService" class="cn.burcher.service.UserServiceImpl"/>
<!--aop配置类-->
<aop:config>
<!--定义一个切入点-->
<aop:pointcut id="point" expression="execution(* cn.burcher.service.UserServiceImpl.*(..))"/>
<!--对切入点 执行 -->
<aop:advisor advice-ref="logAfter" pointcut-ref="point" />
<aop:advisor advice-ref="logBefore" pointcut-ref="point" />
</aop:config>
结果测试
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
结果:
UserServiceImpl的add方法执行了
添加了一个用户
UserServiceImpl的add方法执行了
2.使用自定义切面类
编写自定义切面类
package cn.burcher.pointcut;
public class Log {
public void before(){
System.out.println("方法执行前");
}
public void after(){
System.out.println("方法执行后");
}
}
在beans.xml中配置
<bean id="log" class="cn.burcher.pointcut.Log"/>
<aop:config>
<aop:pointcut id="point" expression="execution(* cn.burcher.service.UserServiceImpl.*(..))"/>
<aop:aspect ref="log">
<aop:after method="after" pointcut-ref="point"/>
<aop:before method="before" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
测试类不变,再次执行
结果:
方法执行前
添加了一个用户
方法执行后
3.使用注解实现切面
@Aspect 相当于aop:config
@Before相当于aop:before
package cn.burcher.pointcut;
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;
@Aspect
public class AnnotationPointcut {
@Before("execution(* cn.burcher.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("========方法执行前=========");
}
@After("execution(* cn.burcher.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("========方法执行后=========");
}
@Around("execution(* cn.burcher.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("---------环绕前---------");
pjp.proceed();
System.out.println("---------环绕后---------");
}
}
测试类不改,再次执行
---------环绕前---------
========方法执行前=========
添加了一个用户
========方法执行后=========
---------环绕后---------
4.注解 + aop 实现动态切面
通过上面的几个例子,都简单的知道了Aop的实现方式,但是上面三种也有缺陷,都是通过固定的层级来做切面,没有实现我们真正想要的,通过加注解动态实现aop,下面展示如何通过注解+aop实现动态配置
声明切面注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
String value() default "";
}
配置切面,声明注解为连接点,而不是具体的包层级
@Aspect
@Component
public class AnnotationAop {
@Pointcut(value = "@annotation(log)", argNames = "log")
public void pointcut(Log log) {
}
@Around(value = "pointcut(log)", argNames = "joinPoint,log")
public Object around(ProceedingJoinPoint joinPoint, Log log) throws Throwable {
try {
System.out.println(log.value());
System.out.println("around");
return joinPoint.proceed();
} catch (Throwable throwable) {
throw throwable;
} finally {
System.out.println("around");
}
}
}
@Before("@annotation(com.jiuxian.annotation.Log)")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Log log = method.getAnnotation(Log.class);
System.out.println("注解式拦截 " + log.value());
}
Service 方法实现测试
public interface UserService {
String save(String user);
void testAnnotationAop();
}
@Service
public class UserServiceImpl implements UserService {
@Override
public String save(String user) {
System.out.println("保存用户信息");
if ("a".equals(user)) {
throw new RuntimeException();
}
return user;
}
@Log(value = "test")
@Override
public void testAnnotationAop() {
System.out.println("testAnnotationAop");
}
}
参考文章 :
版权声明:本文为qq_39355721原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。