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,所属的类的应用id为orderItem-->
<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&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 增强类型
- 前置增强:在原对象的某方法之前插入的增强处理为前置增强,
- 后置增强:在原对象的某方法正常执行完以后插入的增强处理为后置增强
- 异常抛出增强:特点是在目标方法抛出异常时织入增强处理,使用异常抛出增强,可以为各功能提供统一的、可拔插的异常处理方案。
- 最终增强:特点是无论方法抛出异常还是正常退出,该增强都会得到执行,类似于异常处理机制中的finally块的作用,一般用于释放资源。使用最终增强,可以为各功能提供统一的、可拔插的异常处理方案。
- 环绕增强:在目标方法的前后都可以织入增强处理。环绕增强是功能最强大的的增强处理,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的形式。