教育行業(yè)A股IPO第一股(股票代碼 003032)

全國(guó)咨詢/投訴熱線:400-618-4000

Java培訓(xùn)實(shí)戰(zhàn)教程之自定義spring

更新時(shí)間:2015年12月29日13時(shí)44分 來(lái)源:傳智播客Java培訓(xùn)學(xué)院 瀏覽次數(shù):

1   Java培訓(xùn)實(shí)戰(zhàn)教程之自定義spring

1.1   描述

       在企業(yè)級(jí)開(kāi)發(fā)中,spring框架應(yīng)用非常廣。為了讓已經(jīng)學(xué)習(xí)過(guò)spring框架同學(xué),可以更深入的理解和應(yīng)用spring,本文將通過(guò)自定義spring,更佳系統(tǒng)的闡述spring核心:IoC、AOP。
       IoC(Inversion of Control)控制反轉(zhuǎn):將對(duì)象的創(chuàng)建權(quán)交與spring框架,及將創(chuàng)建權(quán)反轉(zhuǎn)給spring框架。IoC主要解決計(jì)算機(jī)程序的耦合問(wèn)題。
       AOP(Aspect Oriented Programming)面向切面編程:通過(guò)運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。
 

1.2   分析

l  如果要實(shí)現(xiàn)自定義spring,可以將器拆分成多個(gè)功能實(shí)現(xiàn)。
l  階段一:編寫配置文件,服務(wù)器tomcat啟動(dòng)時(shí),加載配置文件
l  階段二:使用Jsoup解析xml,并封裝到指定的JavaBean中
l  階段三:編寫工廠類用于創(chuàng)建指定bean,并完成property注入
l  階段四:使用@Transactional進(jìn)行事務(wù)管理
 

1.3   搭建環(huán)境

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 暫時(shí)只打印
      System.out.println(user);
   }
 
}
 

1.3.3   service

public interface UserService {
   /**
    * 注冊(cè)
    * @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   階段一:編寫配置文件,服務(wù)器啟動(dòng)加載

1.4.1   xml配置文件

l  在src下添加“applicationContext.xml”,并將dao和service配置到xml文件中。
l  使用<bean>標(biāo)簽配置一個(gè)實(shí)現(xiàn)類
       class:    配置實(shí)現(xiàn)類的全限定名稱
       id: 進(jìn)行唯一命名,用于提供給程序獲得
l  使用<property>配置javabean屬性的注入
       name:   配置的service的屬性名
       ref:       配置其他bean對(duì)象的引用
      
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <!-- dao -->
    <bean id="userDaoId" class="cn.itcast.demo.dao.impl.UserDaoImpl"></bean>
    <!-- service -->
    <bean id="userServiceId" class="cn.itcast.demo.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDaoId"></property>
    </bean>
</beans>
 

1.4.2   加載配置文件

l  tomcat啟動(dòng)時(shí),加載配置文件方式總結(jié):
       1.編寫Servlet,配置servlet,并添加<load-on-startup>,在init(ServletConfig)初始化方式中加載。
       2.編寫Filter,配置filter,在init(FilterConfig)初始化方法中加載
       3.編寫Listener,實(shí)現(xiàn)接口ServletContext,配置listener,在contextInitialized(ServletContextEvent sce)方法中加載。
l  spring采用listener方案
       1.提供實(shí)現(xiàn)類ContextLoaderListener
       2.編寫全局初始化參數(shù)contextConfigLocation,用于確定xml位置
              <param-value>classpath:applicationContext.xml</param-value> 加載類路徑下的xml文件
        <param-value>applicationContext.xml</param-value> 加載WEB-INF目錄的配置文件
 
l  xml配置
<!-- 確定xml位置 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
   
    <!-- 配置監(jiān)聽(tīng)器 -->
    <listener>
        <listener-class>cn.itcast.myspring.listener.ContextLoaderListener</listener-class>
    </listener>
 
l  實(shí)現(xiàn)類
      
public class ContextLoaderListener implements ServletContextListener {
 
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 0 獲得ServletContext對(duì)象應(yīng)用
        ServletContext context = sce.getServletContext();
        // 1 加載配置
        String config = context.getInitParameter("contextConfigLocation");
        if(config == null){ //默認(rèn)配置文件位置
            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+"]沒(méi)有找到");
        }
       
        //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=" " class=" ">
       一個(gè)<bean> 標(biāo)簽體中可以配置多個(gè)<property>需要一個(gè)容器存放,沒(méi)有順序要求,且不能重復(fù),選擇Set
3.提供BeanFactory類,并在類同提供容器存放多個(gè)Bean 類,為了方便獲取使用Map。

1.5.1    property javabean

/**
 * 用于封裝 <property name="userDao" ref="userDaoId"></property>
 */
public class Property {
  
   //屬性名稱
   private String name;
   //另一個(gè)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的實(shí)現(xiàn)類
   private String beanClass;
   //取值:singleton 單例,prototype 原型(多例)【擴(kuò)展】
   private String beanType;   
  
   //所有的property
   private Set<Property> propSet = new HashSet<Property>();
  
   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(){
    }
 
    /**
     * 獲得工廠實(shí)例
     * @author lt
     * @return
     */
    public static BeanFactory getInstance() {
        return factory;
    }
 

1.5.4    BeanFactory 提供Map 緩存

    //////////////////緩存所有的Bean/////////////////////////////////
    //bean數(shù)據(jù)緩存集合 ,key:bean名稱 ,value:bean封裝對(duì)象
    private static Map<String, Bean> beanData;// = new HashMap<String, String>();
    static{
        // 從配置文件中獲得相應(yīng)的數(shù)據(jù) 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解析,導(dǎo)入jar包
      
 
l  修改contextInitialized 方法
//TODO 3 解析配置
      if (xmlIs != null) {
         //3.1解析
         Map<String, Bean> data = parserBeanXml(xmlIs);
         //3.2 將解析結(jié)果放置到工廠中
         BeanFactory.setBeanData(data);
      }
 
l  解析方法parserBeanXml(InputStream)
/**
    * 將數(shù)據(jù)解析成bean
    * @param xmlIs
    * @return
    */
   public static Map<String, Bean> parserBeanXml(InputStream xmlIs) {
      try {
         //0提供緩沖區(qū)域
         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 將解析的結(jié)果封裝到bean中
            // 5.1 bean名稱
            String beanId = beanElement.attr("id");
            // 5.2 bean實(shí)現(xiàn)類
            String beanClass = beanElement.attr("class");
           
            // 5.3 封裝到Bean對(duì)象
            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獲得實(shí)例

l  使用 BeanUtils.setProperty 設(shè)置數(shù)據(jù),需要導(dǎo)入jar包
      
l  BeanFactory 提供 getBean方法
////////////////獲得Bean實(shí)例///////////////////////////////////
    public Object getBean(String beanId) {
       
        try {
            // 通過(guò)bean 的名稱獲得具體實(shí)現(xiàn)類
            Bean bean = beanData.get(beanId);
            if(bean ==null) return null;
            String beanClass = bean.getBeanClass();
            Class clazz = Class.forName(beanClass);
            Object beanObj = clazz.newInstance();
            //DI 依賴注入,將在配置文件中設(shè)置的內(nèi)容,通過(guò)bean的set方法設(shè)置到bean實(shí)例中
            Set<Property> props = bean.getPropSet();
            for (Property property : props) {
                String propName = property.getName();
                String propRef = property.getRef(); //另一個(gè)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);
        }
    }
 
l  測(cè)試
//測(cè)試
UserService userService = (UserService) BeanFactory.getInstance().getBean("userServiceId");
userService.register(null);
 
 

1.7   階段四:spring事務(wù)管理

 

1.7.1   修改自定義spring

l  提供JdbcUtils工具類,用于在當(dāng)前線程中共享Connection
l  提供@Transaction 用于標(biāo)記那些類需要進(jìn)行事務(wù)管理
 
 

1.7.1.1         JdbcUtils工具類

l  使用ThreadLocal 保存Connection,在當(dāng)前線程中共享Connection
l  并提供提交和回滾方法,自動(dòng)關(guān)閉連接
public class JdbcUtils {
   
    private static ThreadLocal<Connection> local = new ThreadLocal<Connection>();
   
    static{
        try {
            //注冊(cè)驅(qū)動(dòng)
            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);
        }
    }
    /**
     * 提交事務(wù)
     */
    public static void commit() {
        Connection conn = getConnection();
        DbUtils.commitAndCloseQuietly(conn);
    }
 
    /**
     * 回滾事務(wù)
     */
    public static void rollback() {
        Connection conn = getConnection();
        DbUtils.rollbackAndCloseQuietly(conn);
    }
 
}
 

1.7.1.2         @Transactional

 
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {
 
}
 

1.7.1.3         修改BeanFactory

l  通過(guò)getBean 獲得對(duì)象實(shí)例時(shí),如果對(duì)象有@Transactional注解,將返回代理對(duì)象,對(duì)目標(biāo)對(duì)象上所有的方法都進(jìn)行事務(wù)管理。
l  如果沒(méi)有異常提交事務(wù),并關(guān)閉連接
l  如果有異?;貪L事務(wù),并關(guān)閉連接
 
public Object getBean(String beanId) {
       
        try {
            // 通過(guò)bean 的名稱獲得具體實(shí)現(xiàn)類
            Bean bean = beanData.get(beanId);
            if(bean ==null) return null;
            String beanClass = bean.getBeanClass();
            Class<?> clazz = Class.forName(beanClass);
            Object beanObj = clazz.newInstance();
            //DI 依賴注入,將在配置文件中設(shè)置的內(nèi)容,通過(guò)bean的set方法設(shè)置到bean實(shí)例中
            Set<Property> props = bean.getPropSet();
            for (Property property : props) {
                String propName = property.getName();
                String propRef = property.getRef(); //另一個(gè)javabean,需要重容器中獲得
                Object propRefObj = getBean(propRef);
                //PropertyDescriptor pd = new PropertyDescriptor(propName, clazz);
                //pd.getWriteMethod().invoke(bean, propRefObj);
                BeanUtils.setProperty(beanObj, propName, propRefObj);
            }
           
            //如果類上有注解返回代理對(duì)象
            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 {
                                    //開(kāi)啟事務(wù)
                                    JdbcUtils.getConnection().setAutoCommit(false);
                                   
                                    //執(zhí)行目標(biāo)方法
                                    Object obj = method.invoke(_beanObj, args);
                                   
                                    //提交事務(wù)
                                    JdbcUtils.commit();
                                   
                                    return obj;
                                   
                                } catch (Exception e) {
                                    //回顧事務(wù)
                                    JdbcUtils.rollback();
                                    throw new RuntimeException(e);
                                }
                            }
                        });
            }
           
            return beanObj;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
 
 

1.7.2   初始化數(shù)據(jù)庫(kù)

create table account(
  id int primary key auto_increment,
  username varchar(50),
  money int
);
insert into account(username,money) values('jack','10000');
insert into account(username,money) values('rose','10000');
 

1.7.3   dao層

l  必須使用自定義spring提供的JdbcUtils獲得連接,從而保證當(dāng)前線程使用的是同一個(gè)線程。
l  通過(guò)xml配置文件創(chuàng)建QueryRunner實(shí)例,并注入給dao
l  擴(kuò)展:如果將QueryRunner和JdbcUtils都省略,需要自己實(shí)現(xiàn)JdbcTemplate完成。
 
public class AccountDaoImpl implements AccountDao {
   
    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);
        }
    }
 
}
 

1.7.4   service層

l  在實(shí)現(xiàn)類上添加注解
 
@Transactional
public class AccountServiceImpl implements AccountService {
 
    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);
    }
 
}
 

1.7.5   編寫spring配置

    <!-- 創(chuàng)建queryRunner -->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner"></bean>
   
    <bean id="accountDao" class="cn.itcast.dao.impl.AccountDaoImpl">
        <property name="runner" ref="runner"></property>
    </bean>
    <!-- service -->
    <bean id="accountService" class="cn.itcast.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
 

1.7.6   編寫servlet測(cè)試

l  通過(guò)請(qǐng)求servlet,進(jìn)行轉(zhuǎn)賬,此處使用固定值進(jìn)行測(cè)試
public class AccountServlet extends HttpServlet {
 
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        AccountService accountService = (AccountService) BeanFactory.getInstance().getBean("accountService");
        accountService.transfer("jack", "rose", 100);
    }
 
 
本文版權(quán)歸傳智播客Java培訓(xùn)學(xué)院所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明作者出處。謝謝!
作者:傳智播客Java培訓(xùn)學(xué)院
首發(fā):http://www.xamj520.com/javaee 
 
 
 
 
 

0 分享到:
和我們?cè)诰€交談!