Open zjs1224522500 opened 5 years ago
为了更好的理解Spring的核心思想(IOC和AOP),开始阅读并总结体会 code4craft 的 tinySpring模仿Spring的微型项目,并做一些笔记记录。
git checkout step-1-container-register-and-get
1、初始化beanFactory(容器)
/** * BeanFactory 主要包含一个存储 Bean 的 ConcurrentHashMap 对象,以及对应的注入 Bean 和获取 Bean 的方法 * Fields: private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(); * Methods: public Object getBean(String name) * public void registerBeanDefinition(String name, BeanDefinition beanDefinition) */ BeanFactory beanFactory = new BeanFactory();
2、向容器中注入Bean(实体)
/** * 为了保存一些额外的元信息,使用 BeanDefinition 来包装对应的 Bean * Fields: private Object bean; * Extra info(Class,ClassName) * Method: public BeanDefinition(Object bean); * 属性对应的 set 和 get方法 */ BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldService()); //注入时,则是利用 HashMap 存放相应的键值对 beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
3、获取对应的Bean实例
// 获取 Bean 时对应的利用 HashMap 从中取出一开始注入的 Bean HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); helloWorldService.helloWorld();
git checkout step-2-abstract-beanfactory-and-do-bean-initilizing-in-it
实际中需要使用容器来对所有的Bean进行统一的管理,自然对于Bean的整个生命周期都需要容器进行相应的干预。所以对于Bean的创建和初始化需要放入容器中进行处理,同时考虑到程序的扩展性,需要使用Extra Interface将BeanFactory更改为接口并提供相应的实现。
Bean
Extra Interface
BeanFactory
对于 BeanFactory 的继承关系
/** * Interface BeanFactory -> * Abstract class AbstractBeanFactory -> * class AutowireCapableBeanFactory */ public interface BeanFactory { Object getBean(String name); void registerBeanDefinition(String name, BeanDefinition beanDefinition); }
public abstract class AbstractBeanFactory implements BeanFactory { private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(); @Override public Object getBean(String name) { // 先获取对应的包装对象,再获取对应的实体 return beanDefinitionMap.get(name).getBean(); } @Override public void registerBeanDefinition(String name, BeanDefinition beanDefinition) { // 注入Bean之前先创建对应Bean Object bean = doCreateBean(beanDefinition); // 创建Bean之后并进行相应的包装 beanDefinition.setBean(bean); // 注入包装后的Bean对象 beanDefinitionMap.put(name, beanDefinition); } /**
}
public class AutowireCapableBeanFactory extends AbstractBeanFactory { @Override protected Object doCreateBean(BeanDefinition beanDefinition) { try { // 利用包装类对应的 class 对象,再利用 反射 创建对象 Object bean = beanDefinition.getBeanClass().newInstance(); return bean; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } }
#### 实现步骤:(相比step1区别仅在于 ==初始化BeanFactory和注入Bean==) - 1、初始化BeanFactory ```java // 创建能够 使用容器创建Bean实例 的容器对象 BeanFactory beanFactory = new AutowireCapableBeanFactory();
/** * BeanDefinition: * Fields: Object bean; * String beanClassName; * Class beanclass; * 其中在 beanClassName 的 set 方法进行注入时, * 利用反射和全类名创建出对应的 Class 对象并赋予给 beanClass属性 * public void setBeanClassName(String beanClassName) { * this.beanClassName = beanClassName; * try { * this.beanClass = Class.forName(beanClassName); * } catch (ClassNotFoundException e) { * e.printStackTrace(); * } * } */ BeanDefinition beanDefinition = new BeanDefinition(); // 通过使用 包装类BeanDefinition的全类名className 为容器使用反射创建对象做准备 beanDefinition.setBeanClassName("us.codecraft.tinyioc.HelloWorldService"); // 利用反射进行对象的创建并注入 beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
git checkout step-3-inject-bean-with-property
PropertyValue
BeanDefination
Spring
setter
demo
Field
1、初始化Factory
BeanFactory beanFactory = new AutowireCapableBeanFactory();
2、Bean定义
// BeanDefinition 定义中添加对应的 PropertyValues 属性 BeanDefinition beanDefinition = new BeanDefinition(); // 注入全类名便于之后利用反射创建相应的对象 beanDefinition.setBeanClassName("us.codecraft.tinyioc.HelloWorldService");
3、设置属性
/** * PropertyValue: 属性(键值对) * Fields: final String name; * final Object value; * * PropertyValues:保存对象的全部属性 * Fields: final List<PropertyValue> propertyValueList; * Methods: void addPropertyValue(PropertyValue p); */ PropertyValues propertyValues = new PropertyValues(); propertyValues.addPropertyValue(new PropertyValue("text", "Hello World!")); beanDefinition.setPropertyValues(propertyValues);
4、生成Bean
/** * 1、利用反射创建相应的Bean对象 * 2、向创建的对象中注入对应的属性(PropertyValues) * protected void applyPropertyValues(Object bean, BeanDefinition mbd) throws Exception { * // 遍历 PropertyValues中对应的属性 * for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) { * // 获取对象中指定属性名( PropertyValue 的 key)对应的属性 * Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName()); * // 设置Access使得私有属性能够通过反射访问 * declaredField.setAccessible(true); * // 向实体对象对应的属性字段注入属性值(value) 类似于setter * declaredField.set(bean, propertyValue.getValue()); * } * } */ beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
5、获取 Bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); helloWorldService.helloWorld();
git checkout step-4-config-beanfactory-with-xml
BeanDefinitionReader
void loadBeanDefinitions(String location)
AbstractBeanDefinitionReader
XmlBeanDefinitionReader
/** * interface BeanDefinitionReader -> * abstract class AbstractBeanDefinitionReader -> * class XmlBeanDefinitionReader */ public interface BeanDefinitionReader { void loadBeanDefinitions(String location) throws Exception; } public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader { // 保存从配置文件中加载的所有的 beanDefinition 对象 private Map<String,BeanDefinition> registry; /** * 依赖 ResourceLoader,该类又依赖 UrlResource * UrlResource 继承自 Spring 自带的 Resource 内部资源定位接口 * Resource 接口,标识一个外部资源。通过 getInputStream() 方法 获取资源的输入流 。 * UrlResource 实现 Resource 接口的资源类,通过 URL 获取资源。 * ResourceLoader 资源加载类。通过 getResource(String) 方法获取一个 Resource 对象,是获取 Resource 的主要途径. */ private ResourceLoader resourceLoader; protected AbstractBeanDefinitionReader(ResourceLoader resourceLoader){ this.registry = new HashMap<String, BeanDefinition>(); this.resourceLoader = resourceLoader; } public Map<String, BeanDefinition> getRegistry() { return registry; } public ResourceLoader getResourceLoader() { return resourceLoader; } } public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader{ public XmlBeanDefinitionReader(ResourceLoader resourceLoader) { super(resourceLoader); } // 从配置文件中加载 Bean 的相关信息 @Override public void loadBeanDefinitions(String location) throws Exception { // 利用文件路径创建相应的输入流 InputStream inputStream = getResourceLoader().getResource(location).getInputStream(); doLoadBeanDefinitions(inputStream); } protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = factory.newDocumentBuilder(); // 输入流转换成对应的 Document 对象便于获取对应的元素 Document doc = docBuilder.parse(inputStream); // 解析bean registerBeanDefinitions(doc); inputStream.close(); } public void registerBeanDefinitions(Document doc) { // 获取文件中包含的元素 <beans></beans> Element root = doc.getDocumentElement(); parseBeanDefinitions(root); } protected void parseBeanDefinitions(Element root) { // 获取元素包含的子节点链 <bean></bean> 链 NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { // 获取子节点链上对应的子节点 单个<bean></bean> Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; // 将节点强转为 Element 对象并进行解析 processBeanDefinition(ele); } } } protected void processBeanDefinition(Element ele) { // 获取子节点对应的属性(name,class)来对应 Bean // <bean name="" class=""></bean> String name = ele.getAttribute("name"); String className = ele.getAttribute("class"); // 创建与之对应的 BeanDefinition ,并设置相应的属性 BeanDefinition beanDefinition = new BeanDefinition(); // 将节点包含的 Bean相关的属性信息注入创建的 BeanDefinition 中。 processProperty(ele,beanDefinition); beanDefinition.setBeanClassName(className); // 统一管理(HashMap)通过配置文件加载的 beanDefinition 对象 getRegistry().put(name, beanDefinition); } private void processProperty(Element ele,BeanDefinition beanDefinition) { // 获取元素对应的 Property 节点 // <bean><property name="" value=""></property></bean> NodeList propertyNode = ele.getElementsByTagName("property"); for (int i = 0; i < propertyNode.getLength(); i++) { // 遍历节点并取出节点对应的 key-value,添加到 BeanDefinition 对应的属性中 Node node = propertyNode.item(i); if (node instanceof Element) { Element propertyEle = (Element) node; String name = propertyEle.getAttribute("name"); String value = propertyEle.getAttribute("value"); beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name,value)); } } } }
1、读取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
2、初始化BeanFactory并注册bean
BeanFactory beanFactory = new AutowireCapableBeanFactory(); // 通过Map自带的Entry实体属性遍历Map中存放的实体集合 for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) { beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); }
3、获取bean
git checkout step-5-inject-bean-to-bean
class AutowireCapableBeanFactory 中:
protected void applyPropertyValues(Object bean, BeanDefinition mbd) throws Exception { for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) { // 获取对象中指定属性名( PropertyValue 的 key)对应的属性 Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName()); declaredField.setAccessible(true); Object value = propertyValue.getValue(); // 若该属性的值对应的是一个Bean,是Bean对Bean的引用 if (value instanceof BeanReference) { // 原类型为 Object,需要强转为 BeanReference BeanReference beanReference = (BeanReference) value; // 再利用BeanReference存储的name属性获取到对应的bean(包装类 -> 实体类) value = getBean(beanReference.getName()); } declaredField.set(bean, value); } }
/** * 对于 getBean(),采用"lazy-init"的方式(延迟加载)。 * 避免两个循环依赖的Bean在创建时陷入死锁。 * 在注入Bean的时候,先尝试获取,获取不到再创建,故总是先创建后注入。 */ public Object getBean(String name) throws Exception { BeanDefinition beanDefinition = beanDefinitionMap.get(name); if (beanDefinition == null) { throw new IllegalArgumentException("No bean named " + name + " is defined"); } Object bean = beanDefinition.getBean(); if (bean == null) { bean = doCreateBean(beanDefinition); } return bean; }
#### 实现步骤: - 1、读取配置 ```java XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
2、初始化BeanFactory并注册Bean
AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory(); for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) { beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); }
3、初始化Bean
/** * class AbstractBeanFactory: * Fileds: + final List<String> beanDefinitionNames 属性 * Methods:在registerBeanDefinition中对应的添加 beanDefinitionNames.add(name); * * public void preInstantiateSingletons() throws Exception { * // 使用迭代器依次创建初始化对应 Bean * for (Iterator it = this.beanDefinitionNames.iterator(); it.hasNext();) { * String beanName = (String) it.next(); * getBean(beanName); * } * } * * 确保所有没有设置lazy-init的单例被实例化 */ beanFactory.preInstantiateSingletons();
4、获取Bean
git checkout step-6-invite-application-context
ApplicationContext
AbstractApplicationContext
refresh()
bean
/** * class BeanFactory -> extends * interface ApplicationContext -> implements * abstract class AbstractApplicationContext -> extends * class ClassPathXmlApplicationContext */ public interface ApplicationContext extends BeanFactory { }
public abstract class AbstractApplicationContext implements ApplicationContext { // 依赖抽象容器 protected AbstractBeanFactory beanFactory; public AbstractApplicationContext(AbstractBeanFactory beanFactory) { this.beanFactory = beanFactory; } //声明方法 初始化所有的 Bean public void refresh() throws Exception{ } // 调用容器的获取Bean方法 @Override public Object getBean(String name) throws Exception { return beanFactory.getBean(name); } }
public class ClassPathXmlApplicationContext extends AbstractApplicationContext { private String configLocation; // 默认使用自动装配容器 public ClassPathXmlApplicationContext(String configLocation) throws Exception { this(configLocation, new AutowireCapableBeanFactory()); } // 根据 配置文件路径 容器类型 构造对应的 ApplicationContext public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception { super(beanFactory); this.configLocation = configLocation; refresh(); }
// 将读取配置文件以及将读取到的属性值注入Bean的方法进行封装 @Override public void refresh() throws Exception { // 读取配置文件 封装原来的实现步骤一 XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); xmlBeanDefinitionReader.loadBeanDefinitions(configLocation); // 将获得的信息注入Bean 封装原来的实现步骤二 for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) { beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); } }
#### 实现步骤: - 1、初始化ApplicationContext(加载配置文件并初始化 Bean) ```java ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService"); helloWorldService.helloWorld();
AOP分为配置(Pointcut,Advice),织入(Weave)两部分工作,当然还有一部分是将AOP整合到整个容器的生命周期中。
git checkout step-7-method-interceptor-by-jdk-dynamic-proxy
public interface AopProxy { Object getProxy(); }
Advice
Joinpoint
public interface MethodInterceptor extends Interceptor { Object invoke(MethodInvocation invocation) throws Throwable; }
Spring的AOP只支持方法级别的调用,所以其实在AopProxy里,我们只需要将MethodInterceptor放入对象的方法调用即可。
我们称被代理对象为TargetSource,而AdvisedSupport就是保存TargetSource和MethodInterceptor的元数据对象。这一步我们先实现一个基于JDK动态代理的JdkDynamicAopProxy,它可以对接口进行代理。于是我们就有了基本的织入功能。
TargetSource
AdvisedSupport
MethodInterceptor
JDK
JdkDynamicAopProxy
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler { /** * AdvisedSupport * Fields: TargetSource targetSource * [Object target,Class targetClass];//target 代理对象,targetClass 继承的接口 * * MethodInterceptor methodInterceptor;//Advice切面逻辑 * Methods:对应属性的getter、setter */ private AdvisedSupport advised; public JdkDynamicAopProxy(AdvisedSupport advised) { this.advised = advised; } //为要代理的对象的的类对应的创建动态代理 @Override public Object getProxy() { /** * public static Object newProxyInstance(ClassLoader loader, * Class<?>[] interfaces, * InvocationHandler h) * @param loader the class loader to define the proxy class * @param interfaces the list of interfaces for the proxy class to implement * @param h the invocation handler to dispatch method invocations to */ return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { advised.getTargetSource() .getTargetClass() }, this); } /** * 重写 InvocationHandler 对应的 invoke() 方法 * 调用拦截器对应的方法 * (通过反射获取对应的切点,再根据切点指定的逻辑进行执行) * @Param Object proxy 代理 * @Param Method method 对应的要调用方法 * @Param Object[] args 方法需要的参数 */ @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { MethodInterceptor methodInterceptor = advised.getMethodInterceptor(); /** * 其中 class MethodInterceptor extends Interceptor { * Object invoke(MethodInvocation invocation) throws Throwable; * } * class ReflectiveMethodInvocation implements MethodInvocation * 故可通过继承 MethodInterceptor 重写相关 invoke() 方法实现 Advice 逻辑 */ return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args)); } }
ReflectiveMethodInvocation.java
public class ReflectiveMethodInvocation implements MethodInvocation { private Object target; private Method method; private Object[] args; public ReflectiveMethodInvocation(Object target, Method method, Object[] args) { this.target = target; this.method = method; this.args = args; } @Override public Method getMethod() { return method; } @Override public Object[] getArguments() { return args; } // 反射,相当于 target.method(args);即调用代理对象对应的方法 @Override public Object proceed() throws Throwable { return method.invoke(target, args); } @Override public Object getThis() { return target; } @Override public AccessibleObject getStaticPart() { return method; } }
1、设置被代理对象,指定切点(JoinPoint)
// 创建AdvisedSupport对象,设置 Joinpoint AdvisedSupport advisedSupport = new AdvisedSupport(); TargetSource targetSource = new TargetSource(helloWorldService, HelloWorldService.class); advisedSupport.setTargetSource(targetSource);
2、设置拦截器(Advice)
/** * // Advice 逻辑实现 * public class TimerInterceptor implements MethodInterceptor { * @Override * public Object invoke(MethodInvocation invocation) throws Throwable { * long time = System.nanoTime(); * System.out.println("Invocation of Method " + invocation.getMethod().getName() + " start!"); * Object proceed = invocation.proceed(); * System.out.println("Invocation of Method " + invocation.getMethod().getName() + " end! takes " + (System.nanoTime() - time) + " nanoseconds."); * return proceed; * } * } */ TimerInterceptor timerInterceptor = new TimerInterceptor(); // 设置Advice advisedSupport.setMethodInterceptor(timerInterceptor);
3、创建代理(Proxy)
JdkDynamicAopProxy jdkDynamicAopProxy = new JdkDynamicAopProxy(advisedSupport); HelloWorldService helloWorldServiceProxy = (HelloWorldService) jdkDynamicAopProxy.getProxy();
4、基于AOP的调用
helloWorldServiceProxy.helloWorld();
git checkout step-8-invite-pointcut-and-aspectj
完成了织入之后,我们要考虑另外一个问题:对什么类以及什么方法进行AOP?对于“在哪切”这一问题的定义,我们又叫做“Pointcut”。Spring中关于Pointcut包含两个角色:ClassFilter和MethodMatcher,分别是对类和方法做匹配。Pointcut有很多种定义方法,例如类名匹配、正则匹配等,但是应用比较广泛的应该是和AspectJ表达式的方式。
AOP
Pointcut
ClassFilter
MethodMatcher
AspectJ
public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher();
public interface ClassFilter {
boolean matches(Class targetClass);
public interface MethodMatcher {
boolean matches(Method method, Class targetClass);
- `AspectJ`是一个“对`Java`的`AOP`增强”。它最早是其实是一门语言,我们跟写`Java`代码一样写它,然后静态编译之后,就有了`AOP`的功能。下面是一段`AspectJ`代码: ```java aspect PointObserving { private Vector Point.observers = new Vector(); public static void addObserver(Point p, Screen s) { p.observers.add(s); } public static void removeObserver(Point p, Screen s) { p.observers.remove(s); } ... }
这种方式无疑太重了,为了AOP,还要适应一种语言?所以现在使用也不多,但是它的Pointcut表达式被Spring借鉴了过来。于是我们实现了一个AspectJExpressionPointcut:
public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher { // 对用户自定义的相关关键字子集合,可进行切点表达式PointcutExpression的构造 private PointcutParser pointcutParser; // 表达式字符串 private String expression; // 要构造的切点表达式 private PointcutExpression pointcutExpression; // 存储表达式相关关键字 private static final Set<PointcutPrimitive> DEFAULT_SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>(); static { DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS); DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET); } // 进行关键字集合初始化 public AspectJExpressionPointcut() { this(DEFAULT_SUPPORTED_PRIMITIVES); } // 初始化表达式构造器 public AspectJExpressionPointcut(Set<PointcutPrimitive> supportedPrimitives) { pointcutParser = PointcutParser .getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(supportedPrimitives); } protected void checkReadyToMatch() { if (pointcutExpression == null) { pointcutExpression = buildPointcutExpression(); } } // 字符串转换为切点表达式 private PointcutExpression buildPointcutExpression() { return pointcutParser.parsePointcutExpression(expression); } public void setExpression(String expression) { this.expression = expression; } // 对类做匹配 @Override public ClassFilter getClassFilter() { return this; } // 对方法做匹配 @Override public MethodMatcher getMethodMatcher() { return this; } // 将表达式和类做匹配,返回匹配结果 @Override public boolean matches(Class targetClass) { checkReadyToMatch(); return pointcutExpression.couldMatchJoinPointsInType(targetClass); } // 将表达式和方法做匹配,返回匹配结果 @Override public boolean matches(Method method, Class targetClass) { checkReadyToMatch(); ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method); if (shadowMatch.alwaysMatches()) { return true; } else if (shadowMatch.neverMatches()) { return false; } // TODO:其他情况不判断了!见org.springframework.aop.aspectj.RuntimeTestWalker return false; } }
// 对类做匹配 返回匹配结果 @Test public void testClassFilter() throws Exception { String expression = "execution(* us.codecraft.tinyioc.*.*(..))"; AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut(); aspectJExpressionPointcut.setExpression(expression); boolean matches = aspectJExpressionPointcut.getClassFilter().matches(HelloWorldService.class); Assert.assertTrue(matches); } // 对方法做匹配 返回匹配结果 @Test public void testMethodInterceptor() throws Exception { String expression = "execution(* us.codecraft.tinyioc.*.*(..))"; AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut(); aspectJExpressionPointcut.setExpression(expression); boolean matches = aspectJExpressionPointcut.getMethodMatcher().matches(HelloWorldServiceImpl.class.getDeclaredMethod("helloWorld"),HelloWorldServiceImpl.class); Assert.assertTrue(matches); }
git checkout step-9-auto-create-aop-proxy
BeanPostProcessor
<aop:aspectj-autoproxy/>
- `AspectJAwareAdvisorAutoProxyCreator`就是`AspectJ`方式实现织入的核心。它其实是一个`BeanPostProcessor`。在这里它会扫描所有`Pointcut`,并对`bean`做织入。 - BeanPostProcessor: ```java public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception; Object postProcessAfterInitialization(Object bean, String beanName) throws Exception; }
AspectJAwareAdvisorAutoProxyCreator:
public class AspectJAwareAdvisorAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware { private AbstractBeanFactory beanFactory; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception { if (bean instanceof AspectJExpressionPointcutAdvisor) { return bean; } if (bean instanceof MethodInterceptor) { return bean; } /** * class AspectJExpressionPointcutAdvisor implements PointcutAdvisor * Fields: AspectJExpressionPointcut pointcut * Advice advice * Methods: Advice getter/setter * Pointcut getter * void setExpression(String expression) {this.pointcut.setExpression(expression);} * */ // 根据 Type 获取所有的 PointCut 和 Advice 组成的 bean List<AspectJExpressionPointcutAdvisor> advisors = beanFactory .getBeansForType(AspectJExpressionPointcutAdvisor.class); for (AspectJExpressionPointcutAdvisor advisor : advisors) { // 判断是否是要拦截的类 if (advisor.getPointcut().getClassFilter().matches(bean.getClass())) { /** * Class AdvisedSupport * Fields: TargetSource targetSource; * MethodInterceptor methodInterceptor; * MethodMatcher methodMatcher; * Methods: getter/setter */ AdvisedSupport advisedSupport = new AdvisedSupport(); // 从扫描出的 bean 中获取 Advice 逻辑并注入 // 设置 Advice advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice()); /** * MethodMatcher getMethodMatcher() {return this;} * AspectJExpressionPointcut 实现了 MethodMatcher 接口。 */ // 设置切点 Pointcut,即哪些方法需要做拦截 advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher()); // 设置要代理的对象 TargetSource targetSource = new TargetSource(bean, bean.getClass().getInterfaces()); advisedSupport.setTargetSource(targetSource); // 创建动态代理 return new JdkDynamicAopProxy(advisedSupport).getProxy(); } } return bean; } // 获取容器的引用,进而获取容器中所有的切点对象 @Override public void setBeanFactory(BeanFactory beanFactory) throws Exception { this.beanFactory = (AbstractBeanFactory) beanFactory; } }
- 此时的`JdkDynamicAopProxy` ```java public class JdkDynamicAopProxy implements AopProxy, InvocationHandler { private AdvisedSupport advised; public JdkDynamicAopProxy(AdvisedSupport advised) { this.advised = advised; } @Override public Object getProxy() { return Proxy.newProxyInstance(getClass().getClassLoader(), advised.getTargetSource().getTargetClass(), this); } @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { MethodInterceptor methodInterceptor = advised.getMethodInterceptor(); // invoke 时判断是否为要拦截的方法,是则执行 Advice 逻辑 if (advised.getMethodMatcher() != null && advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) { return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args)); } else { return method.invoke(advised.getTargetSource().getTarget(), args); } } }
xml
<bean id="autoProxyCreator" class="us.codecraft.tinyioc.aop.AspectJAwareAdvisorAutoProxyCreator"></bean>
##### 动态代理的步骤 - `AutoProxyCreator`(实现了 `BeanPostProcessor` 接口)在实例化所有的 `Bean` 前,最先被实例化。`postProcessBeforeInitialization` - 其他普通 `Bean` 被实例化、初始化,在初始化的过程中,`AutoProxyCreator` 加载 `BeanFactory` 中所有的 `PointcutAdvisor`(这也保证了 `PointcutAdvisor` 的实例化顺序优于普通 `Bean`。),然后依次使用 `PointcutAdvisor` 内置的 `ClassFilter`,判断当前对象是不是要拦截的类。 - 如果是,则生成一个 `TargetSource`(要拦截的对象和其类型),并取出 `AutoProxyCreator` 的 `MethodMatcher`(对哪些方法进行拦截)、`Advice`(拦截的具体操作),再交给 `AopProxy` 去生成代理对象。 - `AopProxy` 生成一个 `InvocationHandler`,在它的 `invoke` 函数中,首先使用 `MethodMatcher` 判断是不是要拦截的方法,如果是则交给 `Advice` 来执行(`Advice` 由用户来编写,其中也要手动/自动调用原始对象的方法),如果不是,则直接交给 `TargetSource` 的原始对象来执行。 ### step4-使用CGLib进行类的织入 > git checkout step-10-invite-cglib-and-aopproxy-factory - 前面的`JDK`动态代理只能对接口进行代理,对于类则无能为力。这里我们需要一些字节码操作技术。这方面大概有几种选择:`ASM`,`CGLib`和`javassist`,后两者是对`ASM`的封装。`Spring`中使用了`CGLib`。 - 在这一步,我们还要定义一个工厂类`ProxyFactory`,用于根据`TargetSource`类型自动创建代理,这样就需要在调用者代码中去进行判断。 - TargetSource: ```java public class TargetSource { private Class<?> targetClass; private Class<?>[] interfaces; private Object target; public TargetSource(Object target, Class<?> targetClass,Class<?>... interfaces) { this.target = target; this.targetClass = targetClass; this.interfaces = interfaces; } public Class<?> getTargetClass() { return targetClass; } public Object getTarget() { return target; } public Class<?>[] getInterfaces() { return interfaces; } }
ProxyFactory : 策略模式和工厂模式的结合使用
public class ProxyFactory extends AdvisedSupport implements AopProxy { // 在 creator 中,生成相应的动态代理的时候就可以使用工厂类的 getProxy() @Override public Object getProxy() { return createAopProxy().getProxy(); } protected final AopProxy createAopProxy() { return new Cglib2AopProxy(this); } //...可以根据 TargetSource 决定使用不同的动态代理 protected final AopProxy createAopProxy(TargetSource targetSource) { return new JdkDynamicAopProxy(this); } }
Cglib2AopProxy
public class Cglib2AopProxy extends AbstractAopProxy { public Cglib2AopProxy(AdvisedSupport advised) { super(advised); } @Override public Object getProxy() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(advised.getTargetSource().getTargetClass()); enhancer.setInterfaces(advised.getTargetSource().getInterfaces()); enhancer.setCallback(new DynamicAdvisedInterceptor(advised)); Object enhanced = enhancer.create(); return enhanced; } private static class DynamicAdvisedInterceptor implements MethodInterceptor { private AdvisedSupport advised; private org.aopalliance.intercept.MethodInterceptor delegateMethodInterceptor; private DynamicAdvisedInterceptor(AdvisedSupport advised) { this.advised = advised; this.delegateMethodInterceptor = advised.getMethodInterceptor(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { if (advised.getMethodMatcher() == null || advised.getMethodMatcher().matches(method, advised.getTargetSource().getTargetClass())) { return delegateMethodInterceptor.invoke(new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, args, proxy)); } return new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, args, proxy).proceed(); } } private static class CglibMethodInvocation extends ReflectiveMethodInvocation { private final MethodProxy methodProxy; public CglibMethodInvocation(Object target, Method method, Object[] args, MethodProxy methodProxy) { super(target, method, args); this.methodProxy = methodProxy; } @Override public Object proceed() throws Throwable { return this.methodProxy.invoke(this.target, this.arguments); } }
- 代理模式相关参见 [代理模式(Proxy Pattern)- 最易懂的设计模式解析](https://blog.csdn.net/carson_ho/article/details/54910472)
title: 'tinySpring学习笔记(一)-实现IOC容器' date: 2017-11-18 16:01:49 tags: Spring
tinySpring学习笔记(一)-实现IOC容器
step1-最基本的容器
IOC最基本的组成:BeanFactory 和 Bean(容器和实体)
实现步骤:
1、初始化beanFactory(容器)
2、向容器中注入Bean(实体)
3、获取对应的Bean实例
step2-将Bean创建放入工厂
实际中需要使用容器来对所有的
Bean
进行统一的管理,自然对于Bean
的整个生命周期都需要容器进行相应的干预。所以对于Bean
的创建和初始化需要放入容器中进行处理,同时考虑到程序的扩展性,需要使用Extra Interface
将BeanFactory
更改为接口并提供相应的实现。对于 BeanFactory 的继承关系
public abstract class AbstractBeanFactory implements BeanFactory { private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(); @Override public Object getBean(String name) { // 先获取对应的包装对象,再获取对应的实体 return beanDefinitionMap.get(name).getBean(); } @Override public void registerBeanDefinition(String name, BeanDefinition beanDefinition) { // 注入Bean之前先创建对应Bean Object bean = doCreateBean(beanDefinition); // 创建Bean之后并进行相应的包装 beanDefinition.setBean(bean); // 注入包装后的Bean对象 beanDefinitionMap.put(name, beanDefinition); } /**
}
public class AutowireCapableBeanFactory extends AbstractBeanFactory { @Override protected Object doCreateBean(BeanDefinition beanDefinition) { try { // 利用包装类对应的 class 对象,再利用 反射 创建对象 Object bean = beanDefinition.getBeanClass().newInstance(); return bean; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } }
step3-为Bean注入属性
PropertyValue
对象,并且保存到BeanDefination
,初始化Bean
的时候,可以根据PropertyValue
来进行Bean
属性的注入,Spring
本身是用了setter
来注入,该demo
使用Field
注入。实现步骤:
1、初始化Factory
2、Bean定义
3、设置属性
4、生成Bean
5、获取 Bean
step4-读取xml配置来初始化bean
BeanDefinitionReader
接口,提供void loadBeanDefinitions(String location)
方法从指定位置获取相应的配置来进行初始化,定义抽象类AbstractBeanDefinitionReader
,其中一个实现是XmlBeanDefinitionReader
实现步骤:
1、读取配置
2、初始化BeanFactory并注册bean
3、获取bean
step5-为bean注入bean(处理Bean之间的依赖)
protected void applyPropertyValues(Object bean, BeanDefinition mbd) throws Exception { for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) { // 获取对象中指定属性名( PropertyValue 的 key)对应的属性 Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName()); declaredField.setAccessible(true); Object value = propertyValue.getValue(); // 若该属性的值对应的是一个Bean,是Bean对Bean的引用 if (value instanceof BeanReference) { // 原类型为 Object,需要强转为 BeanReference BeanReference beanReference = (BeanReference) value; // 再利用BeanReference存储的name属性获取到对应的bean(包装类 -> 实体类) value = getBean(beanReference.getName()); } declaredField.set(bean, value); } }
2、初始化BeanFactory并注册Bean
3、初始化Bean
4、获取Bean
step6-ApplicationContext
BeanFactory
,引入ApplicationContext
接口,并在AbstractApplicationContext
的refresh()
方法中进行bean
的初始化工作。public abstract class AbstractApplicationContext implements ApplicationContext { // 依赖抽象容器 protected AbstractBeanFactory beanFactory; public AbstractApplicationContext(AbstractBeanFactory beanFactory) { this.beanFactory = beanFactory; } //声明方法 初始化所有的 Bean public void refresh() throws Exception{ } // 调用容器的获取Bean方法 @Override public Object getBean(String name) throws Exception { return beanFactory.getBean(name); } }
public class ClassPathXmlApplicationContext extends AbstractApplicationContext { private String configLocation; // 默认使用自动装配容器 public ClassPathXmlApplicationContext(String configLocation) throws Exception { this(configLocation, new AutowireCapableBeanFactory()); } // 根据 配置文件路径 容器类型 构造对应的 ApplicationContext public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception { super(beanFactory); this.configLocation = configLocation; refresh(); }
}
title: 'tinySpring学习笔记(二)-实现AOP' date: 2018-04-10 16:01:49 tags: Spring
AOP及其实现
step1-使用JDK动态代理实现AOP织入
AOP 中两个重要角色:MethodInterceptor和MethodInvocation
Advice
和Joinpoint
。Advice
定义了在切点指定的逻辑,而Joinpoint
则代表切点。Spring的AOP只支持方法级别的调用,所以其实在AopProxy里,我们只需要将MethodInterceptor放入对象的方法调用即可。
我们称被代理对象为
TargetSource
,而AdvisedSupport
就是保存TargetSource
和MethodInterceptor
的元数据对象。这一步我们先实现一个基于JDK
动态代理的JdkDynamicAopProxy
,它可以对接口进行代理。于是我们就有了基本的织入功能。ReflectiveMethodInvocation.java
实现步骤:
1、设置被代理对象,指定切点(JoinPoint)
2、设置拦截器(Advice)
3、创建代理(Proxy)
4、基于AOP的调用
step2-使用AspectJ管理切面
完成了织入之后,我们要考虑另外一个问题:对什么类以及什么方法进行
AOP
?对于“在哪切”这一问题的定义,我们又叫做“Pointcut
”。Spring
中关于Pointcut
包含两个角色:ClassFilter
和MethodMatcher
,分别是对类和方法做匹配。Pointcut
有很多种定义方法,例如类名匹配、正则匹配等,但是应用比较广泛的应该是和AspectJ
表达式的方式。}
public interface ClassFilter {
}
public interface MethodMatcher {
}
这种方式无疑太重了,为了AOP,还要适应一种语言?所以现在使用也不多,但是它的Pointcut表达式被Spring借鉴了过来。于是我们实现了一个AspectJExpressionPointcut:
实现测试:
step3-将AOP融入Bean的创建过程中
BeanPostProcessor
。BeanPostProcessor
是BeanFactory
提供的,在Bean
初始化过程中进行扩展的接口。只要你的Bean
实现了BeanPostProcessor
接口,那么Spring在初始化时,会优先找到它们,并且在Bean
的初始化过程中,调用这个接口,从而实现对BeanFactory
核心无侵入的扩展。AOP
的xml配置AspectJAwareAdvisorAutoProxyCreator:
xml
配置,在tiny-spring中直接使用Bean的方式,而不是用aop前缀进行配置:ProxyFactory : 策略模式和工厂模式的结合使用
Cglib2AopProxy
}