文章目录
- AOP底层实现原理
- AOP实现原理分析
- Java设计模式(代理模式)
- 静态代理
- JDK动态代理
- CGLIB动态代理
- AOP操作术语
AOP底层实现原理
AOP实现原理分析
1、AOP采取横向抽取机制,取代传统的纵向抽取机制(继承关系)。
2、AOP底层实现原理是动态代理(JDK动态代理+CGLIB动态代理),就是在不修改原有类对象方法的源代码基础上,通过代理对象实现对原有类对象方法的增强,也就是拓展原有类对象的功能。
Java设计模式(代理模式)
Java中的设计模式-代理模式
-
静态代理(定义一个平级的实现类完成代理模式)
-
动态代理
-
JDK动态代理(定义拦截器实现InvocationHandler接口,通过Proxy产生代理对象)
-
CGLIB动态代理(定义拦截器实现MethodInterceptor接口,通过Enhancer类生成代理对象)
-
静态代理
代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是 Real Object.
静态代理就是通过创建平级的实现类的形式完成代理,能够实现代理的优势,但是同时缺点也很多:
以下就是基于静态代理进行的操作。静态代理存在如下几个缺点:
1、如果项目中存在N个接口,那么就会产生N个对应的实现类。我们的代理类也会随着实现类的增加而增加。
2、随着接口中的功能的增加,实现类中的功能也会同步增加。我们的代理类中的增强方法也会增加。
3、从严格意义上来讲:我们定义的增强的内容(事务处理)。其实是没有达到彻底复用。
4、优点:就是通过代理的方式,完成了目标对象的代理,并且在方法中置入了增强的处理,而且没有针对源代码进行修改。
- 需要代理的接口
package com.sin.service;
/**
* @author sin
* @date 2022/10/27
* @apiNote
*/
public interface ProxySubject {
public String inrtroduce(String name);
}
- 实现类
package com.sin.service.impl;
import com.sin.service.ProxySubject;
/**
* @author sin
* @date 2022/10/27
* @apiNote
*/
public class ProxySubjectImpl implements ProxySubject {
@Override
public String inrtroduce(String name) {
System.out.println("我的名字是:" + name);
return name;
}
}
- 代理类
package com.sin.proxy;
import com.sin.service.ProxySubject;
import com.sin.service.impl.ProxySubjectImpl;
/**
* @author sin
* @date 2022/10/27
* @apiNote
*/
public class StaticProxy implements ProxySubject {
private ProxySubjectImpl proxySubject;
public StaticProxy(ProxySubjectImpl proxySubject){
this.proxySubject = proxySubject;
}
@Override
public void inrtroduce(String name) {
System.out.println("调用前业务");
proxySubject.inrtroduce(name);
System.out.println("调用后的业务");
}
}
- 测试
package com.sin.test.proxy;
import com.sin.proxy.StaticProxy;
import com.sin.service.ProxySubject;
import com.sin.service.impl.ProxySubjectImpl;
import org.junit.Test;
/**
* @author sin
* @date 2022/10/27
* @apiNote
*/
public class StaticProxyTest {
@Test
public void test(){
ProxySubjectImpl proxySubject = new ProxySubjectImpl();
StaticProxy staticProxy = new StaticProxy(proxySubject);
staticProxy.inrtroduce("张三");
}
}
- 测试结果
JDK动态代理
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
Java1.3 以后, JAVA 实现了动态代理技术,允许开发者在运行期创建接口的代理实例 。
JDK 的动态代理主要使用的是 java.lang.reflect
包中的Proxy 和 InvocationHandler。
- InvocationHandler 是接口,可以通过实现该接口来定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起 。
- Proxy 利用 InvocationHandler 动态创建出一个实现某一接口的实例,来生成目标类的代理对象 。
//自定义定获取被代理接口实现类的实例对象
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
提示!
jdk动态代理必须要依赖于接口。
- 需要动态代理的接口
package com.sin.service;
/**
* 需要动态代理的接口
* @author sin
* @date 2022/10/27
* @apiNote
*/
public interface JDKAgentSubjectService {
/**
* 你好
* @param name
* @return
*/
public String sayHello(String name);
/**
* 再见
* @return
*/
public String sayGoodBye();
}
- 需要代理的实际对象
package com.sin.service.impl;
import com.sin.service.JDKAgentSubjectService;
/**
* 实例接口对象
*
* @author sin
* @date 2022/10/27
* @apiNote
*/
public class JDKAgentSubjectServiceImpl implements JDKAgentSubjectService {
/**
* 你好
*
* @param name
* @return
*/
@Override
public String sayHello(String name) {
return "你好" + name;
}
/**
* 再见
*
* @return
*/
@Override
public String sayGoodBye() {
return "再见";
}
}
- 调用处理器实现类
package com.sin.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author sin
* @date 2022/10/27
* @apiNote
*/
public class InvocationHandlerImpl implements InvocationHandler {
//要代理的对象
private Object subject;
public InvocationHandlerImpl(Object subject){
this.subject = subject;
}
/**
* 负责集中处理动态代理上的所有方法调用
* @param proxy 代理类实例
* @param method 被调用的方法对象
* @param args 调用参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("模拟的添加操作");
System.out.println("Method" + method);
Object value = method.invoke(subject,args);
System.out.println("接收后模拟的添加操作");
return value;
}
}
- 测试
package com.sin.test.proxy;
import com.sin.proxy.InvocationHandlerImpl;
import com.sin.service.JDKAgentSubjectService;
import com.sin.service.impl.JDKAgentSubjectServiceImpl;
import org.junit.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* @author sin
* @date 2022/10/27
* @apiNote
*/
public class InvocationHandlerTest {
@Test
public void testReal(){
//代理的对象
Object subjectService =new JDKAgentSubjectServiceImpl();
/**
* InvocationHandlerImpl 实现了InvocationHandler接口,实现方法调用从代理类到委托类的发派转发
* 内部包含了指向委托类实例的引用,用于真正执行分派转发过来的方法调用
* 要代理那个对象就将那个对象传进来,最后通过该对象来调用其方法
*/
InvocationHandler handler= new InvocationHandlerImpl(subjectService);
ClassLoader loader = subjectService.getClass().getClassLoader();
Class[] interfaces = subjectService.getClass().getInterfaces();
JDKAgentSubjectService subject = (JDKAgentSubjectService) Proxy.newProxyInstance(loader,interfaces,handler);
System.out.println("要动态代理对象的类型:"+subject.getClass().getName());
String hello = subject.sayHello("张三");
System.out.println(hello);
String bye = subject.sayGoodBye();
System.out.println(bye);
}
}
- 测试结果
CGLIB动态代理
采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。
实现方式实现 MethodInterceptor 接口,重写 intercept 方法,通过 Enhancer 类的回调方法来实现。
- 要代理对象接口
package com.sin.service;
/**
* @author sin
* @date 2022/10/27
* @apiNote
*/
public interface UserServiceCGlib {
public void addUser();
}
- 要代理对象接口实现类
package com.sin.service.impl;
import com.sin.service.UserServiceCGlib;
/**
* @author sin
* @date 2022/10/27
* @apiNote
*/
public class UserServiceCGlibImpl implements UserServiceCGlib {
@Override
public void addUser() {
System.out.println("增加了一条用户");
}
}
- CGlib动态代理实现类
package com.sin.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* CGlib 动态代理实现类
* @author sin
* @date 2022/10/27
* @apiNote
*/
public class MethodInterceptorImpl implements MethodInterceptor {
//CGlib需要代理的对象
private Object object;
public Object createProxyObject( Object o){
this.object = o;
//通过CGlib动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
//设置Enhancer对象的父类
enhancer.setSuperclass(o.getClass());
//设置Enhancer的回顾对象
enhancer.setCallback(this);
//设置Enhancer对象的父类
Object o1 = enhancer.create();
return o1;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object obj ;
System.out.println("调用前的业务");
obj = method.invoke(object,objects);
System.out.println("调用后的业务");
return obj;
}
}
- 测试
package com.sin.test.proxy;
import com.sin.proxy.MethodInterceptorImpl;
import com.sin.service.impl.UserServiceCGlibImpl;
import org.junit.Test;
/**
* @author sin
* @date 2022/10/27
* @apiNote
*/
public class MethodInterceptorTest {
@Test
public void test(){
UserServiceCGlibImpl userServiceCGlib = new UserServiceCGlibImpl();
MethodInterceptorImpl methodInterceptorimpl = new MethodInterceptorImpl();
UserServiceCGlibImpl serviceCGlib = (UserServiceCGlibImpl)methodInterceptorimpl.createProxyObject(userServiceCGlib);
serviceCGlib.addUser();
}
}
- 测试结果
静态代理
- 静态代理模式并没有做到事务的重用
- 假设dao有100个类,100个proxy,接口中有多少方法,在proxy层就得实现多少方法,有多少方法就要开启和提交多少事务
- 如果一个proxy实现了多个接口,如果其中的一个接口发生变化(添加了一个方法),那么proxy也要做相应改变
JDK动态代理总结
动态代理类:在程序运行时,运用反射机制动态创建而成。
JDK的动态代理必须具备四个条件:1、目标接口 2、目标类 3、拦截器 4、代理类
- 因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
- 生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
- 利用JDKProxy方式必须有接口的存在。
- invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。
缺点:
- 在拦截器中除了能调用目标对象的目标方法以外,功能是比较单一的,在这个例子中只能处理事务
- 使用JDK创建代理有一个限制,即JDK动态代理只能为接口创建代理。
CGLIB动态代理总结:
- CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
- 用CGlib生成代理类是目标类的子类。
- 用CGlib生成 代理类不需要接口
- 用CGLib生成的代理类重写了父类的各个方法。
- 拦截器中的intercept方法内容正好就是代理类中的方法体
JDK代理和CGLib 代理区别
- JDK动态代理只能针对接口实现类生成代理实例,而不能针对类;也就是说它是面向接口的
- CGLIB是针对类实现代理,主要是对指定的类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,
- JDK 代理是java 语言自带的,无需加载第三方类去实现,CGLIB是一个基于ASM实现的
- 使用动态代理的对象必须实现一个或多个接口
- 使用cglib代理的对象则无需实现接口,达到代理类无侵入。
AOP操作术语
-
目标对象(target)
- 目标对象指要增加的对象,即包含业务逻辑的类对象
-
连接点(Joinpoint):
- 在一个类中,可以被做增强处理的那些方法,称为“连接点”。
-
代理对象(Proxy)
- AOP中会通过代理的方式,对目标生成一个代理对象,代理对象中会加入需要增强功能,通过代理对象来间接的方式目标对象,起到增强目标对象的效果;
-
通知(Advice)
- 需要在目标对象中增加的功能,通知中有两个重要信息:方法的什么地方,执行什么操作
-
切入点(Pointcut):
- 用来指定需要将通知使用到哪些地方,比如需要用在哪些类的哪些方法上,切入点就时做这个配置的
-
切面(Aspect):
- 通知(Advice)和切入点(Pointcut)的组合。切面来定义在哪些地方(Pointcut)执行什么操作(Advice)。
-
顾问(Advisor):
- Advisor(顾问) 其实它就是 Pointcut(切入) 与 Advice(通知) 的组合,Advice (通知) 是要增强的逻辑,而增强的逻辑要在什么地方执行是通过Pointcut(点)来指定的,所以 Advice(顾问) 必需与 Pointcut(切入点) 组合在一起,这就诞生了 Advisor(顾问) 这个类,spring Aop中提供了一个Advisor(顾问)接口将Pointcut切入点(切入点) 与 Advice (通知)的组合起来。