Spring's AOP from shallow to deep

Spring's AOP from shallow to deep

1. The role of AOP

  In OOP, it is the existence of this kind of code (cross-cutting code) that is scattered everywhere and has nothing to do with the core function of the object, which makes the reuse of modules more difficult. AOP splits the encapsulated object, finds out the common behaviors that affect multiple objects, and encapsulates it into a reusable module named "Aspect". The business has nothing to do, but is extracted and encapsulated by the logic called by the business modules, which reduces the repetitive code in the system, reduces the coupling between modules, and improves the maintainability of the system.

2. DI and IOC concepts

  In the definition of dependency injection or inversion of control, the caller is not responsible for the creation of the callee's instance. The container in the Spring framework is responsible for this work. It determines the instance type through the developer's configuration, and then injects it into the caller after creation. . Since the Spring container is responsible for the callee instance, and after the instance is created, it is responsible for injecting the instance into the caller, so it is called dependency injection. The callee's instance creation is no longer created by the caller but by Spring. The control is transferred from the application code to the external container, and the control is reversed, so it is called inversion of control.

3.BeanFactory与ApplicationContext

  ApplicationContext is a subinterface of BeanFactory, also known as application context. BeanFactory provides Spring's configuration framework and basic functions, and ApplicationContext adds more enterprise-level functions (such as internationalization support). Another important advantage is that when the ApplicationContext container is initialized, all singleton beans in the container are also It is instantiated, that is to say, when you need to use a singleton Bean, it can be used in the application without waiting, while other implementation classes of the BeanFactory interface will be delayed until the getBean() method is called, and the initialization time of the ApplicationContext will be slightly Longer, calling getBean() is faster because the Bean has already been constructed. Therefore, most systems use ApplicationContext, and only consider using BeanFactory when there are fewer resources.

4.AOP implementation strategy

(1) Java SE dynamic proxy:
    Using dynamic proxy, you can dynamically generate implementation objects for one or more interfaces at runtime, and you can add enhanced code when the methods of the interface are implemented in the generated objects to implement AOP. The disadvantage is that it can only be proxied for interfaces, and because dynamic proxies are implemented through reflection, the overhead of reflection calls may sometimes be considered.
(2) Bytecode generation (CGLib dynamic proxy)
    Dynamic bytecode generation technology refers to dynamically generating a subclass object of a specified class at runtime, and overriding specific methods. When overriding methods, you can add enhanced code to achieve AOP . Its common tool is cglib.
(3) When the customized class loader
    needs to add enhancements to all objects of the class, both dynamic proxy and bytecode generation essentially need to dynamically construct proxy objects, that is, the final enhanced object is generated by the AOP framework, not the developer. new out. The solution is to implement a custom class loader that enhances a class as it is loaded. JBoss implements AOP functionality in this way.
(4) Code generation
    Utilize tools to generate new code based on existing code, in which any cross-cutting code can be added to realize AOP.
(5) Language extensions
    can enhance the assignment operations of constructors and attributes. AspectJ is a common Java language extension that implements AOP in this way.

 

Note: The aspect in AOP encapsulates the enhancement (Advice) and the pointcut (Pointcut) . Below, only the enhancement is used, and the pointcut is not added for the time being.

 

5. Programmatic enhancements

  Here I use the "programmatic" method first, that is, I don't need Spring's configuration file to define the Bean object, and I don't replace the new operation in the code.

(1) Create an interface and implementation class

 

(2) Write pre-enhancement and post-enhancement (here I combine the two enhancements, that is, implement two interfaces)

 

 

(3) JUnit to test

 

 

(3) Surround enhancement (when two interfaces are combined, in fact, one interface can be used)

 

  环绕增强类需要实现 org.aopalliance.intercept.MethodInterceptor 接口。注意,这个接口不是 Spring 提供的,它是 AOP 联盟写的,Spring 只是借用了它。

 

之后再JUnit中添加

 

6. 声明式增强

  现在通过Spring配置文件配置bean。同时使用Bean扫描,可以不用在配置文件中配置<bean id="..." class="..."/>.

(1)Spring配置文件(增强类为环绕增强)

 

 

(2)在相应的实现类和增强类上添加Component注解

 

 

(3)JUnit测试

  从 Context 中根据 id 获取 Bean 对象(其实就是一个代理),调用代理的方法。

 

得到结果

 7.Introduction Advice(引入增强)

  上面的增强仅仅是对方法增强,也就是织入,对类的增强才能叫做引入增强,比如说我不想让GreetingImpl去直接实现Greeting接口,因为这样的话,我就必须去实现他的方法。这时我就能靠Spring引入增强来帮我动态实现。

(1)定义一个新接口Love

 

 

(2)定义授权引入增强类

  定义一个授权引入增强类,实现Love接口,用以丰富GreetingImpl类的功能,这样GreetingImpl就能很巧妙的使用Love接口里的方法而不用去implement。

 

 

配置如下:

 

 

proxyTargetClass属性表示是否代理目标类,默认是false,也就是代理接口,上面一个例子的配置就是没有这一项属性所以用JDK动态代理,现在是true即使用CGLib动态代理。所以在测试方法中是GreetingImpl greetingImpl = (GreetingImpl)context.getBean("beans.xml"),而不会是Greeting greeting = (Greeting)context.getBean("beans.xml"),因为现在是代理目标类而不是代理接口。

 

(3)JUnit测试

 

 

 

注意:这里的Love love = (Love)greetingImpl 是将目标类强制向上转型为Love接口,就是引入增强(DelegatingIntroductionInterceptor)的特性--接口动态实现”功能。所以display()方法可以由GreetingImpl的对象来调用,只需要强制转换接口就行。

8. 面向切面编程

(1)通知(增强)Advice

  通知定义了切面是什么以及何时使用,应该应用在某个方法被调用之前?之后?还是抛出异常时?等等。

(2)连接点 Join point

  连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时,抛出异常时,甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程中,并添加新的行为。

(3)切点 Pointcut

  切点有助于缩小切面所通知的连接点的范围。如果说通知定义了切面的“什么”和“何时”的话,那么切点就定义了“何处”,切点会匹配通知所要织入的一个或多个连接点,一般常用正则表达式定义所匹配的类和方法名称来指定这些切点。

(4)切面 Aspect

  切面是通知和切点的结合。通知和切点定义了切面的全部内容——它是什么,在何时何处完成其功能。

(5)引入 Introduction

  引入允许我们向现有的类添加新方法或属性,从而无需修改这些现有类的情况下,让他们具有新的行为和状态。

(6)织入 Weaving

  在过去我常常把织入与引入的概念混淆,我是这样来辨别的,“引入”我把它看做是一个定义,也就是一个名词,而“织入”我把它看做是一个动作,一个动词,也就是切面在指定的连接点被织入到目标对象中。

9.总结一下

  通知包含了需要用于多个应用对象的横切行为;连接点是程序执行过程中能够应用通知的所有点;切点定义了通知被应用的具体位置(在哪些连接点)。其中关键的概念是切点定义了哪些连接点会得到通知(增强)。创建切点来定义切面所织入的连接点是AOP框架的基本功能。

  另外,Spring是基于动态代理的,所以Spring只支持方法连接点,而像AspectJ和JBoss除了方法切点,它们还提供字段和构造器接入点。如果需要方法拦截之外的连接点拦截功能,则可以利用AspectJ来补充SpringAOP的功能。

10.使用基于正则表达式的SpringAOP切面类

  这里使用springAOP的切面类RegexpMethodPointcutAdvisor来配置切面,并在GreetingImpl类中增加两个都以“good”开头的方法,下面要做的就是拦截两个新增方法,而对sayHello()不拦截。

 

 

 

在上面的InterceptorNames属性不再是原来的增强,而是一个定义好的切面greetingAdvisor,切面里面还用正则表达式定义了一个切点,即拦截GreetingImpl类中以good开头的方法。

JUnit测试:

 11.AOP自动代理

 (1)Spring框架自动生成代理。

 

 

 

   属性optimize意思是对代理生成策略是否优化,true表示如果目标类有接口则代理接口(JDK动态代理),如果没有则代理类(CGLib动态代理),这样便可以取代前面强制代理类的proxyTargetClass属性。

 

 

 

此时因为是自动代理,getBean()中的值不再是原来代理id(greetingProxy),而是目标类GreetingImpl的Bean的id(greetingImpl),他同样也是一个代理对象

 

 

(2)spring根据Bean名称来生成自动代理

beanNames属性代表只为bean的id后缀是“Impl”生成代理。

12. AspectJ execution 表达式拦截

  定义一个切面类,实现环绕增强。@Aspect注解就不需要类再实现接口,@Around注解为AspectJ切点表达式,参数ProceedingJoinPoint的对象即为连接点,此连接点可以取得方法名,参数等等。

这样两行配置,节约了配置大量代理和切面的时间,proxy-target-class为true表示代理目标类。

 

之前的切点表达式定义了拦截类中所有方法,所以每个方法都被增强。同时在ApplicationContext中获取的greetingImpl代理对象,可转型为自己静态实现的接口Greeting也可以是实现类GreetingImpl。属性proxy-target-class默认为false,代表只代理接口,也就是说只能将代理转型为Greeting,而不能是GreetingImpl

实现类GreetingImpl:

 

 

P.s 如果将切面类里的切点从原来的实现类GreetingImpl改为接口Greeting又会发生什么呢?

 

改为:

 

 

结果发现,实现类中的实现接口的方法被增强了,而自己创建的good方法没有被增强,这就是因为切点设置为Greeting接口里面所有方法被加强,所以实现了这个接口中的方法被增强了

 

 

 

13. AspectJ @DeclareParents 注解(引入增强)

  定义一个切面类AroundAspect:value属性指定了哪种类型的bean要引入该接口。defaultImpl属性指定了为引入功能提供实现的类,@DeclareParents注解所标注的属性指明要引入的接口。

 

LoveImpl实现类:将这个实现类引入目标类GreetingImpl中,就能使用display方法。

JUnit测试:

 

注意:在ApplicationContext中获取的greetingImpl对象是个代理对象,可转型为自己静态实现的接口Greeting,也可以转型为自己动态实现的接口Love,可随意切换。现在的AspectJ的引入增强跟上面的SpringAOP的引入增强只能面向实现类相比,还可面向接口编程。所以有两种方式实现:

 

控制台输出:

 

而对于SpringAOP引入的增强,则只能面向实现类:

 

 

14.Spring的AspectJ自动代理

  Spring的AspectJ自动代理仅仅使用@AspectJ作为创建切面的指导,切面依然是基于代理的。在本质上,它依然是Spring基于代理的切面。这意味着尽管使用的是@AspectJ注解,但我们仍然限于代理方法的调用。当Spring发现一个bean使用了@Aspect注解时,Spring就会创建一个代理,然后将调用委托给被代理的bean或被引入的实现,这取决于调用的方法属于被代理的bean还是属于被引入的接口。

15.在XML中声明切面

  在Spring中,注解和自动代理提供了一种很方便的方式来创建切面,但是面向注解的切面有一个明显的劣势:你必须能够为通知类添加注解,为了这一点,必须要有源码。如果你没有源码的话,或者不想将AspectJ注解放到你的代码之中,Spring提供了另外一种方法,Spring XML 配置文件中声明切面。

  将前面实现类GreetingImpl和切面类AroundAspect的相关注解@Component,@Aspect,@Around全都移除。编辑XML:

 

我们发现原来的两条配置都可以删除,但是要注意,没有显式配置<aop:aspectj-autoproxy/>不代表不使用自动代理,这条配置默认属性为“false”,表示只代理接口(JDK动态代理),所以如果只想代理接口,可以不用显式写出。

如果想要使用CGLib动态代理,则增加

这时又可以代理目标类了:

 

 

原文链接:https://www.cnblogs.com/zhaozihan/p/5953063.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325898303&siteId=291194637