凉衫薄

人生哪能多如意,万事只求半称心。
Spring
事务四种特性 特性 说明 原子性 强调事务的不可分割 一致性 事务执行前后数据完整性保持一致 隔离性 一个事物执行的过程中,不应该受到其他事务的干扰 持久性 事务一旦结束,数据就持久到数据库 安全性问题 问题 说明 脏读 读取其他事务未提交的数据 不可重复读 一个事务前后多次读取其他事务已提交update的数据 幻读 一个事务前后多次读取另一个事务已提交的insert或delete数据,导致数据总量不一致 事务隔离级别 隔离级别 说明 default 使用数据库的默认隔离级别 未提交读 脏读,不可重复读,虚幻读都有可能发生 已提交读 避免脏读,不可重复读,虚幻读可能发生 可重复读 避免脏读和不可重复读,虚幻读可能发生 串行化 避免以上所有问题 Mysql默认级别 可重复读 Oracle默认级别 已提交读 事务传播行为 事务传播行为类型 说明 PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。 PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。 PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。 PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。 PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。 PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
Bean生命周期 1、实例化对象(构造函数或工厂函数) 2、设置对象属性 3、Aware相关接口(BeanNameAware,BeanFactoryAware) 4、BeanPostProcessor接口中的postProcessBeforeInitialization()方法 5、@PostConstruct注解方法 6、InitialingBean接口中的afterPropertiesSet()方法 7、xml配置中的init-method方法 8、BeanPostProcessor接口中的postProcessAfterInitialization()方法 9、@PreDestroy注解方法 10、DisposableBean接口中的destroy()方法 11、xml配置中的destroy-method方法
一、简介 在传统的开发模式中,需要在代码中手动new对象,这样不仅不美观,而且不易于维护。有了IoC,我们可以将对象的管理与维护交给容器管理,使得程序的设计更加地解耦合。为了让大家更好地理解IoC原理,我参考了Spring IoC源码和网络上的一些例子,这篇文章将会教大家如何从零开始手动构建我们自己的Spring框架,其中我去除了源码中一些复杂的特性和设计模式,争取以最简洁易懂的方式实现IoC。接下来,我还会陆续完成AOP,MVC等常用Spring模块的从零构建。不要害怕重复造轮子,手撸框架是一件很伟大的事情。完整版的代码已经上传到个人GitHub,请点击这里。 二、代码结构 └─java └─net.stackoverflow.summer ├─aop (aop模块相关代码) │ ├─beans (IoC模块相关代码) │ ├─core (核心) │ │ AbstractBeanDefinitionReader.java bean定义信息读取器抽象类 │ │ BeanDefinition.java bean定义信息类 │ │ BeanDefinitionReader.java bean定义信息读取器接口 │ │ BeanReference.java bean依赖参照类 │ │ XmlAttributeConstant.java Xml配置文件属性常量定义 │ │ XmlBeanDefinitionReader.java 基于xml配置方式bean定义信息读取器 │ │ │ ├─factory (工厂类) │ │ AbstractBeanFactory.java 抽象bean工厂类 │ │ BeanFactory.java bean工厂接口 │ │ XmlBeanFactory.java 基于xml配置bean工厂类 │ │ │ └─io (输入输出相关) │ ClassPathResource.java 类路径资源 │ Resource.java 资源接口 │ ResourceLoader.java 资源加载器 │ ├─context (context模块) │ AbstractApplicationContext.java 应用上下文抽象类 │ ApplicationContext.java 应用上下文接口 │ ClassPathXmlApplicationContext.java Xml配置方式应用上下文 │ └─mvc (MVC模块) 三、重要接口和组件设计 BeanDefinition:首先我们需要一个类来保存bean的元数据,所谓元数据就是描述数据的数据,其中包括bean的全限定名,Class对象,是否懒加载,是否多例或单例,属性等,源码如下: public class BeanDefinition { //bean实例 private Object bean; //bean的Class对象 private Class beanClass; //bean的全限定名 private String className; //是否懒加载 private boolean lazyInit; //bean的属性信息 private Map properties; public BeanDefinition() { this.properties = new HashMap<>(); this.lazyInit = false; } public Object getBean() { return bean; } public void setBean(Object bean) { this.bean = bean; } public Class getBeanClass() { return this.beanClass; } public void setClassName(String name) { this.className = name; try { this.beanClass = Class.forName(name); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public String getClassName() { return this.className; } public Map getProperties() { return properties; } public void setProperties(Map properties) { this.properties = properties; } public boolean isLazyInit() { return lazyInit; } public void setLazyInit(boolean lazyInit) { this.lazyInit = lazyInit; } } BeanReference:使用过Spring的人都知道有些bean的属性是另一个bean,另一个bean在其他地方定义了,这时候我们需要通过参照引用过来,BeanReference就是用来描述参照信息的一个类,源码如下: public class BeanReference { private String name; private Object bean; public BeanReference(String name) { this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void setBean(Object bean) { this.bean = bean; } public Object getBean() { return this.bean; } } XmlBeanDefinitionReader:有了元数据和参照的定义,我们该考虑如何从配置中读取这些信息。从该类的类名可以看出这是一个BeanDefition的读取器,并且还是从Xml配置文件中读取的。该类继承了AbstractBeanDefinitionReader,且实现了BeanDefinitionReader接口,这样设计的目的考虑到将来从其他途径读取BeanDefinition的扩展,比如AnnotationConfigBeanDefinitionReader就是从注解配置信息来读取我们的BeanDefinition,源码如下: public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { public XmlBeanDefinitionReader(ResourceLoader resourceLoader) { super(resourceLoader); } /** * 从路径中读取xml配置文件 * * @param location xml配置文件路径 * @throws Exception 异常 */ public void readXml(String location) throws Exception { InputStream inputstream = getResourceLoader().getResource(location).getInputStream(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = factory.newDocumentBuilder(); Document doc = docBuilder.parse(inputstream); registerBeanDefinitions(doc); inputstream.close(); } /** * 注册BeanDefinition * * @param doc xml文档对象 */ private void registerBeanDefinitions(Document doc) { Element root = doc.getDocumentElement(); parseBeanDefinitions(root); } /** * 从bean节点中解析BeanDefinition * * @param root 根节点对象 */ private void parseBeanDefinitions(Element root) { NodeList nl = root.getElementsByTagName(XmlAttributeConstant.BEAN); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element element = (Element) node; processBeanDefinition(element); } } } /** * 处理BeanDefinition,解析id,class,lazy-init等属性 * * @param element xml配置文件中的 bean 节点 */ private void processBeanDefinition(Element element) { String id = element.getAttribute(XmlAttributeConstant.ID); String className = element.getAttribute(XmlAttributeConstant.CLASS); BeanDefinition beanDefinition = new BeanDefinition(); String lazyInit = element.getAttribute(XmlAttributeConstant.LAZY_INIT); if (lazyInit != null && lazyInit.equals("true")) { beanDefinition.setLazyInit(true); } beanDefinition.setClassName(className); addProperty(element, beanDefinition); getRegistry().put(id, beanDefinition); } /** * 给 beanDefinition 添加属性 * * @param element bean节点对象 * @param beanDefinition beanDefinition对象 */ private void addProperty(Element element, BeanDefinition beanDefinition) { NodeList propertyNode = element.getElementsByTagName(XmlAttributeConstant.PROPERTY); for (int i = 0; i < propertyNode.getLength(); i++) { Node node = propertyNode.item(i); if (node instanceof Element) { Element propertyEle = (Element) node; String name = propertyEle.getAttribute(XmlAttributeConstant.NAME); String value = propertyEle.getAttribute(XmlAttributeConstant.VALUE); if (value != null && value.length() > 0) { beanDefinition.getProperties().put(name, value); } else { String ref = propertyEle.getAttribute(XmlAttributeConstant.REFERENCE); if (ref == null || ref.length() == 0) { throw new IllegalArgumentException("参数ref不能为空"); } BeanReference beanRef = new BeanReference(name); beanDefinition.getProperties().put(name, beanRef); } } } } } XmlBeanFactory:现在我们已经能够从配置信息中读取我们的BeanDefinition了,接下来我们需要根据这些bean的定义来生成bean实例。XmlBeanFactory是一个从Xml配置中生成bean的工厂类,它用来生成bean,并将bean注册到容器。该类继承自AbstractBeanFactory抽象类,并实现了BeanFactory接口,目的同样是考虑将来的扩展性,因为将来还可能会有AnnotationConfigBeanFactory,从注解配置中生成bean的工厂类等,源码如下: public class XmlBeanFactory extends AbstractBeanFactory { /** * 根据bean定义信息创建bean实例(忽略懒加载) * * @param beanDefinition beanDefinition对象 * @return 返回bean实例 * @throws Exception 异常 */ @Override protected Object createBean(BeanDefinition beanDefinition) throws Exception { Object bean = beanDefinition.getBeanClass().newInstance(); for (Map.Entry entry : beanDefinition.getProperties().entrySet()) { Field declaredField = bean.getClass().getDeclaredField(entry.getKey()); declaredField.setAccessible(true); Object value = entry.getValue(); if (value instanceof BeanReference) { BeanReference beanReference = (BeanReference) value; value = getBean(beanReference.getName()); beanReference.setBean(value); } declaredField.set(bean, value); } return bean; } /** * 根据bean定义信息创建bean实例(不忽略懒加载) * * @param beanDefinition beanDefinition对象 * @return 返回bean实例 * @throws Exception 异常 */ @Override protected Object createBeanLazy(BeanDefinition beanDefinition) throws Exception { Object bean = beanDefinition.getBeanClass().newInstance(); for (Map.Entry entry : beanDefinition.getProperties().entrySet()) { Field declaredField = bean.getClass().getDeclaredField(entry.getKey()); declaredField.setAccessible(true); Object value = entry.getValue(); if (value instanceof BeanReference) { BeanReference beanReference = (BeanReference) value; BeanDefinition definition = registry.get(entry.getKey()); if (definition.isLazyInit()) { value = null; } else { value = getBeanLazy(beanReference.getName()); beanReference.setBean(value); } } declaredField.set(bean, value); } return bean; } } AbstractBeanFactory:这个是bean工厂类的抽象类,实现了BeanFactory中的接口,并对子类提供了一些公共的方法和抽象方法,源码如下: public abstract class AbstractBeanFactory implements BeanFactory { //bean容器 protected Map registry = new HashMap<>(); /** * 根据名称(id)获取bean实例(不忽略懒加载) * * @param name 名称(id) * @return 返回bean实例 * @throws Exception 异常 */ protected Object getBeanLazy(String name) throws Exception { BeanDefinition beanDefinition = registry.get(name); if (beanDefinition == null) { throw new IllegalArgumentException("未定义的bean"); } Object bean = beanDefinition.getBean(); if (bean == null && !beanDefinition.isLazyInit()) { bean = createBeanLazy(beanDefinition); beanDefinition.setBean(bean); registry.put(name, beanDefinition); } return bean; } /** * 根据bean名称(id)获取bean实例 * * @param name bean名称(id) * @return 返回bean实例 * @throws Exception 异常 */ @Override public Object getBean(String name) throws Exception { BeanDefinition beanDefinition = registry.get(name); if (beanDefinition == null) { throw new IllegalArgumentException("未定义的bean"); } Object bean = beanDefinition.getBean(); if (bean == null) { bean = createBean(beanDefinition); beanDefinition.setBean(bean); registry.put(name, beanDefinition); } else { checkReference(bean, beanDefinition); } return bean; } /** * 检查bean依赖的参照对象是否为空 * * @param bean 被检查的bean对象 * @param beanDefinition beanDefinition对象 * @throws Exception 异常 */ protected void checkReference(Object bean, BeanDefinition beanDefinition) throws Exception { for (Map.Entry entry : beanDefinition.getProperties().entrySet()) { Field declaredField = bean.getClass().getDeclaredField(entry.getKey()); declaredField.setAccessible(true); Object value = entry.getValue(); if (value instanceof BeanReference) { BeanReference beanReference = (BeanReference) value; if (beanReference.getBean() == null) { value = getBean(beanReference.getName()); beanReference.setBean(value); declaredField.set(bean, value); } else { checkReference(beanReference.getBean(), registry.get(beanReference.getName())); } } } } /** * 向容器注册bean定义信息 * * @param name bean名称(id) * @param beanDefinition beanDefinition对象 * @throws Exception 异常 */ public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception { if (!beanDefinition.isLazyInit()) { Object bean = createBeanLazy(beanDefinition); beanDefinition.setBean(bean); } registry.put(name, beanDefinition); } /** * 根据bean定义信息创建bean实例(忽略懒加载) * * @param beanDefinition beanDefinition对象 * @return 返回bean实例 * @throws Exception 异常 */ protected abstract Object createBean(BeanDefinition beanDefinition) throws Exception; /** * 根据bean定义信息创建bean实例(不忽略懒加载) * * @param beanDefinition beanDefinition对象 * @return 返回bean实例 * @throws Exception 异常 */ protected abstract Object createBeanLazy(BeanDefinition beanDefinition) throws Exception; } ClassPathXmlApplicationContext:使用过Sprnig的人对这个类名应该很熟悉了,这是整个程序的上下文入口,也是bean的容器。它基本的工作流程是调用读取器读取BeanDefinition,并通过工厂类将bean注册到容器。它实现了BeanFactory接口,并且静态代理了BeanFactory接口的实现类,对外提供了一个统一的getBean(String name)方法,源码如下: public class ClassPathXmlApplicationContext extends AbstractApplicationContext { private String location; public ClassPathXmlApplicationContext(String location) throws Exception { super(new XmlBeanFactory()); this.location = location; refresh(); } /** * 重载bean定义实现方法 * * @param beanFactory bean工厂类 * @throws Exception 异常 */ @Override protected void loadBeanDefinitions(AbstractBeanFactory beanFactory) throws Exception { XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(new ResourceLoader()); reader.readXml(location); for (Map.Entry entry : reader.getRegistry().entrySet()) { beanFactory.registerBeanDefinition(entry.getKey(), entry.getValue()); } } } 以上是比较重要的接口和组件,如果看得比较迷糊,请上Github参考完整的代码,点击这里。 四、测试用例 定义一个pojo类和一个服务类: public class User { public User() { System.out.println("User constructor"); } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } public class UserService { private User user; public UserService() { System.out.println("UserService constructor"); } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public void say() { System.out.println("I am " + user.getName()); } } 定义Xml配置文件: 单元测试用例代码: public class TestIoC { @Test public void testIoC() { try { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = (UserService) applicationContext.getBean("userService"); userService.say(); } catch (Exception e) { e.printStackTrace(); } } } 运行后输出: User constructor UserService constructor I am matrix 接下来测试懒加载,将Xml配置文件中的user节点中加入懒加载属性lazy-init="true": 在单元测试代码中的UserService userService = (UserService) applicationContext.getBean("userService");处打上断点,运行程序,输出: UserService constructor 继续往下运行代码,输出: UserService constructor User constructor I am matrix 产生这种现象的原因是,user对象被懒加载了,所以上下文初始化的时候,并没有创建user对象,直到获取userService对象的时候,该IoC框架检查到userService依赖user,但是容器中user对象为空,所以这时候才将user实例化,并依赖注入给userService,这就是懒加载功能。 五、结束 进行到这,一个最基础的IoC框架算是完成了,接下来会陆续推出AOP和MVC等模块的从零构建,敬请期待。人生苦短,多写点技术代码,少写点业务代码,别让自己活成了一个码农。今天就23岁了,一没房贷,二没车贷,三没成家,四没孩子,一人吃饱全家不饿,人生中最轻松的一段时光,可以毫无顾虑地去自己想去的地方,放手去追求自己想做的事,新的开始,加油!
一、区别 1.:用于激活已经在容器中注册过的bean上面的注解,例如@Autowired,@Resource。 2.:具备功能,同时还能够在指定包下扫描注册bean。 二、举个例子 假如有A,B两个bean public class A{ @Autowired private B b; public B getB(){ return b; } public void setB(B b){ this.b = b; } } public class B{ } 通过xml在容器中注册 这个时候输出a中的b是null,原因是因为类A中的@Autowired并没有起作用,我们在xml中加入就行了。 或者在类A和类B上分别加上@Component注解,xml中直接用也行。