动态代理-JDK和CGLib


动态代理

  • 什么是动态代理
  • 两种常用的动态代理方式
    • JDK动态代理相关说明
    • JDK动态代理代码展示
    • CGLib动态代理相关说明
    • CGLib动态代理代码展示
  • 结合两种动态代理模式对AOP的理解
    • AOP的相关术语
    • AOP动态代理过程(实现原理)

什么是动态代理

利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象),代理的是接口(Interfaces),不是类(Class),也不是抽象类

动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术
在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
代理类在程序运行期间,创建的代理对象称之为动态代理对象。这种情况下,创建的代理对象,并不是事先在Java代码中定义好的。而是在运行期间,根据我们在动态代理对象中的“指示”,动态生成的。也就是说,你想获取哪个对象的代理,动态代理就会为你动态的生成这个对象的代理对象。动态代理可以对被代理对象的方法进行功能增强。有了动态代理的技术,那么就可以在不修改方法源码的情况下,增强被代理对象的方法的功能,在方法执行前后做任何你想做的事情
 经典应用:事务管理,性能监控,安全检查,缓存,日志。
 接口+实现类,Spring采用JDK的动态代理Proxy
 实现类:Spring采用cglib字节码增强

两种常用的动态代理方式

JDK动态代理相关说明

JDK动态代理是基于接口的方式,换句话来说就是代理类和目标类都实现同一个接口,那么代理类和目标类的方法名就一样了
1、基于接口的动态代理
·提供者:JDK
·使用JDK官方的Proxy类创建代理对象
核心:①代理的目标对象必须实现接口InvocationHandler
** ②Proxy.newProxyInstance(三个参数)**

   public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

newProxyInstance,方法有三个参数:
loader: 用哪个类加载器去加载代理对象
interfaces:动态代理类需要实现的接口
h:动态代理方法在执行时,会调用h里面的invoke方法去执行

JDK动态代理代码展示

1.定义一个接口:IUserDao

public interface IUserDao {
    public String getUserName();
}

2.定义一个实现类UserDao

public class UserDao implements IUserDao {
    @Override
    public void getUserName() {
        System.out.println("JAVA");
    }
}

3.创建代理类

public class ProxyUserDao {
    
    public static void main(String[] args) {
		// 定义需要代理的对象
        IUserDao user = new UserDao();

		// Proxy.newProxyInstance 生成动态代理对象
        IUserDao userDao = (IUserDao) Proxy.newProxyInstance
                (user.getClass().getClassLoader(), // 选择类加载器去加载代理对象
                 UserDao.class.getInterfaces(), // 动态代理类需要实现的接口
  //动态代理方法在执行时,会调用VehicalInvacationHandler里面的invoke方法去执行
 // new VehicalInvacationHandler(user)构造一个代理的目标对象实现接口InvocationHandler
                 new VehicalInvacationHandler(user));       
        userDao.run();
    }
}

3.代理的目标对象实现接口InvocationHandler

public class VehicalInvacationHandler implements InvocationHandler {

    private final IUserDao userDao;

	//有参构造器,new VehicalInvacationHandler(user)构造一个代理的目标对象
    public VehicalInvacationHandler(IUserDao userDao) {
        this.userDao = userDao;
    }

	//Object proxy是Proxy.newProxyInstance 生成的动态代理对象
	//Method method是我们需要代理的类中的方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   	 //AOP中的增强的方法就是在这里写的
        System.out.println("---------before-------");
        Object invoke = method.invoke(userDao, args);//代理类bean:userDao,参数args
        System.out.println("---------after-------");

        return invoke;
    }
}

CGLib动态代理相关说明

CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法,这样也可以保证代理类拥有目标类的同名方法
·使用CGLib的Enhancer类创建代理对象

  Object proxyObj = Enhancer.create(obj.getClass(), new MethodInterceptor() {

看一下CGLib的基本结构,下图所示,代理类去继承目标类,每次调用代理类的方法都会被方法拦截器拦截,在拦截器中才是调用目标类的该方法的逻辑,结构还是一目了然的;
动态代理-JDK和CGLib_第1张图片

CGLib动态代理代码展示

1.首先要导入cglib的依赖(第三方 CGLib)

<dependency>
        <groupId>cglibgroupId>
        <artifactId>cglibartifactId>
        <version>2.2.2version>
dependency>

2.定义目标类(一个公开方法,另外一个用final修饰):

public class Dog{    
    final public void run(String name) {
        System.out.println("狗"+name+"----run");
    }    
    public void eat() {
        System.out.println("狗----eat");
    }
}

3.方法拦截器

import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MyMethodInterceptor implements MethodInterceptor{
    @Override
   public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("这里是对目标类进行增强!!!");
        //注意这里的方法调用,不是用反射哦!!!
        Object object = proxy.invokeSuper(obj, args);
        return object;

		//或者	
		Object object = method.invoke(obj, args);//执行方法的调用
    }  
}

测试类

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;

public class CgLibProxy {
    public static void main(String[] args) {
        //在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\java\\java_workapace");
        
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(Dog.class);
        //设置回调函数
        enhancer.setCallback(new MyMethodInterceptor());
        
        //这里的creat方法就是正式创建代理类
        Dog proxyDog = (Dog)enhancer.create();
        //调用代理类的eat方法
        proxyDog.eat();       
    }
}

结合两种动态代理模式对AOP的理解

AOP的相关术语

1.Target Object(目标对象):需要被代理的类 例如:UserService
2.Joinpoint(连接点):那些可能被拦截的方法 例如:UserService中的所有方法
3.Pointcut(切入点):是指已经被增强的方法 例如:addUser()
4.Advice(通知/增强处理):增强代码 例如:befor(),after()
5.Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程
6.Proxy(代理):动态创建的代理类
7.Aspect(切面):advice通知和Pointcut切入点组成的面
一条线是一个特殊的面
一个advice通知的点和Pointcut切入点组成的特殊的面

AOP动态代理过程(实现原理)

动态代理-JDK和CGLib_第2张图片
我想对UserService类中的方法,每一个都添加开启事务和提交事务的方法。但是又不改动UserService,所以采用了AOP思想,用Spring生成了一个UserService的代理类,可以调用UserService的方法,也可以把事务类的方法添加到代理类中。完成了我们想要的情况
动态代理-JDK和CGLib_第3张图片
步骤解读:
①找到Target Object(目标对象),需要被代理的类,UserService
②找到Joinpoint(连接点),那些可能被拦截的方法 可以是UserService中的所有方法
③指定Pointcut(切入点),需要我们增强的方法 例如:addUser()
④写我们需要增强的内容,Advice(通知/增强处理):增强代码 例如:befor(),after()
Spring增强(advisor)=通知(advice)+切入点(PointCut)
⑤Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程
⑥Proxy(代理):动态创建的代理类
⑦形成Aspect(切面):advice通知和Pointcut切入点组成的面

你可能感兴趣的:(Spring,java,spring,开发语言)