1、讲动态代理之前,我们先了解下什么叫代理,从字面意思我们也能看出来意思就是代替某人做事情,看下面的例子:
package eureka.server.proxy;
public interface UserService {
void printName();
}
这是一个UserService接口类;
package eureka.server.proxy;
public class UserServiceImpl implements UserService {
@Override
public void printName() {
System.out.println("I am zhangsan");
}
}
这是一个UserService接口实现类UserServiceImpl,加入说我们想要在doString方法前面加上打印日志的功能怎么做呢?看下面的代码实现:
package eureka.server.proxy;
public class UserServiceProxy {
private UserService userService;
public UserServiceProxy(UserService userService){
this.userService = userService;
}
//代理类的方法
public void proxyMethod() {
before();
userService.doString();
after();
}
private void before(){
System.out.println("method exe before");
}
private void after(){
System.out.println("method exe after");
}
public static void main(String[] args) {
UserServiceProxy userServiceProxy = new UserServiceProxy(new UserServiceImpl());
userServiceProxy.proxyMethod();
}
}
上面的代码运用的就是设计模式中的代理模式,不过运用的是静态代理模式,可能有的读者会问了,假如UserService接口新增了一个方法也需要打印日志,按照现在的方式只能在UserServiceProxy 类中新增一个proxyMethod1然后加上打印日志的代码,这种方式虽然可以实现,但是可拓展性不是很好,因为每新增一个方法就需要修改代理类的代码,而且还要在多个方法中调用打印日志的代码,那么有没有什么好办法解决这个问题呢,当然是有的,看下面的代码:
package eureka.server.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* jdk的动态代理
*/
public class JdkUserServiceProxy implements InvocationHandler{
private Object target;
public JdkUserServiceProxy(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
beforeLog();
Object obj = method.invoke(this.target, params);
afterLog();
return obj;
}
private void beforeLog(){
System.out.println("method exe before");
}
private void afterLog(){
System.out.println("method exe after");
}
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
JdkUserServiceProxy jdkProxy = new JdkUserServiceProxy(userService);
//第一个参数是代理类的类加载器,第二个参数是被代理类实现的接口,第三个是实现InvocationHandler接口的代理类
UserService userProxy =(UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), jdkProxy);
userProxy.printName();
}
}
这个就是JDK的动态代理技术,可以在运行时动态的代理的被代理的类,完美的解决了上面的硬编码和可拓展行问题,但是还是有个缺陷,就是被代理的类都必须实现一个接口,如果不实现接口,就不能用JDK的动态代理技术,那是不是不实现接口就不能使用动态代理技术了呢,当然不是,我们还有Cglib动态代理技术,代码如下:
package eureka.server.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Cglib动态代理
*/
public class CglibUserServiceProxy implements MethodInterceptor{
/**
* 参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,
* MethodProxy为生成的代理类对方法的代理引用。返回:从代理实例的方法调用返回的值。
* 其中,proxy.invokeSuper(obj,arg):
* 调用代理类实例上的proxy方法的父类方法(即实体类ConcreteClassNoInterface中对应的方法)
* @param proxy
* @param method
* @param params
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
beforeLog();
Object obj = methodProxy.invokeSuper(proxy, params);
afterLog();
return obj;
}
public Object newProxy(){
Enhancer enhancer = new Enhancer();
//设置代理类class为父类
enhancer.setSuperclass(UserServiceImpl.class);
//设置拦截器
enhancer.setCallback(this);
return enhancer.create();
}
private void beforeLog(){
System.out.println("method exe before");
}
private void afterLog(){
System.out.println("method exe after");
}
public static void main(String[] args) {
CglibUserServiceProxy cglibProcy = new CglibUserServiceProxy();
UserService userProxy =(UserService)cglibProcy.newProxy();
userProxy.printName();
}
}
那么Cglib是不是就没有缺陷呢?当然不是,由于Cglib原理是生成目标类的一个子类并且覆盖目标类的方法,所以目标类和目标类的方法都不能修饰成final的,这点要注意,但是Cglib的效率比JDK的动态代理高;