Spring Aop原理

AOP实现原理

  Spring 为解耦而生,其中AOP(面向切面编程)是很浓重的一笔。

  本文来探讨一下AOP实现的原理。

一. 概述

  代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 
  按照代理的创建时期,代理类可以分为两种。

  • 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
  • 动态代理:在程序运行时,运用反射机制动态创建而成。

  另外还有Cglib动态代理,它是针对类实现的代理。

二. 静态代理

1.  Count.java 

复制代码
 1 /** 
 2 * 定义一个账户接口 
 3 */  
 4 public interface Count {  
 5     // 查看账户方法  
 6     public void queryCount();  
 7   
 8     // 修改账户方法  
 9     public void updateCount();    
10 }
复制代码

2.  CountImpl.java

复制代码
 1 import net.battier.dao.Count;  
 2 /** 
 3  * 委托类(包含业务逻辑) 
 4  */  
 5 public class CountImpl implements Count {  
 6   
 7     @Override  
 8     public void queryCount() {  
 9         System.out.println("查看账户方法...");  
10   
11     }  
12 
13     @Override  
14     public void updateCount() {  
15         System.out.println("修改账户方法...");  
16     }  
17 }  
复制代码

3.  CountProxy.java  

复制代码
 1 import net.battier.dao.Count;  
 2 /** 
 3  * 这是一个代理类(增强CountImpl实现类) 
 4  */  
 5 public class CountProxy implements Count {  
 6     private CountImpl countImpl;  
 7     /** 
 8      * 覆盖默认构造器 
 9      *  
10      * @param countImpl 
11      */  
12     public CountProxy(CountImpl countImpl) {  
13         this.countImpl = countImpl;  
14     }  
15   
16     @Override  
17     public void queryCount() {  
18         System.out.println("事务处理之前");  
19         // 调用委托类的方法;  
20         countImpl.queryCount();  
21         System.out.println("事务处理之后");  
22     }  
23   
24     @Override  
25     public void updateCount() {  
26         System.out.println("事务处理之前");  
27         // 调用委托类的方法;  
28         countImpl.updateCount();  
29         System.out.println("事务处理之后");  
30     }  
31 } 
复制代码

4.  TestCount.java

复制代码
 1 import net.battier.dao.impl.CountImpl;  
 2 import net.battier.dao.impl.CountProxy;    
 3 /** 
 4  *测试Count类 
 5  */  
 6 public class TestCount {  
 7     public static void main(String[] args) {  
 8         CountImpl countImpl = new CountImpl();  
 9         CountProxy countProxy = new CountProxy(countImpl);  
10         countProxy.updateCount();  
11         countProxy.queryCount();  
12     }  
13 }  
复制代码

  观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。 

三. 动态代理

  与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。 

JDK动态代理中包含一个接口和一个类: 

1).InvocationHandler接口

1 public interface InvocationHandler {
2     public Object invoke(Object proxy, Method method, Object[] args)
3         throws Throwable;
4 }

参数说明: 
Object proxy:指被代理的对象。 
Method method:要调用的方法 
Object[] args:方法调用时所需要的参数

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。

2).Proxy类: 

Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:

复制代码
1 public static Object newProxyInstance(ClassLoader loader,
2                                           Class<?>[] interfaces,
3                                           InvocationHandler h)
4         throws IllegalArgumentException
5     {
6         /**************
7         *具体内容略去**
8         **************/
9     }
复制代码

参数说明: 
ClassLoader loader:类加载器 
Class<?>[] interfaces:得到全部的接口 
InvocationHandler h:得到InvocationHandler接口的子类实例

Ps:类加载器 
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器:

  • Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的;
  • Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类;
  • AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。

动态代理示例:

1.  BookFacade.java

1 public interface BookFacade {  
2     public void addBook();  
3 }  

2.  BookFacadeImpl.java 

复制代码
1 import net.battier.dao.BookFacade;  
2   
3 public class BookFacadeImpl implements BookFacade {  
4   
5     @Override  
6     public void addBook() {  
7         System.out.println("增加图书方法。。。");  
8     }  
9 }
复制代码

3.  BookFacadeProxy.java 

复制代码
 1 import java.lang.reflect.InvocationHandler;  
 2 import java.lang.reflect.Method;  
 3 import java.lang.reflect.Proxy;  
 4 /** 
 5  * JDK动态代理代理类 
 6  */  
 7 public class BookFacadeProxy implements InvocationHandler {  
 8     private Object target;  
 9     /** 
10      * 绑定委托对象并返回一个代理类 
11      * @param target 
12      * @return 
13      */  
14     public Object bind(Object target) {  
15         this.target = target;  
16         //取得代理对象  
17         return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
18                 target.getClass().getInterfaces(), this);   //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)  
19     }  
20   
21     @Override  
22     /** 
23      * 调用方法 
24      */  
25     public Object invoke(Object proxy, Method method, Object[] args)  
26             throws Throwable {  
27         Object result=null;  
28         System.out.println("事物开始");  
29         //执行方法  
30         result=method.invoke(target, args);  
31         System.out.println("事物结束");  
32         return result;  
33     }  
34 }
复制代码

4.  TestProxy.java

复制代码
 1 import net.battier.dao.BookFacade;  
 2 import net.battier.dao.impl.BookFacadeImpl;  
 3 import net.battier.proxy.BookFacadeProxy;  
 4   
 5 public class TestProxy {  
 6     public static void main(String[] args) {  
 7         BookFacadeProxy proxy = new BookFacadeProxy();  
 8         BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());  
 9         bookProxy.addBook();  
10     }    
11 }
复制代码

  但是,JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。 

四. Cglib动态代理

  JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 

示例如下:

1.  BookFacadeCglib.java 

1 public interface BookFacade {  
2     public void addBook();  
3 } 

2.  BookCadeImpl1.java 

复制代码
1 /** 
2  * 这个是没有实现接口的实现类 
3  */  
4 public class BookFacadeImpl1 {  
5     public void addBook() {  
6         System.out.println("增加图书的普通方法...");  
7     }  
8 }
复制代码

3.  BookFacadeProxy.java

复制代码
 1 import net.sf.cglib.proxy.Enhancer;  
 2 import net.sf.cglib.proxy.MethodInterceptor;  
 3 import net.sf.cglib.proxy.MethodProxy;    
 4 /** 
 5  * 使用cglib动态代理  
 6  */  
 7 public class BookFacadeCglib implements MethodInterceptor {  
 8     private Object target;  
 9   
10     /** 
11      * 创建代理对象 
12      *  
13      * @param target 
14      * @return 
15      */  
16     public Object getInstance(Object target) {  
17         this.target = target;  
18         Enhancer enhancer = new Enhancer();  
19         enhancer.setSuperclass(this.target.getClass());  
20         // 回调方法  
21         enhancer.setCallback(this);  
22         // 创建代理对象  
23         return enhancer.create();  
24     }  
25   
26     @Override  
27     // 回调方法  
28     public Object intercept(Object obj, Method method, Object[] args,  
29             MethodProxy proxy) throws Throwable {  
30         System.out.println("事物开始");  
31         proxy.invokeSuper(obj, args);  
32         System.out.println("事物结束");  
33         return null;  
34     }  
35 }
复制代码

4.  TestCglib.java

复制代码
 1 import net.battier.dao.impl.BookFacadeImpl1;  
 2 import net.battier.proxy.BookFacadeCglib;  
 3   
 4 public class TestCglib {  
 5       
 6     public static void main(String[] args) {  
 7         BookFacadeCglib cglib=new BookFacadeCglib();  
 8         BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());  
 9         bookCglib.addBook();  
10     }  
11 }

 

参考:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html#3460821


<think>好的,用户想了解Spring AOP原理,特别是代理模式、动态代理和静态代理。首先,我需要回顾一下代理模式的基本概念,然后区分静态代理和动态代理的不同,再联系到Spring AOP是如何应用这些技术的。 首先,代理模式的定义。根据引用[1],代理模式允许在不修改被代理对象的情况下,通过代理类进行功能增强。这里的关键是代理类分为静态和动态两种。静态代理需要手动创建代理类,而动态代理在运行时生成。用户可能想知道这两种代理在Spring AOP中的应用场景。 接下来,静态代理的细节。引用[1]提到静态代理的类在运行前已经存在,通常由程序员手动编写或工具生成。例如,假设有一个业务接口Subject和实现类RealSubject,静态代理类Proxy会实现Subject并持有RealSubject的引用,在方法调用前后添加逻辑。这种情况的缺点是每个被代理类都需要一个对应的代理类,导致代码冗余。 然后是动态代理,Spring AOP主要使用JDK动态代理和CGLIB。引用[2]指出JDK代理通过Proxy类生成代理对象,而CGLIB通过Enhancer创建。JDK动态代理要求目标类实现接口,利用InvocationHandler的invoke方法进行增强。CGLIB则通过继承目标类,重写方法来实现代理,适用于没有接口的情况。需要解释这两种方式的区别以及Spring如何选择使用哪一种。 还要结合Spring AOP的源码,如引用[2]提到的AOP源码剖析部分。Spring在运行时决定使用哪种代理方式,如果目标类实现了接口,默认使用JDK代理,否则使用CGLIB。这涉及到AOP的配置和底层机制。 用户可能还关心AOP的应用场景,比如事务管理、日志记录等,这些通过代理模式实现,无需修改原有代码。需要举例说明,比如在方法执行前后加入事务控制,动态代理如何拦截方法调用并添加额外操作。 另外,需要确保回答结构清晰,分点说明原理、代理类型、实现方式以及Spring的选择策略。同时,生成的相关问题要围绕用户可能进一步询问的点,比如性能比较、配置方法或实际应用例子。 最后,检查引用是否正确应用,如引用[1]关于代理模式的分类,引用[2]关于Spring AOP的具体实现,引用[3]关于代理模式的角色。确保每个技术点都有对应的引用支持,增加回答的可信度。</think>### Spring AOP原理与代理模式解析 #### 一、代理模式的核心作用 代理模式通过代理对象控制对真实对象的访问,在**不修改原始代码**的前提下实现功能增强。其核心角色包括: - **Subject**:定义业务接口(可以是接口或抽象类)[^3] - **RealSubject**:具体业务实现类(被代理对象) - **Proxy**:代理类,负责增强和控制对RealSubject的访问 #### 二、静态代理的实现方式 静态代理需**手动编写代理类**,代理关系在编译时确定。例如: ```java // 业务接口 interface UserService { void save(); } // 真实对象 class UserServiceImpl implements UserService { public void save() { /* 业务逻辑 */ } } // 静态代理类 class UserServiceProxy implements UserService { private UserService target; public UserServiceProxy(UserService target) { this.target = target; } public void save() { System.out.println("前置增强"); target.save(); // 调用真实对象方法 System.out.println("后置增强"); } } ``` **缺点**:每个业务类需对应一个代理类,代码冗余度高[^1]。 #### 三、动态代理的实现机制 Spring AOP主要采用两种动态代理技术: 1. **JDK动态代理**(基于接口) - 要求目标类必须实现至少一个接口 - 通过`java.lang.reflect.Proxy`生成代理对象 - 核心类`InvocationHandler`实现方法拦截: ```java class JdkProxyHandler implements InvocationHandler { private Object target; public Object getProxy(Object target) { this.target = target; return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) { System.out.println("前置增强"); Object result = method.invoke(target, args); System.out.println("后置增强"); return result; } } ``` 2. **CGLIB代理**(基于继承) - 通过生成目标类的子类实现代理 - 使用`MethodInterceptor`接口: ```java class CglibInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) { System.out.println("前置增强"); Object result = proxy.invokeSuper(obj, args); System.out.println("后置增强"); return result; } } // 通过Enhancer创建代理 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(TargetClass.class); enhancer.setCallback(new CglibInterceptor()); TargetClass proxy = (TargetClass)enhancer.create(); ``` - **优势**:可代理无接口的类[^2] #### 四、Spring AOP的代理选择策略 | 条件 | 使用的代理方式 | |--------------------------|----------------| | 目标类实现了至少一个接口 | JDK动态代理 | | 目标类未实现任何接口 | CGLIB代理 | **底层实现**: - 通过`DefaultAopProxyFactory`类判断代理方式 - Spring Boot 2.x后默认强制使用CGLIB(可通过`spring.aop.proxy-target-class=true`配置) #### 五、AOP核心流程 1. 解析切面定义(@Aspect) 2. 匹配连接点(Pointcut) 3. 生成代理对象 4. 编织增强逻辑(Advice) 5. 通过责任链模式执行拦截器链 #### 六、性能对比 - **JDK动态代理**:生成速度快,执行效率稍低 - **CGLIB**:生成速度慢,执行效率高(现代JVM差距已不明显)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值