Spring框架(AOP)
面向对象编程:调用对象中的方法属性。 --》对象是堆中的一块内存空间,包含方法、全局变量。
面向切面编程(AOP):不改变原有代码,直接把要增加的功能的代码切入进去。
AOP相关术语
- 连接点:类里面可以被增强的方法被称为连接点。
- 增强:对原有方法进行增强。
- 切入点:实际被增强的方法就是切入点。
- 通知:实际被增强的逻辑叫做通知(增强)。
- 通知的类型:
- 前置通知(在判断方法之前通知)、后置通知(在判断方法之后通知,切入点方法异常不执行)、环绕通知(在判断方法前、后通知)、异常通知(在判断方法出现异常的情况下通知)、最终通知(无论切入点方法是否出现异常都会通知)。
- 注:方法指的是切入点。
- 切面:一个动作,将通知切入到切入点方法的过程
XML配置方式实现
导入相关依赖(pom.xml)
<dependencies>
<!--Spring框架--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><!--测试--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.2.RELEASE</version></dependency><!--日志--><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><!--测试单元 @Test来自junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!--AOP联盟--><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><!--Spring Aspects--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.0.2.RELEASE</version></dependency><!--aspectj 面向切面的开源框架--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.3</version></dependency></dependencies>
创建Demo类(被增强类)
//Demo类中所有方法都可以作为切入点,此处我们选择insert()作为切入点
public class Demo {public void add(String name,int age){System.out.println("add--------------");}public void delete(String name,int age){System.out.println("delete--------------");}public void update(String name,int age){System.out.println("update--------------");}//在插入之前验证name和agepublic void insert(String name,int age){System.out.println("insert--------------");}
}
创建DemoProxy类(增强类)
public class DemoProxy {public void judge(){System.out.println("对name和age进行验证......");}
}
配置Spring.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 id="demo" class="com.qcby.Demo"/><!--驼峰命名--><bean id="demoProxy" class="com.qcby.DemoProxy"/><!--直接进行配置--><!--配置切面--><aop:config><aop:aspect ref="demoProxy"><!--前置通知--><!--pointcut后是切入点--><!--insert(..) 中(..)表示参数,没有参数括号中不用写--><aop:before method="judge" pointcut="execution(public void com.qcby.Demo.insert(..))"/></aop:aspect></aop:config>
</beans>
测试代码
public class DemoTest {@Testpublic void run(){ApplicationContext context = new ClassPathXmlApplicationContext("Spring.xml");Demo demo = (Demo) context.getBean("demo");demo.insert("张三",18);}
}
测试结果
四种通知实现在Spring.xml中的配置
通知类型 | 执行时机 |
---|---|
前置通知(@Before) | 在切入点方法执行之前执行 |
后置通知(@AfterReturning) | 在切入点方法正常执行后执行(异常时不执行) |
环绕通知(@Around) | 在切入点方法执行前后执行(功能最强大的通知类型) |
异常通知(@AfterThrowing) | 在切入点方法抛出异常时执行 |
最终通知(@After) | 无论切入点方法是否出现异常都会执行(类似finally块) |
<?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="demo" class="com.qcby.Demo"/><!--驼峰命名--><bean id="demoProxy" class="com.qcby.DemoProxy"/><!--直接进行配置--><!--配置切面--><aop:config><aop:aspect ref="demoProxy"><!--前置通知--><!--pointcut后是切入点--><!--insert(..) 中(..)表示参数,没有参数括号中不用写-->
<!-- <aop:before method="judge" pointcut="execution(public void com.qcby.Demo.insert(..))"/>--><!--最终通知-->
<!-- <aop:after method="judge" pointcut="execution(public void com.qcby.Demo.insert(..))"/>--><!--环绕通知-->
<!-- <aop:around method="judge" pointcut="execution(public void com.qcby.Demo.insert(..))"/>--><!--异常通知-->
<!-- <aop:after-throwing method="judge" pointcut="execution(public void com.qcby.Demo.insert(..))"/>--><!--后置通知-->
<!-- <aop:after-returning method="judge" pointcut="execution(public void com.qcby.Demo.insert(..))"/>--></aop:aspect></aop:config>
</beans>
其中环绕通知需要在DemoProxy中进行配置
public class DemoProxy {public void judge(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("对name和age进行验证......,在insert()执行前");//执行切入点proceedingJoinPoint.proceed();System.out.println("对name和age进行验证......,在insert()执行后");}
}
利用注解的方式实现AOP
Demo类整体不变(面向切面编程不会对该类中的代码进行改动,只需在类中加上注解@Service或@Controller表明能够生成bean对象)
Spring.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"xmlns:context="http://www.springframework.org/schema/context"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 http://www.springframework.org/schema/context/spring-context.xsd"><!--开启注解扫描--><context:component-scan base-package="com.qcby"/><!--开启Aspect生成代理对象 第三方工具--><aop:aspectj-autoproxy/>
</beans>
DemoProxy
package com.qcby;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;@Service
@Aspect //表示是增强类
public class DemoProxy {//环绕通知//@Around(value = "execution(public void com.qcby.Demo.insert(..))")public void judge(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("对name和age进行验证......,在insert()执行前");//执行切入点proceedingJoinPoint.proceed();System.out.println("对name和age进行验证......,在insert()执行后");}//前置通知@Before(value = "execution(public void com.qcby.Demo.insert(..))")public void Ajudge() throws Throwable {System.out.println("对name和age进行验证");}//最终通知//@After(value = "execution(public void com.qcby.Demo.insert(..))")public void Bjudge() throws Throwable {System.out.println("对name和age进行验证");}//异常通知//@AfterThrowing(value = "execution(public void com.qcby.Demo.insert(..))")public void Cjudge() throws Throwable {System.out.println("对name和age进行验证");}//后置通知//@AfterReturning(value = "execution(public void com.qcby.Demo.insert(..))")public void Djudge() throws Throwable {System.out.println("对name和age进行验证");}
}