1.1 描述
在企业级开发中,spring框架应用非常广。为了让已经学习过spring框架同学,可以更深入的理解和应用spring,本文将通过自定义spring,更佳系统的阐述spring核心:IoC、AOP。
        IoC(Inversion of Control)控制反转:将对象的创建权交与spring框架,及将创建权反转给spring框架。IoC主要解决计算机程序的耦合问题。
        AOP(Aspect Oriented Programming)面向切面编程:通过运行期动态代理实现程序功能的统一维护的一种技术。
1.2 分析
l  如果要实现自定义spring,可以将器拆分成多个功能实现。
 l  阶段一:编写配置文件,服务器tomcat启动时,加载配置文件
 l  阶段二:使用Jsoup解析xml,并封装到指定的JavaBean中
 l  阶段三:编写工厂类用于创建指定bean,并完成property注入
 l  阶段四:使用@Transactional进行事务管理
1.3 搭建环境

1.3.1 javabean
| public class User {
private Integer uid;
    private String username;
    private String password; |
1.3.2 dao
| public interface UserDao {
    /**
     * 保存
     * @param user
     */
    public void save(User user);
} |
 | public class UserDaoImpl implements UserDao {
@Override
    public void save(User user) {
       //TODO 暂时只打印
       System.out.println(user);
    }
} |
1.3.3 service
| public interface UserService {
    /**
     * 注册
     * @param user
     */
    public void register(User user);
 } |
 | public class UserServiceImpl implements UserService {
private UserDao userDao;
    public void setUserDao(UserDao userDao) {
       this.userDao = userDao;
    }
@Override
    public void register(User user) {
       this.userDao.save(user);
    }
 } |
1.4 阶段一:编写配置文件,服务器启动加载
1.4.1 xml配置文件
l  在src下添加“applicationContext.xml”,并将dao和service配置到xml文件中。想学习交流HashMap,nginx、dubbo、Spring MVC,分布式、高性能高可用、MySQL,redis、jvm、多线程、netty、kafka、的加尉xin(同英):1253431195 扩列获取java进阶资料学习,无工作经验不要加哦!
 l  使用标签配置一个实现类
        class:    配置实现类的全限定名称
        id: 进行唯一命名,用于提供给程序获得
 l  使用配置javabean属性的注入
        name:   配置的service的属性名
        ref:       配置其他bean对象的引用

| <?xml version=*"1.0"* encoding=*"UTF-8"*?>
 
     
 
     <bean id=“userDaoId” class=“cn.itcast.demo.dao.impl.UserDaoImpl”>
     
 
     <bean id=“userServiceId” class=“cn.itcast.demo.service.impl.UserServiceImpl”>
         <property name=“userDao” ref=“userDaoId”>
     
 |
1.4.2 加载配置文件
l  tomcat启动时,加载配置文件方式总结:
        1.编写Servlet,配置servlet,并添加,在init(ServletConfig)初始化方式中加载。
        2.编写Filter,配置filter,在init(FilterConfig)初始化方法中加载
        3.编写Listener,实现接口ServletContext,配置listener,在contextInitialized(ServletContextEvent sce)方法中加载。
 l  spring采用listener方案
        1.提供实现类ContextLoaderListener
        2.编写全局初始化参数contextConfigLocation,用于确定xml位置
               classpath:applicationContext.xml 加载类路径下的xml文件
         applicationContext.xml 加载WEB-INF目录的配置文件
l xml配置
| 
 
     
         contextConfigLocation
         classpath:applicationContext.xml
     
 
     
         cn.itcast.myspring.listener.ContextLoaderListener
     |
l 实现类

| public class ContextLoaderListener implements ServletContextListener {
@Override
     public void contextInitialized(ServletContextEvent sce) {
         // 0 获得ServletContext对象应用
         ServletContext context = sce.getServletContext();
         // 1 加载配置
         String config = context.getInitParameter(“contextConfigLocation”);
         if(config == null){ //默认配置文件位置
             config= “applicationContext.xml”;
         }
InputStream xmlIs = null;
         // 2  处理路径不同情况
         // * classpath:applicationContext.xml --> 表示 src/applicationContext.xml
         // * applicationContext.xml --> 表示 /WEB-INF/applicationContext.xml
         if (config.startsWith(“classpath:”)) { // 2.1 加载 类路径 (classpath、src)下的xml
             xmlIs = ContextLoaderListener.class.getClassLoader().getResourceAsStream(config.substring(“classpath:”.length()));
         } else { //2.2 加载/WEB-INF/目录下的资源
             xmlIs = context.getResourceAsStream("/WEB-INF/" + config);
         }
         //2.3 配置文件必须存在,否则抛异常
         if(xmlIs == null){
             throw new RuntimeException(“资源文件[”+config+"]没有找到");
         }
//TODO 3 解析配置
         if (xmlIs != null) {
             System.out.println(xmlIs);
         }
     }
     @Override
     public void contextDestroyed(ServletContextEvent sce) {
     }
 } |
1.5 阶段二:解析xml,并封装到指定javabean中
1.提供Property类,用于封装<property name=" " ref=" ">
 2.提供Bean类,用于封装<bean id=" " class=" ">
        一个 标签体中可以配置多个需要一个容器存放,没有顺序要求,且不能重复,选择Set
 3.提供BeanFactory类,并在类同提供容器存放多个Bean 类,为了方便获取使用Map。想学习交流HashMap,nginx、dubbo、Spring MVC,分布式、高性能高可用、MySQL,redis、jvm、多线程、netty、kafka、的加尉xin(同英):1253431195 扩列获取java进阶资料学习,无工作经验不要加哦!

1.5.1 property javabean
| /**
  * 用于封装 <property name=“userDao” ref=“userDaoId”>
  */
 public class Property {
//属性名称
    private String name;
    //另一个bean引用名
    private String ref;
public Property(String name, String ref) {
       super();
       this.name = name;
       this.ref = ref;
    } |
1.5.2 bean javabean
| public class Bean {
//bean名称
    private String beanId;
    //bean的实现类
    private String beanClass;
    //取值:singleton 单例,prototype 原型(多例)【扩展】
    private String beanType;
//所有的property
    private Set propSet = new HashSet();
public Bean(String beanId, String beanClass) {
       super();
       this.beanId = beanId;
       this.beanClass = beanClass;
    } |
1.5.3 BeanFactory 工厂模式类
| public class BeanFactory {
//工厂模式
     private static BeanFactory factory = new BeanFactory();
     private BeanFactory(){
     }
/**
      * 获得工厂实例
      * @author lt
      * @return
      */
     public static BeanFactory getInstance() {
         return factory;
     } |
1.5.4 BeanFactory 提供Map 缓存
|     //缓存所有的Bean/
     //bean数据缓存集合 ,key:bean名称 ,value:bean封装对象
     private static Map<String, Bean> beanData;// = new HashMap<String, String>();
     static{
         // 从配置文件中获得相应的数据 1.properties 2.xml
         //beanData.put(“userDao”, “com.itheima.ebs.service.impl.BusinessServiceImpl”);
     }
public static void setBeanData(Map<String, Bean> beanData) {
         BeanFactory.beanData = beanData;
     } |
1.5.5 修改Listener解析xml
l 使用Jsoup解析,导入jar包

l 修改contextInitialized 方法
| //TODO 3 解析配置
       if (xmlIs != null) {
          //3.1解析
          Map<String, Bean> data = parserBeanXml(xmlIs);
          //3.2 将解析结果放置到工厂中
          BeanFactory.setBeanData(data);
       } |
l 解析方法parserBeanXml(InputStream)
| /**
     * 将数据解析成bean
     * @param xmlIs
     * @return
     */
    public static Map<String, Bean> parserBeanXml(InputStream xmlIs) {
       try {
          //0提供缓冲区域
          Map<String, Bean> data = new HashMap<String, Bean>();
//1解析文件,并获得Document
          Document document = Jsoup.parse(xmlIs, “UTF-8”, “”);
//2 获得所有的bean元素
          Elements allBeanElement = document.getElementsByTag(“bean”);
//3遍历
          for (Element beanElement : allBeanElement) {
             //5 将解析的结果封装到bean中
             // 5.1 bean名称
             String beanId = beanElement.attr(“id”);
             // 5.2 bean实现类
             String beanClass = beanElement.attr(“class”);
// 5.3 封装到Bean对象
             Bean bean = new Bean(beanId,beanClass);
// 6 获得所有的子元素 property
             Elements allPropertyElement = beanElement.children();
             for (Element propertyElement : allPropertyElement) {
                 String propName = propertyElement.attr(“name”);
                 String propRef = propertyElement.attr(“ref”);
                 Property property = new Property(propName, propRef);
// 6.1 将属性追加到bean中
                 bean.getPropSet().add(property);
             }
data.put(beanId, bean);
          }
          return data;
       } catch (Exception e) {
          throw new RuntimeException(e);
       }
    } |










