AOP
一、AOP概述
1. 什么是AOP
- AOP(Aspect Oriented Programing) 面向切面编程
- AOP 采取横向抽取机制,取代了传统的继承纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
- Spring 的AOP 采用了纯Java 实现,不需要专门的编译过程和类加载器,在运行期间通过动态代理的方式向目标类注入增强代码。
2. AOP的应用场景
- 在不修改源代码的情况下对程序进行增强
- 权限校验、日志记录、性能监控、事务控制
3. AOP的底层实现
代理机制
Spring的AOP的底层用到两种代理机制
- JDK的动态代理 针对实现了接口的类产生代理
- CGLIB的动态代理 针对没有实现接口的类产生代理
二、什么是代理模式
1. 代理模式的概述
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象,这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
这里使用到编程种的一个思想:不要随意去修改别人已经写好的代码或者方法,可以通过代理的方式来扩展该方法。(对修改关闭,对扩展开放)
2. 代理模式的分类
- 静态代理
- 动态代理
三、静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。
1. 实现方法
先把项目结构放出来
1). 使用继承关系实现静态代理
- 创建Person
- 创建SuperPerson
- 测试
2). 使用装饰者模式实现静态代理
- 创建SuperPerson2
- 测试
3). 静态代理实例(对数据持久层进行事务管理)
- 创建UserDao
- 创建UserDaoImpl
- 创建UserDaoProxy
- 测试
2. 静态代理总结
- 可以做到在不修改目标对象的功能前提下,对目标功能进行扩展
- 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。同时,一旦接口增加方法,目标对象与代理对象都要维护。
- 解决静态代理种的缺点:使用动态代理方式。
4. 动态代理
1. JDK动态代理
- 项目结构图
- 创建UserDao
- 创建UserDaoImpl
- 创建ProxyFactory
package com.lasing.dao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理工厂
* 作用:创建代理类和代理对象
* */
public class ProxyFactory{
//声明目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
/**
* 得到代理对象
* @return
* */
public Object getProxyInstance() {
/**
* 参数说明
* ClassLoader loader, 类加载器
* Class<?>[] interfaces, 目标类所实现的所有接口数组
* InvocationHandle h 当代理对象被创建之后,调用目标对象的方法时触发的方法回调
**/
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),
new InvocationHandler() {
/**
* proxy:代理对象
* method:用户要调用的目标对象的方法
* args:目标对象的方法的参数
* */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前置增强
begin();
//执行目标对象的方法
Object invoke = method.invoke(target, args);
//后置增强
end();
return invoke;
}
});
}
public void end() {
System.out.println("提交事务");
System.out.println("关闭事务");
}
public void begin() {
System.out.println("建立连接");
System.out.println("开启事务");
}
}
- 测试
- JDK动态代理总结
优点:
|-- 有了目标对象,只要通过代理工厂就可以为所有的目标对象去创建代理对象不用显示创建代理类了
缺点:
|-- 目标对象必须至少实现一个接口;
|-- 代理对象强转时只能转成目标对象所实现的接口类型
原理:
|-- 当调用Proxy.newProxtInstance 方法时,系统会在内存里面根据代理对象的类加载器和目标对象所实现的所有接口去内存里面创建一个实现了目标对象所有接口的代理类,并使用这个代理类创建一个代理对象返回给调用者
2. CGLIB动态代理
上面的静态代理和动态代理都要要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用目标对象子类的方式类实现,这种方法就叫做CGLIB代理。
CGLIB代理,也叫作子类代理,它是在内存种构建一个子类对象从而实现目标对象功能的扩展。
JDK 的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想目标没有实现接口的类,就可以使用CGLIB实现。
CGLIB 是一个强大的高性能的代码生成包,它可以在运行期间扩展java 类与实现Java 接口,它广泛的被许多AOP 的框架使用,例如Spring AOP 和synaop,为它们提供方法的interception(拦截)
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类,不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class 文件的格式和指令集都很熟悉。
CGLIB子类代理实现方法:
1). 引入CGLIB 的jar 文件,但是Spring 的核心包中包括了CGLIB 功能,所以直接引入spring-core-5.0.2.RELEASE.jar 即可。
2). 引入功能包后,就可以在内存中动态构建子类
3). 代理的类不能为final,否则报错
4). 目标对象的方法如果为final/static,那么就不会拦截,既不会执行目标对象额外的业务方法
- 项目结构图
- 创建目标类
- 创建代理工厂
package com.lasing.dao;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 代理工厂
* 作用:创建代理类和代理对象
* */
public class ProxyFactory implements MethodInterceptor{
//声明目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
/**
* 得到代理对象
* @return
* */
public Object getProxyInstance() {
//1,创建一个子类对象的构造器
Enhancer enhancer = new Enhancer();
//2,设置父类
enhancer.setSuperclass(target.getClass());
//3,设置回调 就是当前对象
enhancer.setCallback(this);
//4,在内存里面生成代理对象
return enhancer.create();
}
/**
* 拦截方法
* obj:目标对象
* method:目标对象所调用的方法
* args:方法参数
* proxy:方法代理
* */
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
begin();
method.invoke(target, args);
end();
return null;
}
public void end() {
System.out.println("提交事务");
System.out.println("关闭事务");
}
public void begin() {
System.out.println("建立连接");
System.out.println("开启事务");
}
}
- 测试
- 总结
在spring的AOP编程中
|-- 如果加入容器的目标对象有实现接口,用JDK的动态代理
|-- 如果目标对象没有实现接口,用CGLIB代理