zoukankan      html  css  js  c++  java
  • (一)Spring框架基础

    一、什么是spring框架

    • springJ2EE应用程序框架,是轻量级的IoCAOP的容器框架,主要是针对javaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,ibatis框架等组合使用。

    二、架构概述

      2.1  IoC(Inversion of Control) :控制反转

    • 对象创建责任的反转,在springBeanFacotoryIoC容器的核心接口,负责实例化,定位,配置应用程序中的对象及建立这些对象间的依赖。XmlBeanFacotory实现BeanFactory接口,通过获取xml配置文件数据,组成应用对象及对象间的依赖关系。

    • spring中有三种注入方式,一种是set注入,一种是接口注入,另一种是构造方法注入。

      2.1.1  第一个基于spring框架的程序(使用spring框架的ioc的优势)

    • 导包,由于本例是最简单的spring的程序,所以jar包比较少。

    •  创建spring的配置文件。

        spring.xml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:util="http://www.springframework.org/schema/util"
     xmlns:p="http://www.springframework.org/schema/p"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
     http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
     
     <!-- 以上为头文件,必须在eclipse的xml catalog里有相应xsd文件, 有了头文件之后会有提示-->
     
     <bean  class="test.Man"  name="man"></bean>   <!-- 创建test包里的Man类的对象,对象名为man -->
     </beans>
    •  Test.java
    package test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.context.support.FileSystemXmlApplicationContext;
    
    public class Test {
        
        public static void main(String[] args) {
            Test test=new Test();
            
            test.one();
            test.two();
            test.three();
        
        }
    /**
     * 普通模式:哪里需要对象,就在哪个方法里new 一个类的对象然后是用。
     * 存在的不足:如果有很多个类都使用一个对象,而这个对象改变了的话,就需要在很多个类里修改这个对象。
     */
        private void one() {
            PersonI man=new Man();
            man.eat();
            man.sleep();
            
        }
        
        /**
         * 设计模式(工厂模式、门面模式):
         * ,优势:如果PersonI的实现类修改了,只要在工厂PersonFactory中修改相应的类即可,而这里无须修改。
         */
        private void two() {
            PersonI woman=PersonFactory.getWomen(); //工厂模式 ,优势:如果PersonI的实现类修改了,只要在工厂PersonFactory中修改相应的类即可,而这里无须修改。
            woman.eat();
            woman.sleep();
            
        }
        
        /**
         * spring的ioc模式,将对象的实例化交给spring框架(spring容器)
         */
        private void three() {
            /**
             * 这里的Man对象是在spring.xml中创建(new)好了
             */
        //    ApplicationContext applicationContext=new FileSystemXmlApplicationContext("src/spring.xml");  //推荐使用new ClassPathXmlApplicationContext
            ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml"); 
            Man man=(Man)applicationContext.getBean("man");  //getBean("man")里的man是spring.xml文件里的id或者name为man的bean
            man.sleep();
            man.eat();
            
        }
        
    }
    • PersonFactory.java
    package test;
    
    /**
     * 工厂模式,
     * @author Administrator
     *
     */
    public class PersonFactory {
        public static PersonI getMan(){
            
            PersonI man=new Man();
            return man;
            
        }
        
        public static PersonI getWomen(){
            PersonI women=new Women();
            
            return women;
            
        }
    }
    • PersonI.java
    package test;
    
    public interface PersonI {
        void eat();
        void sleep();
    }
    
    class Man implements PersonI{
        
        public void eat() {
            System.out.println("Man 中的eat方法");
            
        }
    
        public void sleep() {
            System.out.println("Man 中的sleep方法");
            
        }
    
    }
    
     class Women implements PersonI{
    
        public void eat() {
            System.out.println("Women 中的eat方法");
            
        }
    
        public void sleep() {
            System.out.println("Women 中的sleep方法");
            
        }
    }

    结果:


     

    • 其中id和name标签属性都是bean的名称,区别在于id表示的bean如果这个xml被别的xml文件继承,则这个bean也可以在别的xml文件里使用。而name则不可以。
    • abstract="true" 表示这个bean是抽象的,不能实例化只能被继承。
    • parent="" 表示继承于某个类。
    • init-method="" 表示这个bean创建的时候就调用这个方法,当然这个方法的调用会在构造方法调用之后。 destroy-method="" 表示bean销毁的时候调用这个方法,有时候不会调用。
    • scope="singleton ||  prototype " singleton表示单例模式,对象只产生一个实例,init-method方法和destroy-method方法只执行一次。  prototype表示原型模式,每次产生一个对象,每次产生一个对象init-method方法和destroy-method方法都会执行一次。

     

     2.2 DI:依赖注入 (三种)

    • 一种是set注入,一种是接口注入,另一种是构造方法注入。

       (1) set注入

    这是最简单的注入方式,假设有一个SpringAction,类中需要实例化一个SpringDao对象,那么就可以定义一个private的SpringDao成员变量,
      然后创建SpringDao的set方法(这是ioc的注入入口):
        package com.bless.springdemo.action;  
        public class SpringAction {  
    //注入对象springDao private SpringDao springDao;
    //一定要写被注入对象的set方法 public void setSpringDao(SpringDao springDao) { this.springDao = springDao;    } public void ok(){ springDao.ok();    } }
    随后编写spring的xml文件,<bean>中的name属性是class属性的一个别名,class属性指类的全名,因为在SpringAction中有一个公共属性Springdao,
    所以要在<bean>标签中创建一个<property>标签指定SpringDao。<property>标签中的name就是SpringAction类中的SpringDao属性名,ref指下面<bean name="springDao"...>,
    这样其实是spring将SpringDaoImpl对象实例化并且调用SpringAction的setSpringDao方法将SpringDao注入:
    <!--配置bean,配置后该类由spring管理-->  
        <bean name="springAction" class="com.bless.springdemo.action.SpringAction">  
            <!--(1)依赖注入,配置当前类中相应的属性-->  
            <property name="springDao" ref="springDao"></property>  
        </bean>  
    <bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>  

       (2) 构造器注入

     这种方式的注入是指带有参数的构造函数注入,看下面的例子,我创建了两个成员变量SpringDao和User,但是并未设置对象的set方法,所以就不能支持第一种注入方式,

    这里的注入方式是在SpringAction的构造函数中注入,也就是说在创建SpringAction对象时要将SpringDao和User两个参数值传进来:

        public class SpringAction {  
            //注入对象springDao  
            private SpringDao springDao;  
            private User user;  
              
            public SpringAction(SpringDao springDao,User user){  
                this.springDao = springDao;  
                this.user = user;  
                System.out.println("构造方法调用springDao和user");  
            }  
                  
                public void save(){  
                user.setName("卡卡");  
                springDao.save(user);  
            }  
        }  

    在XML文件中同样不用<property>的形式,而是使用<constructor-arg>标签,ref属性同样指向其它<bean>标签的name属性:

        <!--配置bean,配置后该类由spring管理-->  
            <bean name="springAction" class="com.bless.springdemo.action.SpringAction">  
                <!--(2)创建构造器注入,如果主类有带参的构造方法则需添加此配置-->  
                <constructor-arg ref="springDao" index="0"></constructor-arg>  
                <constructor-arg ref="user"  index="1"></constructor-arg>  
            </bean>  
                <bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>  
                 <bean name="user" class="com.bless.springdemo.vo.User"></bean>  

       (3) 接口注入

    • Person.java
    package test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Person {
    
        private Phone phone;
        
    /**
     * set注入
     * @param phone 接口
     */
        public void setPhone(Phone phone) {
            this.phone = phone;
        }
    
        public void usePhone(){
            this.phone.games();
            
        }
    
        public static void main(String[] args) {
            ApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
            Person person=(Person)context.getBean("person");
            person.usePhone();
        }
    }
    • spring.xml
     xmlns:util="http://www.springframework.org/schema/util"
     xmlns:p="http://www.springframework.org/schema/p"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
     http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
     
     <!-- 以上为头文件,必须在eclipse的xml catalog里有相应xsd文件, 有了头文件之后会有提示-->
    
        <bean name="person" class="test.Person">
            <property name="phone" ref="huawei"></property> 
        </bean>
    
        <bean name="iPhone" class="test.iPhone"></bean>
        <bean name="huawei" class="test.HuaWei"></bean>
    
        </beans>
    • Person类的成员属性Phone phone;是一个接口,在spring.xml实例化Person的时候,把Phone接口的实现类对象“huawei”引入到Person类中,如果要引用其他对象,只需要把ref="huawei" 即可,其他代码不用改变。

     2.3  AOP面向切面编程

       aop就是纵向的编程,如下图所示,业务1和业务2都需要一个共同的操作,与其往每个业务中都添加同样的代码,不如写一遍代码,让两个业务共同使用这段代码。

     spring中面向切面变成的实现有两种方式,一种是动态代理,一种是CGLIB,动态代理必须要提供接口,而CGLIB实现是有继承。

      

      (1)AOP之代理模式(proxy模式)

    •     代理模式:通俗来讲,比如我们去买动车票,可以去动车站售票点买票,或者去代理点买票。通常来讲,售票点只提供售票而代理点不仅可以售票而且可以实现其他的功能。    从程序设计上讲,如果我们希望某个类不被直接使用,那么我们可以创建这个类的代理,代理并不真正实现功能,而是经过处理之后调用被代理类的功能,比如客户到代理点买票,代理点的票是从售票点买来的(相当于调用实现类的方法),可以得出:  代理应该实现一个接口(被代理类也实现这个接口),也有被代理类作为成员变量。
    •   静态代理 or 动态代理

        案例一: 静态代理

    TicketIFC.java(售票接口)

    package proxy;
    
    public interface TicketIFC {
        
        void sellTicket();
        
    }
    Ticket_station.java(实现类)
    package proxy;
    
    public class Ticket_station implements TicketIFC{
    
        public void sellTicket() {
            System.out.println("售票站售票");
        }
        
        
    }
    Ticket_proxy.java(代理类,实现售票接口且有实现类对象)
    package proxy;
    
    public class Ticket_proxy implements TicketIFC{
    
        private TicketIFC ticketIFC;
        
        public Ticket_proxy(TicketIFC ticketIFC){
            this.ticketIFC=ticketIFC;
            
        }
        
        public void sellTicket() {
            this.ticketIFC.sellTicket();
        }
    
    }

    Test.java(测试类)

    package proxy;
    
    public class Test {
    
        public static void main(String[] args) {
            
            TicketIFC ticketIFC=new Ticket_proxy(new Ticket_station());
            
            ticketIFC.sellTicket();
            
        }
    }

    结果:

       案例二:动态代理(代理类是在运行时动态产生的)

    •  AOP的原理就是java的动态代理机制。
    •  在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。
    • 每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
    • 代理类原则: 1.实现某一接口  2.被代理的实现类。

    PersonI.java (接口)

    package spring_project_01;
    
    public interface PersonI {
    
        void eat();
        void sleep();
    }

    Man.java(实现类)

    package spring_project_01;
    
    public class Man implements PersonI{
    
        public void eat() {
            System.out.println("Man中的eat方法");
            
        }
    
        public void sleep() {
            System.out.println("Man中的sleep方法");
            
        }
    
    }

    Women.java(实现类)

    package spring_project_01;
    
    public class Women implements PersonI{
        public void eat() {
            System.out.println("Women中的eat方法");
            
        }
    
        public void sleep() {
            System.out.println("Women中的sleep方法");
            
        }
    
    }

    ProxyUtil.java(代理类创建工具)

    package proxy_dynamic;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    /**
     * 动态创建代理类
     * @author Administrator
     *
     */
    public class ProxyUtil implements InvocationHandler{
    
        private Object targetObj;
        public Object createProxy(Object targetObj){
            this.targetObj=targetObj;
            //Proxy.newProxyInstance参数为被代理类的类加载器、被代理类的接口,以及一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
            Object proxyObj=Proxy.newProxyInstance(targetObj.getClass().getClassLoader(), targetObj.getClass().getInterfaces(), this);
        
            return proxyObj;
        }
        /**
         * proxy:指代我们所代理的那个真实对象
         * method:指代的是我们所要调用真实对象的某个方法的Method对象
         * args:指代的是调用真实对象某个方法时接受的参数
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("在被代理对象方法调用前会自动调用");
        
            System.out.println("代理对象方法调用前");
            Object objectReturnValue=    method.invoke(this.targetObj, args);  //在代理实例上处理方法调用并返回结果。java反射机制,即用方法调用对象。
            System.out.println("代理对象方法调用后");
            
            return objectReturnValue;
        } 
    }

    Test.java(测试类)

    package proxy_static;
    
    public class Test {
        
        
        public static void main(String[] args) {
            
            TicketIFC ticketIFC=new Ticket_proxy(new Ticket_station());
            
            ticketIFC.sellTicket();
            
        }
    }

    结果:


       案例三:模拟事务(实物的提交和回滚都不再dao层实现,而是采用代理模式实现)

     1. 数据字典:

      2. RoleServiceI.java(接口)

    package proxy_jdbc;
    
    public interface RoleServiceI {
        
        void addRole() throws Exception;
        
    }

      3. RoleServiceImpl.java(实现类,在动态代理模式下本类并不做事务的处理,把事务处理在代理类的invoke方法里处理)

    package proxy_jdbc;
    
    import java.sql.Connection;
    import java.sql.Statement;
    
    public class RoleServiceImpl implements RoleServiceI{
    
        public void addRole() throws Exception {
            
                Connection conn=null;
                Statement stat=null;
                StringBuffer SQL=new StringBuffer();
                
        
                conn=DBUtil.getConn();
                System.out.println("dao中的conn="+conn);
                stat=conn.createStatement();
                
                /**
                 * 插入到角色表
                 */
                
                SQL.setLength(0);
                SQL.append("insert into role values(2,'管理员','管理员备注')");
                stat.executeUpdate(SQL.toString());
                
                /**
                 * 插入到用户表
                 */
                
                SQL.setLength(0);
                SQL.append("insert into user values('111',11,'女')");
                stat.executeUpdate(SQL.toString());
    
                DBUtil.close(null, stat, null);   //这里只关闭stat流,conn流在ServiceProxyUtil.invoke方法里统一关闭。
        
        }
    }

      4.  ServiceProxyUtil.java(动态代理生成工具):统一处理事务。

    package proxy_jdbc;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    /**
     * 动态代理类创建工具
     * @author Administrator
     *
     */
    public class ServiceProxyUtil implements InvocationHandler{
    
        private Object targetObj;
        
        public Object createServiceProxy(Object targetObj){
            this.targetObj=targetObj;
            
            Object proxy=Proxy.newProxyInstance(targetObj.getClass().getClassLoader(), targetObj.getClass().getInterfaces(), this);
        
            return proxy;
        }
        
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            /**
             * 在本方法里对jdbc的事务进行处理和conn流的关闭,在dao层就不用再处理了。
             */
            Object returnValue=null;
            Connection conn=null;
            try {
                conn=DBUtil.getConn();
                System.out.println("proxy中的conn="+conn);
                conn.setAutoCommit(false); //禁止自动提交事务
                
                returnValue=method.invoke(this.targetObj, args); //执行实现类方法
                
                System.out.println("提交事务");
                conn.commit(); 
                
            } catch (Exception e) {
                  System.out.println("出现异常,回滚事务");
                  conn.rollback();
            }finally{
                    DBUtil.close(conn, null, null);    
            } 
            return returnValue;
        }
    
    }

      5. ServiceFactory.java(静态工厂)

    package proxy_jdbc;
    /**
     * 静态工厂
     * @author Administrator
     *
     */
    public class ServiceFactory {
        public static RoleServiceI getRoleServiceImpl(){
            
            RoleServiceI roleService=new RoleServiceImpl();
            return roleService;
            
        }
    }

      6. Test.java(测试类)

    package proxy_jdbc;
    
    public class Test {
        public static void main(String[] args) { 
            //创建动态代理创建工具
            ServiceProxyUtil proxyUtil=new ServiceProxyUtil(); 
            
            //用动态代理创建工具创建出一个服务层的实现类,
            RoleServiceI roleServiceImpl=(RoleServiceI)proxyUtil.createServiceProxy(ServiceFactory.getRoleServiceImpl()); 
            
            try {
                roleServiceImpl.addRole();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    }

      7. DBUtil.java

    package proxy_jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class DBUtil {
        /**
         * ThreadLocal本地线程,类似HttpSession操作,HttpSession针对每一个用户请求,而ThreadLocal针对每一个线程。
       * 这个对象用于实现当Connection创建之后除非关闭否则一直都是操作这个Connection对象
    */ private static ThreadLocal<Connection> local=new ThreadLocal<Connection>(); private static final String DRIVER="com.mysql.jdbc.Driver"; private static final String USER="root"; private static final String PASSWD=""; private static final String URL="jdbc:mysql://127.0.0.1:3306/user?useUnicode=true&characterEncoding=UTF-8"; static{ try { Class.forName(DRIVER); } catch (Exception e) { throw new RuntimeException("无法加载驱动包"); } } public static Connection getConn(){ Connection conn=null; try { if(local.get() !=null){ //如果本地线程里有值,说明这个数据库连接被创建过,所以还是使用这个连接 conn=local.get(); }else{ //如果本地线程为空,则创建一个数据库连接。 conn= DriverManager.getConnection(URL,USER,PASSWD); local.set(conn); } } catch (SQLException e) { e.printStackTrace(); } return conn; } public static void close(Connection conn,Statement stat,ResultSet rs) throws Exception{ if(conn!=null && !conn.isClosed()){ if(local.get() !=null ){ local.remove(); } conn.close(); } if(stat!=null && !stat.isClosed()){ stat.close(); } if(rs!=null && !rs.isClosed()){ rs.close(); } } }

    结果:

    数据已插入。

  • 相关阅读:
    华为面向开发者的十大技术
    为什么开发者应该摒弃敏捷?
    程序员创业的特别之处
    这是我的facebook和twitter,欢迎大家来加我
    教程:2014新版新浪博客如何添加音乐播放器?
    Algs4-1.1.11编写一段代码,打印出一个二维布尔数组的内容
    Algs4-1.1.9十进制整数转二进制
    Algs4-1.1.8下列语句会打印出什么结果?给出解释
    Algs4-1.1.7分别给出以下代码段打印的值
    Algs4-1.1.6下面这段程序会打印出什么
  • 原文地址:https://www.cnblogs.com/shyroke/p/6699072.html
Copyright © 2011-2022 走看看