结构图
代码
接口实现
首先定义一个接口,有两个类来实现它
public interface Buy {
public void Play(String info);
}
实现类
分别实现两个类,boybuy和girlbuy
@Component
public class BoyBuy implements Buy {
@Override
public void Play(String info) {
System.out.println(info);
}
}
@Component
public class GirlBuy implements Buy {
@Override
public void Play(String info) {
System.out.println(info);
}
}
config
这部分代码主要用来:1.扫描指定位置的component组件并且将其加入spring容器中
//扫描com.example.demo包下的所有组件,并将这些组件添加到spring容器中。
@ComponentScan(basePackages = {"com.example.demo"})
@ComponentScan(basePackageClasses = com.example.demo.service.Buy.class)
//告诉Spring Boot这是一个配置类。
@Configuration
//spring要在类上加上@EnableAspectJAutoProxy注解开启AOP的使用
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class config {
}
启动部分
创建一个类,执行两个类的buy方法
public class Test {
public static void main(String[] args) {
//加载配置
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(config.class);
BoyBuy boy = context.getBean(BoyBuy.class);
GirlBuy girl = context.getBean(GirlBuy.class);
girl.Play("around info ");
}
}
启动后是这样的
切面
注解含义
@Aspect:切面。表示一个横切进业务的一个对象。它里面包含切入点(Pointcut)和Advice(通知)。
需要在切面类上方定义,就像@component一样,说明下面我要开始写一些切入方法了。
@Pointcut:切入点。表示需要切入的位置,比如某些类或者某些方法,也就是先定一个范围。
指定符合要求的方法作为被切入方法。
对于被指定的方法,可以用以下不同的注解,进行不同位置的注入。
@Before:Advice(通知)的一种,切入点的方法体执行之前执行。
@Around:Advice(通知)的一种,环绕切入点执行也就是把切入点包裹起来执行。
@After:Advice(通知)的一种,在切入点正常运行结束后执行。
@AfterReturning:Advice(通知)的一种,在切入点正常运行结束后执行,异常则不执行
@AfterThrowing:Advice(通知)的一种,在切入点运行异常时执行。
@Pointcut语法
语法有点复杂,目前也没完全掌握,只做个记录
/**
* 1、使用within表达式匹配
* 下面示例表示匹配com.leo.controller包下所有的类的方法
*/
@Pointcut("within(com.leo.controller..*)")
public void pointcutWithin(){
}
/**
* 2、this匹配目标指定的方法,此处就是HelloController的方法
*/
@Pointcut("this(com.leo.controller.HelloController)")
public void pointcutThis(){
}
/**
* 3、target匹配实现UserInfoService接口的目标对象
*/
@Pointcut("target(com.leo.service.UserInfoService)")
public void pointcutTarge(){
}
/**
* 4、bean匹配所有以Service结尾的bean里面的方法,
* 注意:使用自动注入的时候默认实现类首字母小写为bean的id
*/
@Pointcut("bean(*ServiceImpl)")
public void pointcutBean(){
}
/**
* 5、args匹配第一个入参是String类型的方法
*/
@Pointcut("args(String, ..)")
public void pointcutArgs(){
}
/**
* 6、@annotation匹配是@Controller类型的方法
*/
@Pointcut("@annotation(org.springframework.stereotype.Controller)")
public void pointcutAnnocation(){
}
/**
* 7、@within匹配@Controller注解下的方法,要求注解的@Controller级别为@Retention(RetentionPolicy.CLASS)
*/
@Pointcut("@within(org.springframework.stereotype.Controller)")
public void pointcutWithinAnno(){
}
/**
* 8、@target匹配的是@Controller的类下面的方法,要求注解的@Controller级别为@Retention(RetentionPolicy.RUNTIME)
*/
@Pointcut("@target(org.springframework.stereotype.Controller)")
public void pointcutTargetAnno(){
}
/**
* 9、@args匹配参数中标注为@Sevice的注解的方法
*/
@Pointcut("@args(org.springframework.stereotype.Service)")
public void pointcutArgsAnno(){
}
/**
* 10、使用excution表达式
* execution(
* modifier-pattern? //用于匹配public、private等访问修饰符
* ret-type-pattern //用于匹配返回值类型,不可省略
* declaring-type-pattern? //用于匹配包类型
* name-pattern(param-pattern) //用于匹配类中的方法,不可省略
* throws-pattern? //用于匹配抛出异常的方法
* )
*
* 下面的表达式解释为:匹配com.leo.controller.HelloController类中以hello开头的修饰符为public返回类型任意的方法
*/
@Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))")
public void pointCut() {
}
切面代码
下面的代码首先声明了作为一个切面,且作为一个容器,然后定义了一个@pointcut,即哪些方法作为可以被切入的方法。
所以下面的@before和@Around只需要获得这个切点的value就可以对这个切点的方法进行各种操作了。
注意@Around其中的point.proceed()方法可以控制方法的执行,如果使用了@Around又没有执行proceed方法,被切入的方法连执行都不会执行,而proceed还可以替换方法内部的参数,功能十分强大。
@Aspect
@Component
public class Point {
/**
* 定义一个切点
*/
//Pointcut 是指那些方法需要被执行"AOP",
@Pointcut("execution(* com.example.demo.service.Buy.Play(..))")
public void AspectJPoint() {
}
//在play这个方法执行之前执行
@Before(value = "AspectJPoint()")
public void Before(){
System.out.println("男孩女孩都有自己喜欢的事情");
}
@Around(value = "AspectJPoint()")
//开启环绕通知, 以及打印切入点的信息 point指进入的方法
//应该叫增强通知
public void Around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕前....");
System.out.println(point);
//得到point方法的参数
Object[] args = point.getArgs();
System.out.println("args=> "+args.toString());
//得到point方法的类型
String kind = point.getKind();
System.out.println("kind=> "+kind);
//得到point方法的方法签名
MethodSignature signature = (MethodSignature) point.getSignature();
//得到其方法(?)确实有点像反射了。
Method method = signature.getMethod();
System.out.println("method=> "+method);
//获得其名字
Signature signature1 = point.getSignature();
String name = signature1.getName();
Class declaringType = signature1.getDeclaringType();
int modifiers = signature1.getModifiers();
String declaringTypeName = signature.getDeclaringTypeName();
System.out.println("name= > " + name + " declaringType => " + declaringType + " modifiers => " + modifiers + " declaringTypeName => " + modifiers);
System.out.println("signature=> " + signature);
//得到目标
Object target = point.getTarget();
System.out.println("target=> " + target);
//执行这个方法
point.proceed();
//修改play方法的参数 打印新东西
point.proceed(new String[]{"玩新的游戏"});
System.out.println("环绕后.....");
}
}
执行顺序
@Around ->@Before->主方法体->@Around中proceed()->@After->@AfterReturning