手写Spring框架准备工做之Spring核心AOP

Spring AOP 知识点思惟导图git

file

什么是 AOP?

AOP(Aspect-oriented Programming) , 名字与 OOP(Object-oriented programming) 仅差一个字母, 其实它是对 OOP 编程的一种补充. AOP 翻译过来叫面向切面编程, 核心就是这个切面. 切面表示从业务逻辑中分离出来的横切逻辑, 好比性能监控, 日志记录, 权限控制等, 这些功能均可以从核心业务逻辑代码中抽离出来. 也就是说, 经过 AOP 能够解决代码耦合问题, 让职责更加单一.正则表达式

咱们来经过代码来理解下概念, 这里有一个转帐业务:spring

public interface IAccountService {
    //主业务逻辑: 转帐
    void transfer();
}
public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转帐主业务.");
    }
}
复制代码

如今有一个需求是, 在转帐以前须要验证用户身份, 通常状况下咱们直接就去修改源代码了:express

public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
	  System.out.println("对转帐人身份进行验证.");

        System.out.println("调用dao层,完成转帐主业务.");
    }
}
复制代码

但做为一个"有经验"的 Java 开发, 咱们知道 “身份验证” 这个业务彻底是能够剥离出来的, 因此使用下代理设计模式的思想:编程

public class AccountProxy implements IAccountService {
    //目标对象
    private IAccountService target;

    public AccountProxy(IAccountService target) {
        this.target = target;
    }

    /**
     * 代理方法,实现对目标方法的功能加强
     */
    @Override
    public void transfer() {
        before();
        target.transfer();
    }

    /**
     * 身份验证
     */
    private void before() {
        System.out.println("对转帐人身份进行验证.");
    }
}

public class Client {
    public static void main(String[] args) {
        //建立目标对象
        IAccountService target = new AccountServiceImpl();
        //建立代理对象
        AccountProxy proxy = new AccountProxy(target);
        proxy.transfer();
    }
}
复制代码

AOP 的实现原理就是代理设计模式, 上面这个是静态代理, 但 Spring AOP 是经过动态代理实现的, 因此咱们须要了解下动态代理: blog.csdn.net/litianxiang…后端

AOP 并非由 Spring 首创, 在 Spring 以前就有了 AOP 开源框架—AspectJ, 并且 AspectJ 的 AOP 功能要比 Spring 更强大, 因此在 Spring 的后期版本中就集成了 AspectJ. 但咱们仍是有必要了解下 Spring 的 AOP 功能.设计模式

AOP 术语

  • Targe 目标对象
  • Joinpoint 链接点, 全部可能被加强的方法都是链接点.
  • Pointcut 切入点, 将被加强的方法.
  • Advice 加强, 从主业务逻辑中剥离出来的横切逻辑.
  • Aspect 切面, 切入点加上加强就是切面.
  • Weaving 织入, 把切面应用到目标对象上的过程.
  • Proxy 代理对象, 被加强过的目标对象.

Advice 常见类型

  • 前置加强 org.springframework.aop.MethodBeforeAdvice, - 在目标方法执行前实施加强.
  • 后置加强 org.springframework.aop.AfterReturningAdvice, 在目标方法执行后实施加强.
  • 环绕加强 org.aopalliance.intercept.MethodInterceptor, 在目标方法执行先后都实施加强.
  • 异常抛出加强 org.springframework.aop.ThrowsAdvice, 在方法抛出异常后实施加强.
  • 引入加强 org.springframework.aop.IntroductionInterceptor, 对类进行加强, 即在目标类中添加一些新的方法和属性.

Spring AOP 之编程式

咱们用编程式来感觉下 Spring AOP. 下面例子使用的是环绕加强.bash

(1) 业务类架构

public interface IAccountService {
    //主业务逻辑: 转帐
    void transfer();
}
public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转帐主业务.");
    }
}
复制代码

(2) 加强mvc

public class AccountAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        before();
        Object result = invocation.proceed();
        after();
        return result;
    }

    private void before() {
        System.out.println("Before");
    }

    private void after(){
        System.out.println("After");
    }
}
复制代码

(3) 测试

public class Test {
    public static void main(String[] args) {
        //建立代理工厂
        ProxyFactory proxyFactory = new ProxyFactory();
        //配置目标对象
        proxyFactory.setTarget(new AccountServiceImpl());
        //配置加强
        proxyFactory.addAdvice(new AccountAdvice());

        IAccountService proxy = (IAccountService) proxyFactory.getProxy();
        proxy.transfer();
    }
}

结果:
Before
调用dao层,完成转帐主业务.
After
复制代码

Spring AOP 之声明式

Spring AOP 之声明式就是使用配置文件来声明各类 bean.

(1) 业务类

public interface IAccountService {
    //主业务逻辑: 转帐
    void transfer();
}
public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转帐主业务.");
    }
}
复制代码

(2) 加强

public class AccountAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        before();
        Object result = invocation.proceed();
        after();
        return result;
    }

    private void before() {
        System.out.println("Before");
    }

    private void after(){
        System.out.println("After");
    }
}
复制代码

(3) 配置文件

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--声明bean-->
    <bean id="accountService" class="org.service.impl.AccountServiceImpl"></bean>
    <bean id="accountAdvice" class="org.aspect.AccountAdvice"></bean>

    <!--配置代理工厂-->
    <bean id="accountProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--目标接口-->
        <property name="interfaces" value="org.service.IAccountService"/>
        <!--目标对象-->
        <property name="target" ref="accountService"/>
        <!--加强-->
        <property name="interceptorNames" value="accountAdvice"/>
    </bean>

</beans>
复制代码

(4) 测试

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-service.xml");
        IAccountService proxy = (IAccountService) context.getBean("accountProxy");
        proxy.transfer();
    }
}

结果:
Before
调用dao层,完成转帐主业务.
After
复制代码

Spring AOP 之切面

前面的编程式和声明式都没有用到切面, 他们对一个类中的全部方法都实施了加强. 若是咱们须要针对类中的某个方法进行加强, 就可使用切面来解决这个问题.

在声明式的代码基础上修改下配置文件, 加入切面:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--声明bean-->
    <bean id="accountService" class="org.service.impl.AccountServiceImpl"></bean>
    <bean id="accountAdvice" class="org.aspect.AccountAdvice"></bean>

    <!--配置切面-->
    <bean id="accountAspect" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!--加强-->
        <property name="advice" ref="accountAdvice"/>
        <!--切入点-->
        <property name="pattern" value="org.service.impl.AccountServiceImpl.transfer.*"/>
    </bean>


    <!--配置代理-->
    <bean id="accountProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--目标对象-->
        <property name="target" ref="accountService"/>
        <!--切面-->
        <property name="interceptorNames" value="accountAspect"/>
    </bean>

</beans>
复制代码

这里的切面配置的是基于正则表达式的 RegexpMethodPointcutAdvisor, 表示拦截全部以 “transfer” 开头的方法. 除此以外, Spring AOP 还有如下配置:

  • org.springframework.aop.support.DefaultPointcutAdvisor 匹配继承了该类的切面.
  • org.springframework.aop.support.NameMatchMethodPointcutAdvisor 根据方法名称进行匹配.
  • org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor 用于匹配静态方法.

Spring AOP 之自动代理

让用户去配置一个或少数几个代理, 彷佛还能够接受, 但随着项目的扩大, 代理配置会愈来愈多, 这个时候再让你手动配置, 那整我的都很差了. 不过不用担忧, Spring AOP 为咱们提供了自动生成代理的功能.

在声明式的代码基础上修改下配置文件和测试类:

(1) 配置文件

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--声明bean-->
    <bean id="accountService" class="org.service.impl.AccountServiceImpl"></bean>
    <bean id="accountAdvice" class="org.aspect.AccountAdvice"></bean>

    <!--配置切面-->
    <bean id="accountAspect" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!--加强-->
        <property name="advice" ref="accountAdvice"/>
        <!--切入点-->
        <property name="pattern" value="org.service.impl.AccountServiceImpl.transfer.*"/>
    </bean>

    <!--配置自动代理: 自动扫描全部切面类, 并为其生成代理-->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
</beans>
复制代码

(2) 测试

public class Test{
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-service.xml");
        IAccountService proxy = (IAccountService) context.getBean("accountService");
        proxy.transfer();
    }
}

结果:
Before
调用dao层,完成转帐主业务.
After
复制代码

Spring + AspectJ

在企业开发中几乎不使用 Spring 自身的 AOP 功能, 而是用 AspectJ 代替, Spring 在后期本身集成了 AspectJ 也间接证实了 AspectJ 的强大, 咱们下面来了解下 Spring + AspectJ.

AspectJ 加强类型

  • 前置加强
    注解: @Before, 配置: < aop:before>
  • 后置加强
    注解: @After, 配置: < aop:after>
  • 环绕加强
    注解: @Around, 配置: < aop:around>
  • 异常抛出加强
    注解: @AfterThrowing, 配置: < aop:after-throwing>
  • 引入加强
    注解: @DeclareParents, 配置: < aop:declare-parents> 切入点表达式
execution(* org.service.impl.AccountServiceImpl.*(..))
复制代码
  • execution()表示拦截方法, 括号中可定义须要匹配的规则.
  • 第一个 “*” 表示方法的返回值是任意的.
  • 第二个 “*” 表示匹配该类中全部的方法.
  • (…) 表示方法的参数是任意的. 能够看到, AspectJ 的切入点表达式要比 Spring AOP 的正则表达式可读性更强.

Spring AspectJ 实例—基于配置

(1) 业务类

public interface IAccountService {
    //主业务逻辑: 转帐
    void transfer();
}
public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转帐主业务.");
    }
}
复制代码

(2) 加强

public class AccountAdvice{
    //前置加强
    public void myBefore(JoinPoint joinPoint){
        before();
    }

    //后置加强
    public void myAfter(JoinPoint joinPoint) {
        after();
    }

    //环绕加强
    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
        before();
        Object result  = joinPoint.proceed();
        after();
        return result;
    }

    //抛出异常加强
    public void myThrowing(JoinPoint joinPoint, Throwable e) {
        System.out.println("抛出异常加强: " + e.getMessage());
    }


    private void before() {
        System.out.println("Before");
    }

    private void after(){
        System.out.println("After");
    }
}
复制代码

(3) 配置

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--声明bean-->
    <bean id="accountService" class="org.service.impl.AccountServiceImpl"></bean>
    <bean id="accountAdvice" class="org.aspect.AccountAdvice"></bean>

    <!--切面-->
    <aop:config>
        <aop:aspect ref="accountAdvice">
            <!--切入点表达式-->
            <aop:pointcut expression="execution(* org.service.impl.AccountServiceImpl.*(..))" id="myPointCut"/>
            <!--环绕加强-->
            <aop:around method="myAround" pointcut-ref="myPointCut"/>
        </aop:aspect>
    </aop:config>
</beans>
复制代码

(4) 测试

public class Test{
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-service.xml");
        IAccountService proxy = (IAccountService) context.getBean("accountService");
        proxy.transfer();
    }
}

结果:
Before
调用dao层,完成转帐主业务.
After
复制代码

Spring AspectJ 实例—基于注解

(1) 业务类

public interface IAccountService {
    //主业务逻辑: 转帐
    void transfer();
}
@Component
public class AccountServiceImpl implements IAccountService {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转帐主业务.");
    }
}
复制代码

(2) 切面

这里就再也不叫加强了, 由于有了切入点和加强, 叫切面更好.

@Component
@Aspect
public class AccountAspect{
    //切入点
    @Pointcut("execution(* org.tyshawn.service.impl.AccountServiceImpl.*(..))")
    private void pointCut(){};

    //前置加强
    @Before("pointCut()")
    public void myBefore(JoinPoint joinPoint){
        before();
    }

    //后置加强
    @After("pointCut()")
    public void myAfter(JoinPoint joinPoint) {
        after();
    }

    //环绕加强
    @Around("pointCut()")
    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
        before();
        Object result  = joinPoint.proceed();
        after();
        return result;
    }

    //抛出异常加强
    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void myThrowing(JoinPoint joinPoint, Throwable e) {
        System.out.println("抛出异常加强: " + e.getMessage());
    }

    private void before() {
        System.out.println("Before");
    }

    private void after(){
        System.out.println("After");
    }
}
复制代码

(3) 配置

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--注解扫描-->
    <context:component-scan base-package="org.tyshawn"></context:component-scan>
    <!--自动代理 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
复制代码

(4) 测试

public class Test{
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-service.xml");
        IAccountService proxy = (IAccountService) context.getBean("accountServiceImpl");
        proxy.transfer();
    }
}
结果:
Before
Before
调用dao层,完成转帐主业务.
After
After
复制代码

来源:t.im/kzfr

开源项目推荐

做者的开源项目推荐:

关注公众号回复开源项目便可获取

相关文章
相关标签/搜索