AOP概述及出现背景
面向切面编程
aop概述
AOP全称为Aspect Oriented Programming的缩写,意为:面向切面编程。将程序中公用代码进行抽离,通过动态代理实现程序功能的统一维护的一种技术。
使代码耦合性降低,提高了开发的效率。
aop可以完成的功能
日志记录,性能统计,安全控制,事务处理,异常处理等等。
aop与oop区别
OOP:(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。java实体类就是面向对象编程的最准确的体现。
AOP:则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。
spring AOP底层实现介绍
spring的AOP底层是由 JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术)实现。
**JDK动态代理:**Jdk动态代理只针对于接口操作。
CGLIB:可以针对没有接口的java类和有接口的java类。
静态代理
package cn.cqie.service; public interface UserService { void save(); }
目标Target
package cn.cqie.service; public class UserServiceImpl implements UserService{ @Override public void save() { System.out.println("UserServiceImpl.save"); } }
代理Proxy:代理类需要和目标类实现同一个接口。
package cn.cqie.service; public class UserServiceProxyImpl implements UserService{ private UserService userService; public UserServiceProxyImpl(UserService userService){ this.userService = userService; } @Override public void save() { System.out.println("tiancx "); userService.save(); } }
Test类
@Test public void testStaticProxy(){ UserService userService = new UserServiceProxyImpl(new UserServiceImpl()); userService.save(); }
动态代理
1、编写类介入到bean的生命周期中
package cn.cqie.service; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class MyProxy implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(final Object o, String s) throws BeansException { System.out.println("MyProxy.postProcessBeforeInitialization"); //使用了jdk的动态代理 //目标对象的类加载器,目标对象所实现的接口,切面代码的编写 Object obj = Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("我在前面"); //找到目标对象的方法 method.invoke(o, args); System.out.println("我在后面"); return proxy; } }); return obj; } @Override public Object postProcessAfterInitialization(Object o, String s) throws BeansException { System.out.println("MyProxy.postProcessAfterInitialization"); return o; } }
2、在Spring中配置需要切入的Bean和切入类(MyProxy)
<?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:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <bean id="myProxy" class="cn.cqie.service.MyProxy"/> <bean id="userService" class="cn.cqie.service.UserServiceImpl"/> <!-- deptService是为了测试才加的,看看效果什么样--> <bean id="deptService" class="cn.cqie.service.DeptServiceImpl"/> </beans>
3、后续
再加入一个DeptServiceImpl类试一试
package cn.cqie.service; public class DeptServiceImpl implements DeptService{ @Override public void save() { System.out.println("DeptServiceImpl.save"); } }
测试类
@Test public void testActiveProxy(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("app.xml"); UserService userService = applicationContext.getBean(UserService.class); // System.out.println(UserService.class); System.out.println(userService.getClass()); userService.save(); DeptService deptService = applicationContext.getBean(DeptService.class); deptService.save(); }
Spring的生命周期
bean的生命周期
1.Spring对bean进行实例化,调用bean的构造参数
2.设置对象属性,调用bean的set方法,将属性注入到bean的属性中
3.检查bean是否实现BeanNameAware、BeanFactoryAware、ApplicationContextAware接口,如果实现了这几个接口Spring会分别调用其中实现的方法。
BeanNameAware:setBeanName(String name)方法,参数是bean的ID
BeanFactoryAware:setBeanFactory(BeanFactory bf)方法,参数是BeanFactory容器
ApplicationContextAware:setApplicationContext(ApplicationContext context)方法,参数是bean所在的引用的上下文,如果是用Bean工厂创建bean,那就可以忽略ApplicationContextAware。
4.如果bean是否实现BeanPostProcessor接口,Spring会在初始化方法的前后分别调用postProcessBeforeInitialization和postProcessAfterInitialization方法
5.如果bean是否实现InitalizingBean接口,将调用afterPropertiesSet()方法
6.如果bean声明初始化方法,也会被调用
7.使用bean,bean将会一直保留在应用的上下文中,直到该应用上下文被销毁。
8.检查bean是否实现DisposableBean接口,Spring会调用它们的destory方法
9.如果bean声明销毁方法,该方法也会被调用
AOP核心概念
1、横切关注点
向代码中切入的内容。
2、切面(aspect)
是一个类,包含了若干个横切关注点
3、连接点(joinpoint)
真实被切面切入的方法
4、切入点(pointcut)
想要切入的方法
5、通知(advice)
5种通知:
a、前置通知
b、后置通知
c、异常通知
d、返回通知
e、环绕通知
6、目标对象(Target)
要运行的对象。
7、织入(weave)
将目标对象加入通知,生成代理对象的过程
XML配置实现AOP
1、添加依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.18.RELEASE</version> </dependency>
2、编写切面类
package cn.cqie.aop; import org.aspectj.lang.ProceedingJoinPoint; /** * Description: * Author: tiancx * Date: Created in 2021/11/20 16:51 */ public class MyAOP { // public void before(){ // System.out.println("MyAOP.before"); // } // public void after(){ // System.out.println("MyAOP.after"); // } // public void exception(){ // System.out.println("MyAOP.exception"); // } // public void afterReturning(){ // System.out.println("MyAOP.afterReturning"); // } public Object around(ProceedingJoinPoint joinPoint){ Object proceed = null; try { System.out.println("事务开始"); //前置通知 proceed = joinPoint.proceed(); //执行连接点对应的方法 System.out.println("事务提交"); //返回通知 }catch (Throwable throwable) { throwable.printStackTrace(); System.out.println("事务回滚"); //异常通知 } finally { System.out.println("事务关闭"); //后置通知 } return proceed; } }
3、编写service类
package cn.cqie.aop; import cn.cqie.aop.UserService; public class UserServiceImpl implements UserService { @Override public void save() { System.out.println("UserServiceImpl.save"); // System.out.println(1/0); } }
4、配置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:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" > <!-- AOP的xml配置 1、添加AOP的命名空间 xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd 2、添加需要被Spring管理的实例 3、添加切面类 --> <bean id="userService" class="cn.cqie.aop.UserServiceImpl"/> <bean id="empService" class="cn.cqie.aop.EmpServiceImpl"/> <bean id="myAOP" class="cn.cqie.aop.MyAOP"/> <!-- 4、aop配置 --> <aop:config > <!-- 5、切面配置 ref="myAOP"关联到切面类 --> <aop:aspect ref="myAOP"> <!-- 6、配置切入点 --> <aop:pointcut id="pc" expression="execution(* cn.cqie.aop..*(..)) "/> <!-- 7、通知配置 前置 后置 异常 返回 环绕 --> <!-- <aop:before method="before" pointcut-ref="pc"/>--> <!-- <aop:after method="after" pointcut-ref="pc"/>--> <!-- <aop:after-throwing method="exception" pointcut-ref="pc"/>--> <!-- <aop:after-returning method="afterReturning" pointcut-ref="pc"/>--> <aop:around method="around" pointcut-ref="pc"/> </aop:aspect> </aop:config> </beans>
部分运行结果
AspectJ实现AOP
1、相关类添加注解
比如 @Service
2、编写切面类
package cn.cqie.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * Description: * Author: tiancx * Date: Created in 2021/11/21 12:49 */ @Aspect //描述本类为切面类 @Component //被Spring容器管理 public class MyAOP2 { /** * execution(* com.woniu..*.*(..)) * 第一个*表示返回类型 * 第二个*表示类 * 第三个*表示方法 * 第一个..表示根包下类及子包下类 * 第二个..表示方法的所有参数类型 */ @Pointcut("execution(* cn.cqie..*.*(..))") public void pointCut(){} @Before("pointCut()") public void before(){ System.out.println("MyAOP2.before"); } @After("pointCut()") public void after(){ System.out.println("MyAOP2.after"); } @AfterReturning("pointCut()") public void afterReturning(){ System.out.println("MyAOP2.afterReturning"); } @AfterThrowing("pointCut()") public void afterThrowing(){ System.out.println("MyAOP2.afterThrowing"); } @Around("pointCut()") public Object around(ProceedingJoinPoint joinPoint){ System.out.println("MyAOP2.around1"); Object proceed = null; try { proceed = joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("MyAOP2.around2"); return proceed; } }