叩丁狼培训实战Java教程之自定义spring

    技术2022-07-13  90

    Java培训实战教程之自定义spring1.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   private UserDao userDao;   public void setUserDao(UserDao userDao) {   this.userDao = userDao;   }     @Override   public void register(User user) {   this.userDao.save(user);   } 1.4.1  xml配置文件l  在src下添加 applicationContext.xml ,并将dao和service配置到xml文件中。 l  使用 bean 标签配置一个实现类   class:  配置实现类的全限定名称   id: 进行唯一命名,用于提供给程序获得 l  使用 property 配置javabean属性的注入   name:  配置的service的属性名   ref:  配置其他bean对象的引用    bean id= userServiceId >   property name= userDao ref= userDaoId /property   /bean /beans l  tomcat启动时,加载配置文件方式总结:   1.编写Servlet,配置servlet,并添加 load-on-startup ,在init(ServletConfig)初始化方式中加载。   2.编写Filter,配置filter,在init(FilterConfig)初始化方法中加载   3.编写Listener,实现接口ServletContext,配置listener,在contextInitialized(ServletContextEvent sce)方法中加载。 l  spring采用listener方案   1.提供实现类ContextLoaderListener   2.编写全局初始化参数contextConfigLocation,用于确定xml位置   param-value classpath:applicationContext.xml /param-value 加载类路径下的xml文件   param-value applicationContext.xml /param-value 加载WEB-INF目录的配置文件  

    l  xml配置   param-name contextConfigLocation /param-name   param-value classpath:applicationContext.xml /param-value   /context-param     !-- 配置监听器 --   listener   listener-class cn.itcast.myspring.listener.ContextLoaderListener /listener-class   /listener   @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= /property 2.提供Bean类,用于封装 bean id= >  一个 bean 标签体中可以配置多个 property 需要一个容器存放,没有顺序要求,且不能重复,选择Set 3.提供BeanFactory类,并在类同提供容器存放多个Bean 类,为了方便获取使用Map。1.5.1   property javabean   private String beanClass;   //取值:singleton 单例,prototype 原型(多例)【扩展】   private String beanType;      //所有的property   private Set Property propSet = new HashSet Property     public Bean(String beanId, String beanClass) {   super();   this.beanId = beanId;   this.beanClass = beanClass;   }   //工厂模式   private static BeanFactory factory = new BeanFactory();   private BeanFactory(){   }     /**    * 获得工厂实例    * @author lt    * @return    */   public static BeanFactory getInstance() {   return factory;   }   //缓存所有的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;   } l  使用Jsoup解析,导入jar包    l  修改contextInitialized 方法   Map String, Bean data = parserBeanXml(xmlIs);   //3.2 将解析结果放置到工厂中   BeanFactory.setBeanData(data);   }    */   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);   }   } 1.6  阶段三:完善BeanFactory获得实例l  使用 BeanUtils.setProperty 设置数据,需要导入jar包  l  BeanFactory 提供 getBean方法 获得Bean实例///   public Object getBean(String beanId) {     try {   // 通过bean 的名称获得具体实现类   Bean bean = beanData.get(beanId);   if(bean ==null) return null;   String beanClass = bean.getBeanClass();   Class clazz = Class.forName(beanClass);   Object beanObj = clazz.newInstance();   //DI 依赖注入,将在配置文件中设置的内容,通过bean的set方法设置到bean实例中   Set Property props = bean.getPropSet();   for (Property property : props) {   String propName = property.getName();   String propRef = property.getRef(); //另一个javabean,需要重容器中获得   Object propRefObj = getBean(propRef);   //PropertyDescriptor pd = new PropertyDescriptor(propName, clazz);   //pd.getWriteMethod().invoke(bean, propRefObj);   BeanUtils.setProperty(beanObj, propName, propRefObj);   }     return beanObj;   } catch (Exception e) {   throw new RuntimeException(e);   }   } //测试 UserService userService = (UserService) BeanFactory.getInstance().getBean( userServiceId userService.register(null); 1.7.1  修改自定义springl  提供JdbcUtils工具类,用于在当前线程中共享Connection l  提供@Transaction 用于标记那些类需要进行事务管理    1.7.1.1  JdbcUtils工具类l  使用ThreadLocal 保存Connection,在当前线程中共享Connection l  并提供提交和回滚方法,自动关闭连接     private static ThreadLocal Connection local = new ThreadLocal Connection     static{   try {   //注册驱动   Class.forName( com.mysql.jdbc.Driver   } catch (Exception e) {   throw new RuntimeException(e);   }   }   /**    * 获得连接    * @return    */   public static Connection getConnection(){   try {   Connection conn = local.get();   if (conn == null) {   conn = DriverManager.getConnection( jdbc:mysql://localhost:3306/test2 , root , 1234   local.set(conn);   }   return conn;   } catch (Exception e) {   throw new RuntimeException(e);   }   }   /**    * 提交事务    */   public static void commit() {   Connection conn = getConnection();   DbUtils.commitAndCloseQuietly(conn);   }  

      /**    * 回滚事务    */   public static void rollback() {   Connection conn = getConnection();   DbUtils.rollbackAndCloseQuietly(conn);   }   1.7.1.3  修改BeanFactoryl  通过getBean 获得对象实例时,如果对象有@Transactional注解,将返回代理对象,对目标对象上所有的方法都进行事务管理。 l  如果没有异常提交事务,并关闭连接 l  如果有异常回滚事务,并关闭连接     // 通过bean 的名称获得具体实现类   Bean bean = beanData.get(beanId);   if(bean ==null) return null;   String beanClass = bean.getBeanClass();   Class ? clazz = Class.forName(beanClass);   Object beanObj = clazz.newInstance();   //DI 依赖注入,将在配置文件中设置的内容,通过bean的set方法设置到bean实例中   Set Property props = bean.getPropSet();   for (Property property : props) {   String propName = property.getName();   String propRef = property.getRef(); //另一个javabean,需要重容器中获得     Object propRefObj = getBean(propRef);   //PropertyDescriptor pd = new PropertyDescriptor(propName, clazz);   //pd.getWriteMethod().invoke(bean, propRefObj);   BeanUtils.setProperty(beanObj, propName, propRefObj);   }     //如果类上有注解返回代理对象   if(clazz.isAnnotationPresent(Transactional.class)){   final Object _beanObj = beanObj;   return Proxy.newProxyInstance(   clazz.getClassLoader(),   clazz.getInterfaces(),   new InvocationHandler() {   @Override   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   try {   //开启事务   JdbcUtils.getConnection().setAutoCommit(false);     //执行目标方法   Object obj = method.invoke(_beanObj, args);     //提交事务   JdbcUtils.commit();     return obj;     } catch (Exception e) {   //回顾事务   JdbcUtils.rollback();   throw new RuntimeException(e);   }   }   });   }     return beanObj;   } catch (Exception e) {   throw new RuntimeException(e);   }   } insert into account(username,money) values( jack , 10000 insert into account(username,money) values( rose , 10000 1.7.3  dao层l  必须使用自定义spring提供的JdbcUtils获得连接,从而保证当前线程使用的是同一个线程。 l  通过xml配置文件创建QueryRunner实例,并注入给dao l  扩展:如果将QueryRunner和JdbcUtils都省略,需要自己实现JdbcTemplate完成。     private QueryRunner runner;   public void setRunner(QueryRunner runner) {   this.runner = runner;   }     @Override   public void out(String outer, Integer money) {   try {   Connection conn = JdbcUtils.getConnection();   runner.update(conn, update account set money = money - ? where username = ? , money, outer);   } catch (Exception e) {   throw new RuntimeException(e);   }   }     @Override   public void in(String inner, Integer money) {   try {   Connection conn = JdbcUtils.getConnection();   runner.update(conn, update account set money = money + ? where username = ? , money,inner);   } catch (Exception e) {   throw new RuntimeException(e);   }   }     private AccountDao accountDao;   public void setAccountDao(AccountDao accountDao) {   this.accountDao = accountDao;   }   @Override   public void transfer(String outer, String inner, Integer money) {   accountDao.out(outer, money);   //断电 //  int i = 1/0;   accountDao.in(inner, money);   }     bean id= accountService >   property name= accountDao ref= accountDao /property   /bean 1.7.6  编写servlet测试l  通过请求servlet,进行转账,此处使用固定值进行测试     public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {   AccountService accountService = (AccountService) BeanFactory.getInstance().getBean( accountService   accountService.transfer( jack , rose , 100);   }

     

    Processed: 0.031, SQL: 9