AOP 面向切面编程

AOP学习

AOP概述

  AOP(Aspect Oriented Programming)是面向切面编程,是OOP的扩展,可以解决OOP代码开发过程中遇到的问题。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP的应用场景

  应用场景有很多,这里只列举一小部分

  • 打印操作日志,防止小伙伴删库跑路
  • 测试系统性能
  • 事务控制

AOP和Spring AOP、AspectJ

  • AOP最早是AOP联盟提出的,并且制定规范
  • Spring AOP 遵循AOP规范,Spring AOP只能在Spring中使用
  • AspectJ是一个面向切面的框架,它扩展了Java语言,AspectJ定义了AOP语法。Aspect可以在Spring框架以及其他框架内使用

AOP实现原理

AOP实现是通过动态代理实现的

  • jdk动态代理: 对实现接口的类产生代理
  • cglib动态代理:对没有实现接口的类产生代理

Spring底层如果类实现了接口用jdk动态代理,没有实现接口默认用CGLIB动态代理。当然这是默认的,如果需要也可以强制使用。当然这是后话了

  

/**
 * JDK动态代理,利用反射完成
 */
public class JdkProxy implements InvocationHandler {
    private Object target;

    public JdkProxy(Class target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object target, Method method, Object[] args) throws Throwable {

        System.out.println("===before===");
        Object obj = method.invoke(target, args);
        System.out.println("===after===");
        return obj;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                this);
    }
}
JDK动态代理
/**
 * Cglib动态代理,通过继承生成目标类的代理类
 */
public class CglibProxy implements MethodInterceptor {

    private Object target;
    public CglibProxy(Object target) {
        this.target = target;
    }
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //方法前增强
        System.out.println("***before***");
        Object obj = methodProxy.invoke(proxy, args);
        //方法后增强
        System.out.println("***after****");
        return obj;
    }

    public Object getInstance() {
        //创建增强器
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(this.target.getClass());
        //设置回调
        enhancer.setCallback(this);
        //返回对象
        Object obj = enhancer.create();
        return obj;
    }
}
Cglib 动态代理
Cglib动态代理,要记得引包
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.8</version>
</dependency>

AOP相关术语

  • Joincut :连接点,可以理解为被拦截的点
  • Pointcut: 切入点,实际上被拦截的点。因为项目中有许多能够被拦截的方法,我们只需根据需求拦截方法
  • Advice: 通知,也叫做增强,可以理解为对方法的增强。比如要在方法的前面增加业务逻辑
  • Introduction:引介,对类的增强,很少用。除非需要更改被代理类的内部属性
  • Weaving: 织入,把通知应用到目标对象的过程
  • Aspect: 切面,是多个通知和切入点的组合
  • Target: 目标对象
  • Proxy: 代理对象

Spring中五种通知类型

  • @Before: 前置通知,在方法之前增强
  • @After: 后置通知,在方法之后增强
  • @AfterReturing: 后置返回通知,可以接收方法的返回值
  • @AfterThrowing: 异常抛出通知,方法有异常的时候执行
  • @Around: 环绕通知,可以在方法执行前和执行后进行增强。事务就是用@Around实现的

Spring整合AOP

 github项目源码地址: https://github.com/AmberBar/Learning/tree/master/SpringAOP

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>groupId</groupId>
    <artifactId>SpringAOP</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.10.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.0.8.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.0.8.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.2</version>
            <!--<scope>test</scope>-->
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.0.8.RELEASE</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.8</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.1</version>
        </dependency>

    </dependencies>
</project>
pom.xml
package com.amber.dao;

public interface ProductDao {

    void create();

    void delete();

    String update();

    void addBatch();

    void delBatch();
}
ProductDao
package com.amber.dao;

public class ProduceDaoImpl implements ProductDao {

    @Override
    public void create() {
        System.out.println("=========this is create method ==============");
    }

    @Override
    public void delete() {
        System.out.println("=========this is delete method ==============");
    }

    @Override
    public String update() {
        System.out.println("=========this is update method ==============");
        return "amber";
    }

    @Override
    public void addBatch() {
        System.out.println("=========this is addBatch method ==============");
    }

    @Override
    public void delBatch() {
        System.out.println("=========this is delBatch method ==============");
        int i = 1/0;
    }
}
ProduceDaoImpl 

基于Xml的形式

<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置扫描-->
    <context:component-scan base-package="com.amber"> </context:component-scan>
    <bean id="userDao" class="com.amber.dao.ProduceDaoImpl"></bean>
    <bean id="aspectOption" class="com.amber.aspect.AspectOption"></bean>

    <!--xml aop配置-->
    <aop:config>
        <!--设置切入点-->
        <aop:pointcut id="create" expression="execution(* com.amber.dao.ProductDao.create(..))" ></aop:pointcut>
        <aop:pointcut id="delete" expression="execution(* com.amber.dao.ProductDao.delete(..))" ></aop:pointcut>
        <aop:pointcut id="delBatch" expression="execution(* com.amber.dao.ProductDao.delBatch(..))" ></aop:pointcut>
        <aop:pointcut id="addBatch" expression="execution(* com.amber.dao.ProductDao.addBatch(..))" ></aop:pointcut>
        <aop:pointcut id="update" expression="execution(* com.amber.dao.ProductDao.update(..))" ></aop:pointcut>
        <!--设置切面类-->
        <aop:aspect ref="aspectOption">
            <!--设置前置通知-->
            <aop:before method="before" pointcut-ref="create"></aop:before>
            <!--设置后置通知-->
            <aop:after method="after" pointcut-ref="delete"></aop:after>
            <!--环绕通知-->
            <aop:around method="around" pointcut-ref="addBatch"></aop:around>
            <!--设置后置返回通知-->
            <aop:after-returning method="afterReturning" pointcut-ref="update" returning="result"></aop:after-returning>
            <!--设置后置异常通知-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="delBatch" throwing="e"></aop:after-throwing>
        </aop:aspect>
    </aop:config>
</beans>
applicationContext_xml.xml
package com.amber.xml;

import com.amber.dao.ProductDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext_xml.xml")
public class ProductDaoTest {

    @Autowired
    ProductDao productDao;

    @Test
    public void beforeTest() {
        productDao.create();
    }

    @Test
    public void afterTest() {
        productDao.delete();
    }

    @Test
    public void afterReturingTest() {
        productDao.update();
    }

    @Test
    public void aroundTest() {
        productDao.addBatch();
    }

    @Test
    public void delBatchTest() {
        productDao.delBatch();
    }

}
测试

基于注解形式

package com.amber.xml;

import com.amber.dao.ProductDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext_xml.xml")
public class ProductDaoTest {

    @Autowired
    ProductDao productDao;

    @Test
    public void beforeTest() {
        productDao.create();
    }

    @Test
    public void afterTest() {
        productDao.delete();
    }

    @Test
    public void afterReturingTest() {
        productDao.update();
    }

    @Test
    public void aroundTest() {
        productDao.addBatch();
    }

    @Test
    public void delBatchTest() {
        productDao.delBatch();
    }

}
测试
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置扫描-->
    <context:component-scan base-package="com.amber"> </context:component-scan>
    <bean id="userDao" class="com.amber.dao.ProduceDaoImpl"></bean>
    <bean id="aspectOption" class="com.amber.aspect.AspectOption"></bean>

    <!--配置注解代理-->
    <aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
</beans>
ApplicationContext.xml
package com.amber.xml.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

/**
 * 基于注解
 */
@Aspect
public class AspectOption {

    /**
     * 前置增强,在方法之前执行
     */

    public void before() {
        System.out.println("AspectOption before");
    }

    /**
     * 后置增强,在方法执行之后执行
     */

    public void after() {
        System.out.println("AspectOption after");
    }

    /**
     * 后置通知增强,可以获取到方法的返回值
     * @param result
     */

    public void afterReturning(String result) {
        System.out.println("AspectOption afterReturning " + result);
    }

    /**
     * 环绕通知,在方法执行之前和执行之后运行
     * @param joinPoint
     */

    public void around(ProceedingJoinPoint joinPoint) {
        System.out.println("AspectOption around before");
        try {
            Object obj = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("AspectOption around after");
    }

    /**
     * 异常抛出通知,方法抛出异常的时候执行
     * @param e
     */

    public void afterReturing(Throwable e) {
        System.out.println("AspectOption afterReturing");
        System.out.println(e);
    }
}
AspectOption
posted @ 2018-10-10 16:43  amberbar  阅读(987)  评论(0编辑  收藏  举报