SpringBoot通过⾃定义注解实现AOP切⾯编程实例
⼀直⼼⼼念的想写⼀篇关于AOP切⾯实例的博⽂,拖更了许久之后,今天终于着⼿下笔将其完成。
基础概念
1、切⾯(Aspect)
⾸先要理解‘切’字,需要把对象想象成⼀个⽴⽅体,传统的⾯向对象变成思维,类定义完成之后(封装)。每次实例化⼀个对象,对类定义中的成员变量赋值,就相当于对这个⽴⽅体进⾏了⼀个定义,定义完成之后,那个对象就在那⾥,不卑不亢,不悲不喜,等着被使⽤,等着被回收。
⾯向切⾯编程则是指,对于⼀个我们已经封装好的类,我们可以在编译期间或在运⾏期间,对其进⾏切割,把⽴⽅体切开,在原有的⽅法⾥⾯添加(织⼊)⼀些新的代码,对原有的⽅法代码进⾏⼀次增强处理。⽽那些增强部分的代码,就被称之为切⾯,如下⾯代码实例中的通⽤⽇志处理代码,常见的还有事务处理、权限认证等等。
2、切⼊点(PointCut)
要对哪些类中的哪些⽅法进⾏增强,进⾏切割,指的是被增强的⽅法。即要切哪些东西。
3、连接点(JoinPoint)
我们知道了要切哪些⽅法后,剩下的就是什么时候切,在原⽅法的哪⼀个执⾏阶段加⼊增加代码,这个就是连接点。如⽅法调⽤前,⽅法调⽤后,发⽣异常时等等。
4、通知(Advice)
通知被织⼊⽅法,改如何被增强。定义切⾯的具体实现。那么这⾥⾯就涉及到⼀个问题,空间(切哪⾥)和时间(什么时候切,在何时加⼊增加代码),空间我们已经知道了就是切⼊点中定义的⽅法,⽽什么时候切,则是连接点的概念,如下⾯实例中,通⽤⽇志处理(切⾯),@Pointcut规则中指明的⽅法即为切⼊点,@Before、@After是连接点,⽽下⾯的代码就是对应通知。
@Before("cutMethod()")
public void begin() {
System.out.println("==@Before== lingyejun blog logger : begin");
}
5、⽬标对象(Target Object)
被⼀个或多个切⾯所通知的对象,即为⽬标对象。
6、AOP代理对象(AOP Proxy Object)
AOP代理是AOP框架所⽣成的对象,该对象是⽬标对象的代理对象。代理对象能够在⽬标对象的基础上,在相应的连接点上调⽤通知。
7、织⼊(Weaving)
将切⾯切⼊到⽬标⽅法之中,使⽬标⽅法得到增强的过程被称之为织⼊。
实例代码
相关依赖包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.8.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
定义和实现⽇志切⾯
package com.lingyejun.annotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.flect.MethodSignature;
import t.annotation.Lazy;
import org.springframework.stereotype.Component;
import flect.Method;
/**
* @Author: Lingye
* @Date: 2018/11/11
* @Describe:
* 定义⽇志切⾯
* @Lazy 注解:容器⼀般都会在启动的时候实例化所有单实例 bean,如果我们想要 Spring 在启动的时候延迟加载 bean,需要⽤到这个注解
* value为true、false 默认为true,即延迟加载,@Lazy(false)表⽰对象会在初始化的时候创建
*
* @Modified By:
*/
@Aspect
@Component
@Lazy(false)
public class LoggerAspect {
/**
* 定义切⼊点:对要拦截的⽅法进⾏定义与限制,如包、类
*
* 1、execution(public * *(..)) 任意的公共⽅法
* 2、execution(* set*(..))以set开头的所有的⽅法
* 3、execution(* com.lingyejun.annotation.LoggerApply.*(..))com.lingyejun.annotation.LoggerApply这个类⾥的所有的⽅法
* 4、execution(* com.lingyejun.annotation.*.*(..))com.lingyejun.annotation包下的所有的类的所有的⽅法
* 5、execution(* com.lingyejun.annotation..*.*(..))com.lingyejun.annotation包及⼦包下所有的类的所有的⽅法
* 6、execution(* com.lingyejun.annotation..*.*(String,?,Long)) com.lingyejun.annotation包及⼦包下所有的类的有三个参数,第⼀个参数为String类型,第⼆个参数为任意类型,第三个参数为Long类型的⽅法 * 7、execution(@annotation(com.lingyejun.annotation.Lingyejun))
*/
@Pointcut("@annotation(com.lingyejun.annotation.Lingyejun)")
private void cutMethod() {
}
/**
* 前置通知:在⽬标⽅法执⾏前调⽤
*/
@Before("cutMethod()")
public void begin() {
System.out.println("==@Before== lingyejun blog logger : begin");
}
/**
* 后置通知:在⽬标⽅法执⾏后调⽤,若⽬标⽅法出现异常,则不执⾏
*/
@AfterReturning("cutMethod()")
public void afterReturning() {
System.out.println("==@AfterReturning== lingyejun blog logger : after returning");
}
/**
* 后置/最终通知:⽆论⽬标⽅法在执⾏过程中出现⼀场都会在它之后调⽤
*/
@After("cutMethod()")
public void after() {
System.out.println("==@After== lingyejun blog logger : finally returning");
}
/**
* 异常通知:⽬标⽅法抛出异常时执⾏
*/
@AfterThrowing("cutMethod()")
public void afterThrowing() {
System.out.println("==@AfterThrowing== lingyejun blog logger : after throwing");
}
/**
* 环绕通知:灵活⾃由的在⽬标⽅法中切⼊代码
*/
@Around("cutMethod()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取⽬标⽅法的名称
String methodName = Signature().getName();
/
/ 获取⽅法传⼊参数
Object[] params = Args();
Lingyejun lingyejun = getDeclaredAnnotation(joinPoint);
System.out.println("==@Around== lingyejun blog logger --》 method name " + methodName + " args " + params[0]);
// 执⾏源⽅法
joinPoint.proceed();
// 模拟进⾏验证
if (params != null && params.length > 0 && params[0].equals("Blog Home")) {
System.out.println("==@Around== lingyejun blog logger --》 " + dule() + " auth success");
} else {
System.out.println("==@Around== lingyejun blog logger --》 " + dule() + " auth failed");
}
}
/**
* 获取⽅法中声明的注解
*
* @param joinPoint
* @return
* @throws NoSuchMethodException
*/
public Lingyejun getDeclaredAnnotation(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
// 获取⽅法名
String methodName = Signature().getName();
// 反射获取⽬标类
Class<?> targetClass = Target().getClass();
// 拿到⽅法对应的参数类型
Class<?>[] parameterTypes = ((MethodSignature) Signature()).getParameterTypes();
// 根据类、⽅法、参数类型(重载)获取到⽅法的具体信息
Method objMethod = Method(methodName, parameterTypes);
// 拿到⽅法定义的注解信息
Lingyejun annotation = DeclaredAnnotation(Lingyejun.class);
// 返回
return annotation;
}
}
⾃定义⼀个注解
package com.lingyejun.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author: Lingye
* @Date: 2018/11/11
* @Describe:
* @Modified By:
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Lingyejun {
spring是什么意思怎么念/**
* 何种场景下的通⽤⽇志打印
*
* @return
*/
String module();
}
调⽤切⾯类
package com.lingyejun.annotation;
import org.springframework.stereotype.Component;
/**
* @Author: Lingye
* @Date: 2018/11/11
* @Describe:
* @Modified By:
*/
@Component
public class LoggerApply {
@Lingyejun(module = "wwwblogs/lingyejun/")
public void lingLogger(String event) throws Exception {
System.out.println("lingLogger(String event) : lingyejun will auth by blog address"); throw new Exception();
}
}
测试代码
package com.lingyejun.annotation;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.st.SpringApplicationConfiguration;
import st.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class AnnotationTest {
@Autowired
private LoggerApply loggerApply;
@Test
public void testAnnotationLogger() {
try {
loggerApply.lingLogger("Blog Home");
} catch (Exception e) {
System.out.println("a exception be there");
}
}
}
效果展⽰
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论