学习设计模式不光要学习设计模式的思想,还要去深入理解,为什么要用这个设计模式。
如何深入理解?读优秀的框架代码,看别人代码,了解它们的使用场景。 - - - 博主老师(感谢他)
本文先介绍了代理模式的概念及简单实现。简单聊了下为什么要使用代理模式,并介绍了代理模式在spring中的使用
1、概念
定义:为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
2、实现
考虑这么个例子:
只有管理员才能看销售数据
2.1 静态代理
静态代理在代理类中持有关键业务实例的引用
用户接口
public interface IUser {
void visitData();
}
用户类
public class User implements IUser {
private String type;
public User(String type) {
this.type = type;
}
@Override
public void visitData() {
System.out.println("看到了数据...");
}
public String getType() {
return type;
}
}
用户代理
public class UserProxy implements IUser {
private User user;
public UserProxy(User user) {
this.user = user;
}
@Override
public void visitData() {
if (user.getType().equals("admin")) {
user.visitData();
} else {
System.out.println("没有权限");
}
}
}
测试
public class Test {
public static void main(String[] args) {
User admin = new User("admin");
User normal = new User("normal");
UserProxy adminProxy = new UserProxy(admin);
adminProxy.visitData();
UserProxy normalProxy = new UserProxy(normal);
normalProxy.visitData();
}
}
输出
看到了数据...
没有权限
2.2 动态代理
代理目标可以在运行时动态注入,代理的关键业务随着代理目标实例的不同而不同。常见的技术实现手段有:JDK提供的Proxy类+InvocationHandler接口,CGLIB,Javassist库等。我们使用jdk的InvocationHandler实现。
动态代理可以基于jdk的InvocationHandler实现
public class DynamicProxy implements InvocationHandler {
private Object obj;
public DynamicProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(obj, args);
return result;
}
}
测试类
public class Test2 {
public static void main(String[] args) {
IUser user = new User("admin");
DynamicProxy proxy = new DynamicProxy(user);
ClassLoader loader = proxy.getClass().getClassLoader();
// 1、ClassLoader 方法需要动态生成一个类 需要一个ClassLoader来加载该类
// 2、Class[] interfaces 需要代理的对象的Class数组
// 3、InvocationHandler 调用处理器
IUser userProxy = (IUser) Proxy.newProxyInstance(loader, new Class[] {IUser.class}, proxy);
userProxy.visitData();
}
}
输出
看到了数据...
3、为什么使用代理类模式
在上面的例子中,权限过滤为什么要让代理类去做?而不是直接在user的visitData方法里做判断呢?
因为面向对象的六大原则中有个叫:单一职责原则
- 代理模式可以在不破坏原有业务处理边界的前提下,增加额外的定制需求。
- 假如要调用的对象是一个远程的对象,需要跨网络访问。如果让你直接coding去调用,你需要处理网络连接、处理打包、解包等等非常复杂的步骤,所以为了简化客户端的处理,我们使用代理模式,在客户端建立一个远程对象的代理,客户端就象调用本地对象一样调用该代理,再由代理去跟实际对象联系。
4、Spring中的代理模式
我们都知道spring aop是基于动态代理实现的。
通过@EnableAspectJAutoProxy可以开启aop
package org.springframework.context.annotation;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
// aop的实现方式,ture:CGLIB, false:jdk的InvocationHandler, 默认是false
boolean proxyTargetClass() default false;
// 暴露方式,默认为false, 为true的时候,可以解决内部调用不能使用的场景
// 假设类有方法doSomething1()和doSomething2(),doSomething1()方法内部调用doSomething2()。在调用doSomething1()的时候,默认情况下aop只会对doSomething1()做增强,不会对doSomething2()做增强(尽管doSomething1内部调用了doSomething2(),且单独调用doSomething2()的是,2也能被增强)。
// 为true的时候,可以实现内部调用,不过在doSomething1()中调用doSomething2()的代码也要改下:XX proxy=(xx) AopContext.currentProxy();proxy.doSomething2();
boolean exposeProxy() default false;
}
@Import:将bean加载到spring容器
源码里:@Import({AspectJAutoProxyRegistrar.class}) 就是将AspectJAutoProxyRegistrar这个bean加载到容器中。
// ImportBeanDefinitionRegistrar的类只能通过@import加载
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 将AspectJAnnotationAutoProxyCreator装载到spring容器。
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
...
}
}
将AspectJAnnotationAutoProxyCreator装载到spring容器。
AnnotationAwareAspectJAutoProxyCreator实现了(父类实现)BeanPostProcessor接口,接口的postProcessAfterInitialization方法会在bean初始化完成后被调用。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 对bean进行包装,返回代理类
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
...
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
}
上面的代码可以看到,如果有切面,bean会在初始化完只后,被包装成代理对象。
createProxy方法
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 1.创建proxyFactory, 代理的工厂类
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 2.将当前bean适合的advice,重新封装下,封装为Advisor类,然后添加到ProxyFactory中
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
getProxy根据不同的情况,生产不同的代理对象。我们看使用JDK自带的代理方式的实现
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
}
JDK动态代理模式,真正的方法执行在invoke()方法里,继续看这个类的invoke方法
/**
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*/
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
try {
// 获取当前bean被拦截方法链表
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 如果为空,则直接调用target的method
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
// 不为空,则逐一调用chain中的每一个拦截方法的proceed
else {
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
...
return retVal;
}
...
}
参考:
https://www.cnblogs.com/silverLee/archive/2010/02/05/1664577.html
https://blog.csdn.net/JinXYan/article/details/89302126