目录
1、什么是AOP
2、AOP在Spring中的作用
3、Spring实现AOP
1、什么是AOP
- 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
- AOP是OOP的延续是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
- 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合程度降低,提高程序的可用性,同时也提供了开发的效率。
总结:翻译成人话,就是说AOP可以不再原有的代码上增加内容,在指定的位置加入一个类的方法,保证原有的业务逻辑代码不会越改越错。
2、AOP在Spring中的作用
提供事务式声明,语序用户自定义切面
-
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的那部分,就是横切关注点。如日志、安全、缓存、事务等等…
- 我要插入什么东西(日志、事务、安全、缓存等)
-
切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 将我要插入的东西转换成一个类。
-
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 具体的实现,我要用类的哪一个方法去插入。
-
目标(Target):被通知对象。
- 告诉被插入的类,我要插入方法(Sping内部实现)。
-
代理(Proxy):向目标对象应用通知之后创建的对象。
- 去实现被插入类实现接口,进行代理(Sping内部实现)。
-
切入点(PointCut):切面通知 执行的"地点"的定义。
- 被插入的方法,根据切入点的前后进行插入。
-
连接点(JoinPoint):与切入点匹配的执行点。
- 被插入的方法,可以获取切入点信息。
本图来自于bilibili UP主 狂神说Java
如果在学ssm等,还是非常推荐去看的
SpringAOP中,通过Adivce定义横切逻辑,Spring中支持5中类型的Advice;
个人总结:在原有代码不变的前提下,使用横切的方式为业务增加一些额外的功能,保证业务的正确性。
3、Spring实现AOP
要在配置文件中加入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"
<!--aop-->
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
<!--aop-->
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
在Mavne中导入(必须)
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
3.1、三种方式实现
测试方法
@Test
public void test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
//获取的必须为接口,否则Proxy不能代理
UserDao user = context.getBean("user", UserDao.class);
user.add();
}
- 方式一:Spring内部API实现(功能最强大)
<bean id="first" class="com.tx.aop.FirstLog"/>
<bean id="last" class="com.tx.aop.LostLog"/>
<bean id="user" class="com.tx.dao.UserDaoImpl"/>
<aop:config>
<!-- 在什么位置切入(插入)方法 -->
<!--横切关注点-->
<aop:pointcut id="point" expression="execution(* com.tx.dao.UserDaoImpl.*(..))"/>
<!--通知-->
<aop:advisor advice-ref="first" pointcut-ref="point"/>
<aop:advisor advice-ref="last" pointcut-ref="point"/>
</aop:config>
- 方式二:DIY自定义类实现(切面)
<bean id="first" class="com.tx.aop.FirstLog"/>
<bean id="last" class="com.tx.aop.LostLog"/>
<bean id="user" class="com.tx.dao.UserDaoImpl"/>
<bean id="diy" class="com.tx.diy.AopDiy"/>
<aop:config>
<!--切面-->
<!--自制DIY类-->
<aop:aspect ref="diy">
<!--横切关注点-->
<aop:pointcut id="point" expression="execution(* com.tx.diy.AopDiy.*(..))"/>
<!--通知-->
<aop:before method="first" pointcut-ref="point"/>
<!--通知-->
<aop:before method="last" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
- 方式三:注解
<context:annotation-config/>(重点,开启注解)
<aop:aspectj-autoproxy/>(重点,开启AOP注解支持)
<bean id="annotationAop" class="com.tx.annotation.AnnotationAop"/>
<bean id="userDaoImpl" class="com.tx.dao.UserDaoImpl"/>
@Aspect//表示这个类是一个切面,必写,否则不能识别
public class AnnotationAop {
//@Before 表示在前面插入
//第一个* 表示返回值(在这个方法为void)
//第二个为需要插入的路径,后面表示路径下的所有方法
//(..)表示可以为任何参数
@Before("execution(* com.tx.dao.UserDaoImpl.*(..))")
public void before(){
System.out.println("===前面===");
}
//@After 表示在后面插入
//第一个* 表示返回值(在这个方法为void)
//第二个为需要插入的路径,后面表示路径下的所有方法
//(..)表示可以为任何参数
@After("execution(* com.tx.dao.UserDaoImpl.*(..))")
public void after(){
System.out.println("===后面===");
}
}