Aop是什么

与OOP对比,面向切面,传统的OOP开发中的代码逻辑是自上而下的,而这些过程会产生一些横切性问题,这些横切性的问题和我们的主业务逻辑关系不大,这些横切性问题不会影响到主逻辑实现的,但是会散落到代码的各个部分,难以维护。AOP是处理一些横切性问题,AOP的编程思想就是把这些问题和主业务逻辑分开,达到与主业务逻辑解耦的目的。使代码的重用性和开发效率更高。

aop的应用场景

1. 日志记录

2. 权限验证

3. 效率检查

4. 事务管理

5. exception

springAop的底层技术

 

JDK动态代理

CGLIB代理

 

编译时期的织入还是运行时期的织入?

运行时期织入

运行时期织入

初始化时期织入还是获取对象时期织入?

初始化时期织入

初始化时期织入

springAop和AspectJ的关系

Aop是一种概念

springAop、AspectJ都是Aop的实现,SpringAop有自己的语法,但是语法复杂,所以SpringAop借助了AspectJ的注解,但是底层实现还是自己的

spring AOP提供两种编程风格
@AspectJ support         ------------>利用aspectj的注解
Schema-based AOP support ----------->xml aop:config 命名空间

证明:spring,通过源  码分析了,我们可以知道spring底层使用的是JDK或者CGLIB来完成的代理,并且在官网
上spring给出了aspectj的文档,和springAOP是不同的

spring Aop的概念

aspect:一定要给spring去管理 抽象 aspectj->类

pointcut:切点表示连接点的集合 -------------------> 表

(我的理解:PointCut是JoinPoint的谓语,这是一个动作,主要是告诉通知连接点在哪里,切点表达式决定 JoinPoint 的数量)

Joinpoint:连接点 目标对象中的方法 ----------------> 记录

(我的理解:JoinPoint是要关注和增强的方法,也就是我们要作用的点)

Weaving :把代理逻辑加入到目标对象上的过程叫做织入

target 目标对象 原始对象

aop Proxy 代理对象 包含了原始对象的代码和增加后的代码的那个对象

advice:通知 (位置 + logic)

 

 

advice通知类型:

Before 连接点执行之前,但是无法阻止连接点的正常执行,除非该段执行抛出异常

After 连接点正常执行之后,执行过程中正常执行返回退出,非异常退出

After throwing 执行抛出异常的时候

After (finally) 无论连接点是正常退出还是异常退出,都会执行

Around advice: 围绕连接点执行,例如方法调用。这是最有用的切面方式。around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续加入点还是通过返回自己的返回值或抛出异常来快速建议的方法执行。

 

 

Proceedingjoinpoint 和JoinPoint的区别:

Proceedingjoinpoint 继承了JoinPoint,proceed()这个是aop代理链执行的方法。并扩充实现了proceed()方法,用于继续执行连接点。JoinPoint仅能获取相关参数,无法执行连接点。

JoinPoint的方法

1.java.lang.Object[] getArgs():获取连接点方法运行时的入参列表; 

2.Signature getSignature() :获取连接点的方法签名对象; 

3.java.lang.Object getTarget() :获取连接点所在的目标对象; 

4.java.lang.Object getThis() :获取代理对象本身;

proceed()有重载,有个带参数的方法,可以修改目标方法的的参数

 

Introductions

perthis

使用方式如下:

@Aspect("perthis(this(com.chenss.dao.IndexDaoImpl))")

要求:

1. AspectJ对象的注入类型为prototype

2. 目标对象也必须是prototype的

原因为:只有目标对象是原型模式的,每次getBean得到的对象才是不一样的,由此针对每个对象就会产生新的切面对象,才能产生不同的切面结果。
 

@Aspect
public class UsageTracking {

    @DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class)
    public static UsageTracked mixin;

    @Before("com.xyz.myapp.SystemArchitecture.businessService() && this(usageTracked)")
    public void recordUsage(UsageTracked usageTracked) {
        usageTracked.incrementUseCount();
    }

}

 Aspect Instantiation Models

By default, there is a single instance of each aspect within the application context. AspectJ calls this the singleton instantiation model. It is possible to define aspects with alternate lifecycles. Spring supports AspectJ’s perthis and pertarget instantiation models ( percflow, percflowbelow, and pertypewithin are not currently supported).

You can declare a perthis aspect by specifying a perthis clause in the @Aspect annotation. Consider the following example:

@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())")
public class MyAspect {

    private int someState;

    @Before(com.xyz.myapp.SystemArchitecture.businessService())
    public void recordServiceUsage() {
        // ...
    }

}

springAop支持AspectJ

1、启用@AspectJ支持

使用Java Configuration启用@AspectJ支持

要使用Java @Configuration启用@AspectJ支持,请添加@EnableAspectJAutoProxy注释

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}

使用XML配置启用@AspectJ支持

要使用基于xml的配置启用@AspectJ支持,可以使用aop:aspectj-autoproxy元素

<aop:aspectj-autoproxy/>

2、声明一个Aspect

申明一个@Aspect注释类,并且定义成一个bean交给Spring管理。

@Component
@Aspect
public class UserAspect {
}

3、申明一个pointCut

切入点表达式由@Pointcut注释表示。切入点声明由两部分组成:一个签名包含名称和任何参数,以及一个切入点表达式,该表达式确定我们对哪个方法执行感兴趣。

@Pointcut("execution(* transfer(..))")// 切入点表达式
private void anyOldTransfer() {}// 切入点签名

切入点确定感兴趣的 join points(连接点),从而使我们能够控制何时执行通知。Spring AOP只支持Spring bean的方法执行 join points(连接点),所以您可以将切入点看作是匹配Spring bean上方法的执行。

/**
 * 申明Aspect,并且交给spring容器管理
 */
@Component
@Aspect
public class UserAspect {
    /**
     * 申明切入点,匹配UserDao所有方法调用
     * execution匹配方法执行连接点
     * within:将匹配限制为特定类型中的连接点
     * args:参数
     * target:目标对象
     * this:代理对象
     */
    @Pointcut("execution(* com.yao.dao.UserDao.*(..))")
    public void pintCut(){
        System.out.println("point cut");
    }

4、申明一个Advice通知

advice通知与pointcut切入点表达式相关联,并在切入点匹配的方法执行@Before之前、@After之后或前后运行。

/**
 * 申明Aspect,并且交给spring容器管理
 */
@Component
@Aspect
public class UserAspect {
    /**
     * 申明切入点,匹配UserDao所有方法调用
     * execution匹配方法执行连接点
     * within:将匹配限制为特定类型中的连接点
     * args:参数
     * target:目标对象
     * this:代理对象
     */
    @Pointcut("execution(* com.yao.dao.UserDao.*(..))")
    public void pintCut(){
        System.out.println("point cut");
    }
    /**
     * 申明before通知,在pintCut切入点前执行
     * 通知与切入点表达式相关联,
     * 并在切入点匹配的方法执行之前、之后或前后运行。
     * 切入点表达式可以是对指定切入点的简单引用,也可以是在适当位置声明的切入点表达式。
     */
    @Before("com.yao.aop.UserAspect.pintCut()")
    public void beforeAdvice(){
        System.out.println("before");
    }
}

各种连接点joinPoint的意义:

execution

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-
pattern) throws-pattern?)
这里问号表示当前项可以有也可以没有,其中各项的语义如下
modifiers-pattern:方法的可见性,如public,protected;
ret-type-pattern:方法的返回值类型,如int,void等;
declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
name-pattern:方法名类型,如buisinessService();
param-pattern:方法的参数类型,如java.lang.String;
throws-pattern:方法抛出的异常类型,如java.lang.Exception;
example:
@Pointcut("execution(* com.chenss.dao.*.*(..))")//匹配com.chenss.dao包下的任意接口和类的任意
方法
@Pointcut("execution(public * com.chenss.dao.*.*(..))")//匹配com.chenss.dao包下的任意接口和
类的public方法
@Pointcut("execution(public * com.chenss.dao.*.*())")//匹配com.chenss.dao包下的任意接口和类的
public 无方法参数的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String, ..))")//匹配com.chenss.dao包下的
任意接口和类的第一个参数为String类型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意
接口和类的只有一个参数,且参数为String类型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意
接口和类的只有一个参数,且参数为String类型的方法
@Pointcut("execution(public * *(..))")//匹配任意的public方法
@Pointcut("execution(* te*(..))")//匹配任意的以te开头的方法
@Pointcut("execution(* com.chenss.dao.IndexDao.*(..))")//匹配com.chenss.dao.IndexDao接口中任
意的方法
@Pointcut("execution(* com.chenss.dao..*.*(..))")//匹配com.chenss.dao包及其子包中任意的方法

关于这个表达式的详细写法,可以脑补也可以参考官网很容易的,可以作为一个看spring官网文档的入门,打破你
害怕看官方文档的心理,其实你会发觉官方文档也是很容易的
https://docs.spring.io/spring-framework/docs/current/spring-framework-
reference/core.html#aop-pointcuts-examples

由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的信息,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的

within

表达式的最小粒度为类
// ------------
// within与execution相比,粒度更大,仅能实现到包和接口、类级别。而execution可以精确到方法的返回
值,参数个数、修饰符、参数类型等
@Pointcut("within(com.chenss.dao.*)")//匹配com.chenss.dao包中的任意方法
@Pointcut("within(com.chenss.dao..*)")//匹配com.chenss.dao包及其子包中的任意方法

args

args表达式的作用是匹配指定参数类型和指定参数数量的方法,与包名和类名无关


/**
 * args同execution不同的地方在于:
 * args匹配的是运行时传递给方法的参数类型
 * execution(* *(java.io.Serializable))匹配的是方法在声明时指定的方法参数类型。
 */
@Pointcut("args(java.io.Serializable)")//匹配运行时传递的参数类型为指定类型的、且参数个数和顺序匹配
@Pointcut("@args(com.chenss.anno.Chenss)")//接受一个参数,并且传递的参数的运行时类型具有@Classified

this

JDK代理时,指向接口和代理类proxy,cglib代理时 指向接口和子类(不使用proxy)

target

指向接口和子类

/**
 * 此处需要注意的是,如果配置设置proxyTargetClass=false,或默认为false,则是用JDK代理,否则使用
的是CGLIB代理
 * JDK代理的实现方式是基于接口实现,代理类继承Proxy,实现接口。
 * 而CGLIB继承被代理的类来实现。
 * 所以使用target会保证目标不变,关联对象不会受到这个设置的影响。
 * 但是使用this对象时,会根据该选项的设置,判断是否能找到对象。
 */
@Pointcut("target(com.chenss.dao.IndexDaoImpl)")//目标对象,也就是被代理的对象。限制目标对象为
com.chenss.dao.IndexDaoImpl类
@Pointcut("this(com.chenss.dao.IndexDaoImpl)")//当前对象,也就是代理对象,代理对象时通过代理目
标对象的方式获取新的对象,与原值并非一个
@Pointcut("@target(com.chenss.anno.Chenss)")//具有@Chenss的目标对象中的任意方法
@Pointcut("@within(com.chenss.anno.Chenss)")//等同于@targ
这个比较难.......
proxy模式里面有两个重要的术语
proxy Class
target Class
CGLIB和JDK有区别    JDK是基于接口   cglib是基于继承所有this可以在cglib作用

@annotation

这个很简单........
作用方法级别

上述所有表达式都有@ 比如@Target(里面是一个注解类xx,表示所有加了xx注解的类,和包名无关)

注意:上述所有的表达式可以混合使用,|| && !

@Pointcut("@annotation(com.chenss.anno.Chenss)")//匹配带有com.chenss.anno.Chenss注解的方法

bean

@Pointcut("bean(dao1)")//名称为dao1的bean上的任意方法
@Pointcut("bean(dao*)")

 

Declaring Advice

Before Advice

You can declare before advice in an aspect by using the @Before annotation:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

    @Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }

}

After Returning Advice

After returning advice runs when a matched method execution returns normally. You can declare it by using the @AfterReturning annotation:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

    @AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }

}

After Throwing Advice

After throwing advice runs when a matched method execution exits by throwing an exception. You can declare it by using the @AfterThrowing annotation, as the following example shows:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

    @AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doRecoveryActions() {
        // ...
    }

}

After (Finally) Advice

After (finally) advice runs when a matched method execution exits. It is declared by using the @After annotation. After advice must be prepared to handle both normal and exception return conditions. It is typically used for releasing resources and similar purposes. The following example shows how to use after finally advice:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;

@Aspect
public class AfterFinallyExample {

    @After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doReleaseLock() {
        // ...
    }

}

Around Advice

The following example shows how to use around advice:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class AroundExample {

    @Around("com.xyz.myapp.SystemArchitecture.businessService()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        // start stopwatch
        Object retVal = pjp.proceed();
        // stop stopwatch
        return retVal;
    }

}

Schema-based AOP Support

If you prefer an XML-based format, Spring also offers support for defining aspects using the new aop namespace tags. The exact same pointcut expressions and advice kinds as when using the @AspectJ style are supported. Hence, in this section we focus on the new syntax and refer the reader to the discussion in the previous section (@AspectJ support) for an understanding of writing pointcut expressions and the binding of advice parameters.

Declaring an Aspect

When you use the schema support, an aspect is a regular Java object defined as a bean in your Spring application context. The state and behavior are captured in the fields and methods of the object, and the pointcut and advice information are captured in the XML.

You can declare an aspect by using the <aop:aspect> element, and reference the backing bean by using the ref attribute, as the following example shows:

<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        ...
    </aop:aspect>
</aop:config>

<bean id="aBean" class="...">
    ...
</bean>

The bean that backs the aspect (aBean in this case) can of course be configured and dependency injected just like any other Spring bean.

Declaring a Pointcut

You can declare a named pointcut inside an <aop:config> element, letting the pointcut definition be shared across several aspects and advisors.

A pointcut that represents the execution of any business service in the service layer can be defined as follows:

<aop:config>

    <aop:pointcut id="businessService"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>

</aop:config>

In much the same way as an @AspectJ aspect, pointcuts declared by using the schema based definition style can collect join point context. For example, the following pointcut collects the this object as the join point context and passes it to the advice:

<aop:config>

    <aop:aspect id="myAspect" ref="aBean">

        <aop:pointcut id="businessService"
            expression="execution(* com.xyz.myapp.service.*.*(..)) && this(service)"/>

        <aop:before pointcut-ref="businessService" method="monitor"/>

        ...

    </aop:aspect>

</aop:config>

 Introductions

<aop:aspect id="usageTrackerAspect" ref="usageTracking">

    <aop:declare-parents
        types-matching="com.xzy.myapp.service.*+"
        implement-interface="com.xyz.myapp.service.tracking.UsageTracked"
        default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/>

    <aop:before
        pointcut="com.xyz.myapp.SystemArchitecture.businessService()
            and this(usageTracked)"
            method="recordUsage"/>

</aop:aspect>

 Spring AOP or Full AspectJ?

Use the simplest thing that can work. Spring AOP is simpler than using full AspectJ, as there is no requirement to introduce the AspectJ compiler / weaver into your development and build processes. If you only need to advise the execution of operations on Spring beans, Spring AOP is the right choice. If you need to advise objects not managed by the Spring container (such as domain objects, typically), you need to use AspectJ. You also need to use AspectJ if you wish to advise join points other than simple method executions (for example, field get or set join points and so on).

When you use AspectJ, you have the choice of the AspectJ language syntax (also known as the “code style”) or the @AspectJ annotation style. Clearly, if you do not use Java 5+, the choice has been made for you: Use the code style. If aspects play a large role in your design, and you are able to use the AspectJ Development Tools (AJDT) plugin for Eclipse, the AspectJ language syntax is the preferred option. It is cleaner and simpler because the language was purposefully designed for writing aspects. If you do not use Eclipse or have only a few aspects that do not play a major role in your application, you may want to consider using the @AspectJ style, sticking with regular Java compilation in your IDE, and adding an aspect weaving phase to your build script.

@AspectJ or XML for Spring AOP?

If you have chosen to use Spring AOP, you have a choice of @AspectJ or XML style. There are various tradeoffs to consider.

The XML style may most familiar to existing Spring users, and it is backed by genuine POJOs. When using AOP as a tool to configure enterprise services, XML can be a good choice (a good test is whether you consider the pointcut expression to be a part of your configuration that you might want to change independently). With the XML style, it is arguably clearer from your configuration which aspects are present in the system.

The XML style has two disadvantages. First, it does not fully encapsulate the implementation of the requirement it addresses in a single place. The DRY principle says that there should be a single, unambiguous, authoritative representation of any piece of knowledge within a system. When using the XML style, the knowledge of how a requirement is implemented is split across the declaration of the backing bean class and the XML in the configuration file. When you use the @AspectJ style, this information is encapsulated in a single module: the aspect. Secondly, the XML style is slightly more limited in what it can express than the @AspectJ style: Only the “singleton” aspect instantiation model is supported, and it is not possible to combine named pointcuts declared in XML. For example, in the @AspectJ style you can write something like the following: