什么是AOP
官方:AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
我理解的AOP:面向切面编程,是一种纵向编程的思想;是把业务中公共的代码逻辑(日志记录,事物控制,异常)跨越不同代码层级在需要的时候切入该代码的逻辑。允许我们将通用的功能横切到多个对象之间,来提高代码的复用性和可维护性。在java编程中,动态代理和AOP是一起使用的,动态代理创建出代理对象,使用aop在代理对象中增加一些附属的操作,从而实现业务的增强。
1.代理模式
学习AOP之前先学习一下代理模式,因为AOP的底层机制就是动态代理。
1.代理模式
什么是代理,日常中最常见的就是租房,我需要租房子,但是我又找不到房租,房租又找不到要租房子的人,所以有了租房中介,房东把房源给房屋中介,我去房屋中介租房子,房屋中介这个角色就是一个代理对象。
代理模式中包含的几个角色:
抽象接口: 接口或者抽象类,表明要做什么?(租房子) 代理角色(代理类): 去代理真实的角色,并增加一些公共的方法,(比如看房子,签合同),管理真实角色 (房屋中介) 真实角色(被代理的类):继承接口或者实现抽象类,具体实现某类功能(租房子) 客户: 访问代理角色,通过访问代理角色来完成我们的需求功能,不需要去关心真实角色 (我) 静态代理: 如果我想在租房子的几个操作中添加一些日志的功能,我就不能直接修改业务代码,需要通过添加一个代理来实现,但是如果这种代理层在service很多时,每个service都需要添加一个代理类,实际使用起来还是很麻烦的
2.JDK动态代理
静态代理需要我们手动添加一个代理对象,在被代理对象很多时,就需要添加很多代理对象,使代码量大大增加;所以java提供了一种JDK动态代理.它使用反射机制,提供了 Proxy 类和一个InvocationHandler接口,通过一个类和一个接口就可以实现动态代理。
1.通过实现InvocationHandler接口,定义横切逻辑,然后通过invoke方法调用目标类的代码,也可以添加一些前置后置处理等 2.Proxy类: 利用InvocationHandler动态创建一个目标类实现的接口的实例,生产目标类的代理对象 newProxyInstance:用于创建一个代理实例 public class DTProxy implements InvocationHandler { //2.生成代理对象 public Proxy getproxy(){ return (Proxy) Proxy.newProxyInstance(this.getClass().getClassLoader(), zuCar.getClass().getInterfaces(),this); } //3.执行代理并返回结果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object invoke = method.invoke(zuCar, args); after(); return invoke; } public void before(){ System.out.println("方法执行前"); } public void after(){ System.out.println("方法执行后"); } }
测试:
public static void main(String[] args) { //代理的对象 ZuCarImpl zuCar = new ZuCarImpl(); DTProxy dtProxy = new DTProxy(zuCar); //生成代理对象 //dtProxy.getproxy():用于获取代理类的Class对象,再通过调用构造函数创建代理实例。 ZuCar getproxy = (ZuCar) dtProxy.getproxy();//动态代理代理的是接口 getproxy.buyche(); getproxy.zuche(); }
2. Aop在Spring中的作用
提供声明式事务;允许用户自定义切面
以下名词需要了解下:
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....
切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
目标(Target):被通知对象。
代理(Proxy):向目标对象应用通知之后创建的对象。
切入点(PointCut):切面通知 执行的 “地点”的定义。
连接点(JointPoint):与切入点匹配的执行点。
3.使用Spring实现AOP
三种方式: 通过Spring API实现 通过自定义类来实现 使用注解实现
使用Spring实现AOP首先要在pom.xml中增加依赖
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.0</version> </dependency>
1) 第一种通过API接口实现:
首先写业务接口和实现类
public interface UeseService { public void add(); public void delete(); public void update(); public void select(); }
public class UserServiceImpl implements UeseService{ @Override public void add() { System.out.println("增加了了一个方法"); } @Override public void delete() { System.out.println("删除了一个方法"); } @Override public void update() { System.out.println("修改了一个方法"); } @Override public void select() { System.out.println("查询了一个方法"); } }
再去写业务增强类,写一个前置增强和一个后置增强
前置增强:
public class BeforeLog implements MethodBeforeAdvice { //method 要执行的目标对象的方法 //args 被调用的方法的参数 //target 目标对象 @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了"); } }
后置增强:
public class AfterLog implements AfterReturningAdvice { /** * @param returnValue 返回值 * @param method 要执行的目标对象的方法 * @param aegs 被调用的方法的参数 * @param target 目标对象 * @throws Throwable */ @Override public void afterReturning(Object returnValue, Method method, Object[] aegs, Object target) throws Throwable { System.out.println("执行了"+method.getName()+"返回了"+returnValue); } }
在spring配置文件中注册,实现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: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 id="afterlog" class="ghm.log.AfterLog"></bean> <bean id="beforelog" class="ghm.log.BeforeLog"></bean> <bean id="userService" class="ghm.service.UserServiceImpl"></bean> <aop:config> <!-- 切入点:expression(要执行的位置)--> <aop:pointcut id="pointcut" expression="execution(* ghm.service.UserServiceImpl.*(..))"/> <!--第一种 执行环绕增强--> <aop:advisor advice-ref="beforelog" pointcut-ref="pointcut"></aop:advisor><aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"></aop:advisor> </aop:config>
execution 要执行的位置! (*(修饰词) *(返回值) *(类名) *(方法名) *(参数))
测试
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //动态代理代理的是接口 UserService userService = (UserService) context.getBean("userService"); userService.update(); }
2) 自定义类来实现AOP
第一步:写一个自己的切入类
public class Pointcut { public void before(){ System.out.println("方法执行前"); } public void after(){ System.out.println("方法执行后"); } }
第一步:spring中配置
<bean id="diy" class="ghm.log.Pointcut"></bean> <!--第二种 自定义类--> <aop:config> <!-- 自定义切面 ref 要引用的类 --> <aop:aspect ref="diy" > <!--切入点--> <aop:pointcut id="pointcut" expression="execution(* ghm.service.UserServiceImpl.*(..))"/> <!-- 通知--> <aop:before method="before" pointcut-ref="pointcut"></aop:before> <aop:after method="after" pointcut-ref="pointcut"></aop:after> </aop:aspect> </aop:config>
3) 使用注解实现
第一步:写一个注解实现的增强类,类上加上@Aspect,标注这个类为一个切面
@Aspect //标注这个类为一个切面 public class AnnotionPoint { @Before("execution(* ghm.service.UserServiceImpl.*(..))") public void before(){ System.out.println("方法执行前-============"); } @After("execution(* ghm.service.UserServiceImpl.*(..))") public void after(){ System.out.println("方法执行后"); } }
第二步去Spring中配置bean,并开启注解支持
<!--第三种 使用注解实现--> <bean id="annotionPoint" class="ghm.log.AnnotionPoint"></bean> <!--开启注解支持 自动代理--> <aop:aspectj-autoproxy />