2020全面总结"AOP"动态代理两种方法(Jdk和CGLIB)。
"AOP"动态代理两种方法(Jdk和CGLIB)。
因为Spring以"IoC"和"AOP"为内核,所以同"IoC"一样,"AOP"的概念也十分重要。
AOP:面向切面编程:面向对象编程(OOP)的一种补充,而不是取代面向对象编程。
传统业务代码中,如果我们用面向对象编程实现某些功能(如日志记录操作),就会分散到各个方法中,如果哪一天我们要修改这个操作或者其他行为,那么每个涉及到这个操作的相关方法都要修改,工作量巨大。所以我们引入了"面向切面编程",它采用横向抽取机制,将分散在各个方法里的重复的代码提取出来,将这些提取好的代码应用到需要执行的地方。
AOP思想:
AOP术语:
切面:用于横向插入系统功能(如日志记录操作)的类。
(这个类叫切面类,这个类中有方法,这些方法比如是日志记录操作,日志操作就是"通知"或者"增强处理")。
连接点:对象的一个操作,方法的调用。
切入点:切面与程序流程的交叉点,就是需要处理的连接点,通常在程序中,切入点指的是类或者方法名,如某个通知要应用到所有以 add开头方法中,那么所有满足这一规则的方法都是切入点。(add方法就是切入点,意思就是在哪个位置加功能,这个位置,也就是这个方法,它就是切入点,在这个切入点的前面加就是前置通知,后面加就是后置通知)
通知/增强处理:切面类中的方法,(想日志记录文件这个方法就是一个通知),也就是想增加的那个具体功能。添加了日志记录就是意味着通知/增强了处理。
目标对象:被通知的对象,被代理对象。
代理:将通知应用到目标对象之后,动态创建的对象。
织入:将切面代码插入到目标对象上,从而生成代理对象的过程
动态代理的两种常用的方法:
1.jdk动态代理
2.CGLIB代理
1.jdk动态代理方法:目标类需要实现一个或者多个接口。
通过代码深入理解:
1.创建一个UserDao接口:
package com.meicong.jdk;
public interface UserDao {
public void adduser(); //定义添加用户方法
public void deleteuser(); //定义删除
}
2.创建UserDaoImpl实现UserDao接口:
目标类:
package com.meicong.jdk;
public class UserDaoImpl implements UserDao{
//创建实现类
public void adduser() {
//重写方法
System.out.println("增加用户");
}
public void deleteuser() {
//重写方法
System.out.println("删除用户");
}
}
3.创建切面类:
package com.meicong.aspect;
public class Aspect {
//创建切面类
public void log01() {
//切面类中的方法log01()和log02():这里就是通知/增强处理(举例:日志记录操作)。
System.out.println("日志记录01"); //通过了通知/增强处理,输出的就会带上日志记录。
}
public void log02() {
System.out.println("记录日志02");
}
}
4.创建代理类:
package com.meicong.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.meicong.aspect.Aspect;
public class JdkProxy implements InvocationHandler{
//创建代理类必须实现InvocationHandler接口
private UserDao userdao; //声明目标类(要加入通知的那个类)的接口,UserDaoImpl是目标类
public Object createProxy(UserDao userdao) {
//创建代理类的方法,参数传入目标类接口。
this.userdao=userdao;
ClassLoader classloader=JdkProxy.class.getClassLoader(); //获取类加载器
Class[] clazz=userdao.getClass().getInterfaces(); //被代理对象(也就是要增强处理的类)实现所有的接口
return Proxy.newProxyInstance(classloader,clazz, this); //这个方法的返回值:Proxy类调用newProxyInstance方法。
//所有动态代理类方法的调用,都由invoke方法处理 //方法参数传入:1.类加载器2.实现的所有接口3.代理类本身。
} //增强了,返回的是代理后的对象。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//重写的invoke方法
Aspect myaspect=new Aspect(); //创建了切面类的对象
myaspect.log01(); //这个切面类对象调用了log01方法,也就是"通知",这是前增强。
Object obj=method.invoke(userdao, args);//反射:invoke(Object obj,Object...args)方法:利用指定对象执行obj中的方法。返回值:Object
//意思就是运用了反射机制的invoke方法,执行了userdao这个目标对象中的方法,实现了就是增加用户和删除用户的那两个方法。
myaspect.log02(); //切面类对象调用log02方法,属于后增强
return obj; //返回obj。
}
}
5.测试程序:
package com.meicong.jdk;
public class Test {
public static void main(String[] args) {
JdkProxy jdkproxy=new JdkProxy(); //创建代理类对象。
UserDao userdao=new UserDaoImpl(); //创建目标类对象。
UserDao userdao01=(UserDao)jdkproxy.createProxy(userdao); //代理类对象调用createProxy方法,把目标类对象传入,生成的就是增强后的对象。
userdao01.adduser(); //增强后的对象再调用方法就会有通知(前增强和后增强(日志记录))。
userdao01.deleteuser();
}
}
6.输出:
日志记录01
增加用户
记录日志02
日志记录01
删除用户
记录日志02
2.CGLIB代理:
jdk代理有一定局限性,必须实现接口,如果没有接口,就可以使用CGLIB代理。
对指定目标类生成子类,对子类增强,这个子类就是代理类。
1.创建目标类,不需要实现接口。
package com.meicong.cg1ib;
public class UserDao {
public void addUser() {
//定义两个方法,增加用户,删除用户。
System.out.println("增加用户") ;
}
public void deleteUser() {
System.out.println("删除用户") ;
}}
2.创建代理类:
package com.meicong.cglib;
import ...(省略)
public class Cglibproxy implements Methodlnterceptor{
//必须实现这个接口。
public Object createProxy(Object target) {
//代理方法:创建代理。
Enhancer enhancer = new Enhancer(); //创建动态类对象,它是 CGLIB的核心类
enhancer.setSuperclass(target.getClass()); //确定需要增强的类,设置其父类。
enhancer.setCallback(this); //回调函数
return enhancer.create(); //返回创建的代理类。
public Object intercept(Object proxy, Method method, Object[) args, //重写方法,
MethodProxy methodProxy) throws Throwable {
//(根据父类生成的代理对象,拦截的方法,拦截方法的参数,方法的代理对象)
MyAspect myAspect = new MyAspect(); //切面类对象
myAspect.check Permissions(); //前增强
Object obj = methodProxy.invokeSuper(proxy , args); //目标方法执行
myAspect.log(); // 后增强
return obj;
}}
3.创建测试类:
package com.meicong.cglib;
public class CglibTest {
public static void main(String[] args) {
Cglibproxy cglibproxy = new CglibProxy();
UserDao userDao = new UserDao();
UserDao userDao1 = (UserDao) cglibProxy.createProxy (userDao) ;
userDao1 . addUser() ;
userDao1.deleteUser() ;
}}
输出:与jdk代理输出一致