Spring框架-AOP详细学习[转载]

本文内容纲要:Spring框架-AOP详细学习[转载]

参考博客:https://blog.csdn.net/qq_22583741/article/details/79589910#4-%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E8%A3%85%E9%85%8Dbean-%E5%9F%BA%E4%BA%8Exml

---------------------

作者:huang-yang

来源:CSDN

原文:https://blog.csdn.net/qq_22583741/article/details/79589910

这个大佬写的太厉害了, 索性直接转了

一. AOP概念

AOP(Aspect Oriented Programming , 面向切面编程), 通过预编译和运行期动态代理实现程序功能的统一维护的一种技术. AOP是OOP的延续,是Spring框架的重要内容, 是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

  • AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
  • 经典应用:事务管理、性能监视、安全检查、缓存 、日志等
  • Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
  • AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入

二. AOP实现原理:

  • aop底层将采用代理机制进行实现。
  • 接口 + 实现类 :spring采用 jdk 的动态代理Proxy。
  • 实现类:spring 采用 cglib字节码增强。

三. AOP术语 [重点掌握]

1.target(目标类): 需要被代理的类。例如:UserService

2.Joinpoint(连接点): 所谓连接点是指那些可能被拦截到的方法。例如:所有的方法

3.PointCut 切入点: 已经被增强的连接点。例如:addUser()

4.advice 通知/增强: 增强代码。例如:after、before

5. Weaving(织入): 是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.

6.proxy (代理类)

7. Aspect(切面): 是切入点pointcut和通知advice的结合

一个线是一个特殊的面。

一个切入点和一个通知,组成一个特殊的面。

也就是说,我们最终会获得的, 是advice 和 pointcut 结合起来的proxy代理类

Image

四. AOP 实现方式

AOP实现方式包括 手动模式, 半自动模式, 全自动模式

4.1.手动模式:

4.1.1JDK动态代理

(1)目标类: 接口+实现类

public interface ProductService {

public void addProduct();

public void updateProduct();

public void deleteProduct();

}

public class ProductServiceImpl implements ProductService {

@Override

public void addProduct() {

System.out.println("add Product");

}

@Override

public void updateProduct() {

System.out.println("update Product");

}

@Override

public void deleteProduct() {

System.out.println("delete Product");

}

}

(2)切面类: 用于实现通知/增强

public class LoggerAspect {

public void before(){

System.out.println("在添加商品前做些什么");

}

public void after(){

System.out.println("在添加商品后做些什么");

}

}

(3)工厂类: 编写工厂生成代理

public class ProductLogFactory {

public static ProductService createService(){

//目标类

final ProductService productService = new ProductServiceImpl();

//切面类

final LoggerAspect loggerAspect = new LoggerAspect();

//代理类

ProductService proxyService = (ProductService) Proxy.newProxyInstance(

LoggerAspect.class.getClassLoader(), //参数1

productService.getClass().getInterfaces(), //参数2

new InvocationHandler() { //参数3

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

//前执行

loggerAspect.before();

//执行目标类方法

Object obj = method.invoke(productService,args);

//后执行

loggerAspect.after();

return obj;

}

}

);

return proxyService; //返回创建的代理类对象

}

}

参数1:loader ,类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。

* 一般情况:当前类.class.getClassLoader();

* 目标类实例.getClass().get...

---------------------

参数2:Class[] interfaces 代理类需要实现的所有接口

* 方式1:目标类实例.getClass().getInterfaces() ;注意:只能获得自己接口,不能获得父元素接口

* 方式2:new Class[]{UserService.class}

* 例如:jdbc 驱动 --> DriverManager 获得接口 Connection

---------------------

参数3:InvocationHandler 处理类,接口,必须进行实现类,一般采用匿名内部

* 提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke

* 参数31:Object proxy :代理对象

* 参数32:Method method : 代理对象当前执行的方法的描述对象(反射)

* 执行方法名:method.getName()

* 执行方法:method.invoke(对象,实际参数)

* 参数33:Object[] args :方法实际参数

(4)测试:

使用被织入了切面方法的代理类的方法

@Test

public void test3(){

ProductService ps = ProductLogFactory.createService();

ps.addProduct();

ps.deleteProduct();

ps.updateProduct();

}

4.1.1CGLIB字节码增强

  • 没有接口,只有实现类。

  • 采用字节码增强框架 cglib,在运行时 创建目标类的子类,从而对目标类进行增强。

    public class ProductLogFactory1 {

    public static ProductServiceImpl createService(){

    //目标类

    final ProductServiceImpl productService = new ProductServiceImpl();

    //切面类

    final LoggerAspect loggerAspect = new LoggerAspect();

    //代理类, 采用cglib, 底层创建目标类的子类

    //核心类

    Enhancer enhancer = new Enhancer();

    //设置父类为目标类

    enhancer.setSuperclass(productService.getClass());

    //设置回调函数

    enhancer.setCallback(new MethodInterceptor() {

    @Override

    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

    //前执行

    loggerAspect.before();

    //执行目标类的方法

    Object obj = method.invoke(productService,args);

    //执行代理类的父类 目标类是代理类的父类

    methodProxy.invokeSuper(proxy,args);

    //后执行

    loggerAspect.after();

    return obj;

    }

    });

    //创建代理

    ProductServiceImpl proxyService = (ProductServiceImpl)enhancer.create();

    return proxyService;

    }

    }

用法和上面类似,获得代理类对象后, 使用其中已经织入切面类方法的方法

4.2.半自动方式: 让Spring创建代理对象, 从Spring容器中手动获得代理对象

(1)目标类: 接口+实现类

public interface ProductService {

public void addProduct();

public void updateProduct();

public void deleteProduct();

}

public class ProductServiceImpl implements ProductService {

@Override

public void addProduct() {

System.out.println("add Product");

}

@Override

public void updateProduct() {

System.out.println("update Product");

}

@Override

public void deleteProduct() {

System.out.println("delete Product");

}

}

(2)切面类: 与前面的切面类不同, 这个切面类实现了MethodInterceptor接口, 环绕通知

public class LoggerAspect implements MethodInterceptor {

@Override

public Object invoke(MethodInvocation methodInvocation) throws Throwable {

//前执行

System.out.println("在操作商品前做些什么");

//手动执行目标方法

Object obj = methodInvocation.proceed();

//后执行

System.out.println("在操作商品后做些什么");

return obj;

}

}

(3) Spring配置: 要在applicationContext.xml文件中声明目标类,切面类 并让Spring帮忙生成代理类

<!--目标类 -->

<bean id="productServiceId" class="service.ProductServiceImpl"></bean>

<!-- 切面类-->

<bean id="loggerAspectId" class="aspect.LoggerAspect"></bean>

<!-- 代理类 -->

<bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">

<property name="interfaces" value="service.ProductService"></property>

<property name="target" ref="productServiceId"></property>

<property name="interceptorNames" value="loggerAspectId"></property>

</bean>

* 通过使用工厂bean FactoryBean ,底层调用 getObject() 返回特殊bean

* ProxyFactoryBean 用于创建代理工厂bean,生成特殊代理对象

参数1: interfaces : 确定接口们

通过可以设置多个值

只有一个值时,value=""

参数2: target : 确定目标类

参数3: interceptorNames : 通知 切面类的名称,类型String[],如果设置一个值 value=""

可选参数4: optimize :强制使用cglib


底层机制

如果目标类有接口,采用jdk动态代理

如果没有接口,采用cglib 字节码增强

如果声明 optimize = true ,无论是否有接口,都采用cglib

(4)测试

@Test

public void test3(){

String xmlPath = "applicationContext.xml";

ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);

ProductService ps = (ProductService)context.getBean("proxyServiceId");

ps.addProduct();

ps.deleteProduct();

ps.updateProduct();

}

4.3.全自动方式: 从Spring容器获得目标类, 通过配置aop, 让Spring自动生成代理

(1)目标类: 接口+实现类 这里就不放代码了

(2)切面类: 与半自动方式相似

(3)xml配置:

<aop:config>

<aop:pointcut id="productPointCut" expression="execution(* service.ProductServiceImpl.*(..))"/>

<aop:advisor advice-ref="loggerAspectId" pointcut-ref="productPointCut"/>

</aop:config>

aop编程 :

3.1 导入命名空间

3.2 使用 aop:config进行配置

proxy-target-class="true" 声明时使用cglib代理

aop:pointcut 切入点 ,从目标对象获得具体方法

aop:advisor 特殊的切面,只有一个通知 和 一个切入点

advice-ref 通知引用

pointcut-ref 切入点引用

3.3 切入点表达式

execution(* com.itheima.c_spring_aop.*.*(..))

选择方法 返回值任意 包 类名任意 方法名任意 参数任意

(4)测试:

@Test

public void test3(){

String xmlPath = "applicationContext.xml";

ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);

ProductService ps = (ProductService)context.getBean("productServiceId");

ps.addProduct();

ps.deleteProduct();

ps.updateProduct();

}

注意这里,不再是获取由Spring容器创建的代理类了, 而是直接获取ProductService的bean, 生成代理的定义几乎完全不可视了

4.4. 另一种全自动方式:

(1)目标类: 接口+实现类

(2)切面类: 切面类不再实现某个接口, 将获取切入点的操作交给了Spring

public class LoggerAspect {

public Object log(ProceedingJoinPoint joinPoint) throws Throwable {

//前执行

System.out.println("在操作商品前做些什么");

//手动执行目标方法

Object obj = joinPoint.proceed();

//后执行

System.out.println("在操作商品后做些什么");

return obj;

}

}

(3)xml配置: 指定切面类的bean, 指定切入点是ProductService的所有方法, 指定切面方法为log

<!-- Aspect -->

<bean id="loggerAspectId" class="aspect.LoggerAspect"></bean>

<!-- aop -->

<aop:config>

<aop:pointcut id="loggerCutpoint"

expression="execution(* service.ProductService.*(..))"/>

<aop:aspect id="logAspect" ref="loggerAspectId">

<aop:around pointcut-ref="loggerCutpoint" method="log"/>

</aop:aspect>

</aop:config>

本文内容总结:Spring框架-AOP详细学习[转载]

原文链接:https://www.cnblogs.com/wqq-blog/p/10650680.html

以上是 Spring框架-AOP详细学习[转载] 的全部内容, 来源链接: utcz.com/z/296712.html

回到顶部