移动端网站制作谷歌浏览器怎么下载
学习设计模式不光要学习设计模式的思想,还要去深入理解,为什么要用这个设计模式。
如何深入理解?读优秀的框架代码,看别人代码,了解它们的使用场景。 - - - 博主老师(感谢他)
本文先介绍了代理模式的概念及简单实现。简单聊了下为什么要使用代理模式,并介绍了代理模式在spring中的使用
代理模式
- 1、概念
- 2、实现
- 2.1 静态代理
- 2.2 动态代理
- 3、为什么使用代理类模式
- 4、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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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, 默认是falseboolean 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 {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 将AspectJAnnotationAutoProxyCreator装载到spring容器。 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);...}}
将AspectJAnnotationAutoProxyCreator装载到spring容器。
AnnotationAwareAspectJAutoProxyCreator实现了(父类实现)BeanPostProcessor接口,接口的postProcessAfterInitialization方法会在bean初始化完成后被调用。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupportimplements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {@Overridepublic 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 {@Overridepublic 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@Nullablepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {...try {// 获取当前bean被拦截方法链表List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// 如果为空,则直接调用target的methodif (chain.isEmpty()) {Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);}// 不为空,则逐一调用chain中的每一个拦截方法的proceedelse {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