11.1、什么是AOP
AOP(Aspect Oriented Programming) 要为: 面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP是延续, 是软件开发中的一个热点, 也是Spring框架中的一个重要内容, 是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离, 从而使得业务逻辑各部分之间的耦合度降低, 提高程序的可重用性, 同时提高开发的效率
![image.png](https://img-blog.csdnimg.cn/img_convert/eb7828db5d6da070a8da77c6711ac556.png#clientId=u2755809b-38bf-4&from=paste&height=468&id=u310a1feb&margin=[object Object]&name=image.png&originHeight=531&originWidth=920&originalType=binary&ratio=1&size=396729&status=done&style=none&taskId=u1dfecb83-9580-4f1d-a845-e335098d2bf&width=811)
11.2、Aop在Spring中的作用
提供声明式事务; 允许用户自定义切面
①横切关注点 : 跨越应用程序多个模块的方法或功能。即, 与我们业务逻辑无关的, 但是我们需要关注的部分, 就是横切关注点。如日志、安全、缓存、事务等等
②切面(ASPECT) : 横切关注点被模块化的特殊对象。即, 他是一个类。
③通知(Advice) : 切面必须要完成的工作, 即, 他是类中的一个方法。
④目标(Target) : 被通知的对象。即, 一个接口或者方法
⑤代理(Proxy) : 向目标对象应用通知后创建的对象。
⑥切入点(PointCut) : 切面通知执行的"地点"的定义。
⑦连接点(JointPoint) : 与切入点匹配的执行点。
![image.png](https://img-blog.csdnimg.cn/img_convert/074be86e818bae9f83eda689baf40b21.png#clientId=u2755809b-38bf-4&from=paste&height=399&id=ufde0a9ed&margin=[object Object]&name=image.png&originHeight=429&originWidth=863&originalType=binary&ratio=1&size=175644&status=done&style=none&taskId=ued668556-34ec-4275-8a8e-5953a996139&width=802)
SpringAOP中, 通过Advice定义横切逻辑, Spring中支持5中类型的Advice:
![image.png](https://img-blog.csdnimg.cn/img_convert/7b49121f265568bf48692b402af13ec6.png#clientId=u2755809b-38bf-4&from=paste&height=486&id=u1fa3cba2&margin=[object Object]&name=image.png&originHeight=401&originWidth=665&originalType=binary&ratio=1&size=170676&status=done&style=none&taskId=u3f611206-ce66-4ec9-8dc3-691480bc845&width=806)
即AOP在不改变原有代码的情况下, 去增加新的功能
11.3、使用Spring实现AOP
**[重点]**使用AOP织入, 需要导入一个依赖包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
<scope>runtime</scope>
</dependency>
![image.png](https://img-blog.csdnimg.cn/img_convert/2e363dd43df57226aeb1a5e9ddba9739.png#clientId=u2755809b-38bf-4&from=paste&height=571&id=cMs2u&margin=[object Object]&name=image.png&originHeight=571&originWidth=1245&originalType=binary&ratio=1&size=315953&status=done&style=none&taskId=u49f34c0a-3ae8-48e5-b5de-a141d7438e8&width=1245)
方式一: 使用Spring的API接口
①配置beans.xml
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
②完整的beans.xml
<aop:config>
_
_<aop:pointcut id=“pointcut” expression="execution( com.service.UserServiceImpl.*())"*/>
_<!--执行环绕增加!--><br /> _<**aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"**/><br /> <**aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"**/><br /> _<!--意思是将beforeLog和afterLog这个类切入到目标类的指定地方--><br />_</**aop:config**>
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userServiceImpl" class="com.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.log.beforeLog"/>
<bean id="afterLog" class="com.log.afterLog"/>
<!--配置aop: 需要导入aop的约束-->
<aop:config>
<!--切入点: expression(表达式), execution(要执行的位置)[修饰词,返回值,类名,方法名,参数]-->
<aop:pointcut id="pointcut" expression="execution(* com.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加!-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
<!--意思是将beforeLog和afterLog这个类切入到目标类的指定地方-->
</aop:config>
</beans>
③测试:
public class MyTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//动态代理代理的是接口: 注意点
UserService userServiceImpl = context.getBean("userServiceImpl", UserService.class);//后面的参数是规定得到对象的类型
userServiceImpl.add();
}
}
注意: Spring AOP的底层是动态代理, 所以实现的是接口不是实现类
方式二: 使用自定义类
比方式一简单, 但少了一些功能
①配置beans.xml
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
②完整的beans.xml_<
<aop:config>
_
_<aop:aspect ref=“diy”>
_
_<aop:pointcut id=“point” expression="execution( com.service.UserServiceImpl.*(…))"*/>
_<!--通知(什么时候执行)--><br /> _<**aop:before method="before" pointcut-ref="point"**/><br /> <**aop:after method="after" pointcut-ref="point"**/><br /> _<!--意思是将diy类的before和after这两个方法切入到目标类的执行前和执行后--><br /> _</**aop:aspect**><br /></**aop:config**>
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userServiceImpl" class="com.service.UserServiceImpl"/>
<bean id="diy" class="com.diy.diy"/>
<!--方式二: 自定义类-->
<aop:config>
<!--自定义切面(aspect), ref 要引用的自定义类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.service.UserServiceImpl.*(..))"/>
<!--通知(什么时候执行)-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
<!--意思是将diy类的before和after这两个方法切入到目标类的执行前和执行后-->
</aop:aspect>
</aop:config>
</beans>
③测试:
public class MyTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans2.xml");
//动态代理代理的是接口: 注意点
UserService userServiceImpl = context.getBean("userServiceImpl", UserService.class);//后面的参数是规定得到对象的类型
userServiceImpl.add();
}
}
方式三: 使用注解实现
①切面类(注解)
//方式三: 使用注解方式实现AOP
@Aspect//标注这个类是一个切面(Aspect)
public class AnnotationPointCut {
@Before("execution(* com.service.UserServiceImpl.*(..))")//写切入点(PointCut)。(方法)执行前
public void before() {
System.out.println("执行前");
}
@After("execution(* com.service.UserServiceImpl.*(..))")//写切入点(PointCut)。(方法)执行后
public void after() {
System.out.println("执行后");
}
//在环绕增强中,我们可以给定一个参数, 代表我们要获取处理切入的点
@Around("execution(* com.service.UserServiceImpl.*(..))")//写切入点(PointCut)。(方法)执行后
public void around(ProceedingJoinPoint jp) throws Throwable {//连接点(JoinPoint) 和切入点差不多,没什么用
System.out.println("环绕前");
Signature signature = jp.getSignature();//获得签名(执行的方法的信息)
System.out.println("signature: " + signature);
Object proceed = jp.proceed();//执行方法时()
System.out.println("环绕后");
}
}
②beans3.xml
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userServiceImpl" class="com.service.UserServiceImpl"/>
<!--方式三: 使用注解方式实现AOP-->
<bean id="annotationPointCut" class="com.diy.AnnotationPointCut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
</beans>
③测试
public class MyTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans3.xml");
//动态代理代理的是接口: 注意点
UserService userServiceImpl = context.getBean("userServiceImpl", UserService.class);//后面的参数是规定得到对象的类型
userServiceImpl.delete();
}
}
注意:
开启注解支持 :
_JDK(默认 proxy-target-class=“false”) _
_cglib(需要手动设置 proxy-target-class=“true”)
结果不会有区别<**aop:aspectj-autoproxy proxy-target-class="false"**/>
PS: 环绕@Around
![image.png](https://img-blog.csdnimg.cn/img_convert/23ab7aadb3e96375acd39c78940ee0b2.png#clientId=u4943e8e5-6d30-4&from=paste&height=280&id=u3c3e8505&margin=[object Object]&name=image.png&originHeight=280&originWidth=760&originalType=binary&ratio=1&size=18456&status=done&style=none&taskId=u8d6e4627-1339-43a3-b9a5-caf271b6d2a&width=760)