原理

AOP 即 Aspect Oriented Program 面向切面编程
首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。
所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务
所谓的周边功能,比如性能统计,日志,事务管理等等

周边功能在Spring的面向切面编程AOP思想里,即被定义为切面

在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发
然后把切面功能和核心业务功能 “编织” 在一起,这就叫AOP

常用场景:

切面一般对如下内容进行封装,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。

  • 事务
  • 权限处理
  • 日志
  • 性能检测
  • 异常处理

示例工程:

图片.png

代码:

XML配置AOP

applicationContext.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xmlns:tx="http://www.springframework.org/schema/tx"
  6. xmlns:p="http://www.springframework.org/schema/p"
  7. xmlns:context="http://www.springframework.org/schema/context"
  8. xsi:schemaLocation="
  9. http://www.springframework.org/schema/beans
  10. http://www.springframework.org/schema/beans/spring-beans.xsd
  11. http://www.springframework.org/schema/context
  12. http://www.springframework.org/schema/context/spring-context.xsd
  13. http://www.springframework.org/schema/aop
  14. http://www.springframework.org/schema/aop/spring-aop.xsd">
  15. <bean id="productservice" class="com.how2java.service.ProductService"/>
  16. <bean id="loggerAspect" class="com.how2java.aspect.LoggerAspect"/>
  17. <bean id="diy" class="com.how2java.aspect.DiyAspect"/>
  18. <aop:config>
  19. <aop:pointcut id="loggerCutpoint"
  20. expression=
  21. "execution(* com.how2java.service.ProductService.*(..)) "/>
  22. <aop:aspect id="logAspect" ref="loggerAspect">
  23. <aop:around pointcut-ref="loggerCutpoint" method="log" />
  24. <aop:around pointcut-ref="loggerCutpoint" method="performance" />
  25. </aop:aspect>
  26. <aop:aspect ref="diy">
  27. <aop:pointcut id="point" expression="execution(* com.how2java.service.ProductService.*(..))"/>
  28. <aop:before method="before" pointcut-ref="point"/>
  29. <aop:after method="after" pointcut-ref="point"/>
  30. </aop:aspect>
  31. </aop:config>
  32. </beans>

DiyAspect.java
  1. package com.how2java.aspect;
  2. public class DiyAspect {
  3. public void before() {
  4. System.out.println("方法执行前");
  5. }
  6. public void after() {
  7. System.out.println("方法执行后");
  8. }
  9. }

LoggerAspect.java
  1. package com.how2java.aspect;
  2. import java.util.Date;
  3. import org.aspectj.lang.ProceedingJoinPoint;
  4. public class LoggerAspect {
  5. public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
  6. System.out.println("start log:" + joinPoint.getSignature().getName());
  7. Object object = joinPoint.proceed();
  8. System.out.println("end log:" + joinPoint.getSignature().getName());
  9. return object;
  10. }
  11. public Object performance(ProceedingJoinPoint joinPoint) throws Throwable {
  12. long startTime = System.nanoTime(); // 获取开始时间
  13. System.out.println(new Date().toString());
  14. Object object = joinPoint.proceed();
  15. long endTime = System.nanoTime(); // 获取结束时间
  16. System.out.println(new Date().toString());
  17. System.out.println("程序运行时间: " + (endTime - startTime) + "ns");
  18. return object;
  19. }
  20. }

ProductService.java
  1. package com.how2java.service;
  2. import org.springframework.stereotype.Component;
  3. public class ProductService {
  4. public void doSomeService(){
  5. System.out.println("doSomeService");
  6. }
  7. public void doSomeOtherService() {
  8. System.out.println("doSomeOtherService");
  9. }
  10. }

TestSpring.java
  1. package com.how2java.test;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. import com.how2java.service.ProductService;
  5. public class TestSpring {
  6. public static void main(String[] args) {
  7. // TODO Auto-generated method stub
  8. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  9. ProductService s = (ProductService) context.getBean("productservice");
  10. s.doSomeService();
  11. s.doSomeOtherService();
  12. }
  13. }

注解配置AOP

applicationContext.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xmlns:tx="http://www.springframework.org/schema/tx"
  6. xmlns:p="http://www.springframework.org/schema/p"
  7. xmlns:context="http://www.springframework.org/schema/context"
  8. xsi:schemaLocation="
  9. http://www.springframework.org/schema/beans
  10. http://www.springframework.org/schema/beans/spring-beans.xsd
  11. http://www.springframework.org/schema/context
  12. http://www.springframework.org/schema/context/spring-context.xsd
  13. http://www.springframework.org/schema/aop
  14. http://www.springframework.org/schema/aop/spring-aop.xsd">
  15. <context:component-scan base-package="com.how2java.aspect"/>
  16. <context:component-scan base-package="com.how2java.service"/>
  17. <aop:aspectj-autoproxy/>
  18. </beans>

DiyAspect.java
  1. package com.how2java.aspect;
  2. import org.aspectj.lang.annotation.After;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.aspectj.lang.annotation.Before;
  5. import org.springframework.stereotype.Component;
  6. @Aspect
  7. @Component
  8. public class DiyAspect {
  9. @Before(value = "execution(* com.how2java.service.ProductService.*(..))")
  10. public void before() {
  11. System.out.println("方法执行前");
  12. }
  13. @After(value = "execution(* com.how2java.service.ProductService.*(..))")
  14. public void after() {
  15. System.out.println("方法执行后");
  16. }
  17. }

LoggerAspect.java
  1. package com.how2java.aspect;
  2. import java.util.Date;
  3. import org.aspectj.lang.ProceedingJoinPoint;
  4. import org.aspectj.lang.annotation.Around;
  5. import org.aspectj.lang.annotation.Aspect;
  6. import org.springframework.stereotype.Component;
  7. @Aspect
  8. @Component
  9. public class LoggerAspect {
  10. @Around(value = "execution(* com.how2java.service.ProductService.*(..))")
  11. public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
  12. System.out.println("start log:" + joinPoint.getSignature().getName());
  13. Object object = joinPoint.proceed();
  14. System.out.println("end log:" + joinPoint.getSignature().getName());
  15. return object;
  16. }
  17. @Around(value = "execution(* com.how2java.service.ProductService.*(..))")
  18. public Object performance(ProceedingJoinPoint joinPoint) throws Throwable {
  19. long startTime = System.nanoTime(); // 获取开始时间
  20. System.out.println(new Date().toString());
  21. Object object = joinPoint.proceed();
  22. long endTime = System.nanoTime(); // 获取结束时间
  23. System.out.println(new Date().toString());
  24. System.out.println("程序运行时间: " + (endTime - startTime) + "ns");
  25. return object;
  26. }
  27. }

ProductService.java
  1. package com.how2java.service;
  2. import org.springframework.stereotype.Component;
  3. @Component("productservice")
  4. public class ProductService {
  5. public void doSomeService(){
  6. System.out.println("doSomeService");
  7. }
  8. public void doSomeOtherService() {
  9. System.out.println("doSomeOtherService");
  10. }
  11. }

TestSpring.java
  1. package com.how2java.test;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. import com.how2java.service.ProductService;
  5. public class TestSpring {
  6. public static void main(String[] args) {
  7. // TODO Auto-generated method stub
  8. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  9. ProductService s = (ProductService) context.getBean("productservice");
  10. s.doSomeService();
  11. s.doSomeOtherService();
  12. }
  13. }

要点:

1.Aop在xml要配置

xmlns:aop=”http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xs

分别对应applicationContext.xml第五行、第十四十五行。

2.Advice主要类型:

  • @Before:该注解标注的方法在业务模块代码执行之前执行,其不能阻止业务模块的执行,除非抛出异常;
  • @AfterReturning:该注解标注的方法在业务模块代码执行之后执行;
  • @AfterThrowing:该注解标注的方法在业务模块抛出指定异常后执行;
  • @After:该注解标注的方法在所有的Advice执行完成后执行,无论业务模块是否抛出异常,类似于finally的作用;
  • @Around:该注解功能最为强大,其所标注的方法用于编写包裹业务模块执行的代码,其可以传入一个ProceedingJoinPoint用于调用业务模块的代码,无论是调用前逻辑还是调用后逻辑,都可以在该方法中编写,甚至其可以根据一定的条件而阻断业务模块的调用;
  • @DeclareParents:其是一种Introduction类型的模型,在属性声明上使用,主要用于为指定的业务模块添加新的接口和相应的实现。
  • @Aspect:严格来说,其不属于一种Advice,该注解主要用在类声明上,指明当前类是一个组织了切面逻辑的类,并且该注解中可以指定当前类是何种实例化方式,主要有三种:singleton、perthis和pertarget,具体的使用方式后面会进行讲解。

    1. 这里需要说明的是,@Before是业务逻辑执行前执行,与其对应的是@AfterReturning,而不是@After@After是所有的切面逻辑执行完之后才会执行,无论是否抛出异常。

    3.在aop中execution的写法规则

    执行表达式的格式如下:
    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
    除了返回类型模式(上面代码片断中的ret-type-pattern),名字模式和参数模式以外,所有的部分都是可选的。 返回类型模式决定了方法的返回类型必须依次匹配一个连接点。 你会使用的最频繁的返回类型模式是 ,它代表了匹配任意的返回类型。 一个全称限定的类型名将只会匹配返回给定类型的方法。名字模式匹配的是方法名。 你可以使用 通配符作为所有或者部分命名模式。 参数模式稍微有点复杂:() 匹配了一个不接受任何参数的方法, 而 (..) 匹配了一个接受任意数量参数的方法(零或者更多)。 模式 () 匹配了一个接受一个任何类型的参数的方法。 模式 (,String) 匹配了一个接受两个参数的方法,第一个可以是任意类型,第二个则必须是String类型。
    一些常见切入点表达式的例子。
    execution(public (..))  任意公共方法的执行;
    execution( set(..))  任何一个以“set”开始的方法的执行;
    execution( com.xyz.service.AccountService.(..))   AccountService接口的任意方法的执行;
    execution( com.xyz.service..(..))  定义在service包里的任意方法的执行;
    execution(
    com.xyz.service...(..))  定义在service包或者子包里的任意方法的执行;

    4.作用同@Component的注解

    @Repository 作用同@Component; 在持久层使用

@Service 作用同@Component; 在业务逻辑层使用

@Controller 作用同@Component; 在控制层使用