fighting!fighting!
AOP
-
面向切面编程(Aspect Oriented Programming)
-
正常程序执行流程都是纵向执行流程
- 又叫 面向切面编程,在原有的纵向执行流程中添加横切面
- 不需要修改原有程序的代码(体现程序的高扩展性)
- 高扩展性
- 原有功能 相当于释放了部分逻辑,让职责更加明确
-
面向切面编程是什么?
- 在程序原有纵向执行流程中,针对某一个或某一些方法添加通知,形成横切面过程就叫面向切面编程
-
常用概念
- 原有功能:切点,pointcut
- 前置通知:在切点之前执行的功能,before advice
- 后置通知:在切点之后执行的功能,after advice
- 如果切点执行过程中出现异常,会触发异常通知,throws advice
- 所有功能总称叫做切面
- 织入:把切面嵌入到原有功能的过程叫做织入
-
spring提供了 2种 AOP实现方式
-
Schema-based
- 每个通知都需要实现接口后类
- 配置spring配置文件时在 aop:config 配置
-
AspectJ
-
每个通知不需要实现接口或类
-
配置spring配置文件是在aop:config的子标签aop:aspect中配置
-
-
Schema-based 实现步骤
-
导入jar
-
新建通知类
-
新建前置通知类
public class MyBeforeAdvice implements MethodBeforeAdvice{ @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { // TODO Auto-generated method stub System.out.println("执行前置通知"); } }
-
新建后置通知类
- arg0:切点方法的返回值
- arg1:切点方法的对象
- arg2:切点方法的参数
- arg3:切点方法所在类的对象
public class MyAfterAdvice implements AfterReturningAdvice{ @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { // TODO Auto-generated method stub System.out.println("执行后置通知"); } }
-
配置spring配置文件
- 引入 aop 命名空间
- 配置通知类的
- 配置切面
- *表示通配符,它可以配置任意方法名,任意类名,任意一级包名
- 如果希望匹配任意方法参数 (..)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="https://www.springframework.org/schema/beans" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:aop="https://www.springframework.org/schema/aop" xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd "> <!-- 配置通知类对象,在切面中引入 --> <bean id="mybefore" class="work.idler.advice.MyBeforeAdvice"></bean> <bean id="myafter" class="work.idler.advice.MyAfterAdvice"></bean> <!-- 配置切面 --> <aop:config> <!-- 配置切点 --> <aop:pointcut expression="execution(* work.idler.test.Demo.demo2())" id="mypoint"/> <!-- 通知 --> <aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/> <aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/> </aop:config> <!-- 配置demo类,测试用 --> <bean id="demo" class="work.idler.test.Demo"></bean> </beans>
-
编写测试代码
public class Test { public static void main(String[] args) { // Demo demo = new Demo(); // demo.demo1(); // demo.demo2(); // demo.demo3(); ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); Demo demo = ac.getBean("demo",Demo.class); demo.demo1(); demo.demo2(); demo.demo3(); } }
-
运行结果
demo1 执行前置通知 demo2 执行后置通知 demo3
-
配置异常通知的步骤(AspectJ方式)
- 只用当切点报异常才能触发异常通知
- 在spring中只有AspectJ方式提供了异常通知的办法
- 如果希望通过 schema-base 实现需要按照特定的要求自己编写方法
- 实现步骤:
- 新建类,任意名称的方法
- spring配置文件中配置
- acp:aspect的ref 属性表示:方法在那个类中
- aop:xxxx/表示什么通知
- method:当触发这个通知时,调用那个方法
- throwing:异常对象名必须和通知中方法参数名相同
异常通知(Schema-based方式)
环绕通知(Schema-based方式)
- 把前置和后置通知写到一个通知中,组成了环绕通知
使用注解(基于 Aspect)
-
spring不会自动去寻找注解必须告诉spring那些包下的类可能有注解
-
引入xmlns:context
<context:component-scan base-package="work.idler.advice,work.idler.test"></context:component-scan>
-
-
@Component
- 相当于
- 如果没有参数,把类名首字母变小写,相当于<bean id="/>
- @Component("自定义名称")
- 相当于
-
实现步骤
-
在spring配置文件中设置注解在哪些包中
<context:component-scan base-package="work.idler.advice,work.idler.test"></context:component-scan>
-
在Demo类中添加@Componet
- 在方法上添加@Pointcut("")
@Component("demo123") public class Demo { @Pointcut("execution(* work.idler.test.Demo.demo1())") public void demo1() { // int i=5/0; System.out.println("demo1"); } }
-
在通知类中配置
- @Component 类被spring管理
- @Aspect 相当于 aop:aspect/表示通知方法在当前类中
@Component @Aspect public class MyAdvice { @Before("work.idler.test.Demo.demo1()") public void mybefore() { System.out.println("前置"); } @After("work.idler.test.Demo.demo1()") public void myafter() { System.out.println("后置"); } @AfterThrowing("work.idler.test.Demo.demo1()") public void mythrow() { System.out.println("异常"); } @Around("work.idler.test.Demo.demo1()") public Object myarround(ProceedingJoinPoint p) throws Throwable{ System.out.println("环绕-前置"); Object result = p.proceed(); System.out.println("环绕-后置"); return result; } }
-
代理设计模式
-
前人总结的一套解决特定问题的代码
-
优点:
- 保护真实对象
- 让真实对象职责更明确
- 扩展
-
代理设计模式
- 真实对象(老总)
- 代理对象(秘书)
- 抽象对象(干什么的)
静态代理设计模式
-
由代理对象代理所有真实对象的功能
- 自己编写代理类
- 每个代理的功能需要单独编写
-
缺点:
- 当代理功能多时,代理类中方法需要写很多
动态代理
-
为了解决静态代理频繁编写代理功能缺点
-
分类:
- jdk 提供的
- cglib 动态代理
JDK 动态代理
-
和cglib 动态代理对比
-
优点:jdk 自带,不需要频繁导入jar
-
缺点:
-
真实对象必须实现接口
-
利用反射机制,效率不高
-
-
cglib 动态代理
-
优点:
- 基于字节码,生成真实对象的子类
- 运行效率高于 JDK 动态代理
- 不需要实现接口
- 基于字节码,生成真实对象的子类
-
缺点:
- 非JDK功能,需要额外导入jar
-
使用 spring aop时,只要出现Proxy和真实对象转换异常
- 设置为true 使用 cglib
- 设置为false 使用 jdk(默认值)
<context:component-scan base-package="work.idler.advice,work.idler.test"></context:component-scan>