一.引入
-
程序为什么需要代理?
对象 如果嫌身上的事太多的话,可以通过代理来转移部分职责 -
生活中都有什么代理呢?
最明显的就是明星和经纪人的例子: 一个明星只需要会唱歌/跳舞就好,不需要自己再去谈合作/租场地等 而谈合作/租场地这些工作都由经纪人来完成 这就是一个简单的代理
-
什么是代理?
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理.
动态代理原理详解 -
两种代理方式
(1) JDK 原生动态代理
(2) CGLIB 动态代理
二.JDK动态代理
- JDK动态代理的4个条件
目标接口,目标类,代理类,代理类里的拦截器 - 动态代理接口:InvocationHandler
- 动态代理类:Proxy
目标接口
//目标接口
public interface UserService {
...
//一个简单的添加用户的接口
Boolean add(User user);
...
}
目标类
//目标类UserServiceImpl 继承了目标接口UserService
public class UserServiceImpl implements UserService{
...
//实现目标接口的方法
@Override
public Boolean add(User user) {
...
return null;
}
...
}
代理类 and 代理类里的拦截器
//代理类
public class JDKProxy implements InvocationHandler {
...
//该方法的作用是 返回创建一个代理对象
public Object createProxyObject(Object targetObject){...}
...
//该方法就是代理类中的 拦截器invoke
@Override
public Object invoke(Object proxy, Method method, Object[] args) {...}
...
}
动态代理—>你让我办事可以,我可能把你的事变成我的事
代码案例
- UserService 目标接口
package com.neuedu.service;
import com.neuedu.entity.User;
//目标接口
public interface UserService {
//一个简单的添加用户的接口
Boolean add(User user);
Boolean select();
}
- UserServiceImpl 目标类(实现目标接口的方法)
package com.neuedu.service;
import com.neuedu.entity.User;
import org.springframework.stereotype.Service;
//目标类---->一定要实现目标接口(规定)
//一个类通过关键字implements声明自己使用一个或者多个接口
/**
* 这个add()方法写好就不动了--->作为目标类--->我们想要通过一个代理类,实现 调用add()方法时可以执行其他内容
* 就是不动这个代码,但是可以让它实现其他内容
*/
@Service
public class UserServiceImpl implements UserService{
@Override
public Boolean add(User user) {
System.out.println("添加用户方法");
return null;
}
@Override
public Boolean select() {
System.out.println("查询方法");
return null;
}
}
- JDKProxy 代理类+代理类中的拦截器(invoke方法)
package com.neuedu.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理类---->必须实现InvocationHandler这个接口
* 注意导包要导java.lang.reflect这个包的InvocationHandler接口
*/
public class JDKProxy implements InvocationHandler {
// 获取要代理的对象(目标对象)---->命名为了targetObject
private Object targetObject;
// 写一个方法--->通过目标对象获取一个代理对象
public Object createProxyObject(Object targetObject){
this.targetObject=targetObject;
/**
* newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
* ClassLoader loader---->参数1:用于指定一个类加载器
* Class<?>[] interfaces---->参数2:指定生成的代理长什么样子,也就是有哪些方法(因为方法不止一个,所以是数组类型的)
* InvocationHandler h---->参数3:用来指定生成的代理对象要干什么事情
*/
//返回了一个代理对象
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this);
}
// invoke就是代理类中的拦截器
/**
* Object proxy---->代理对象
* Method method---->目标方法
* Object[] args---->目标方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("正在执行invoke");
System.out.println("代理类执行的目标方法:"+method.getName());
/**
* invoke(Object obj, Object... args)
* Object obj---->目标对象
* Object... args---->就是上面的Object[] args
*/
// 执行目标类的方法
method.invoke(this.targetObject,args);
return null;
}
}
三.CGLIB动态代理
CGLIB动态代理不需要再添加Maven依赖了—>因为spring-core中自带
-
CGLIB动态代理
1.用CGLIB生成的 动态代理类是目标类的子类 2.用CGLIB生成的 代理类不需要接口 3.用CGLIB生成的 代理类重写了父类的各个方法 4.拦截器中的intercept方法内容正好就是代理类中的方法体
-
目标类
package com.neuedu.service; import org.springframework.stereotype.Service; /** * CGLIB动态代理--->目标类 */ //把目标类放入到spring容器中--->这样就不用new了 @Service public class DepartService { /** * 一个简单的 事务提交 的模拟 * 事务-->需要多个表同时进行 增,删,改 */ public void save(){ /** * 自己造一个异常--->1/0是一个bug-->因为除0是不允许的 * 造异常是为了完善代码--->在代理类的拦截方法(intercept)中-->增加异常抛出的代码 */ int a=1/0; System.out.println("保存部门信息"); } public void update(){ System.out.println("更新部门信息"); } public void select(){ System.out.println("数据查询"); } }
-
代理类—>必须实现MethodInterceptor接口
public class CGLIBProxy implements MethodInterceptor { //目标对象 private Object targetObject; //创建代理对象 public Object createProxy(){ //字节码增强技术--->在反射时提高效率 Enhancer enhancer = new Enhancer(); //设置代理类的父类是目标类 enhancer.setSuperclass(targetObject.getClass()); //设置回调 enhancer.setCallback(this); return enhancer.create(); } ... //实现接口方法 //等同于invoke方法-->拦截器 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy){ ... } }
-
测试
@Autowired private CGLIBProxy cglibProxy; @Autowired private DepartService departService; @Test public void testCGLIBProxy(){ cglibProxy.setTargetObject(departService); //ds是一个代理对象 DepartService ds= (DepartService) cglibProxy.createProxy(); ds.save(); ds.update(); ds.select(); }
代码实例
package com.neuedu.proxy;
import lombok.Setter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* CGLIB动态代理---->代理类
* CGLIB代理类要实现MethodInterceptor接口
* 注意 : MethodInterceptor接口-->导包-->org.springframework.cglib.proxy.MethodInterceptor
*/
//把代理类放入到spring容器中--->这样就不用new了
@Component
public class CGLIBProxy implements MethodInterceptor {
//目标对象
@Setter
private Object targetObject;
//创建代理对象
public Object createProxy(){
//字节码增强技术--->在反射时提高效率
Enhancer enhancer = new Enhancer();
//设置代理类的父类是目标类
enhancer.setSuperclass(targetObject.getClass());
//设置回调
enhancer.setCallback(this);
return enhancer.create();
}
//实现接口方法
/**
* 等同于invoke方法
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object ret=null;
if(method.getName().equals("save")||method.getName().equals("update")){
System.out.println("开启事务...");
try{
ret=method.invoke(this.targetObject,objects);
System.out.println("提交事务");
}catch (Exception e){
System.out.println("事务回滚");
}finally {
System.out.println("始终执行");
}
}else {
ret=method.invoke(this.targetObject,objects);
}
return ret;
}
}
final类
final类