什么是AOP
- AOP意味面向切面编程, 通过预编译和运行期间形成动态代理的当时实现程序的统一维护的一种技术,利用AOP可以对各个业务进行隔离, 从而是得业务各逻辑之间的耦合性降低, 提高了程序的可重用性, 同时提高开发效率
AOP在Spring的作用
提供事务声明, 允许用户自定义切面, 降低代码耦合性, 提高开发效率.
必须了解下面概念
- 横切关注点: 需要给被代理类增加的业务, 比如日志, 安全,缓存,事务
- 目标: 为被代理类
- 切入点: 就是需要执行代理类的某个方法
- 切面: 就是实现横切关注点的业务. 即它是一个类
- 通知: 就是切面的一个方法, 在代理类的某个方法或者功能执行的前或后 增加需要执行的横切关注点, 如有前置通知,后置通知, 环绕通知等等
使用Spring注解实现AOP
- 主要是spring.aop的API接口的实现
- 导入spring.aop的依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
- 在xml文件中添加AOP约束
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
- 有实现目标接口的类, 将来称为被代理类
public class Service implements Database {
@Override
public void add() {
System.out.println("添加一个用户");
}
@Override
public void delete() {
System.out.println("删除一个用户");
}
@Override
public void update() {
System.out.println("修改用户");
}
@Override
public void query() {
System.out.println("查找一个用户");
}
}
- 实现通知的类, 要实现对应AOP的接口
package aop;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* Created with IntelliJ IDEA.
* Description: If you don't work hard, you will be a loser.
* User: Listen-Y.
* Date: 2020-10-23
* Time: 21:45
*/
public class LogBefore implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName() + "执行方法" + method.getName());
}
}
package aop;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
* Created with IntelliJ IDEA.
* Description: If you don't work hard, you will be a loser.
* User: Listen-Y.
* Date: 2020-10-23
* Time: 21:46
*/
public class LogAfter implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("方法名是:" + method.getName() + " 返回值是:" +
o);
}
}
- 注册上述所有类的bean
<!--使用spring的方式, 就必须注册使用类的bean对象-->
<bean id="service" class="aop.Service"/>
<bean id="logBefore" class="aop.LogBefore"/>
<bean id="logAfter" class="aop.LogAfter"/>
- 在xml文件中配置AOP, 配置切入点, 配置切入点与通知的连接
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--使用spring的方式, 就必须注册使用类的bean对象-->
<bean id="service" class="aop.Service"/>
<bean id="logBefore" class="aop.LogBefore"/>
<bean id="logAfter" class="aop.LogAfter"/>
<aop:config>
<!--配置被代理类的切入点, 注意execution的使用规则第一个* 代表任意返回类型,
第二个参数表示切入点, .*表示这个类的全部方法, ..表示参数是任意的-->
<aop:pointcut id="pointcut" expression="execution(* aop.Service.*(..))"/>
<!--切入点与通知的连接, 完成横切关注点的任务-->
<aop:advisor advice-ref="logBefore" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="logAfter" pointcut-ref="pointcut"/>
</aop:config>
</beans>
- 测试
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
Database database = context.getBean("service", Database.class);
database.add();
}
}
总结就是: 导包, 写实体类, 配置xml
使用自定义切面实现AOP
- 导包和上面的依赖是一样的
- 实现切面实体
public class Log {
public void before() {
System.out.println("=====method begin=====");
}
public void after() {
System.out.println("=====method end=====");
}
}
- 配置xml文件
-
主要是配置切面、切入点和配置连接通知
-
要有需要被代理的类和切面的类的bean对象
-
在xml文件中配置aop, 与上面的不同的是, 这里需要配置切面, 然后配置切入点, 最后配置通知与切入点的连接
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dao" class="myProxy.Dao"/>
<bean id="log" class="myProxy.Log"/>
<!--aop配置-->
<aop:config>
<!--配置切面-->
<aop:aspect ref="log">
<!--配置切入点-->
<aop:pointcut id="pointcut" expression="execution(* myProxy.Dao.*(..))"/>
<!--配置通知与切入点相连-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
- 测试
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
Database database = context.getBean("dao", Database.class);
database.add();
}
}
总结就是: 导包, 编写实体类, 但是是直接写切面, 然后在xml文件中配置切面和配置通知和切入点的连接, 使用这个方法最重要的就是配置xml
使用aspectj注解实现AOP
- 导入aspect的包, 还和上面的一样
- 给切面增注解@Aspect
- 给切面中的通知添加注解, 实现什么时候执行, 注解的后面还要配置切入点, 在环绕增强中around, 我们可以给定一个参数,去代表我们要执行的切入点
package annotationProxy;
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;
/**
* Created with IntelliJ IDEA.
* Description: If you don't work hard, you will be a loser.
* User: Listen-Y.
* Date: 2020-10-24
* Time: 13:58
*/
//添加注解表示这是一个切面
@Aspect
public class Log {
//添加注解, 配置切入点
@Before("execution(* myProxy.Dao.*(..))")
public void before() throws Throwable {
System.out.println("=====方法执行前=====");
}
@After("execution(* myProxy.Dao.*(..))")
public void after() {
System.out.println("=====方法执行后=====");
}
//环绕,使用这个注解可以给定一个参数, 去代表我们的切入点
@Around("execution(* myProxy.Dao.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前");
//这个代表去执行这个方法
Object proceed = joinPoint.proceed();
System.out.println("环绕后");
}
}
- 配置xml文件, 开启aop的注解扫描
- 将被代理类和切面添加到容器中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dao" class="myProxy.Dao"/>
<bean id="log" class="annotationProxy.Log"/>
<!--开启aop的注释-->
<aop:aspectj-autoproxy/>
</beans>
总结就是必须先在xml文件中开启aop的注解,
然后编写切面实体,给切面添加@aspect注解, 给切面的方法添加通知注解顺便配置切入点,
总之就是配置简单, 实现更加动态一点, 易懂