文章目录
AOP概念
AOP采取横向抽取机制,即将分散在各个方法中的重复代码提取出来,然后在程序编译或运行阶段,再将这些抽取出来的代码应用到需要执行的地方(一般用于日志记录、性能统计、安全控制、事务处理、异常处理等操作)
AOP术语
- 切面
切面(Aspect)是指封装横切到系统功能(如事务处理)的类。- 连接点
连接点(Joinpoint)是指程序运行中的一些时间点,如方法的调用或异常的抛出。- 切入点
切入点(Pointcut)是指那些需要处理的连接点。在Spring AOP 中,所有的方法执行都是连接点,而切入点是一个描述信息,它修饰的是连接点,通过切入点确定哪些连接点需要被处理。- 通知(增强处理)
由切面添加到特定的连接点(满足切入点规则)的一段代码,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面开启后,切面的方法。因此,通知是切面的具体实现。- 引入
引入(Introduction)允许在现有的实现类中添加自定义的方法和属性。- 目标对象
目标对象(Target Object)是指所有被通知的对象。如果AOP 框架使用运行时代理的方式(动态的AOP)来实现切面,那么通知对象总是一个代理对象。- 代理
代理(Proxy)是通知应用到目标对象之后,被动态创建的对象。- 组入
组入(Weaving)是将切面代码插入到目标对象上,从而生成代理对象的过程。根据不同的实现技术,AOP织入有三种方式:编译器织入,需要有特殊的Java编译器;类装载期织入,需要有特殊的类装载器;动态代理织入,在运行期为目标类添加通知生成子类的方式。Spring AOP框架默认采用动态代理织入,而AspectJ(基于Java语言的AOP框架)采用编译器织入和类装载期织入。
代理模式(就好比中介的存在)
代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。(添加额外的功能)
(虚拟)代理
虚拟代理就是作为创建开销大的对象的代表。虚拟代理经常直到我们真正需要一个对象的时候才创建它。当对象在创建前和创建中的时候,由虚拟代理来扮演对象的替身。对象创建后,代理就会将请求直接委托给对象。
静态代理
需要显式的实现与目标对象类(RealSubject)相同的接口,在程序运行前,代理类就已经创建好了。
案例实现
- 创建被代理类的接口和接口的实现
public interface Math {
public int jia(int a, int b);//加
public int jian(int a, int b);//减
}
public class MathImpl implements Math{
public int jia(int a,int b){
return a+b;
}
public int jian(int a,int b){
return a-b;
}
}
- 创建工具类,实现共有方法
public class Tools {
public void check(){
System.out.println("权限检查......");
}
public void log(){
System.out.println("日志记录......");
}
}
- 创建代理类
public class MathProxy implements Math {
// MathImpl mathImpl = new MathImpl(); //业务逻辑
// Tools tools = new Tools(); //工具类
MathImpl mathImpl; //业务逻辑
Tools tools; //工具类
// public MathProxy(){}
public MathProxy(MathImpl mathImpl, Tools tools) {
//在构造器中直接初始化了
this.mathImpl = mathImpl;
this.tools = tools;
}
@Override
public int jia(int a, int b) {
tools.check();
int result = mathImpl.jia(a,b);
tools.log();
return result;
}
@Override
public int jian(int a, int b) {
tools.check();
tools.log();
return mathImpl.jian(a,b);
}
}
- 创建测试类
public class Test {
public static void main(String[] args) {
/* 被代理对象--业务类 数学计算类 */
MathImpl mathImpl = new MathImpl();
//创建工具类
Tools tools = new Tools();
// 代理对象
Math math = new MathProxy(mathImpl,tools);
int result1 = math.jia(2,6);
System.out.println(result1);
int result2 = math.jia(3,2);
System.out.println(result2);
}
}
- 运行结果
动态代理(Spring AOP中常用JDK和CGLIB动态代理 - 现在一般用aspectj)
不需要显式实现与目标对象类(RealSubject)相同的接口,而是将这种实现推迟到程序运行时由 JVM来实现。动态代理在需要的时候才去创建,而不是提前创建好。
JDK和CGLIB动态代理的主要区别:
JDK动态代理只能针对实现了接口的类的接口方法进行代理
CGLIB动态代理基于继承来实现代理,所以无法对final类、private方法和static方法实现代理
Spring AOP中的代理使用的默认策略是:
如果目标对象实现了接口,则默认采用JDK动态代理
如果目标对象没有实现接口,则采用CgLib进行动态代理
如果目标对象实现了接口,且强制CgLib代理,则采用CgLib进行动态代理
1. JDK动态代理实现Spring AOP
JDK动态代理是java.lang.reflect.*包提供的方式,它必须借助一个接口才能产生代理对象。因此,对于使用业务接口的类,Spring默认使用JDK动态代理实现AOP。
在Spring中默认使用JDK动态代理实现AOP编程。使用org.springframework.aop.framework.ProxyFactoryBean创建代理是Spring AOP实现的最基本方式。
案例实现
- 创建被代理类的接口及实现类
public interface UserDao {
public void save(String name); //保存
public void delete(); //删除
}
//被代理类
public class UserDaoImpl implements UserDao{
@Override
public void save(String name) {
System.out.println("保存用户成功 :"+name);
}
@Override
public void delete() {
System.out.println("删除用户成功:");
}
}
- 创建切面类(公共方法)
//切面类:定义公共方法
public class MyAspect {
public void check(){
System.out.println("check user......正在查找用户");
}
public void log(){
System.out.println("logging.....正在记录日志");
}
}
- 创建代理类
//代理类
public class MyProxy implements InvocationHandler {
private UserDao userDao; //被代理类
private MyAspect myAspect; //切面类 -- 定义公共方法
public MyProxy(UserDao userDao, MyAspect myAspect) {
this.userDao = userDao;
this.myAspect = myAspect;
}
//创建代理对象
public Object create(){
ClassLoader classLoader = userDao.getClass().getClassLoader();
// Class<?>[] interfaces 被代理对象的实现接口
Class<?>[] interfaces = userDao.getClass().getInterfaces();
UserDao proxy = (UserDao) Proxy.newProxyInstance(classLoader,interfaces,this);
System.out.println("创建代理对象create");
return proxy;
}
// 调用方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 织入 advice 加入到 被代理对象方法之前或之后
// method就是调用的被代理对象的方法
String methodName = method.getName();
// Object 被代理对象
// 调用方法
System.out.println("调用invoke方法");
myAspect.check();//调用切面方法
method.invoke(userDao,args);//userDaoImpl.save(参数)
myAspect.log();//调用切面方法
return null;
}
}
- 创建测试类
public class Test {
public static void main(String[] args) {
// 被代理类
UserDao userDao = new UserDaoImpl();
// 切面
MyAspect myAspect = new MyAspect();
//创建代理类 参1:被代理类 参2:切面(公共方法)
MyProxy myProxy = new MyProxy(userDao,myAspect);
//通过代理类来创建实例 - 代理对象
UserDao proxy = (UserDao)myProxy.create();
proxy.save("GF_浪夏一学");
proxy.delete();
}
}
- 运行结果
2. CGLIB动态代理实现Spring AOP
CGLIB(Code Generation Library)是一个高性能开源的代码生成包,采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。在Spring Core包中已经集成了CGLIB所需要的JAR包,不需要另外导入JAR包
案例实现
- 创建被代理类(目标类)
public class UserService {
public void save(){
System.out.println("save service ......");
}
public void delete(){
System.out.println("delete service ......");
}
}
- 创建切面类(公共方法)
public class MyAspect {
public void check(){
System.out.println("check方法......检查包");
}
public void log(){
System.out.println("logging方法......记录日志");
}
}
- 创建代理类
public class MyProxy<T> implements MethodInterceptor {
private T byProxy; //被代理类
private MyAspect myAspect; //切面
public MyProxy(T byProxy, MyAspect myAspect) {
this.byProxy = byProxy;
this.myAspect = myAspect;
}
// 创建代理对象
public T create(){
// 通国Enhancer创建代理类 ---- Proxy
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(byProxy.getClass());
enhancer.setCallback(this);//实现MethodInterceptor接口的对象,就是当前对象
T proxy = (T)enhancer.create();
return proxy;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// Method method,调用的被代理对象方法
String methodName = method.getName();
if(methodName.equals("save")){
myAspect.check();
method.invoke(byProxy,objects);
myAspect.log();
}
if(methodName.equals("delete")){
myAspect.check();
method.invoke(byProxy,objects);
myAspect.log();
}
return null;
}
}
- 创建测试类
public class CGLIBTest {
public static void main(String[] args) {
// 被代理对象
UserService userService = new UserService();
MyAspect myAspect = new MyAspect();
//创建代理对象
MyProxy myProxy = new MyProxy(userService,myAspect);
//通过代理类创建被代理对象
UserService service = (UserService)myProxy.create();
//调用方法
service.save();
service.delete();
}
}
- 运行结果