这里写图片描述

转载请注明出处:

http://blog.csdn.net/gane_cheng/article/details/52759099

http://www.ganecheng.tech/blog/52759099.html (浏览效果更好)

Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个开源的控制反转(Inversion of Control ,IoC)和面向切面(AOP)的容器框架。它的主要目得是简化企业开发。

Spring是潜在地一站式解决方案,定位于与典型应用相关的大部分基础结构。它也涉及到其他framework没有考虑到的内容。

Spring致力于J2EE应用的各层的解决方案,而不是仅仅专注于某一层的方案。可以说Spring是企业应用开发的“一站式”选择,并贯穿表现层、业务层及持久层。然而,Spring并不想取代那些已有的框架,而是与它们无缝地整合。

Spring特点

1.方便解耦,简化开发

通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

2.AOP编程的支持

通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。

3.声明式事务的支持

在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。

4.方便程序的测试

可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。

5.方便集成各种优秀框架

Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。

6.降低Java EE API的使用难度

Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EE API的使用难度大为降低。

7.Java 源码是经典学习范例

Spring的源码设计精妙、结构清晰、匠心独运,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。Spring框架源码无疑是Java技术的最佳实践范例。如果想在短时间内迅速提高自己的Java技术水平和应用开发水平,学习和研究Spring源码将会使你收到意想不到的效果。

Spring框架包括什么?

这里写图片描述

Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式。

组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。

Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。

Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。

Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。

Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 MyBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。

Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

Spring 实现的两个核心技术 IoC 和 AOP

IoC—Inversion of Control,控制反转 ,在 Spring 里的实现是 Dependency Injection ( 依赖注入 ),就是说对象之间的依赖关系在后期通过配置文件(典型为 XML 文件)生成, Spring 里实现了两种注入方式:构造函数注入、 Setter 方法注入。我们可以这样理解这种技术带来的好处,前期我们只需要关注单个对象(组件)的功能实现,具体的业务实现是通过后期配置出来的,不同的配置可以产生不同的业务功能。

AOP—Aspect-oriented programming,面向切面编程。 AOP 大大降低了对象之间的耦合程度,与 IoC 一样,能够通过后期的配置动态为对象增加新的特性,甚至能够为对象动态增加方法。在 Spring 下, AOP 的实现不需要借助专门的 AOP 定义语言,只需要普通的 Java 对象和 XML 配置文件即可。

IoC 控制反转

public class PersonServiceBean {
     private PersonDao personDao = new PersonDaoBean();

      public void save(Person person){
            personDao.save(person);
     }
}

PersonDaoBean 是在应用内部创建及维护的。所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。

当我们把依赖对象交给外部容器负责创建,那么PersonServiceBean 类可以改成如下:

public class PersonServiceBean 
{
     private PersonDao personDao ;
     //通过构造器参数,让容器把创建好的依赖对象注入进PersonServiceBean,当然也可以使用setter方法进行注入。
     public PersonServiceBean(PersonDao personDao)
     {
         this.personDao=personDao;
     }  
     public void save(Person person)
     {
            personDao.save(person);
     }
}

所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中。

为何要使用Spring

  • 降低组件之间的耦合度,实现软件各层之间的解耦。
  • 可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,开发人员就不再需要手工控制事务.也不需处理复杂的事务传播。
  • 容器提供单例模式支持,开发人员不再需要自己编写实现代码。
  • 容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。
  • 容器提供的众多辅作类,使用这些类能够加快应用的开发,如: JdbcTemplate、 HibernateTemplate。
  • Spring对于主流的应用框架提供了集成支持,如:集成Hibernate、JPA、Struts等,这样更便于应用的开发。

如果使用Spring, 我们就不再需要手工控制事务。

Hibernate的事务操作:

public void save()
{
    Session session = sessionFactory.getCurrentSession();

    //session.beginTransaction();

    Info info = new Info("传智播客");
    info.setContent("国内实力最强的java培训机构");
    session.save(info );

    //session.getTransaction().commit();
}

JDBC的事务操作:

Connection conn = null;
try 
{
     .......
     //conn.setAutoCommit(false);
     Statement stmt = conn.createStatement();
     stmt.executeUpdate("update person where name='叶天'");
     //conn.commit();
     .....
} 
//catch (Exception e) 
//{
//  conn.rollback();  
//} 
//finally
//{
//  conn.close();
//}

实例化spring容器

实例化Spring容器常用的两种方式:

方法一:
在类路径下寻找配置文件来实例化容器

ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"beans.xml"});

方法二:
在文件系统路径下寻找配置文件来实例化容器

ApplicationContext ctx = new FileSystemXmlApplicationContext(new String[]{"d:\\beans.xml"}); 

Spring的配置文件可以指定多个,可以通过String数组传入。

当spring容器启动后,因为spring容器可以管理bean对象的创建,销毁等生命周期,所以我们只需从容器直接获取Bean对象就行,而不用编写一句代码来创建bean对象。从容器获取bean对象的代码如下:

ApplicationContext ctx = new ClassPathXmlApplicationContext(“beans.xml”);
OrderService service = (OrderService)ctx.getBean("personService");

实例化bean的方式

①使用Set方法实例化

set方式注入的属性一定要加set方法

/**通过set方法注入示例*/
public class IoC_By_Set {
    /**注入Integer类型参数*/
    private Integer id;
    /**注入String类型参数*/
    private String name;
    /**注入实体Bean*/
    private User user;
    /**注入数组*/
    private Object[] array;
    /**注入List集合*/
    private List<Object> list;
    /**注入Set集合*/
    private Set<Object> set;
    /**注入Map键值对*/
    private Map<Object, Object> map;
    /**注入properties类型*/
    private Properties properties;
    /**注入空字符串*/
    private String emptyValue;
    /**注入null值*/
    private String nullValue = "";

    /**检测注入的属性是否全部正确*/
    public boolean checkAttr() {
        if(id == null) {
            return false;
        }else {
            System.out.println("id:" + id);
        }
        System.out.println("--------------------------");
        if(name == null) {
            return false;
        }else {
            System.out.println("name:" + name);
        }
        System.out.println("--------------------------");
        if(user == null) {
            return false;
        }else {
            System.out.println("Bean:" + user.getId() + "|" + 
                    user.getUserName() + "|" + user.getPassWord());
        }
        System.out.println("--------------------------");
        if(array == null) {
            return false;
        }else {
            System.out.println("array:");
            for (Object object : array) {
                System.out.println(object.toString());
            }
        }
        System.out.println("--------------------------");
        if(list == null) {
            return false;
        }else {
            System.out.println("list:");
            for (Object object : list) {
                System.out.println(object.toString());
            }
        }
        System.out.println("--------------------------");
        if(set == null) {
            return false;
        }else {
            System.out.println("set:");
            for (Object object : set) {
                System.out.println(object.toString());

            }
        }
        System.out.println("--------------------------");
        if(map == null) {
            return false;
        }else {
            Set<Entry<Object, Object>> set = map.entrySet();
            System.out.println("map:");
            for (Entry<Object, Object> entry : set) {
                System.out.println(entry.getKey() + "|" + entry.getValue());
            }
        }
        System.out.println("--------------------------");
        if(properties == null) {
            return false;
        }else {
            Set<Entry<Object, Object>> set = properties.entrySet();
            System.out.println("properties:");
            for (Entry<Object, Object> entry : set) {
                System.out.println(entry.getKey() + "|" + entry.getValue());
            }
        }
        System.out.println("--------------------------");
        if(!"".equals(emptyValue))
            return false;
        System.out.println("--------------------------");
        if(!(null == nullValue))
            return false;
        System.out.println("--------------------------");
        System.out.println("全部正确!!!");
        return true;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public void setArray(Object[] array) {
        this.array = array;
    }

    public void setList(List<Object> list) {
        this.list = list;
    }

    public void setSet(Set<Object> set) {
        this.set = set;
    }

    public void setMap(Map<Object, Object> map) {
        this.map = map;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void setEmptyValue(String emptyValue) {
        this.emptyValue = emptyValue;
    }

    public void setNullValue(String nullValue) {
        this.nullValue = nullValue;
    }

}

applicationContext.xml配置

 <!-- set方式注入 -->
    <bean id="ioC_By_Set" class="com.bc.ioc.demo01.IoC_By_Set">
        <!-- 注入id属性 -->
        <property name="id" value="1"/>
        <!-- 使用<![CDATA[]]>标记处理XML特 殊字符 -->
        <property name="name">
            <!-- 也可以使用P&amp;G -->
            <value><![CDATA[P&G]]></value>
        </property>
        <!-- 定义内部Bean注入 -->
        <property name="user">
            <bean class="com.bc.pojo.User">
                <property name="id" value="1"/>
                <property name="userName" value="内部Bean"/>
                <property name="passWord" value="233"/>
            </bean>
        </property>
        <!-- 注入数组类型 -->
        <property name="array">
            <array>
                <!-- 定义数组元素 -->
                <value>array01</value>
                <value>array02</value>
                <value>array03</value>
            </array>
        </property>
        <!-- 注入List类型 -->
        <property name="list">
            <list>
                <!-- 定义list中元素 -->
                <value>list01</value>
                <value>list02</value>
                <value>list03</value>
            </list>
        </property>
        <!-- 注入Set类型 -->
        <property name="set">
            <set>
                <!-- 定义set中元素 -->
                <value>set01</value>
                <value>set02</value>
                <value>set03</value>
            </set>
        </property>
        <!-- 注入Map类型 -->
        <property name="map">
            <map>
                <!-- 定义map中的键值对 -->
                <entry>
                    <key>
                        <value>mapKey01</value>
                    </key>
                    <value>mapValue01</value>
                </entry>
                <entry>
                    <key>
                        <value>mapKey02</value>
                    </key>
                    <value>mapValue02</value>
                </entry>
            </map>
        </property>
        <!-- 注入properties类型 -->
        <property name="properties">
            <props>
                <!-- 定义properties中的键值对 -->
                <prop key="propKey1">propValue1</prop>
                <prop key="propKey2">propValue2</prop>
            </props>
        </property>
        <!-- 注入空字符串 -->
        <property name="emptyValue">
            <value></value>
        </property>
        <!-- 注入null值 -->
        <property name="nullValue">
            <null/>
        </property>
    </bean>

测试代码

public class IoC_Test {

    private ApplicationContext ctx;

    @Before
    public void load() {
        //读取applicationContext.xml配置文件
        ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    }

    @Test
    public void SetTest() {
        IoC_By_Set ioc = (IoC_By_Set) ctx.getBean("ioC_By_Set");
        ioc.checkAttr();
    }
}

②通过构造方法注入各种类型属性

注意:使用JDK1.8版本请将spring相关jar包升级到4.x版本以上,否则不兼容构造方法注入

/** 通过构造方法注入示例 */
public class IoC_By_Constructor {

    private Integer id;
    private String name;
    private User user;
    private List<Object> list;

    public IoC_By_Constructor() {
    }

    public IoC_By_Constructor(Integer id, String name, User user,
            List<Object> list) {
        this.id = id;
        this.name = name;
        this.user = user;
        this.list = list;
    }

    /**检查是否注入成功*/
    public boolean checkAttr() {
        if(id == null) {
            return false;
        }else {
            System.out.println("id:" + id);
        }
        System.out.println("----------------------------");
        if(name == null) {
            return false;
        }else {
            System.out.println("name:" + name);
        }
        System.out.println("----------------------------");
        if(user == null) {
            return false;
        }else {
            System.out.println("user:" + user.getId() + "|" + 
                    user.getUserName() + "|" + user.getPassWord());
        }
        System.out.println("----------------------------");
        if(list == null) {
            return false;
        }else {
            System.out.println("list:");
            for (Object object : list) {
                System.out.println(object.toString());
            }
        }
        System.out.println("----------------------------");
        System.out.println("全部正确!!!");
        return true;
    }
}

applicationContext.xml配置

    <!-- 构造方法注入 演示几种类型-->
    <bean id="ioC_By_Constructor" class="com.bc.ioc.demo02.IoC_By_Constructor">
        <!-- 注入Integer属性,可以选择使用index指定参数位置,也可以选择使用type指定参数类型 -->
        <constructor-arg index="0" value="1" type="java.lang.Integer"/>
        <!-- 注入字符串 -->
        <constructor-arg value="P&amp;G"/>
        <!-- 注入对象 -->
        <constructor-arg>
            <!-- 内建对象 -->
            <bean class="com.bc.pojo.User">
                <constructor-arg value="1"/>
                <constructor-arg value="构造内部Bean"/>
                <constructor-arg value="666"/>
            </bean>
        </constructor-arg>
        <!-- 注入集合 -->
        <constructor-arg>
            <list>
                <value>list01</value>
                <value>list02</value>
                <value>list03</value>
            </list>
        </constructor-arg>
    </bean>

测试代码:

public class IoC_Test {

    private ApplicationContext ctx;

    @Before
    public void load() {
        //读取applicationContext.xml配置文件
        ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    }

    @Test
    public void constructorTest() {
        IoC_By_Constructor ioc = (IoC_By_Constructor) ctx.getBean("ioC_By_Constructor");
        ioc.checkAttr();
    }

}

③自动注入(自动装配)

/**自动装配注入*/
public class IoC_By_Auto {

    private User user;

    /**检查是否注入成功*/
    public boolean checkAttr() {
        if(user == null) {
            return false;
        }else {
            System.out.println("user:" + user.getId() + "|" + 
                    user.getUserName() + "|" + user.getPassWord());
        }
        System.out.println("正确!!!");
        return true;
    }

    /**自动装配的属性需要设置set方法*/
    public void setUser(User user) {
        this.user = user;
    }

}

applicationContext.xml配置

    <!-- 被自动装配获取的bean -->
    <bean id="user" class="com.bc.pojo.User">
        <property name="id" value="1"/>
        <property name="userName" value="自动装配"/>
        <property name="passWord" value="233"/>
    </bean>
    <!-- 自动装配的bean
         autowire:byName 根据类的属性名查找与之命名相同的id的bean进行装配
                  byType 根据类的属性类型查找唯一一个匹配类型的bean,如果有多个bean匹配则抛出异常
                  constructor 根据类的构造方法参数类型匹配对应的bean
                  no 默认,表示不使用自动装配
                  default:由上级标签<beans>的default-autowire属性确定 -->
    <bean id="ioC_By_Auto" class="com.bc.ioc.demo03.IoC_By_Auto" autowire="byName"></bean>

测试代码

public class IoC_Test {

    private ApplicationContext ctx;

    @Before
    public void load() {
        //读取applicationContext.xml配置文件
        ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    }

    @Test
    public void AutoTest() {
        IoC_By_Auto ioc = (IoC_By_Auto) ctx.getBean("ioC_By_Auto");
        ioc.checkAttr();
    }

}

④使用P命名空间注入属性

使用 < property > 和命名空间P是等价的。但是使用命名空间P可以省去很多的尖括号。

使用 < property >的效果如下。

    <bean id="user2" class="com.bc.pojo.User">
        <property name="id" value="1"/>
        <property name="userName" value="P"/>
        <property name="passWord" value="233"/>
    </bean>

使用命名空间P的效果如下。

<bean id="ioC_By_P" class="com.bc.ioc.demo04.IoC_By_P" p:id="1" 
        p:name="命名空间" p:user-ref="user2"></bean>
/**使用P命名空间注入*/
public class IoC_By_P {

    private Integer id;
    private String name;
    private User user;

    /**检查是否注入成功*/
    public boolean checkAttr() {
        if(id == null) {
            return false;
        }else {
            System.out.println("id:" + id);
        }
        System.out.println("----------------------------");
        if(name == null) {
            return false;
        }else {
            System.out.println("name:" + name);
        }
        System.out.println("----------------------------");
        if(user == null) {
            return false;
        }else {
            System.out.println("user:" + user.getId() + "|" + 
                    user.getUserName() + "|" + user.getPassWord());
        }
        System.out.println("----------------------------");
        System.out.println("全部正确!!!");
        return true;
    }

    //使用P命名空间注入属性需要设置set方法
    public void setId(Integer id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setUser(User user) {
        this.user = user;
    }

}

applicationContext.xml配置

    <!-- 使用P命名空间注入各种类型属性 -->
    <bean id="user2" class="com.bc.pojo.User">
        <property name="id" value="1"/>
        <property name="userName" value="P"/>
        <property name="passWord" value="233"/>
    </bean>
    <bean id="ioC_By_P" class="com.bc.ioc.demo04.IoC_By_P" p:id="1" 
        p:name="命名空间" p:user-ref="user2"></bean>

测试代码

public class IoC_Test {

    private ApplicationContext ctx;

    @Before
    public void load() {
        //读取applicationContext.xml配置文件
        ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    }

    @Test
    public void PTest() {
        IoC_By_P ioc = (IoC_By_P) ctx.getBean("ioC_By_P");
        ioc.checkAttr();
    }

}

⑤ 使用注解方式注入

Spring在2.5以后,提供了基于Annotation(注解)的注入。

1.@Autowired -对成员变量、方法和构造函数进行标注,来完成自动装配的工作,不推荐使用

    @Autowired 
    private PersonDao  personDao;//用于字段上
    @Autowired
    public void setOrderDao(OrderDao orderDao) {//用于属性的setter方法上
        this.orderDao = orderDao;
    }

2.@Qualifier -配合@Autowired来解决装配多个同类型的bean

    @Autowired  @Qualifier("personDaoBean")
    private PersonDao  personDao;

3.@Resource -JSR-250标准注解,作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按byName自动注入 ,当找不到与名称匹配的bean才会按类型装配。

@Resource注解和@Autowired一样,也可以标注在字段或属性的setter方法上,但它默认按名称装配。名称可以通过@Resource的name属性指定,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。

    @Resource(name=“personDaoBean”)
    private PersonDao  personDao;//用于字段上

注意:如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。

4.@PostConstruct -在方法上加上注解@PostConstruct,这个方法就会在Bean初始化之后被Spring容器执行

5.@PreDestroy -在方法上加上注解@PreDestroy,这个方法就会在Bean初始化之后被Spring容器执行

6.@Component -只需要在对应的类上加上一个@Component注解,就将该类定义为一个Bean,不推荐使用,推荐使用更加细化的三种:@Repository、@Service、@Controller

@Repository 存储层Bean
@Service 业务层Bean
@Controller 控制器层Bean

7.@Scope -定义Bean的作用范围

singleton

在每个Spring IoC容器中一个bean定义只有一个对象实例。默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean才会初始化bean。如:

 <bean id="xxx" class="cn.itcast.OrderServiceBean" lazy-init="true"/>

如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,如下:

<beans default-lazy-init="true" ...>  作用域为protoype时 也是延迟初始化

prototype
每次从容器获取bean都是新的对象。

request
session
global session


首先配置applicationContext.xml开启注解,开启扫描。

    <!-- 扫描包中注解标注的类 -->
    <context:component-scan base-package="com.bc.ioc.demo05"/>

实体Bean加注解

@Repository
public class User {

    private Integer id = 1;

    private String userName = "注解注入";

    private String passWord = "233";

    public User() {
        super();
    }

    public User(Integer id, String userName, String passWord) {
        super();
        this.id = id;
        this.userName = userName;
        this.passWord = passWord;
    }

    public Integer getId() {
        return id;
    }

    public String getUserName() {
        return userName;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

}

测试类代码加注解

/**使用注解注入属性*/
@Service("ioC_By_Annotation")
public class IoC_By_Annotation {
    @Resource
    private User user;

    public void setUser(User user) {
        this.user = user;
    }

    /**检查是否注入成功*/
    public boolean checkAttr() {
        if(user == null) {
            return false;
        }else {
            System.out.println("user:" + user.getId() + "|" + 
                    user.getUserName() + "|" + user.getPassWord());
        }
        System.out.println("正确!!!");
        return true;
    }
}

测试代码

public class IoC_Test {

    private ApplicationContext ctx;

    @Before
    public void load() {
        //读取applicationContext.xml配置文件
        ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    }

    @Test
    public void annotationTest() {
        IoC_By_Annotation ioc = (IoC_By_Annotation) ctx.getBean("ioC_By_Annotation");
        ioc.checkAttr();
    }

}

⑥ 通过配置静态工厂方法Bean注入

静态工厂代码

/**静态工厂*/
public class StaticFactory {

    public static Integer getId() {
        return 1;
    }

    public static String getName() {
        return "静态工厂";
    }

    public static User getUser() {
        return new User(1, "工厂User", "666");
    }
}

测试类代码

/** 通过静态工厂方式注入 */
public class IoC_By_StaticFactory {

    private Integer id;
    private String name;
    private User user;

    /** 检查是否注入成功 */
    public boolean checkAttr() {
        if (id == null) {
            return false;
        } else {
            System.out.println("id:" + id);
        }
        System.out.println("----------------------------");
        if (name == null) {
            return false;
        } else {
            System.out.println("name:" + name);
        }
        System.out.println("----------------------------");
        if (user == null) {
            return false;
        } else {
            System.out.println("user:" + user.getId() + "|"
                    + user.getUserName() + "|" + user.getPassWord());
        }
        System.out.println("----------------------------");
        System.out.println("全部正确!!!");
        return true;
    }

    /**需要为需要注入的属性设置set方法*/
    public void setId(Integer id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setUser(User user) {
        this.user = user;
    }

}

applicationContext.xml配置

    <!-- 配置静态工厂方法Bean 其实就是将工厂方法返回的数值配置成Bean -->
    <bean id="factory_id" class="com.bc.ioc.demo06.StaticFactory" factory-method="getId"/>
    <bean id="factory_name" class="com.bc.ioc.demo06.StaticFactory" factory-method="getName"/>
    <bean id="factory_user" class="com.bc.ioc.demo06.StaticFactory" factory-method="getUser"/>
    <!-- 注入对应的静态工厂方法Bean -->
    <bean id="ioC_By_StaticFactory" class="com.bc.ioc.demo06.IoC_By_StaticFactory">
        <property name="id" ref="factory_id"/>
        <property name="name" ref="factory_name"/>
        <property name="user" ref="factory_user"/>
    </bean>

测试代码

public class IoC_Test {

    private ApplicationContext ctx;

    @Before
    public void load() {
        //读取applicationContext.xml配置文件
        ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    }

    @Test
    public void staticFactoryTest() {
        IoC_By_StaticFactory ioc = (IoC_By_StaticFactory) ctx.getBean("ioC_By_StaticFactory");
        ioc.checkAttr();
    }
}

⑦ 通过实例工厂方法注入

与静态工厂区别在于实例工厂不是静态的,需要先new 一个实例工厂对象,才可以配置其方法,而new 的这个对象也由spring来管理

工厂代码

/**实例工厂*/
public class Factory {

    public Integer getId() {
        return 1;
    }

    public String getName() {
        return "实例工厂";
    }

    public User getUser() {
        return new User(1, "实例工厂User", "233");
    }
}

测试类代码

/**实例工厂注入*/
public class IoC_By_Factory {

    private Integer id;
    private String name;
    private User user;

    /** 检查是否注入成功 */
    public boolean checkAttr() {
        if (id == null) {
            return false;
        } else {
            System.out.println("id:" + id);
        }
        System.out.println("----------------------------");
        if (name == null) {
            return false;
        } else {
            System.out.println("name:" + name);
        }
        System.out.println("----------------------------");
        if (user == null) {
            return false;
        } else {
            System.out.println("user:" + user.getId() + "|"
                    + user.getUserName() + "|" + user.getPassWord());
        }
        System.out.println("----------------------------");
        System.out.println("全部正确!!!");
        return true;
    }

    /**需要为需要注入的属性设置set方法*/
    public void setId(Integer id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

applicationContext.xml配置

    <!-- 配置实例工厂Bean -->
    <bean id="factory" class="com.bc.ioc.demo07.Factory"/>
    <!-- 配置实例工厂方法Bean -->
    <bean id="f_id" factory-bean="factory" factory-method="getId"/>
    <bean id="f_name" factory-bean="factory" factory-method="getName"/>
    <bean id="f_user" factory-bean="factory" factory-method="getUser"/>
    <!-- 注入对应的实例工厂方法Bean -->
    <bean id="ioC_By_Factory" class="com.bc.ioc.demo07.IoC_By_Factory">
        <property name="id" ref="f_id"/>
        <property name="name" ref="f_name"/>
        <property name="user" ref="f_user"/>
    </bean>

测试类代码

public class IoC_Test {

    private ApplicationContext ctx;

    @Before
    public void load() {
        //读取applicationContext.xml配置文件
        ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    }

    @Test
    public void factoryTest() {
        IoC_By_Factory ioc = (IoC_By_Factory) ctx.getBean("ioC_By_Factory");
        ioc.checkAttr();
    }
}

AOP 面向切面编程

*如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

*如果目标对象实现了接口,可以强制使用CGLIB实现AOP

*如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

如何强制使用CGLIB实现AOP?

  • 添加CGLIB库,SPRING_HOME/cglib/*.jar
  • 在spring配置文件中加入
  • <aop:aspectj-autoproxy proxy-target-class="true"/>

JDK动态代理和CGLIB字节码生成的区别?

  • JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
public class JDKProxy implements InvocationHandler 
{
    private Object targetObject;//代理的目标对象
    public Object createProxyInstance(Object targetObject)
    {
        this.targetObject = targetObject;
        /*
        * 第一个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器
        * 第二个参数设置代理类实现的接口
        * 第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法
        */
        return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
                this.targetObject.getClass().getInterfaces(), this);
    }
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable 
    {
        return method.invoke(this.targetObject, args);//把方法调用委派给目标对象
    }
}
  • CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法最好不要声明成final。
public class CGLIBProxy implements MethodInterceptor 
{
    private Object targetObject;//代理的目标对象   
    public Object createProxyInstance(Object targetObject)
    {
        this.targetObject = targetObject;
        Enhancer enhancer = new Enhancer();//该类用于生成代理对象
        enhancer.setSuperclass(this.targetObject.getClass());//设置父类
        enhancer.setCallback(this);//设置回调用对象为本身
        return enhancer.create();
    }
    public Object intercept(Object proxy, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable
    {
        return methodProxy.invoke(this.targetObject, args);
    }

}

AOP中的概念

Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象.

joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)

Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.

Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知

  Interception Around环绕通知:JointPoint前后调用

  Before前置通知:JointPoint前调用

  After Returning后置通知:JointPoint后调用

  Throw异常通知:JoinPoint抛出异常时调用

  Introduction最终通知:JointPoint调用完毕后调用

Target(目标对象):代理的目标对象

Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.

Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.


Spring提供了两种切面声明方式,实际工作中我们可以选用其中一种:

基于注解方式声明切面。
基于XML配置方式声明切面。

基于注解方式声明切面

首先启动对@AspectJ注解的支持(<aop:aspectj-autoproxy/>):

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
     <aop:aspectj-autoproxy/>
    <bean id="orderservice" class="cn.itcast.service.OrderServiceBean"/>
    <bean id="log" class="cn.itcast.service.LogPrint"/>
</beans>
@Aspect
public class LogPrint 
{
    @Pointcut("execution(* cn.itcast.service..*.*(..))")
    private void anyMethod() {}//声明一个切入点    

    @Before("anyMethod() && args(userName)")//定义前置通知
    public void doAccessCheck(String userName) {    }   

    @AfterReturning(pointcut="anyMethod()",returning="revalue")//定义后置通知
    public void doReturnCheck(String revalue) { }

    @AfterThrowing(pointcut="anyMethod()", throwing="ex")//定义例外通知
    public void doExceptionAction(Exception ex) {   }

    @After("anyMethod()")//定义最终通知
    public void doReleaseAction() { }

    @Around("anyMethod()")//环绕通知
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable 
    {
        return pjp.proceed();
    }
}

基于基于XML配置方式声明切面

public class LogPrint 
{
    public void doAccessCheck() {}//定义前置通知
    public void doReturnCheck() {}//定义后置通知
    public void doExceptionAction() {}//定义例外通知
    public void doReleaseAction() {}//定义最终通知
    //定义环绕通知
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable 
    {
        return pjp.proceed();
    }
}
<bean id="orderservice" class="cn.itcast.service.OrderServiceBean"/>
<bean id="log" class="cn.itcast.service.LogPrint"/>
<aop:config>
  <aop:aspect id="myaop" ref="log">
    <aop:pointcut id="mycut" expression="execution(* cn.itcast.service..*.*(..))"/>
    <aop:before pointcut-ref="mycut" method="doAccessCheck"/>
    <aop:after-returning pointcut-ref="mycut" method="doReturnCheck "/>
    <aop:after-throwing pointcut-ref="mycut" method="doExceptionAction"/>
    <aop:after pointcut-ref="mycut" method=“doReleaseAction"/>
    <aop:around pointcut-ref="mycut" method="doBasicProfiling"/>
  </aop:aspect>
</aop:config>

Spring 事务

采用注解方式配置事务

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!– 采用@Transactional注解方式使用事务  -->
<tx:annotation-driven transaction-manager="txManager"/>
@Service @Transactional
public class PersonServiceBean implements PersonService 
{
}

采用基于XML方式配置事务

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<aop:config>
    <aop:pointcut id="transactionPointcut" expression="execution(* cn.itcast.service..*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPointcut"/>
</aop:config> 
<tx:advice id="txAdvice" transaction-manager="txManager">
      <tx:attributes>
        <tx:method name="get*" read-only="true" propagation="NOT_SUPPORTED"/>
        <tx:method name="*"/>
      </tx:attributes>
</tx:advice>

Spring事务的7种传播行为,5种隔离级别。

传播行为含义
PROPAGATION_REQUIRED支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

① PROPAGATION_REQUIRED

假如当前正要执行的事务不在另外一个事务里,那么就起一个新的事务

比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚。

这里写图片描述

② PROPAGATION_SUPPORTS

如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行

③ PROPAGATION_MANDATORY

必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常。

④ PROPAGATION_REQUIRES_NEW

这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。

这里写图片描述

⑤ PROPAGATION_NOT_SUPPORTED

当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。

⑥ PROPAGATION_NEVER

不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,那么ServiceB.methodB就要抛出异常了。

⑦ PROPAGATION_NESTED

理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。
而Nested事务的好处是他有一个savepoint。


隔离级别含义
ISOLATION_DEFAULT这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应
ISOLATION_READ_UNCOMMITTED这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读
ISOLATION_READ_COMMITTED保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读
ISOLATION_REPEATABLE_READ这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
ISOLATION_SERIALIZABLE这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读

脏读:一个事务读取到另一事务未提交的更新新据。

不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。

幻读:一个事务读取到另一事务已提交的insert数据。


参考文献

http://baike.baidu.com/item/spring

http://v.itcast.cn/course/41.html

http://blog.csdn.net/voyage_mh1987/article/details/5755729

http://blog.csdn.net/xiaohai0504/article/details/6832990

http://blog.csdn.net/qq_32588349/article/details/51554379

http://blog.csdn.net/u011285162/article/details/19247711

http://blog.csdn.net/paincupid/article/details/48185597

http://blog.csdn.net/it_wangxiangpan/article/details/24180085

http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/overview.html

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐