Spring环境中支持两种动态代理
一、使用jdk提供的动态代理的场景
- 特点:当被代理对象是接口并且有对应的实现类(子类)的时候,可以采用该种代理方式。
- spring环境下的代码实现
首先是父接口
package my.reflect;
import org.springframework.stereotype.Component;
//表述计算器的行为(加减乘除)
//被代理对象是接口(interface)
public interface Calculator {
Integer add(Integer x, Integer y) throws NoSuchMethodException;
Integer sub(Integer x, Integer y) throws NoSuchMethodException;
Integer mul(Integer x, Integer y) throws NoSuchMethodException;
Integer div(Integer x, Integer y) throws NoSuchMethodException;
}
其次是实现类
package my.reflect;
import org.springframework.stereotype.Component;
//被代理的类添加到IOC容器中,保证ioc容器能扫描到
@Component
public class CalculatorImpl implements Calculator {
@Override
public Integer add(Integer x, Integer y) throws NoSuchMethodException {
int result = x + y;
return result;
}
@Override
public Integer sub(Integer x, Integer y) throws NoSuchMethodException {
int result = x - y;
return result;
}
@Override
public Integer mul(Integer x, Integer y) throws NoSuchMethodException {
int result = x * y;
return result;
}
@Override
public Integer div(Integer x, Integer y) throws NoSuchMethodException {
int result = x / y;
return result;
}
}
然后是代理类
package my.proxy;
import my.reflect.Calculator;
import my.utils.LogUtil2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 本案例采用jdk中的动态代理,该方式前提要有接口和实现接口的子类;
* 但是在真实的环境中,并不是所有的被代理类都有父类接口,此时要使用spring提供的cglib实现动态代理
*/
public class CalculatorProxy {
/**
* @param calculator 被代理对象,即目标对象
* @return 代理对象
*/
public static Calculator getCalculator(Calculator calculator){
//获取被代理对象的类加载器
ClassLoader classLoader = calculator.getClass().getClassLoader();
//获取被代理对象的所有接口
Class<?>[] interfaces = calculator.getClass().getInterfaces();
//用来执行被代理类需要执行的方法
InvocationHandler invocationHandler = new InvocationHandler() {
//注意invoke()方法的执行顺序,调用CalculatorProxy.getCalculator(targetObj)的时候,invoke()是没有执行的,只有用代理对象去调用目标对象的具体方法时,invoke()才执行
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//开始调用被代理类的方法
Object result = null;
try{
LogUtil2.methodStartLog(method,args);
result = method.invoke(calculator,args);
LogUtil2.methodStopLog(method,result);
}catch (Exception e){
LogUtil2.methodExceptionLog(method,e);
e.printStackTrace();
}finally {
LogUtil2.methodFinallyLog(method);
}
return result;
}
};
Object o = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
return (Calculator) o;
}
}
日志类
package my.utils;
import java.lang.reflect.Method;
import java.util.Arrays;
public class LogUtil2 {
/**
* 方法的入参个数不确定,所以采用可变参数的形式
* @param objects
*/
public static void methodStartLog(Method method, Object ... objects){
System.out.println(method.getName()+"方法开始执行,入参为:"+ Arrays.asList(objects));
}
public static void methodStopLog(Method method, Object ... objects){
System.out.println(method.getName()+"方法执行完毕,结果为:"+ Arrays.asList(objects));
}
public static void methodExceptionLog(Method method, Exception e){
System.out.println(method.getName()+"方法出现异常:"+ e.getCause().getMessage());
}
public static void methodFinallyLog(Method method){
System.out.println(method.getName()+"方法最终输出结果");
}
}
测试类
import my.proxy.CalculatorProxy;
import my.reflect.Calculator;
import my.reflect.CalculatorImpl;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JunitTest {
@Test
public void jdk_proxy_test() {
try {
CalculatorImpl targetObj = new CalculatorImpl();
//被代理对象targerObj作为入参返生成代理对象calculator_proxy
Calculator calculator_proxy = CalculatorProxy.getCalculator(targetObj);
//代理对象调用被代理对象的具体某个方法时回调invoke()具体执行
calculator_proxy.div(1,2);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
二、使用CGLIB提供的动态代理的场景
- 特点:被代理对象仅仅是普普通通的一个类,没有实现父接口,此时spring使用的cglib提供的动态代理
- spring环境下的代码实现
首先是被代理类
package my.reflect;
import org.springframework.stereotype.Component;
//被代理的类添加到IOC容器中,保证ioc容器能扫描到
@Component
public class CalculatorImpl {
public Integer add(Integer x, Integer y) throws NoSuchMethodException {
int result = x + y;
return result;
}
public Integer sub(Integer x, Integer y) throws NoSuchMethodException {
int result = x - y;
return result;
}
public Integer mul(Integer x, Integer y) throws NoSuchMethodException {
int result = x * y;
return result;
}
public Integer div(Integer x, Integer y) throws NoSuchMethodException {
int result = x / y;
return result;
}
}
其次是代理类
package my.proxy;
import my.utils.LogUtil2;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Calculator_cglib_proxy_factory implements MethodInterceptor {
private Object target;
public Calculator_cglib_proxy_factory() {
super();
}
public Calculator_cglib_proxy_factory(Object target) {
super();
this.target = target;
}
/**
* 创建代理的方法,生成CGLIB代理对象
* target目标对象,需要增强的对象
* 返回目标对象的CGLIB代理对象
*/
public Object createProxy(){
//增强器,创建一个动态类对象,即增强类对象
Enhancer enhancer = new Enhancer();
//继承被代理类(目标对象target),也就继承了target的方法和属性
enhancer.setSuperclass(target.getClass());
//设置回调,程序执行目标方法时调用intercept方法
enhancer.setCallback(this);
//生成代理对象
Object o = enhancer.create();
return o;
}
/**
* intercept方法会在程序执行目标方法时被调用
* proxyObj CGLIB根据指定父类生成的代理对象
* method拦截方法
* args拦截方法的参数数组
* methodProxy方法的代理对象,用于执行父类的方法
* 返回代理结果
*/
@Override
public Object intercept(Object proxyObj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
LogUtil2.methodStartLog(method,args);
Object result = method.invoke(target, args);
LogUtil2.methodStopLog(method,result);
return null;
}
}
日志类
package my.utils;
import java.lang.reflect.Method;
import java.util.Arrays;
public class LogUtil2 {
/**
* 方法的入参个数不确定,所有采用可变参数的形式
* @param objects
*/
public static void methodStartLog(Method method, Object ... objects){
System.out.println(method.getName()+"方法开始执行,入参为:"+ Arrays.asList(objects));
}
public static void methodStopLog(Method method, Object ... objects){
System.out.println(method.getName()+"方法执行完毕,结果为:"+ Arrays.asList(objects));
}
public static void methodExceptionLog(Method method, Exception e){
System.out.println(method.getName()+"方法出现异常:"+ e.getCause().getMessage());
}
public static void methodFinallyLog(Method method){
System.out.println(method.getName()+"方法最终输出结果");
}
}
测试类
import my.proxy.CalculatorProxy;
import my.reflect.Calculator;
import my.reflect.CalculatorImpl;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JunitTest {
@Test
public void cglib_test(){
try {
//入参目标对象,生成代理对象
CalculatorImpl proxy_Obj = (CalculatorImpl)new Calculator_cglib_proxy_factory(new CalculatorImpl()).createProxy();
//用代理对象执行目标方法,此时会回调intercept()方法,由invoke()方法具体执行
proxy_Obj.mul(2,5);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
总结
spring中使用那种动态代理的方式主要取决于代理对象是否实现父接口,如果没有就用cglib的,否则就用jdk自带的,无论采用哪一种方式,核心方法都是invoke()方法,cglib生成的代理对象都是被代理对象的子类,运用的机制是反射机制。(来自小白coder的一点看法,如有错误,请大佬们批评指正,谢谢)