Spring5 AOP的使用
AOP的使用
-
- 1. AOP的概念
- 2. AOP底层原理
-
- 2.1 有接口情况,使用JDK动态代理
- 2.2 没有接口,使用CGLIB动态代理
- 3. AOP常用术语
- 4. AOP操作
-
- 4.1 准备工作
- 4.2 基于全注解方式实现AOP操作
- 4.3 基于XML配置文件实现AOP操作
1. AOP的概念
(1)AOP是Aspect Oriented Programming的缩写,意思是面向切面编程,提供从另一个角度来考虑程序结构以完善面向对象编程(相对于OOP),即可以通过在编译期间、装载期间或运行期间实现在不修改源代码的情况下给程序动态添加功能的一种技术。通俗点说就是把可重用的功能提取出来,然后将这些通用功能在合适的时候织入到应用程序中;比如安全,日记记录,这些都是通用的功能,我们可以把它们提取出来,然后在程序执行的合适地方织入这些代码并执行它们,从而完成需要的功能并复用了这些功能。
(2)使用用户登录例子说明AOP
解释:假如现在我们在简单用户登录的逻辑上想要增加一个逻辑——通过判断登录用户的权限(管理员,普通用户等),然后使其登录到不同的页面。
原始解决方法:通过修改源代码来实现不同权限用户的登录。
if 管理员...else if 普通用户...
源代码相互之间的耦合性较高,可能加一个条件判断语句也会影响其他代码,所以为了降低耦合性,AOP横空出世,可以“凌驾”于源代码之上,不用修改源代码就实现不同权限用户的登录。
2. AOP底层原理
AOP 底层使用动态代理 ,动态代理有两种情况
2.1 有接口情况,使用JDK动态代理
创建接口实现类代理对象,增强类的方法
2.2 没有接口,使用CGLIB动态代理
创建子类的代理对象,增强类的方法。
3. AOP常用术语
- 连接点
一个类中哪些方法可以被增强,这些被增强的方法称为连接点 - 切入点(被增强方法中)
一个类可以有多个方法被增强,但只有被实际增强的方法,才可称为切入点 - 通知(增强方法中)
- 增强方法中,实际去做增强的逻辑部分被称作通知
- 通知的类型
a) 前置通知
b) 后置通知
c) 环绕通知
d) 异常通知
e) 最终通知
- 切面(一个动作过程)
切面是把通知应用到切入点的过程。
4. AOP操作
4.1 准备工作
- Spring 框架一般都是基于 AspectJ 实现 AOP 操作,AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,进行 AOP 操作
- 基于 AspectJ 实现 AOP 操作有两种方式:1)基于 xml 配置文件实现 (2)基于注解方式实现 推荐使用
- 引入相关jar包
- 切入点表达式,如下:
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强 (2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )(3)例子如下: 例 1:对 com.spring.dao.BookDao 类里面的 add 进行增强execution(* com.spring.dao.BookDao.add(..)) 例 2:对 com.spring.dao.BookDao 类里面的所有的方法进行增强execution(* com.spring.dao.BookDao.* (..)) 例 3:对 com.spring.dao 包里面所有类,类里面所有方法进行增强execution(* com.spring.dao.*.* (..))
4.2 基于全注解方式实现AOP操作
- 创建被增强类
//被增强类@Component //此注解的作用为创建对象public class User { public void add() { System.out.println("add...... "); }}
- 创建增强类
@Componentpublic class UserProxy { }
- 创建配置类
@Configuration //作为配置类,替代xml配置文件@ComponentScan(basePackages = {"com.spring"})@EnableAspectJAutoProxy(proxyTargetClass = true)public class ConfigAop {}
@ComponentScan(basePackages = {“com.spring”})@EnableAspectJAutoProxy(proxyTargetClass = true)等价于xml文件中的如下配置
<context:component-scan base-package="com.spring"></context:component-scan><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 配置不同类型的通知
@Component@Aspect //生成代理对象public class UserProxy { //前置通知 //@before注解表示作为前置通知 @Before(value = "execution(* com.spring.aop.User.add(..))") public void before() { System.out.println("before....."); } @After(value = "execution(* com.spring.aop.User.add(..))") public void after() { System.out.println("After....."); } @AfterReturning(value = "execution(* com.spring.aop.User.add(..))") public void afterReturning() { System.out.println("AfterReturning....."); } @AfterThrowing(value = "execution(* com.spring.aop.User.add(..))") public void afterThrowing() { System.out.println("AfterThrowing....."); } @Around(value = "execution(* com.spring.aop.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) { System.out.println("环绕之前...."); try { proceedingJoinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("环绕之后...."); }}
- 测试类
public class TestAop { @Test public void testAop() { ApplicationContext context = new AnnotationConfigApplicationContext(ConfigAop.class); User user = context.getBean("user", User.class); user.add(); }
-
测试结果1
-
代码优化——相同切入点抽取
@Component@Aspect //生成代理对象public class UserProxy { //相同切入点抽取 @Pointcut(value = "execution(* com.spring.aop.User.add(..))") public void point() { } //前置通知 //@before注解表示作为前置通知 @Before(value = "point()") public void before() { System.out.println("before....."); } @After(value = "point()") public void after() { System.out.println("After....."); } @AfterReturning(value = "point()") public void afterReturning() { System.out.println("AfterReturning....."); } @AfterThrowing(value = "point()") public void afterThrowing() { System.out.println("AfterThrowing....."); } @Around(value = "point()") public void around(ProceedingJoinPoint proceedingJoinPoint) { System.out.println("环绕之前...."); try { proceedingJoinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("环绕之后...."); }}
- 设置增强类优先级
当有多个增强类对一个方法进行增强时,需要设置增强类的优先级。
通过@Order(数字类型)注解实现,数字越小,优先级越高
//增强类1@Component@Aspect //生成代理对象@Order(3)public class UserProxy { //相同切入点抽取 @Pointcut(value = "execution(* com.spring.aop.User.add(..))") public void point() { } //前置通知 //@before注解表示作为前置通知 @Before(value = "point()") public void before() { System.out.println("before....."); } @After(value = "point()") public void after() { System.out.println("After....."); } @AfterReturning(value = "point()") public void afterReturning() { System.out.println("AfterReturning....."); } @AfterThrowing(value = "point()") public void afterThrowing() { System.out.println("AfterThrowing....."); } @Around(value = "point()") public void around(ProceedingJoinPoint proceedingJoinPoint) { System.out.println("环绕之前...."); try { proceedingJoinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("环绕之后...."); }}//增强类2@Component@Aspect@Order(1) //数字越小,优先级越高public class PersonProxy { @Pointcut(value = "execution(* com.spring.aop.User.add(..))") public void point() { } @Before(value = "point()") public void before() { System.out.println("增强类2的before..."); }}
- 测试结果
4.3 基于XML配置文件实现AOP操作
<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.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="user" class="com.spring.aop.User"></bean> <bean id="userProxy" class="com.spring.aop.UserProxy"></bean> <aop:config> <aop:pointcut id="p" expression="execution(* com.spring.aop.User.add(..))"/> <aop:aspect ref="userProxy"> <aop:before method="before" pointcut-ref="p"></aop:before> </aop:aspect> </aop:config></beans>
跟上面的基于注解的通知效果是一样的
//测试类@Testpublic void testAop() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); User user = context.getBean("user", User.class); user.add();}