Spring总结-2

总结要点

Spring的三种注入方式

Spring使用注入方式,为什么使用注入方式,这系列问题实际归结起来就是一句话,Spring的注入和IoC(本人关于IoC的阐述)反转控制是一回事。下面我们详细来了解一下
AD:
1. 接口注入(不推荐)
2. getter,setter方式注入(比较常用)
3. 构造器注入(死的应用)
关于getter和setter方式的注入

autowire="defualt" 
autowire=“byName”  
autowire="bytype"  

例如:有如下两个类需要注入
第一个类:

package org.jia;  

  public class Order {  
      private String orderNum;  
      @SuppressWarnings("unused")  
      private OrderItem orderitem;  

      public OrderItem getOrderitem() {  
          return orderitem;  
     }  

     public void setOrderitem(OrderItem orderitem) {  
         this.orderitem = orderitem;  
     }  

     public String getOrderNum() {  
         return orderNum;  
     }  

     public void setOrderNum(String orderNum) {  
         this.orderNum = orderNum;  
     }       
 }

第二个类:

package org.jia;  

 public class OrderItem {  
     private String orderdec;  

     public String getOrderdec() {  
         return orderdec;  
     }  

     public void setOrderdec(String orderdec) {  
         this.orderdec = orderdec;  
     }  
 }

常用getter&&setter方式介绍
方式第一种注入:

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> 

<beans> 
    <bean id="orderItem" class="org.jia.OrderItem"> 
        <property name="orderdec" value="item00001"></property> 
    </bean> 
    <bean id="order" class="org.jia.Order" > 
        <!-----注入变量 名字必须与类中的名字一样-------> 
        <property name="orderNum" value="order000007"></property> 
         <!--注入对象 名字为orderitem,所属的类的应用idorderItem--> 
        <property name="orderitem" ref="orderItem"></property> 

    --></bean> 
</beans>

方式第二种注入:byName

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">  
<beans>  
    <!--此时的id就必须与Order.java中所定义的OrderItem的对象名称一样了,不然就会找不到-->  
    <bean id="orderitem" class="org.jia.OrderItem">  
        <property name="orderdec" value="item00001"></property>  
    </bean>  
    <bean id="order" class="org.jia.Order"<span style="color:ff0000;"> autowire="byName"</span>>  
        <property name="orderNum" value="order000007"></property>  
    </bean>  
</beans>

方式第三种注入:byType

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">  
<beans>  
    <!--按照byType注入则就与id没有关系,可以随便定义id !!!但是不能出现多个此类的id-->  
    <bean id="orderitdfadafaem" class="org.jia.OrderItem">  
        <property name="orderdec" value="item00001"></property>  
    </bean>  
    <bean id="order" class="org.jia.Order" <span style="color:ff0000;">autowire="byType"</span>>  
        <property name="orderNum" value="order000007"></property>  
    </bean>  
</beans> 
autowire="constructor"

需要在Order.java中加入一个构造器

public Order(OrderItem item )  
{   
      orderitem = item;  
}
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> 
<beans> 
    <bean id="orderItem" class="org.jia.OrderItem"> 
        <property name="orderdec" value="item00001"></property> 
    </bean> 
    <bean id="order" class="org.jia.Order" autowire="constructor"> 
        <property name="orderNum" value="order000007"></property> 
    </bean> 
</beans>

三种注入方式比较

接口注入:

    接口注入模式因为具备侵入性,它要求组件必须与特定的接口相关联,因此并不被看好,实际使用有限。

Setter 注入:

    对于习惯了传统 javabean 开发的程序员,通过 setter 方法设定依赖关系更加直观。
    如果依赖关系较为复杂,那么构造子注入模式的构造函数也会相当庞大,而此时设值注入模式则更为简洁。
    如果用到了第三方类库,可能要求我们的组件提供一个默认的构造函数,此时构造子注入模式也不适用。

构造器注入:

    在构造期间完成一个完整的、合法的对象。
    所有依赖关系在构造函数中集中呈现。
    依赖关系在构造时由容器一次性设定,组件被创建之后一直处于相对“不变”的稳定状态。
    只有组件的创建者关心其内部依赖关系,对调用者而言,该依赖关系处于“黑盒”之中。

总结

    Spring使用注入方式,为什么使用注入方式,这系列问题实际归结起来就是一句话,Spring的注入和IoC(本人关于IoC的阐述)反转控制是一回事。
    理论上:第三种注入方式(构造函数注入)在符合java使用原则上更加合理,第二种注入方式(setter注入)作为补充。
    实际上:我个人认为第二种注入方式(setter注入)可以取得更加直观的效果,在使用工作上有不可比拟的优势,所以setter注入依赖关系应用更加广泛。

使用p命名空间实现属性注入(通过setter()方法注入)

作用:使用p命名空间可以简化配置方法
特点:使用属性而不是子元素的的形式配置Bean的属性,从而简化了Bean的配置。

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
    <!--使用p命名空间注入属性值-->
    <bean id="zhanShenArmet" class="cn.homework06.model.Equip"
        p:name="战神头盔" p:type="头盔" p:speedPlus="2" p:attackPlus="4"
        p:defencePlus="6" />
    <bean id="lianHuanLoricae" class="cn.homework06.model.Equip"
        p:name="连环锁子甲" p:type="盔甲" p:speedPlus="6" p:attackPlus="4"
        p:defencePlus="15" />
    <bean id="bosiBoot" class="cn.homework06.model.Equip"
        p:name="波斯追风靴" p:type="靴子" p:speedPlus="8" p:attackPlus="2"
        p:defencePlus="3" />
    <bean id="lanMoRing" class="cn.homework06.model.Equip"
        p:name="蓝魔指环" p:type="指环" p:speedPlus="8" p:attackPlus="12"
        p:defencePlus="2" />
    <bean id="zhangsan" class="cn.homework06.model.Player"
        p:armet-ref="zhanShenArmet" p:loricae-ref="lianHuanLoricae"
        p:boot-ref="bosiBoot" p:ring-ref="lanMoRing" />
</beans>

注意:这里记得导入p命名空间的声明.

使用方式

  • 对于直接量(基本数据类型、字符串)
    p:属性名=”属性值”
  • 对于引用Bean的属性
    p:属性名-ref=”Bean的id”

注入不同的数据类型(对于设置注入和构造注入都适用)

1.注入直接量(基本数据类型、字符串)

    <!-- 使用<value>子元素注入字符串、基本数据类型及其包装类 -->
    <bean id="user" class="entity.User">
    <property name="userame">
        <value>张三</value>
    </property>
        <property name="age">
        <value>23</value>
    </property>
    </bean>

这里写图片描述

        <!-- 使用<![CDATA[]]>标记处理XML特 殊字符 -->
        <property name="specialCharacter1">
            <value><![CDATA[P&G]]></value>
        </property>
        <!-- 把XML特殊字符替换为实体引用 -->
        <property name="specialCharacter2">
            <value>P&amp;G</value>
        </property>

2.引用其他的Bean组件

<!--使用ref属性-->
    <bean>
        <property name="dao">
            <ref bean="userDao"/>
        </property>
    </bean>
<!--使用local属性-->
    <bean>
        <property name="dao">
            <ref local="userDao"/>
        </property>
    </bean>

虽然以上这两个属性都是用来指定Bean的id。但区别在于,使用local属性只能在同一个配置文件中检索Bean的id,而使用bean属性可以在其他配置文件中检索id
3.使用内部Bean

<!-- 定义内部Bean -->
        <property name="innerBean">
            <bean class="entity.User">
                <property name="username">
                    <value>Mr. Inner</value>
                </property>
            </bean>
        </property>

4.注入集合类型的属性

        <!-- 注入List类型 -->
        <property name="list">
            <list>
                <!-- 定义List中的元素 -->
                <value>足球</value>
                <value>篮球</value>
            </list>
        </property>
        <!-- 注入数组类型 -->
        <property name="array">
            <list>
                <!-- 定义数组中的元素 -->
                <value>足球</value>
                <value>篮球</value>
            </list>
        </property>
        <!-- 注入Set类型 -->
        <property name="set">
            <set>
                <!-- 定义Set或数组中的元素 -->
                <value>足球</value>
                <value>篮球</value>
            </set>
        </property>
        <!-- 注入Map类型 -->
        <property name="map">
            <map>
                <!-- 定义Map中的键值对 -->
                <entry>
                    <key>
                        <value>football</value>
                    </key>
                    <value>足球</value>
                </entry>
                <entry>
                    <key>
                        <value>basketball</value>
                    </key>
                    <value>篮球</value>
                </entry>
            </map>
        </property>
        <!-- 注入Properties类型 -->
        <property name="props">
            <props>
                <!-- 定义Properties中的键值对 -->
                <prop key="football">足球</prop>
                <prop key="basketball">篮球</prop>
            </props>
        </property>

5.注入null和空字符串

        <!-- 注入空字符串值 -->
        <property name="emptyValue">
            <value></value>
        </property>
        <!-- 注入null值 -->
        <property name="nullValue">
            <null/>
        </property>

AOP 增强类型

  1. 前置增强:在原对象的某方法之前插入的增强处理为前置增强,
  2. 后置增强:在原对象的某方法正常执行完以后插入的增强处理为后置增强
  3. 异常抛出增强:特点是在目标方法抛出异常时织入增强处理,使用异常抛出增强,可以为各功能提供统一的、可拔插的异常处理方案。
  4. 最终增强:特点是无论方法抛出异常还是正常退出,该增强都会得到执行,类似于异常处理机制中的finally块的作用,一般用于释放资源。使用最终增强,可以为各功能提供统一的、可拔插的异常处理方案。
  5. 环绕增强:在目标方法的前后都可以织入增强处理。环绕增强是功能最强大的的增强处理,Spring把目标方法的控制权全部交给了它。在环绕增强处理中,可以获取或者修改目标方法的参数、返回值,可以对它进行异常处理,甚至可以决定目标方法是否被执行。

使用注解实现loC的配置

1.使用注解定义Bean

package dao.impl;

import org.springframework.stereotype.Component;

import dao.UserDao;
import entity.User;
@Component("userDao")
//此处的@Component("userDao")的作用于在XML配置文件中编写   <bean id="userDao" class="dao.impl.UserDaoImpl " />等效
public class UserDaoImpl implements UserDao {

    @Override
    public void save(User user) {
        System.out.println("保存用户信息到数据库");
        //模拟发生异常
        throw new RuntimeException("发生错误异常");
    }

}
  • @Component:用于标注组件。
  • @Repository:用来标注DAO类。
  • @Service:用来标注业务类。
  • @Controller:用来标注控制器类。

2.使用注解实现Bean组件装配

package service.impl;


import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import dao.UserDao;

import entity.User;
import service.UserService;
@Service("userService")
public class UserServiceImpl implements UserService {
    @Autowired
    //@Autowired(required=false)  required默认为true,即必须找到匹配的
    //Bean完成装配,否则抛出异常。
    @Qualifier("userDao")
    private UserDao dao;
    //还可以使用setter()方法和构造方法注入
    //  public void setDao(@Qualifier("userDao") UserDao dao) {
    //      this.dao = dao;
    //  }
    //  
    //  public UserServiceImpl(){
    //      
    //  }
    //  public UserServiceImpl(@Qualifier("userDao") UserDao dao){
    //      
    //  }
    @Override
    public void addNewUser(User user) {
        dao.save(user);
    }

}

@Autowired采用类型匹配的方式为属性自动装配合适的依赖对象,即容器会查找和属性类型相匹配的Bean组件,并自动为属性注入。
若容器中有一个以上类型相匹配的Bean时,则可以使用@Qulifier指定所需的Bean的名称。

3.加载注解定义的Bean

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.2.xsd">
    <!-- 扫描包中注解标注的类 -->
    <context:component-scan base-package="service,dao" />
</beans>

使用Java标准注解完成装配

除了提供 @Autowired注解,Spring还支持使用JSR-250中定义的@Resource注解实现组件装配。(JSR的全称是 Java Specification Requests 是用来规范功能和接口的标准)

package service.impl;


import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import dao.UserDao;

import entity.User;
import service.UserService;
@Service("userService")
public class UserServiceImpl implements UserService {
    @Resource(name="userDao")
    //如果没有显示地指定Bean的名称,@Resource注解将根据字段名或者setter方法名产生默认的名称:如果注解与字段,将使用字段名作为Bean的名称
    //如果没有显示地指定Bean的名称,且无法找到与默认Bean名称匹配的Bean组件,@Resource注解会由按名称查找的方式自动变为按类型匹配的方式进行装配。
    private UserDao dao;


    @Override
    public void addNewUser(User user) {
        dao.save(user);
    }

}

使用注解定义切面

1.AspecJ是一个面向切面的框架,它扩展了Java语言,定义了AOP语法,能够在编译期提供代码的织入,所以他有一个专门的编译器用来生成遵守字节编码规范的Class文件。
2.在使用@AspecJ之前要保证,使用的JDK是5.0或其以上版本,否则无法使用注解技术。


/**
 * 使用注解定义切面
 */
@Aspect
public class UserServiceLogger {
    private static final Logger log = Logger.getLogger(UserServiceLogger.class);

    @Pointcut("execution(* service.UserService.*(..))")
    public void pointcut(){}

    @Before("pointcut()")
    public void before(JoinPoint jp) {
        log.info("调用" + jp.getTarget() + jp.getSignature().getName()
                + "方法。方法入参:" + Arrays.toString(jp.getArgs()));
    }

    @AfterReturning(pointcut = "pointcut()", returning = "returnValue")
    public void afterReturning(JoinPoint jp, Object returnValue) {
        log.info("调用" + jp.getTarget() + jp.getSignature().getName()
                + "方法。方法返回值:" + returnValue);
    }
<?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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
    <!-- 扫描包中注解标注的类 -->
    <context:component-scan base-package="service,dao" />
    <bean class="aop.UserServiceLogger "></bean>
    <aop:aspectj-autoproxy />
</beans>

使用注解定义其他类型的增强

package aop;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
/**
 *使用注解实现最终增强 
 */
@Aspect
public class AfterLogger {
    private static final Logger log=Logger.getLogger(ErrorLogger.class);

    @After("execution(* service.UserService.*(..))")
    public void afterLogger(JoinPoint jp){
        log.info(jp.getSignature().getName()+"方法执行结束。");
    }
}
package aop;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
/**
 * 注解实现异常抛出增强
 */
@Aspect
public class ErrorLogger {
    private static final Logger log=Logger.getLogger(ErrorLogger.class);

    @AfterThrowing(pointcut="execution(* service.UserService.*(..))",throwing="e")
    public void afterThrowing(JoinPoint jp,RuntimeException e){
        log.error(jp.getSignature().getName()+"方法发生异常:"+e);
    }
}
package aop;

import java.util.Arrays;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
/**
 *通过注解实现环绕增强
 */
@Aspect
public class AroundLogger {
    private static final Logger log = Logger.getLogger(ErrorLogger.class);
    @Around("execution(* service.UserService.*(..))")
    public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
        log.info("调用" + jp.getTarget() + "的" + jp.getSignature().getName()
                + "方法。方法入参:" + Arrays.toString(jp.getArgs()));
        try {
            Object result=jp.proceed();
            log.info("调用" + jp.getTarget() + "的" + jp.getSignature().getName()
                    + "方法。方法返回值:" + result);
            return result;
        } catch (Throwable e) {
            // TODO: handle exception
            log.error(jp.getSignature().getName()+"方法发生异常:"+e);
            throw e;
        }finally{
            log.info(jp.getSignature().getName()+"方法结束执行.");
        }
    }
}

简述Spring实现AOP的几种方式及各自的使用场合?

  • 通过接口实现、通过注解实现、通过schema形式将POJO方法定义为增强。
  • 通过接口实现增强处理是较低版本Spring AOP的做法,如果在一个使用低版本Spring AOP开发的项目上进行升级,可以考虑使用复用已经存在的增强类;如果项目采用JDK 5.0以上版本,可以考虑使用@AspectJ注解方式,减少配置的工作量;如果不愿意使用注解或项目采用的JDK版本较低无法使用注解,则可以选择使用配合普通JavaBean的形式。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章