目录

  • 1 基础知识:代理
  • 2 AOP简介
  • 3 Spring使用AOP
  • 3.1 使用spring API接口实现
  • 3.1.1 配置xml
  • 3.1.2 定义服务接口
  • 3.1.3 服务接口实现类
  • 3.1.4 创建切面编程的类(比如添加日志)
  • 3.1.4.1 在方法执行前添加日志
  • 3.1.4.2 在方法执行后添加日志
  • 3.1.5 xml配置aop把二者连接起来
  • 3.1.6 测试方法
  • 3.2 自定义类实现AOP
  • 3.2.1 切面定义
  • 3.2.2 xml配置
  • 3.3 注解实现
  • 3.3.1 pom导入依赖aspectj
  • 3.3.2 注解切面类
  • 3.3.3 aop自动代理配置
  • 3.4 Advice方法种类
  • 3.5 execution表达式
  • 4 总结

1 基础知识:代理

2 AOP简介

Aspect Oriented Programming(面向切面编程),通过预编译方式运行期动态代理实现程序功能统一维护的一种技术。

理解:程序是纵向开发,依次从后端向前端开发编写,假设由x轴表示,那么AOP就是在y轴上进行开发,针对某一个x轴环节,进行处理,比如需要在x轴某个环节新增一个日志功能,就是横向y轴开发,不改变x轴原有功能。

spring 切面环绕_java

3 Spring使用AOP

需要导入依赖

<!--aspectjweaver(AOP使用)-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
    <scope>runtime</scope>
</dependency>

相关概念

  • 横切关注点:跨越应用程序多个模块的方法或功能。与业务逻辑无关,但是是关注的部分。如日志,安全,缓存,事务等等。。。
  • 切面(Aspect):横切关注点(被模块化)的特殊对象。即,它是一个
  • 通知(Advice):切面必须要完成的工作。即,它是一个方法
  • 目标(Target):被通知(被代理)对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):是Advice执行地点的定义。
  • 连接点(JoinPoint):与切入点匹配的执行点。

3.1 使用spring API接口实现

3.1.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: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:annotation-config/>

    <!--自动扫描包-->
    <context:component-scan base-package="xyz.cqulwj"/>
</beans>

3.1.2 定义服务接口

public interface UserService {
    void add();
    void delete();
    void update();
    void select();
}

3.1.3 服务接口实现类

@Component
public class UserServiceImpl implements UserService{
    static Logger logger = Logger.getLogger(UserServiceImpl.class);
    @Override
    public void add() {
        logger.debug("添加元素");
    }

    @Override
    public void delete() {
        logger.debug("删除元素");
    }

    @Override
    public void update() {
        logger.debug("更新元素");
    }

    @Override
    public void select() {
        logger.debug("查询元素");
    }
}

3.1.4 创建切面编程的类(比如添加日志)

3.1.4.1 在方法执行前添加日志
@Component
public class LogBefore implements MethodBeforeAdvice {
    static Logger logger = Logger.getLogger(LogBefore.class);
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        logger.debug(o.getClass().getName()+"的"+method.getName()+"方法被执行了");
    }
}
3.1.4.2 在方法执行后添加日志
@Component
public class LogAfter implements AfterReturningAdvice {
    static Logger logger = Logger.getLogger(LogAfter.class);
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        logger.debug("执行了"+method.getName()+"方法");
    }
}

3.1.5 xml配置aop把二者连接起来

<!--aop配置-->
<aop:config>
    <!--切入点-->
    <aop:pointcut id="pointcut" expression="execution(* xyz.cqulwj.service.UserServiceImpl.*(..))"/>
    <!--执行环绕增加-->
    <aop:advisor advice-ref="logBefore" pointcut-ref="pointcut"/>
    <aop:advisor advice-ref="logAfter" pointcut-ref="pointcut"/>

</aop:config>

把切面编程的代码运用到原实现中。

3.1.6 测试方法

@Test
public void serviceTest() {
    //获得上下文
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

    //获得对象
    UserService userService=(UserService) context.getBean("userServiceImpl");

    //显示输出
    userService.add();
    userService.delete();
    userService.update();
    userService.select();
}

可以看到在每一个接口方法执行前后,都有日志写入。

3.2 自定义类实现AOP

3.2.1 切面定义

@Component
public class DiyLog {
    static Logger logger = Logger.getLogger(DiyLog.class);
    public void before(){
        logger.debug("方法执行前");
    }
    public void after(){
        logger.debug("方法执行后");
    }
}

注意要加上@Component注册成bean;

3.2.2 xml配置

<!--aop配置-->
<aop:config>
    <aop:aspect ref="diyLog">
        <aop:pointcut id="point" expression="execution(* xyz.cqulwj.service.UserServiceImpl.*(..))"/>
        <aop:before method="before" pointcut-ref="point"/>
        <aop:after method="after" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>

aop:aspectDiyLog作为切面(要插入的类),aop:pointcut定义了切入点,需要给哪个类的哪些方法切入,aop:beforeaop:after定义了要在切入点执行切面的哪些方法,也叫作通知。

3.3 注解实现

3.3.1 pom导入依赖aspectj

<!--AspectJ支持-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.5</version>
</dependency>

3.3.2 注解切面类

@Component
@Aspect
public class DiyLog {
    static Logger logger = Logger.getLogger(DiyLog.class);

    @Before("execution(* xyz.cqulwj.service.UserServiceImpl.*(..))")
    public void before(){
        logger.debug("方法执行前");
    }

    @After("execution(* xyz.cqulwj.service.UserServiceImpl.*(..))")
    public void after(){
        logger.debug("方法执行后");
    }
}

3.3.3 aop自动代理配置

<!--aop自动代理-->
<aop:aspectj-autoproxy/>

3.4 Advice方法种类

  • @Before:方法执行前执行该通知;
  • @AfterThrowing:抛出异常后执行;
  • @AfterReturning:方法返回值执行后;
  • @After:方法执行之后(包括@AfterThrowing后和@AfterReturning)执行;

比较特殊就是@Around先进行以上对于原方法的拼接操作,然后作为ProceedingJoinPoint类型的对象传递给@Around函数体,可以使用jp.proceed()来执行拼接后的方法,另外,在@Around中还会拼接@Before@After的代码。

[xyz.cqulwj.div.DiyLog]--->@Before方法执行前
	[xyz.cqulwj.div.DiyLog]--->@Around环绕前
	[xyz.cqulwj.div.DiyLog]--->@Around方法执行
		[xyz.cqulwj.div.DiyLog]--->@Before方法执行前
				[xyz.cqulwj.service.UserServiceImpl]-执行目标对象函数
			[xyz.cqulwj.div.DiyLog]--->@AfterReturning返回值执行后
		[xyz.cqulwj.div.DiyLog]--->@After方法执行后
	[xyz.cqulwj.div.DiyLog]--->@Around环绕后
[xyz.cqulwj.div.DiyLog]--->@After方法执行后

3.5 execution表达式

4 总结

用动态代理的思想去理解AOP会更好理解,定义一个切面类,里面有一些切面方法,这些方法需要在目标对象的一些方法的生命周期(开始前、返回值后、异常后、结束后等)执行。