Spring 的动态代理
- 方法增强: 在不改变源码的基础上对方法增强
- 继承
- 装饰者模式:(弊端:繁琐,假如有1000个方法,必须重写所有的方法)
- 1.装饰者类和被装饰者类必须实现同一个接口或继承同一个类
- 2.在装饰者类中必须要有被装饰者类的引用
- 3.在装饰者类中对需要增强的方法进行增强
- 4.在装饰者类中对不需要增强的方法调用原来的逻辑
- 动态代理
- 基于接口: JDK 要求: 被代理类必须实现了接口
- 基于子类: cglib 生成被代理类的子类
设计模式:
代码编写的固定格式
1.1 基于接口的动态代理(JDK):
作用: 方法增强
要求: 被代理类至少实现一个接口
Proxy: 动态代理
------------------ 接口
public interface AccountService {
void save();
void update();
void delete();
void select();
}
------------------ 实现类
/**
* 此类中的方法需要增强
* 被代理类
*/
public class AccountServiceImpl implements AccountService {
public void save() {
System.out.println("save...");
}
public void update() {
System.out.println("update...");
}
public void delete() {
System.out.println("delete...");
}
public void select() {
System.out.println("select...");
}
}
----------------- 动态代理
public class JDKProxy {
public static void main(String[] args) {
// 前提: 在不改变源码的基础上实现以下功能
//需求: 在service层的所有方法执行前打印 1111111111111
// 方法执行后打印2222222222222
/**
* 解决方案: 使用JDK提供的动态代理
* JDK的动态代理是基于接口的
* 动态代理其实就是固定格式的代码
* 要求: 被代理的类必须实现了接口
* API:
* java.lang.reflect.Proxy
* static Object newProxyInstance( // 此方法的执行会动态的生成一个类对象
* ClassLoader loader, // 类加载器,将生成的类加载到内存中
* Class<?>[] interfaces, // 生成的类需要实现的接口
* InvocationHandler h) // 调用处理
* 代理类和被代理类必须实现相同的接口
* 在代理类中对需要增强的方法进行增强
* 在代理类中对不需要增强的方法调用原来的逻辑
*/
// 被代理类
final AccountServiceImpl accountService = new AccountServiceImpl();
// 代理类: 此方法的执行会动态的生成一个类对象
AccountService proxy = (AccountService) Proxy.newProxyInstance(
JDKProxy.class.getClassLoader(), // 类加载器,将生成的类加载到内存中
// new Class[]{AccountService.class},// 生成的类需要实现的接口
accountService.getClass().getInterfaces(),
new InvocationHandler() { // 调用处理的方法
// 这个invoke方法与我们反射学习的invoke没有一点关系
// 每次调用生成的代理类的方法时,此方法都会执行
// 调用代理类的哪个方法,invoke就代表执行的那个方法
/**
* @param proxy : 动态生成的代理类对象引用(慎用)
* @param method : 代表当前执行的方法的字节码对象
* @param args : 当前执行的方法传递的参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// java中方法的返回值有两种状态,一种是有返回值,另外一种是无返回值
// 当前执行的方法若是有返回值,直接返回即可,如果没有则返回null
// 获取执行的方法名称
String name = method.getName();
System.out.println("执行的方法名: "+name);
// 原方法执行前打印 111111111
System.out.println("111111111111");
// 反射执行原方法
Object result = method.invoke(accountService,args);
// 原方法执行后打印 222222222
System.out.println("222222222222");
return result;
}
}
);
proxy.insert();
proxy.update();
proxy.delete();
proxy.select();
}
}
1.2 基于子类的动态代理(cglib第三方):
作用: 方法增强
------------------ jar包坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
------------------- 被代理类对象
/**
* 此类中的方法需要增强
* 被代理类
*/
public class AccountServiceImpl1{
public void save() {
System.out.println("save...");
//System.out.println(1/0);
}
public void update() {
System.out.println("update...");
}
public void delete() {
System.out.println("delete...");
}
public void select() {
System.out.println("select...");
}
}
--------------------- 代理类
public class Demo3 {
public static void main(String[] args) {
// 对没有实现接口的类中的方法进行增强
// 思路: 生成被代理类的子类
// cglib: 生成的代理类,为被代理类的子类
// 被代理类对象
final AccountServiceImpl1 serviceImpl1 = new AccountServiceImpl1();
// 此方法的执行会生成被代理类的子类
// 代理类
AccountServiceImpl1 proxy = (AccountServiceImpl1)Enhancer.create(
serviceImpl1.getClass(), // 1.被代理对象的字节码
new MethodInterceptor() { // 2.在原方法执行前进行拦截
// 对方法进行拦截处理
/**
* @param proxy : 生成的代理类对象
* @param method : 当前执行的方法的字节码对象
* @param args : 当前执行的方法传递的参数数组
* @param methodProxy (不用管) : 当前执行的方法的代理对象
* @return
* @throws Throwable
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
try {
// 在方法执行前打印 11111111111111
System.out.println("111111111111");
// 原方法执行
result = method.invoke(serviceImpl1,args);
// 在方法执行后打印 22222222222222
System.out.println("222222222222");
} catch (Exception e) {
// 当发生异常时打印 : 33333333333333
System.out.println("3333333333333");
//e.printStackTrace();
} finally {
// 最终打印 444444444444444
System.out.println("44444444444444");
}
return result;
}
}
);
// 测试
proxy.save();
//proxy.update();
//proxy.delete();
//proxy.select();
}
}
总结:
Proxy基于接口 : JDK
被代理类对象至少实现一个接口
代理类和被代理类为兄弟关系
cglib基于子类 : cglib
导入jar包坐标
Enhancer
代理和被代理类为父子关系
Spring会自动抉择