参考博客: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代理类

四. 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 : 确定接口们
                                                        通过<array>可以设置多个值
                                                        只有一个值时,value=""
                          参数2: target : 确定目标类
                          参数3:  interceptorNames : 通知 切面类的名称,类型String[],如果设置一个值 value=""
                          可选参数4:  optimize :强制使用cglib
                                             <property name="optimize" value="true"></property>
底层机制
如果目标类有接口,采用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详细学习[转载]的更多相关文章

  1. 跟着刚哥学习Spring框架--AOP(五)

    AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.OOP引入 ...

  2. spring框架 AOP核心详解

    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子. 一 AOP的基本概念 (1)Asp ...

  3. spring框架aop用注解形式注入Aspect切面无效的问题解决

    由于到最后我的项目还是有个邪门的错没解决,所以先把文章大概内容告知: 1.spring框架aop注解扫描默认是关闭的,得手动开启. 2.关于Con't call commit when autocom ...

  4. Spring框架AOP学习总结(下)

    目录 1. AOP 的概述 2. Spring 基于AspectJ 进行 AOP 的开发入门(XML 的方式): 3.Spring 基于AspectJ 进行 AOP 的开发入门(注解的方式): 4.S ...

  5. Spring框架零基础学习(一):IOC|DI、AOP

    文章目录 一.IDEA创建Spring项目 二.Spring: IOC和DI 三.Spring: AOP 参考链接: HOW2J.CN:Spring idea创建一个spring项目 一.IDEA创建 ...

  6. Spring框架 AOP面向切面编程(转)

    一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...

  7. 10 Spring框架 AOP (三) Spring对AspectJ的整合

    上两节我们讲了Spring对AOP的实现,但是在我们的开发中我们不太使用Spring自身的对AOP的实现,而是使用AspectJ,AspectJ是一个面向切面的框架,它扩展了Java语言.Aspect ...

  8. Spring框架——AOP代理

    我们知道AOP代理指的就是设计模式中的代理模式.一种是静态代理,高效,但是代码量偏大:另一种就是动态代理,动态代理又分为SDK下的动态代理,还有CGLIB的动态代理.Spring AOP说是实现了AO ...

  9. 08 Spring框架 AOP (一)

    首先我们先来介绍一下AOP: AOP(Aspect Orient Programming),面向切面编程,是面向对象编程OOP的一种补充.面向对象编程是从静态角度考虑程序的结构,面向切面编程是从动态的 ...

随机推荐

  1. opendir函数和readdir函数内涵及用法

    工作中遇到奇怪的事,加载增量的时候加载不上.于是开始分析原因,log里边没有任何错误信息,只有加载完成的标志.增量的数据在目录里边是存在的,但是显示的目录大小却不是4096,而是17,不知道为什么.后 ...

  2. C#字符串和ASCII码的转换

    //字符转ASCII码: public static int Asc(string character) { if (character.Length == 1) { System.Text.ASCI ...

  3. CodeForces Round #554 Div.2

    A. Neko Finds Grapes 代码: #include <bits/stdc++.h> using namespace std; ; int N, M; int a[maxn] ...

  4. Windows kafka_2.11-1.1.0安装

    #broker.id= #broker.list=,, listeners=PLAINTEXT://127.0.0.1:9092 advertised.listeners=PLAINTEXT://12 ...

  5. Python进阶8---面向对象基础1

    面向对象 语言的分类 Python的类 定义 class ClassName: pass class MyCalss: """A example class"& ...

  6. MySQL 的数据目录

    MySQL里面有4个数据库是属于MySQL自带的系统数据库: mysql 这个数据库贼核心,它存储了MySQL的用户账户和权限信息,一些存储过程.事件的定义信息,一些运行过程中产生的日志信息,一些帮助 ...

  7. request+response+jsp+el+jstl

    response: 1.设置响应行的状态码: response.setStatus(int sc); 2.设置response缓冲区的编码:response.setCharacterEncoding( ...

  8. gcc编译C源文件

    gcc编译C程序的主要过程是:预处理---编译---汇编---连接,其中:(以名为hello.c的源文件为例) 预处理:对各种预处理指令(#开头,如#include,#define)进行处理,以及删除 ...

  9. js 里面的各种类型转换

    1:Object 对象在转换为 number 类型时,首先会调用 valueOf 方法,然后再调用 toString 方法,否则会抛出异常. 对象在转换为 string 类型时,首先会调用 toStr ...

  10. iis7设置ftp

    目前是所有网站一个域下.ftp登录后可看到所有网站,目前想ftp一个网站,查看了下服务器,貌似只有serv-u这么个东西,还不能再创建第二个域.不得其解.百度发现两篇文章正好: http://blog ...