springAOP--学习和理解--用注解方式完成面向切面编程
1 创建项目
2 案例读解
音乐会整个流程
入场。。。检票。。。入座。。。看表演。。。鼓掌。。。散场
3 业务分解
核心业务:表演
切面业务:检票,入座,鼓掌
4 核心代码
pom.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zfh</groupId>
<artifactId>springAOP-4</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springAOP-4</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- AOP依赖 start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<!-- AspectJ start-->
<dependency>
<groupId>org.aspectj</groupId>
<!-- runtime包 -->
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<!-- 织入包 -->
<artifactId>aspectjweaver</artifactId>
<version>1.6.11</version>
</dependency>
<!-- 用于动态代理,基于类的代理 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1</version>
</dependency>
<!-- AspectJ end-->
<!-- AOP依赖 end -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
AppConfig.java
package com.fhzheng.demo.concert;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
/**
* 声明核心业务实现bean,交给容器
* @return
*/
@Bean
public Concert concert() {
return new Concert();
}
/**
* 声明切面业务实现bean,交给容器
* @return
*/
@Bean
public Audience audience() {
return new Audience();
}
}
Audience.java
package com.fhzheng.demo.concert;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
* 切面定义,即业务周边增强
* @author fhzheng
*
*/
@Aspect
public class Audience {
@Pointcut("execution(* com.fhzheng.demo.concert.Performance.*(..))")
public void perform() {}
@Before("perform()")
public void checkIn() {
System.out.println("观众需检票入场。。。");
}
@Before("perform()")
public void takeSeats() {
System.out.println("观众入座。。。");
}
@AfterReturning("perform()")
public void applause() {
System.out.println("表演精彩,观众欢呼...");
}
@AfterThrowing("perform()")
public void demandRefund() {
System.out.println("表演出差错,观众退款...");
}
}
concert.java
package com.fhzheng.demo.concert;
/**
* 核心业务实现
* @author fhzheng
*
*/
public class Concert implements Performance{
@Override
public void perform() {
System.out.println("音乐会进行中...");
}
}
Performance.java
package com.fhzheng.demo.concert;
/**
* 核心业务定义
* @author fhzheng
*
*/
public interface Performance {
void perform();
}
Test部分的代码
@Test
public void aopTest() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
Performance concert = context.getBean(Performance.class);
concert.perform();
}
测试结果
5 核心知识-切点表达式
切入点表达式
定义切入点的时候需要一个包含名字和任意参数的签名,还有一个切入点表达式,如execution(public * com.example.aop…(…))
切入点表达式的格式:execution([可见性]返回类型[声明类型].方法名(参数)[异常])
其中[]内的是可选的,其它的还支持通配符的使用:
- *:匹配所有字符
- …:一般用于匹配多个包,多个参数
- +:表示类及其子类
4)运算符有:&&,||,!
切入点表达式关键词用例:
1)execution:用于匹配子表达式。
//匹配com.cjm.model包及其子包中所有类中的所有方法,返回类型任意,方法参数任意
@Pointcut(“execution(* com.cjm.model…(…))”)
public void before(){}
2)within:用于匹配连接点所在的Java类或者包。
//匹配Person类中的所有方法
@Pointcut(“within(com.cjm.model.Person)”)
public void before(){}
//匹配com.cjm包及其子包中所有类中的所有方法
@Pointcut(“within(com.cjm…*)”)
public void before(){}
3) this:用于向通知方法中传入代理对象的引用。
@Before(“before() && this(proxy)”)
public void beforeAdvide(JoinPoint point, Object proxy){
//处理逻辑
}
4)target:用于向通知方法中传入目标对象的引用。
@Before(“before() && target(target)
public void beforeAdvide(JoinPoint point, Object proxy){
//处理逻辑
}
5)args:用于将参数传入到通知方法中。
@Before(“before() && args(age,username)”)
public void beforeAdvide(JoinPoint point, int age, String username){
//处理逻辑
}
6)@within :用于匹配在类一级使用了参数确定的注解的类,其所有方法都将被匹配。
@Pointcut(“@within(com.cjm.annotation.AdviceAnnotation)”)
- 所有被@AdviceAnnotation标注的类都将匹配
public void before(){}
7)@target :和@within的功能类似,但必须要指定注解接口的保留策略为RUNTIME。
@Pointcut(“@target(com.cjm.annotation.AdviceAnnotation)”)
public void before(){}
8)@args :传入连接点的对象对应的Java类必须被@args指定的Annotation注解标注。
@Before(“@args(com.cjm.annotation.AdviceAnnotation)”)
public void beforeAdvide(JoinPoint point){
//处理逻辑
}
9)@annotation :匹配连接点被它参数指定的Annotation注解的方法。也就是说,所有被指定注解标注的方法都将匹配。
@Pointcut(“@annotation(com.cjm.annotation.AdviceAnnotation)”)
public void before(){}
10)bean:通过受管Bean的名字来限定连接点所在的Bean。该关键词是Spring2.5新增的。
@Pointcut(“bean(person)”)
public void before(){}
6 对目标对象和代理对象的理解,理解动态代理的实现方式
目标对象,一般都是指业务类下的方法
代理对象,它肯定是一个临时对象,是在运行时产生的一个对目标方法增强后的方法实现。由AOP自动生成。
动态代理的实现技术:
JDK动态代理
CGLib动态代理
所以,依赖的jar包一定有:
<!-- 用于动态代理,基于类的代理 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1</version>
</dependency>
1 com.sun.proxy.CGLiBXXXX
本质上,是原来业务类被代理成了新的增强后的类,然后用它的对象来做事情,站在用户角度来看,就好象是功能增加了。
其实,目标类和代理类是兄弟属层次关系,但一定有共同的父类接口,编程追求【面向接口编程】
这些动作由Spring容器来完成。
实质上,在具体实现时
如果是接口代理,默认为JDK代理,由JAVA自带功能来完成;
如果是类继承代理,由默认为CGLib代理,由第三方库来完成;
理解上,可以认为就是加了一个方法拦截器,在方法的前后添加了更多的动作,从而增强了方法。
因此
1 目标类和增强类均由Spring来管理,方便装配,注入,调用相应的对象
2 配置Aspect时,一定要注意:做什么,在哪里做,怎么做 之间的关系
特别注意
1 返回后通知和异常后通知,有个相应的返回结果,一个是返回值,一个是返回异常。