1. 代理模式
代理模式:代理模式是Java中一种常用的设计模式,主要就是提供一个代理对象对外提供一个方法,然后用过代理对象去访问目标表对象;
通俗理解:
实际就是创建一个与目标对象(实际要操作的对象)相同类型的代理对象proxy,使用这个proxy对象去访问目标对象完成操作,在这个proxy对象中还可以有其他的功能去丰富目标对象的功能。服务器最后调用的就是这个proxy对象,这样就完成了代理增强。
代理模式最主要的就是在不改变原来代码(就是目标对象)的情况下实现功能的增强
1.1 静态代理
1.1.1 静态代理理解
静态代理:相当于是自己写了一个代理类,在调用的时候调用的是代理类,代理类中的处理还是原生的处理逻辑,不过在前后添加上需要增强功能的代码。
缺点:需要为每一个被代理的对象都创建一个代理类。
1.1.2 静态代理实现
- 创建接口
public interface PetService {
Pet add(String username, String password);
}
- 实现目标对象
public class PetServiceImpl implements PetService {
public Pet add(String username, String password) {
System.out.println("目标对象:petService执行");
return petDao.add(username,password);
}
}
- 实现代理对象
//代理对象
public class ProxyPetService implements PetService {
// 代理对象自己是不做事的,是交给目标对象做,相当于就是创建个目标对象,然后通过目标对象去完成方法,所以需要引入目标对象
private PetService petService;
//通过构造器去完成目标对象的创建
public ProxyPetService(PetService petService) {
this.petService = petService;
}
@Override
public Pet add(String username, String password) {
//可以加功能啥的了,就是要实现的方法,为了方便,就写一句输出就行
System.out.println("代理增强功能1....");
Pet add = petService.add(username, password);
//增加代理功能
System.out.println("代理增强功能2....");
return add;
}
}
- 测试代码
@Test
public void testJingTai(){
//要实现静态代理,就要将代理对象和目标对象进行关联起来,相当于就是要将目标对象放到代理对象中去
PetService petService = new PetServiceImpl();//创建目标对象
PetService proxyPetService = new ProxyPetService(petService);//创建代理对象
//外部调用目标对象去完成增强功能呢
Pet xiaowang = proxyPetService.add("xiaowang", "123456");
}
注意:目标对象和代理对象要实现用一个接口,目标对象完成自己的事情,代理对象调用目标对象,不仅可以完成目标对象的事情,自己还可以再加一些功能上去
1.2 动态代理
动态代理模式最大的优势就是不用自己去写一个代理对象,它的代理对象会在java文件编译的时候,通过Java反射(javac的过程)去创建的。所以减少了程序员的工作。动态代理的实现有两种方式,现在来介绍一下
1.2.1 JDK动态代理
注意:JDK动态代理的目标对象必须有接口实现
jdk实现动态代理理解:
主要是通过Java反射包里面的类和接口去完成的。反射包里面有三个类:InvocationHandler、Method、Prixy
- 创建接口
public interface PetDao {
Pet add(String username, String password);
}
- 目标对象
public class PetDaoImpl implements PetDao {
@Override
public Pet add(String username, String password) {
System.out.println("petDao执行");
return new Pet(username,password);
}
}
- 代理对象执行器invoke
要通过jdk反射来完成的话,要先去将代理对象的执行器invoke方法给构造好,所以需要实现反射包里面的InvovationHandler接口来完成了,相当于就是通过代理对象的执行器来完成
public class PetDaoInvoinvokecation implements InvocationHandler {
//在执行器中还是需要有目标对象呀
private Object obj;
public PetDaoInvoinvokecation(Object obj) {
this.obj = obj;
}
//invoke参数解释:proxy:代理对象,Method:代理对象的方法,args:代理对象所需的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理模式不需要自己去写代理对象,而是在程序运行编译的时候就会创建的
//真正的操作是由目标对象来完成的,所以通过method方法去完成执行器,里面参数就是目标对象以及目标对象要操作的方法的参数
System.out.println("jdk增强之前..");
Object invoke = method.invoke(obj, args);
System.out.println("jdk增强之后..");
return invoke;
}
}
注意:是反射包( java.lang.reflect )里面的InvovationHandler接口哈
- 测试类
@Test
public void testJDK(){
PetDao petDao = new PetDaoImpl();//目标对象
// 代理对象执行器
PetDaoInvoinvokecation petDaoInvoinvokecation = new PetDaoInvoinvokecation(petDao);
/*
代理对象,通过反射完成Proxy.newProxyInstance()
newProxyInstance参数:
类加载器:(代理对象的类型,也就是目标对象的)、
目标对象所实现的接口:(返回是一个数组,因为一个类可以实现多个接口嘛)、
执行器:
*/
//代理对象的类型,是目标对象所实现的接口类型之一,就相当于静态代理的时候一样,目标对象和代理对象都是实现的同一个接口
PetDao o = (PetDao) Proxy.newProxyInstance(petDao.getClass().getClassLoader(), petDao.getClass().getInterfaces(), petDaoInvoinvokecation);
Pet xiaowang = o.add("xiaowang", "123456");//代理对象执行方法
System.out.println(xiaowang);
}
jdk动态模式原理:
主要是由反射包( java.lang.reflect )里面的InvocationHandler、Method、Proxy三个类来完成的。
①通过实现InvocationHandler接口去创建代理对象的执行器(重写invoke方法)
invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { }
invoke方法里面就是通过method去执行具体的代理对象的方法
②然后通过Proxy的newProxyInstance方法去获取代理对象,
newProxyInstance参数:类加载器:(代理对象的类型,也就是目标对象的)、 目标对象所实现的接口:(返回是一个数组,因为一个类可以实现多个接口嘛)、 执行器:
③通过②里面的代理对象去调用目标对象的方法
注意代码中的注释!!
4.2.2 CJLIB动态代理
cglib代理模式是生成一个子类去完成的,并且不需要去实现接口
- 导入依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
- 创建目标对象
@Repository //将此类定义成持久层,然后将其放入spring中,里面可以加value参数的,加了就是这个持久层的id,默认是类名首字母是小写
//@Component //这个注解就是直接将此类放入spring中,不会标记此类的类型,一般用于普通类要放入spring的
public class PetDaoImplCGLIB{
public Pet add(String username, String password) {
System.out.println("目标对象 petDao执行");
return new Pet(username,password);
}
}
- MethodInterceptor拦截器
cglib的实现最重要的就是去实现这个MethodInterceptor接口来完成,就像jdk时候的InvocationHandler一样
//拦截器去完成,记得是cglib包下面的哈
public class PetDaoImplCGLIBIntercepter implements MethodInterceptor {
/*
intercept参数介绍:
Object:目标对象
Method:目标对象的方法
Object:目标对象方法的参数
MethodProxy:代理方法
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib代理增强1.....");
//调用目标对象执行方法
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("cglib代理增强2.....");
return o1;
}
}
4. 测试
cglib的原理就是在运行的时候,cglib会自动去生成一个子类,在子类里面去完成增强操作(就是拦截器里面),这里我们来验证cglib就用保存它的子类来查验,也就是将它自动生成的类放在一个指定路径下去看。
编写cglib的测试类
@Test
public void testCGLIB(){
//设置生成动态代理类的路径
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\xiaowang\\kuangjia\\spring\\cglib存放位置");
//创建Enhancer对象,就相当于jdk中的proxy类一样的,作用就是去执行方法的啦~
Enhancer enhancer = new Enhancer();
//设置代理类的目标对象
enhancer.setSuperclass(PetDaoImplCGLIB.class);
//创建拦截器
PetDaoImplCGLIBIntercepter petDaoImplCGLIBIntercepter = new PetDaoImplCGLIBIntercepter();
//设置回调函数 设置增强对象
enhancer.setCallback(petDaoImplCGLIBIntercepter);
//创建代理对象,类型是目标对象类型哈
PetDaoImplCGLIB o = (PetDaoImplCGLIB) enhancer.create();
//执行方法
Pet zhangsan = o.add("zhangsan", "123456");
System.out.println(zhangsan);
}
执行结果:
也执行出来了呀
注意:我们可以将生成的类反编译一下,就可以看到生成的类继承了我们的目标类,所以可以说明,cglib的原理其实就是cglib在底层继承了目标类然后去实现增强的
cglib动态代理原理:
①创建拦截器:继承MethodInterceptor的 intercepter的类,在拦截器中重写intercerpt( )方法,就是增强+目标对象的方法调用,返回拦截器
②在测试这边创建一个类似proxy的子类对象enhancer,然后设置这个代理对象的类型(setSuperclass(目标对象的类型.class完成))
③创建一个拦截器,enhancer通过去设置回调函数(setCallback(拦截器))
④创建代理对象enhancer.create(),代理对象的类型要和目标对象一致哈,然后通过代理对象去完成方法的调用
1.2.3 JDK和CGLIB区别
- JDK代理是基于有接口的情况下,而CGLIB没有接口也可以实现
- jdk是通过java的反射包的三个类(InvocationHandler、Method、Proxy)来完成的,而cglib是只针对类来实现的,原理就是对指定的目标类生成一个子类,并覆盖目标类的方法实现增强。
- cglib底层是采用了ASM字节码生成框架,使用字节码技术生成代理类,比使用java的反射效率要高一点
- cglib动态代理采用的是继承后重写,所以不能对final修饰的类进行代理,
1.3 静态动态代理区别
静态代理就是自己需要手动去写一个代理对象,实现目标对象所实现的接口,而动态代理却简化了这一步,不需要自己去写代理对象,而是在java编译的时候,通过反射完成了对象的创建。