AOP和IOC是Spring重要的核心思想

1.AOP定义

AOP ( 面向切面编程),AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。

2.AOP的应用场景

  • 日志记录
  • 事务管理
  • 权限验证
  • 性能监测

        AOP可以拦截指定的方法,并且对方法增强,比如:事务、日志、权限、性能监测等增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离。

3.AOP的作用


AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。

public class ProxyFactory {

    //被代理对象
    IAccountService toProxyService;

    public void setToProxyService(IAccountService toProxyService) {
        this.toProxyService = toProxyService;
    }

    //装配事务工具类
    TransactionUtil transactionUtil;

    public void setTransactionUtil(TransactionUtil transactionUtil) {
        this.transactionUtil = transactionUtil;
    }

    //创建代理
    public IAccountService createProxy(){
      return (IAccountService)  Proxy.newProxyInstance(toProxyService.getClass().getClassLoader(), toProxyService.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object obj =null; //被代理对象的所有方法

                try {
                    transactionUtil.beginTx();
                    obj = method.invoke(toProxyService, args);
                    transactionUtil.commitTx();
                } catch (Exception e) {

                    transactionUtil.rollbackTx();
                } finally {
                    transactionUtil.closeTx();
                }


                return obj;
            }
        });
    }
}

4.Spring AOP的术语

4.1AOP核心概念

4.2Spring AOP通知分类

4.3Spring AOP 织入时期

5.AOP实现方式

SpringAOP+AspectJ

5.1实现步骤:

1.添加依赖,aop与aspectj表达式的依赖
2.创建spring的主配置文件,bean内的命名空间要添加aop的
3.创建业务代码并编写日志记录代码(事务管理代码)
4.将业务层与日志记录层注入spring容器
5.<aop:config>--aop配置
        aop:aspect--aop切面
               aop:before--通知内容与通知类型

  <!--注入业务层-->
    <bean id="serviceImp" class="com.apesource.service.AccountServiceImp"></bean>
    <!--注入日志层-->
    <bean id="logger" class="com.apesource.util.Logger"></bean>

    <!--配置AOP-->
    <aop:config>
        <aop:aspect id="aopAspect" ref="logger">
            <!--切点-->
        <aop:pointcut id="dian" expression="execution(* com.apesource.service.*.*(..))" />
       <!--前置通知-->
            <aop:before method="beforeMethod" pointcut-ref="dian"></aop:before>
            <!--返回通知-->
            <aop:after-returning method="returnMethod" pointcut-ref="dian"></aop:after-returning>
            <!--异常通知-->
            <aop:after-throwing method="throwingMethod" pointcut-ref="dian"></aop:after-throwing>
            <!--后置通知-->
            <aop:after method="afterMethod" pointcut-ref="dian"></aop:after>
        <aop:around method="arroundMethod" pointcut-ref="dian"></aop:around>
        </aop:aspect>
    </aop:config>

5.2切点表达式配置语法

execution(修饰符 返回值 包名称.类名称.方法名称(参数列表))
eg:
    execution(public void com.apesource.service.ServiceImp.findAll())
1.修饰符可以省略代表任意
    execution(返回值 包名称.类名称.方法名称(参数列表))
2.返回值可以使用“*”代表任意
    execution(* 包名称.类名称.方法名称(参数列表))
3.包名可以使用“*”代表任意名称
    execution(* *.*.*.类名称.方法名称(参数列表))
4.包名可以使用“..”代表任意个数
    execution(* *...类名称.方法名称(参数列表))
5.类名与方法名可以使用“*”代表任意
    execution(* *...*.*(参数列表))
6.参数列表可以使用".."代表任意个数任意类型
    execution(* *...*.*(..))

5.3使用Aspectj实现切面,使用Spring AOP进行配置

public class Logger {

    public void beforeMethod(){
        System.out.println("前置通知");
    }
    public void returnMethod(){
        System.out.println("返回通知");
    }
    public void throwingMethod(){
        System.out.println("异常通知");
    }
    public void afterMethod(){
        System.out.println("后置通知");
    }

    public Object arroundMethod(ProceedingJoinPoint pjp){

        Object obj =null;

        try {
            System.out.println("前置通知");
            //主业务代码
            Object[] args =pjp.getArgs();
            obj =pjp.proceed(args);
//            int a = 10/0;
            System.out.println("返回通知");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("异常通知");
        } finally {
            System.out.println("后置通知");
        }
        return obj;


    }

}

5.4注解版

@Component
@Aspect //切面
public class Logger {

    @Pointcut("execution(* com.apesource.service.AccountServiceImp.update())")
    public void dian(){

    }

    //前置通知
    @Before("dian()")
    public void beforeMethod(){
        System.out.println("前置通知==>日志类logger中调用BeforeLogger方法进行日志记录");
    }

    //返回通知
    @AfterReturning("dian()")
    public void returnMethod(){
        System.out.println("返回通知");
    }
    //异常通知
    @AfterThrowing("dian()")
    public void throwingMethod(){
        System.out.println("异常通知");
    }

    //后置通知
    @After("dian()")
    public void afterMethod(){
        System.out.println("后置通知");
    }


//    @Around("dian()")
    public Object arroundMethod(ProceedingJoinPoint pjp){

        Object obj =null;

        try {
            System.out.println("前置通知环绕通知的");
            //主业务代码
            Object[] args =pjp.getArgs();
            obj =pjp.proceed(args);
            int a = 10/0;
            System.out.println("返回通知");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("异常通知");
        } finally {
            System.out.println("后置通知");
        }
        return obj;


    }

}

6.Spring AOP的实现原理


Spring的AOP实现原理其实很简单,就是通过动态代理实现的。

Spring AOP 采用了两种混合的实现方式:JDK 动态代理和 CGLib 动态代理。

JDK动态代理:Spring AOP的首选方法。 每当目标对象实现一个接口时,就会使用JDK动态代理。目标对象必须实现接口
CGLIB代理:如果目标对象没有实现接口,则可以使用CGLIB代理。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐