静态代理
原理
在不改变原始类(或叫被代理类)的情况下,通过引入代理类来给原始类附加功能。一般情况下,我们让代理类和原始类实现同样的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的。在这种情况下,我们可以通过让代理类继承原始类的方法来实现代理模式。
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public interface UserService {
public void login(String username, String password);
} public class UserServiceImpl implements UserService {
@Override public void login(String username, String password) { System.out.println("User ["+username+"] logins!"); } } public class UserServiceStaticProxy extends UserServiceImpl {
@Override public void login(String username, String password) { System.out.println("login start:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))); super.login(username, password); System.out.println("login end:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))); } }
|
动态代理
原理
静态代理需要针对每个类都创建一个代理类,并且每个代理类中的代码都有点像模板式的“重复”代码,增加了维护成本和开发成本。对于静态代理存在的问题,我们可以通过动态代理来解决。我们不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。
Java实现动态代理采用的是反射。
实现
动态代理有两种,一种是被代理的类是实体类,而不是接口,这种情况在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
java代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class DynamicInvocationHandler implements InvocationHandler {
private final Object proxiedObject;
public DynamicInvocationHandler(Object proxiedObject) { this.proxiedObject = proxiedObject; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("login start:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))); Object result = method.invoke(proxiedObject, args); System.out.println("login end:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))); return result; } }
public class UserServiceDynamicProxy {
private final UserServiceImpl userService;
public UserServiceDynamicProxy() { this.userService = new UserServiceImpl(); }
public Object createProxy(Object proxiedObject) { Class<?>[] interfaces = proxiedObject.getClass().getInterfaces(); DynamicInvocationHandler handler = new DynamicInvocationHandler(userService); return Proxy.newProxyInstance(proxiedObject.getClass().getClassLoader(), interfaces, handler); }
}
public class Application {
public static void main(String[] args) { UserService userService = new UserServiceStaticProxy(); userService.login("username","password"); UserServiceDynamicProxy userServiceDynamicProxy = new UserServiceDynamicProxy(); UserService userService1 = (UserService) userServiceDynamicProxy.createProxy(new UserServiceImpl()); userService1.login("username","password");
}
}
|
CGLIB代理
动态代理需要被代理类实现接口,如果被代理类没有实现接口,就需要用到CGLib了。这种代理方式就叫做CGlib代理。
Cglib代理也叫作子类代理,他是通过在内存中构建一个子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,然后加入自己需要的操作。因为使用的是继承的方式,所以不能代理final 类。
导入依赖:
1 2 3 4 5
| <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class CglibProxyFactory<T> implements MethodInterceptor {
private final T target;
public CglibProxyFactory(T target) { this.target = target; }
public Object getProxyInstance() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this);
return enhancer.create(); }
@Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("login start:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))); Object result = method.invoke(target, args); System.out.println("login end:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))); return result; } }
|
第二种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class CglibProxyInterfaceFactory implements MethodInterceptor {
public <T> T getProxyInstance(Class<T> clz) { return (T) Enhancer.create(clz, this); }
@Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("login start:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))); Object result = methodProxy.invokeSuper(o, args); System.out.println("login end:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))); return result; } }
|