一、静态代理
1、概述
某天,同事要去香港旅游,我突然灵机一动,让同事帮我代购某某化妆品吧,会很便宜的。其实现在有专门做代购的网站,通过这个网站买商品很便宜。软件工程中也有类似的解决方案,该方案对应的设计模式就是代理模式。
代理模式(Proxy Pattern):给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
2、结构与实现
代理模式包含以下三个角色:
- Subject(抽象主题角色) : 它是代理主题和真实主题的公共父类,客户端通过这个类或接口可以访问代理主题和真实主题。
- Proxy(代理主题角色): 类似代购中的代购网站,它中有真实主题类的对象作为属性,可以随时访问真实主题的方法。在访问真实主题方法前后还可以附加其他业务代码。
- RealSubject(真实主题角色): 类似代购中的商品
3、案例
案例一:某软件公司承接了某信息咨询公司的收费商务信息查询系统的开发任务,该系统的基本需求如下:
(1) 在进行商务信息查询之前用户需要通过身份验证,只有合法用户才能够使用该查询系统
(2) 在进行商务信息查询时系统需要记录查询日志,以便根据查询次数收取查询费用
该软件公司的开发人员已完成了商务信息查询模块的开发任务,现希望能够以一种松耦合的方式向原有系统增加身份验证和日志记录功能,客户端代码可以无区别的对待原始的商务信息查询模块和增加新功能之后的商务信息查询模块,而且可能在将来还要在该信息查询模块中增加一些新的功能。
试使用代理模式设计并实现该收费商务信息查询系统。
案例一实现方案代码如下:
package com.mzy.shejimoshi.ProxyExample;
public interface Searcher {
public String doSearch(String userId, String keyword);
}
package com.mzy.shejimoshi.ProxyExample;
public class RealSearcher implements Searcher {
@Override
public String doSearch(String userId, String keyword) {
System.out.println("用户'"+userId+"'使用关键字'"+keyword+"'查询商务信息!");
return "返回具体内容";
}
}
package com.mzy.shejimoshi.ProxyExample;
public class AccessValidator {
public boolean validate(String userId) {
System.out.println("在数据库中验证用户'"+userId + "'是否为合法用户?");
if(userId.equalsIgnoreCase("杨过")) {
System.out.println("'"+userId+"'登录成功!");
return true;
} else {
System.out.println("'"+userId+"'登录失败!");
return false;
}
}
}
package com.mzy.shejimoshi.ProxyExample;
public class Logger {
public void log(String userId) {
System.out.println("更新数据库,用户'"+userId+"'查询次数加1!");
}
}
package com.mzy.shejimoshi.ProxyExample;
public class ProxySearcher implements Searcher {
private RealSearcher realSearcher = new RealSearcher();
private AccessValidator validator;
private Logger logger;
@Override
public String doSearch(String userId, String keyword) {
if (this.validate(userId)) {
String result = realSearcher.doSearch(userId, keyword);
this.log(userId);
return result;
} else {
return null;
}
}
public boolean validate(String userId) {
validator = new AccessValidator();
return validator.validate(userId);
}
public void log(String userId) {
logger = new Logger();
logger.log(userId);
}
}
package com.mzy.shejimoshi.ProxyExample;
import com.mzy.shejimoshi.util.XMLUtil;
public class Client {
public static void main(String[] args) {
Searcher searcher;
searcher = (Searcher)XMLUtil.getBean3();
String result = searcher.doSearch("杨过", "玉女心经");
}
}
执行结果如下:
在数据库中验证用户'杨过'是否为合法用户?
'杨过'登录成功!
用户'杨过'使用关键字'玉女心经'查询商务信息!
更新数据库,用户'杨过'查询次数加1!
二、动态代理
1、概述
如果需要为不同的真实主题类提供代理类或者代理一个真实主题类中的不同方法,都需要增加新的代理类,这将导致系统中的类个数急剧增加。动态代理可以让系统在运行时根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实主题类而且可以代理不同方法。
2、关键类介绍
从jdk1.3开始,java提供了对动态代理的支持,实现动态代理所需的类在java.lang.reflect包中
- Proxy类
- public static Class<?> getProxyClass(ClassLoader,
Class<?>…interfaces) - public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- InvocationHandle接口
- public Object invoke(Object proxy, Method method, Object[] args)
getProxyClass这个方法用来获取代理类;newProxyInstance这个方法用来获取代理类实例,该方法的参数loader是类加载器,interfaces是Class数组,InvocationHandler 比较关键,当客户端通过代理类来调用真实主题的业务方法时,或默认调用对应实现InvocationHandler接口的invoke方法。
3、案例
案例二:某软件公司要为公司OA系统数据访问层DAO增加方法调用日志,记录每一个方法被调用的时间和调用结果,现使用动态代理进行设计和实现。
案例二实现方案代码如下:
package com.mzy.shejimoshi.DynamicProxy;
public interface AbstractUserDAO {
public Boolean findUserById(String userId);
}
package com.mzy.shejimoshi.DynamicProxy;
public interface AbstractDocumentDAO {
public Boolean deleteDocumentById(String documentId);
}
package com.mzy.shejimoshi.DynamicProxy;
public class UserDAO implements AbstractUserDAO {
@Override
public Boolean findUserById(String userId) {
if (userId.equalsIgnoreCase("张无忌")) {
System.out.println("查询ID为"+userId+"的用户信息成功!");
return true;
} else {
System.out.println("查询ID为"+userId+"的用户信息失败!");
return false;
}
}
}
package com.mzy.shejimoshi.DynamicProxy;
public class DocumentDAO implements AbstractDocumentDAO {
@Override
public Boolean deleteDocumentById(String documentId) {
if (documentId.equalsIgnoreCase("D001")) {
System.out.println("删除ID为"+documentId+"的文档信息成功!");
return true;
} else {
System.out.println("删除ID为"+documentId+"的文档信息失败!");
return false;
}
}
}
package com.mzy.shejimoshi.DynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class DAOLogHandle implements InvocationHandler {
private Calendar calendar;
private Object object;
public DAOLogHandle(){}
public DAOLogHandle(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeInvoke();
Object result = method.invoke(object, args);
afterInvoke();
return result;
}
public void beforeInvoke() {
calendar = new GregorianCalendar();
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
String time = hour + ":" + minute + ":" + second;
System.out.println("调用时间:"+time);
}
public void afterInvoke() {
System.out.println("方法调用结束!");
}
}
package com.mzy.shejimoshi.DynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String [] args) {
InvocationHandler handler = null;
AbstractUserDAO userDAO = new UserDAO();
handler = new DAOLogHandle(userDAO);
AbstractUserDAO proxy = null;
proxy = (AbstractUserDAO) Proxy.newProxyInstance(AbstractUserDAO.class.getClassLoader(), new Class[]{AbstractUserDAO.class}, handler);
proxy.findUserById("张无忌");
System.out.println("-------------------------------------------------");
AbstractDocumentDAO documentDAO = new DocumentDAO();
handler = new DAOLogHandle(documentDAO);
AbstractDocumentDAO proxy_new = null;
proxy_new = (AbstractDocumentDAO)Proxy.newProxyInstance(AbstractDocumentDAO.class.getClassLoader(), new Class[]{AbstractDocumentDAO.class}, handler);
proxy_new.deleteDocumentById("D002");
}
}
执行结果如下:
调用时间:16:28:10
查询ID为张无忌的用户信息成功!
方法调用结束!
-------------------------------------------------
调用时间:16:28:10
删除ID为D002的文档信息失败!
方法调用结束!
三、Spring AOP
1、使用范例
package com.mzy.shejimoshi.SpringAOP.service;
import org.springframework.stereotype.Component;
@Component
public class AopTest {
public void test() {
System.out.println("调用test()!");
}
}
package com.mzy.shejimoshi.SpringAOP.config;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.io.Serializable;
@Aspect
@Component
public class AopTestConfig implements Serializable {
@Pointcut("execution(* com.mzy.shejimoshi.SpringAOP.service.AopTest.test(..))")
public void aspect() {
}
@Before("aspect()")
public void beforeCallback () {
System.out.println("hello!");
}
}
package com.mzy.shejimoshi.SpringAOP;
import com.mzy.shejimoshi.ShejimoshiApplication;
import com.mzy.shejimoshi.SpringAOP.service.AopTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ShejimoshiApplication.class})// 指定启动类
public class Client {
@Autowired
private AopTest t;
@Test
public void test1() {
t.test();
}
}
执行结果如下:
hello!
调用test()!
2、源码分析
思路:
- aop的启动配置里注册了AnnotationAwareAspectJAutoProxyCreator类
- AnnotationAwareAspectJAutoProxyCreator该类实例化时会调用其父类BeanPostProcessor类的方法postProcessAfterInitialization
- postProcessAfterInitialization该方法其一获取目标bean的所有增强,其二创建代理类及对象
源码展示:
思路中的第一步:
public class AopNamespaceHandler extends NamespaceHandlerSupport {
/**
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
* '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
* and '{@code scoped-proxy}' tags.
*/
@Override
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
思路中的第二步:
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
AbstractAutoProxyCreator类下的postProcessAfterInitialization方法
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
思路中的第三步:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 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;
}
需关注getAdvicesAndAdvisorsForBean方法和createProxy方法, getAdvicesAndAdvisorsForBean获取该bean的增强,createProxy获取代理对象,先查看getAdvicesAndAdvisorsForBean。
protected abstract Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName,
@Nullable TargetSource customTargetSource) throws BeansException;
AbstractAdvisorAutoProxyCreator该类下的getAdvicesAndAdvisorsForBean方法
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
重点关注findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
其中findCandidateAdvisors获取所有增强,findAdvisorsThatCanApply获取该bean的增强,先看findCandidateAdvisors。
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
AnnotationAwareAspectJAutoProxyCreator类的findCandidateAdvisors方法
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
重点关注this.aspectJAdvisorsBuilder.buildAspectJAdvisors(),该方法的思路是这样的
- 获取项目中的所有bean
- 找到其中注解过@aspect的bean,保存起来
- 开始解析这些bean,提取增强内容
- 将提取的增强内容缓存起来
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
获取增强的思路一实现代码是:
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
获取增强思路二实现代码是
this.advisorFactory.isAspect(beanType)
获取增强思路三实现代码是
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
重点关注getAdvisors方法
List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory);
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new ArrayList<>();
for (Method method : getAdvisorMethods(aspectClass)) {
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
重点关注getAdvisor方法
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
现在开始看创建代理的源码。
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);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, 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方法
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
Object getProxy(@Nullable ClassLoader classLoader);
JdkDynamicAopProxy类中的getProxy方法,该类是针对jdk动态代理的内容,还有cglib动态代理的源码,以后再讲。
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
JdkDynamicAopProxy该类实现了InvocationHandle方法,重点关注invoke方法,特别是以下代码
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}