博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring 代理模式及AOP基本术语
阅读量:5140 次
发布时间:2019-06-13

本文共 4886 字,大约阅读时间需要 16 分钟。

一、代理模式:

静态代理、动态代理

动态代理和静态代理区别??

解析:静态代理需要手工编写代理类,代理类引用被代理对象。

        动态代理是在内存中构建的,不需要手动编写代理类

代理的目的:是为了在原有的方法上进行增强。

动态代理的两种方式:JDK动态代理与CGLIB代理

默认情况下,Spring AOP的实现对于接口来说就是使用的JDK的动态代理来实现的,而对于类的代理使用CGLIB来实现。

二、静态代理

①定义接口 Subject并添加方法:

1
2
3
public
interface
Subject {
public
void
request();
}

② 定义接口的实现类 RealSubject并实现该接口,重写方法--被代理对象

1
2
3
4
5
6
public
class
RealSubject
implements
Subject{
 
    
public
void
request() {
        
System.out.println(
"真实主题内容"
);
     
    
}

③定义接口的实现类ProxySubject并实现该接口重写方法。自定义属性RealSubject,调用request方法,在这里进行增强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public
class
ProxySubject
implements
Subject{
 
private
RealSubject real;
     
public
void
request() {
    
System.out.println(
"===before==="
);
    
//调用request()
    
real.request();
    
System.out.println(
"===after==="
);
         
    
}
 
 
    
public
RealSubject getReal() {
        
return
real;
    
}
    
public
void
setReal(RealSubject real) {
        
this
.real = real;
    
}
    

④测试类: 

分别创建出被代理对象和代理对象,执行方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public
class
ProxyTest {
//静态代理
   
@Test
   
public
void
test01(){
    
//准备一个真实主题,被代理对象
    
RealSubject real=
new
RealSubject();
    
//创建一个代理对象
    
ProxySubject proxy=
new
ProxySubject();
     
    
proxy.setReal(real);
    
proxy.request();
}
    

 执行效果:

可以看出静态代理类有一个缺点:当如果接口加一个方法,所有的实现类和代理类里都需要做个实现。这就增加了代码的复杂度。动态代理就可以避免这个缺点。 


三、动态代理

1.1、JDK动态代理:

本质:在内存中构建接口实现类

特点:被代理对象必须有接口 

 JDK提供了java.lang.reflect.Proxy类来实现动态代理的,可通过它的newProxyInstance来获得代理实现类。同时对于代理的接口的实际处理,是一个java.lang.reflect.InvocationHandler,它提供了一个invoke方法供实现者提供相应的代理逻辑的实现。可以对实际的实现进行一些特殊的处理,像Spring AOP中的各种advice。下面来看看如何使用。

定义接口IUserDao:

1
2
3
4
5
6
public
interface
IUserDao {
 
public
String add();
 
public
String edit();
}

定义接口实现类,实现某接口,并重写该方法:

1
2
3
4
5
6
7
8
9
10
11
public
String add() {
    
System.out.println(
"==add==="
);
    
return
"我是add"
;
     
}
 
public
String edit() {
    
System.out.println(
"===edit==="
);
    
return
"edit"
;
     
}

测试类:(重点)  

实现效果:

可以由源码可见如何生成代理类的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public
static
Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
throws
IllegalArgumentException {
        
if
(h ==
null
) {
            
throw
new
NullPointerException();
        
}
 
        
final
Class<?>[] intfs = interfaces.clone();
        
final
SecurityManager sm = System.getSecurityManager();
        
if
(sm !=
null
) {
            
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        
}
        
// 这里是生成class的地方
        
Class<?> cl = getProxyClass0(loader, intfs);
        
// 使用我们实现的InvocationHandler作为参数调用构造方法来获得代理类的实例
        
try
{
            
final
Constructor<?> cons = cl.getConstructor(constructorParams);
            
final
InvocationHandler ih = h;
            
if
(sm !=
null
&& ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                
return
AccessController.doPrivileged(
new
PrivilegedAction<Object>() {
                    
public
Object run() {
                        
return
newInstance(cons, ih);
                    
}
                
});
            
}
else
{
                
return
newInstance(cons, ih);
            
}
        
}
catch
(NoSuchMethodException e) {
            
throw
new
InternalError(e.toString());
        
}
    
}
其中newInstance只是调用Constructor.newInstance来构造相应的代理类实例,这里重点是看getProxyClass0这个方法的实现:
    
private
static
Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { 
        
// 代理的接口数量不能超过65535
        
if
(interfaces.length >
65535
) { 
            
throw
new
IllegalArgumentException(
"interface limit exceeded"
); 
        
        
// JDK对代理进行了缓存,如果已经存在相应的代理类,则直接返回,否则才会通过ProxyClassFactory来创建代理 
        
return
proxyClassCache.get(loader, interfaces); 
    

可以看到,动态生成的代理类有如下特性:

   1) 继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
   2) 提供了一个使用InvocationHandler作为参数的构造方法。
   3) 生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。
   4) 重写了Object类的equals、hashCode、toString,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。


 1.2、cglib动态代理:

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要cglib了。cglib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用  

 创建被代理的类:

1
2
3
4
5
public
class
UserService {
public
void
delete(){
    
System.out.println(
"delete ok!!!"
);
}
}

测试类:(重点)  

该类实现了创建子类的方法与代理的方法。SuperClass方法通过入参即父类的字节码,通过扩展父类的class来创建代理对象。intercept()方法拦截所有目标类方法的调用,obj表示目标类的实例,method为目标类方法的反射对象,args为方法的动态入参,proxy为代理类实例。proxy.invoke(obj, args)通过代理类调用父类中的方法 !

 

实现效果:

 


四、AOP基本术语:

AOP Aspect Oriented Programming 面向切面编程

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

面向对象编程是从【静态角度】考虑程序的结构,而面向切面编程是从【动态角度】考虑程序运行过程。

AOP底层,就是采用【动态代理】模式实现的。采用了两种代理:JDK动态代理和CGLIB动态代理。

基本术语(一些名词):

(1)切面(Aspect)
切面泛指[*交叉业务逻辑*]。事务处理和日志处理可以理解为切面。常用的切面有通知(Advice)与顾问(Advisor)。实际就是对主业务逻辑的一种增强。

(2)织入(Weaving)

织入是指将切面代码插入到目标对象的过程。代理的invoke方法完成的工作,可以称为织入。

(3) 连接点(JoinPoint)

连接点是指可以被切面织入的方法。通常业务接口的方法均为连接点

(4)切入点(PointCut)

切入点指切面具体织入的方法
注意:被标记为final的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

(5)目标对象(Target)

目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。
(6)通知(Advice)
通知是切面的一种实现,可以完成简单的织入功能。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是执行之后执行等。切入点定义切入的位置,通知定义切入的时间。

(7)顾问(Advisor)

顾问是切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。

转载于:https://www.cnblogs.com/jingpeipei/p/6001061.html

你可能感兴趣的文章
pyhon时间输出
查看>>
P1518 两只塔姆沃斯牛 The Tamworth Two
查看>>
html的解析
查看>>
打印单词长度的直方图--C语言的多种实现
查看>>
PLSql的使用
查看>>
用CAShapeLayer实现一个简单的饼状图(PieView)
查看>>
LA 3644 易爆物
查看>>
uboot 信息解读
查看>>
越是忙的时候,兴趣越多
查看>>
信步漫谈之Eclipse—插件安装
查看>>
字符串和字符数组的输入输出种类对比
查看>>
Python爬虫:抓取手机APP的数据
查看>>
手指滑动屏幕原理
查看>>
对于javascript里面闭包的理解
查看>>
LANMP安装总结
查看>>
因为没有打开的文档,所以这一命令无效==操作word问题
查看>>
C++获取Windows7 32位系统中所有进程名(类似于任务管理器中的进程)
查看>>
团队作业8----第二次项目冲刺(Beta阶段) 第三天
查看>>
用mrpt库时遇到的一个坑
查看>>
【19】235. Lowest Common Ancestor of a Binary Search Tree
查看>>