利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象),代理的是接口(Interfaces),不是类(Class),也不是抽象类
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。
在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
代理类在程序运行期间,创建的代理对象称之为动态代理对象。这种情况下,创建的代理对象,并不是事先在Java代码中定义好的。而是在运行期间,根据我们在动态代理对象中的“指示”,动态生成的。也就是说,你想获取哪个对象的代理,动态代理就会为你动态的生成这个对象的代理对象。动态代理可以对被代理对象的方法进行功能增强。有了动态代理的技术,那么就可以在不修改方法源码的情况下,增强被代理对象的方法的功能,在方法执行前后做任何你想做的事情
经典应用:事务管理,性能监控,安全检查,缓存,日志。
接口+实现类,Spring采用JDK的动态代理Proxy
实现类:Spring采用cglib字节码增强
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方法去执行
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的Enhancer类创建代理对象
Object proxyObj = Enhancer.create(obj.getClass(), new MethodInterceptor() {
看一下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();
}
}
1.Target Object(目标对象):需要被代理的类 例如:UserService
2.Joinpoint(连接点):那些可能被拦截的方法 例如:UserService中的所有方法
3.Pointcut(切入点):是指已经被增强的方法 例如:addUser()
4.Advice(通知/增强处理):增强代码 例如:befor(),after()
5.Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程
6.Proxy(代理):动态创建的代理类
7.Aspect(切面):advice通知和Pointcut切入点组成的面
一条线是一个特殊的面
一个advice通知的点和Pointcut切入点组成的特殊的面
我想对UserService类中的方法,每一个都添加开启事务和提交事务的方法。但是又不改动UserService,所以采用了AOP思想,用Spring生成了一个UserService的代理类,可以调用UserService的方法,也可以把事务类的方法添加到代理类中。完成了我们想要的情况
步骤解读:
①找到Target Object(目标对象),需要被代理的类,UserService
②找到Joinpoint(连接点),那些可能被拦截的方法 可以是UserService中的所有方法
③指定Pointcut(切入点),需要我们增强的方法 例如:addUser()
④写我们需要增强的内容,Advice(通知/增强处理):增强代码 例如:befor(),after()
Spring增强(advisor)=通知(advice)+切入点(PointCut)
⑤Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程
⑥Proxy(代理):动态创建的代理类
⑦形成Aspect(切面):advice通知和Pointcut切入点组成的面