一、AOP概述(什么是AOP)
(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
(3)使用登录例子说明 AOP
二、AOP底层原理(动态代理)
动态代理的实现(调用newProxyInstance方法实现:
newProxyInstance方法介绍:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
静态方法,有三个形参
(1)ClassLoader loader:获取运行时类的加载器
(2)Class<?>[] interfaces:获取运行类的接口
(3)InvocationHandler h:实现这个接口 InvocationHandler,创建代理对象,写增强的部分
举例说明:
接口:定义生产衣服的规范
代理类:LINING公司、ANTA公司
被代理类:生产衣服的工厂
被代理的方法:生产(LINING/ANTA)公司的衣服
增强的方法:衣服被生产完后需要进行包装
动态代理的代码:
接口:
//生产衣服的规范
public interface norms {
public void production();
}
被代理类:
public class Factory implements norms {
@Override
public void production() {
System.out.println("被代理类(生产衣服的工厂)开始生产衣服");
}
}
动态代理产生被代理类(重点):
//创建被代理类:LINING ANTA 公司
public class ProxyFactory {
public static void main(String[] args) {
//创建接口实现类(Factory)的代理对象
//.class 获取运行时类 .class.getClassLoader() 获取类的加载器
Factory factory = new Factory();
Class[] interfaces = {norms.class};
myInvocationHander myInvocationHander = new myInvocationHander(factory);
norms LINING = (norms) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, myInvocationHander);
//调用方法时去执行myInvocationHander 类中的invoke方法
LINING.production();
}
}
class myInvocationHander implements InvocationHandler {
//1.把创建的是谁的代理对象,就把谁传进来
private Object obj;//obj表示被代理对象
public myInvocationHander(Object obj) {//传入代理类的对象
this.obj = obj;
}
//增强的逻辑
@Override
//proxy 表示代理类的对象 method表示代理类执行的方法 args表示代理类执行的方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
//method.getName() 表示获取执行代理类的方法的名称
System.out.println("执行被代理类方法" +method.getName() + "前的操作");
//执行被代理类的方法
//执行代理类方法的被代理类中的同名方法
Object invoke = method.invoke(obj, args);
//方法之后
System.out.println("衣服生产完后进行包装");
return invoke;
}
}
运行结果:
执行被代理类方法production前的操作
被代理类(生产衣服的工厂)开始生产衣服
衣服生产完后进行包装
三、AOP中的术语
1、连接点:类中那些方法可以被增强,这些方法被称为连接点
2、切入点:实际中被增强的方法,被称为切入点,如上述中生产衣服的方法
3、通知:实际被增强的逻辑部分,如上述的包装
(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知
4、切面:将通知应用到切入点的过程
四、AOP的实现基础
1、Spring 框架一般都是基于 AspectJ 实现 AOP 操作
(1)AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,进行 AOP 操作
2、基于 AspectJ 实现 AOP 操作
(1)基于 xml 配置文件实现(了解)
(2)基于注解方式实现(重点)
3、引入依赖
4、切入点表达式
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构: execution 表示切入点表达式 execution([权限修饰符(可省略)][返回类型(* 表示任意返回类型)][类全路径][方法名(*表示所有的方法)][形参列表(.. 表示任意)])
举例 1:对 BookDao 类里面的 add 进行增强 execution(* dao.BookDao.add(..))
举例 2:对 BookDao 类里面的所有的方法进行增强 execution(* dao.BookDao.* (..))
举例 3:对dao 包里面所有类,类里面所有方法进行增强 execution(* dao.*.* (..))
五、AOP的实现步骤
1、配置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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启组件扫描
1.扫描多个包 多个包使用逗号隔开
2.扫描包上层目录
-->
<context:component-scan base-package="aop_annotation"></context:component-scan>
<!--开启Aspectj生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
2、创建被增强类,在类中创建方法(类似登录界面原始的步骤)
//被增强类
@Component
public class User {
public void add(){
System.out.println("add......");
}
}
3.创建增强类(类似登录界面判断是否是管理人员的操作)
//增强类
@Component//创建类的对象
@Aspect//生成代理对象 使用注解Aspect实现
@Order(1)//设置增强类的优先级 使用注解order实现 数字越小,优先级越高
public class UseProxy {
//相同切入点抽取 利用注解Pointcut实现
@Pointcut(value = "execution(* aop_annotation.User.add(..))")
public void same(){
}
//前置通知 利用注解Before实现
//execution 表示切入点表达式 execution([权限修饰符(可省略)][返回类型(* 表示任意返回类型)][类全路径][方法名(*表示所有的方法)][形参列表(.. 表示任意)])
@Before(value = "same()")//抽取befo抽取
public void before(){
System.out.println("before");
}
//最终通知 利用注解After实现(方法执行后 方法参数返回前)
//execution 表示切入点表达式 execution([权限修饰符(可省略)][返回类型(* 表示任意返回类型)][类全路径][方法名(*表示所有的方法)][形参列表(.. 表示任意)])
@After(value = "execution(* aop_annotation.User.add(..))")
public void After(){
System.out.println("After");
}
//异常通知 利用注解AfterThrowing实现
//execution 表示切入点表达式 execution([权限修饰符(可省略)][返回类型(* 表示任意返回类型)][类全路径][方法名(*表示所有的方法)][形参列表(.. 表示任意)])
@AfterThrowing(value = "execution(* aop_annotation.User.add(..))")
public void AfterThrowing(){
System.out.println("AfterThrowing");
}
//后置通知(方法执行后 方法返回后)
//execution 表示切入点表达式 execution([权限修饰符(可省略)][返回类型(* 表示任意返回类型)][类全路径][方法名(*表示所有的方法)][形参列表(.. 表示任意)])
@AfterReturning(value = "execution(* aop_annotation.User.add(..))")
public void AfterReturning(){
System.out.println("AfterReturning");
}
//环绕通知 利用注解Around实现
//execution 表示切入点表达式 execution([权限修饰符(可省略)][返回类型(* 表示任意返回类型)][类全路径][方法名(*表示所有的方法)][形参列表(.. 表示任意)])
@Around(value = "execution(* aop_annotation.User.add(..))")
public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之前");
}
}
六、完全注解开发实现AOP
创建配置类实现XML配置文件的功能
//代替xml文件进行组件扫描
@Configuration
@ComponentScan("aop_annotation")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}