Spring-02 Spring IoC和AOP使用扩展

一、多种方式实现依赖注入

1、设值注入

  • 通过setter访问器实现了对属性的赋值,这种方式称为设值注入。

2、构造注入

  • 构造方法参数类型为对象

UserServiceImpl.java

public class UserServiceImpl implements UserService {
	private UserDao userDao;
	
	//无参构造
	public UserServiceImpl{}

	//带参构造
	public UserServiceImpl(UserDao userDao){
		this.userDao=userDao;
	}
	
	public void setuDao( UserDao uDao) {
			this.uDao = uDao;
	}
}

applicationContext.xml

	<!--  定义UserDao对象,并定义id为userDao-->
	<bean id="userDao" class="cn.bdqn.dao.impl.UserDaoImpl"/>
	
	<!--定义UserService对象,并指定id为userService  -->
	<bean id="userService" class="cn.bdqn.service.impl.UserServiceImpl">
		<!-- 通过定义的单参构造方法为userService的dao属性赋值 -->
		<constructor-arg>
			<!--  引用id为userDao的dao对象-->
			<ref bean="userDao"/>
		</constructor-arg>
	</bean>	
  • 构造方法为多参且为基本数据类型
	<bean id="zhangGa" class="cn.bdqn.demo.Greeting">
		<constructor-arg index="0" value="张嘎" type="java.lang.String"/>
		<constructor-arg index="1" value="三天不打鬼子,手都痒痒" />
	</bean>

注:
(1) 一个元素表示构造方法的一个参数,且使用时不区分顺序,当构造方法的参数出现混淆无法区分时,可以通过的index属性指定该参数位置索引,位置从0开始,type属性用来指定参数的类型,避免字符串与基本数据类型的混淆。

(2) 构造注入的时效性好,在对象实例化时就得到所依赖的对象, 便于在对象的初始方法中使用依赖对象;但受限于方法重载的形式,使用灵活性不足。设值注入使用灵活,但时效性不足,并有大量的getter/setter访问器增加了类的复杂性,Spring并不倾向于某种方式,根据情况选择。

3、使用p命名空间实现属性注入

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

例: 使用传统的<property>子元素配置的代码如下

Greeting.java

public class Greeting {

	private String person;
	private String words;
	
	public String getPerson() {
		return person;
	}

	public void setPerson(String person) {
		this.person= person;
	}

	public String getWords() {
		return words;
	}

	public void setWords(String words) {
		this.words = words;
	}

	public Greeting(String person,String words){
		this.person=person;
		this.words=words;
	}
	
	public Greeting(){}
	
	public void say(){
		System.out.println(this.name+"说:"+this.words);
	}	
}

applicationContext.java

	<bean id="zhangGa" class="cn.bdqn.demo.Greeting">
		<!--属性赋值需要使用很多的重复标签property-->
		
		<!--为名称为person的属性赋值-->
		<property name="person">
			<value>张嘎</value>
		</property>
		
		<!--为名称为words的属性赋值-->
		<property name="words">
			<value>三天不打鬼子,手都痒痒!</value>
		</property>
	</bean>

使用p命名空间实现属性注入

	<!-- 使用p命名空间实现属性注入 -->
	<!---声明一个User类型Bean组件->
	<bean id="user" class="cn.bdqn.pojo.User"
		p:userName="张三" p:userCode="00001" p:address="北京市东城区"/>
		
	<!---声明UserDao->
	<bean id="userDao" class="cn.bdqn.dao.impl.UserDaoImpl"/>
		
	<!--声明UserServiceImpl组件,且使用p命名空间方式为uDao属性赋值-->
	<bean id="userService" class="cn.bdqn.service.impl.UserServiceImpl"
		p:uDao-ref="userDao"/>

注:
(1)对于直接量(基本数据类型、字符串)属性,使用方式如下

  • 语法: p:属性名=“属性值”

(2)对于引用Bean的属性

  • 语法: p:属性名-ref=“Bean的id”

4、注入不同数据类型

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

	<property name="age">
		<value="18">
	</property>

注: 如果属性值中包含了XML的特殊字符(<、>、&、’、"),则注入时需要进行处理,通常采用两种方式:
第一种:使用<![CDATA[属性值]]>如<![CDATA[P&G]]>
第二种:把特殊字符替换为实体引用,如<value>P&amp;G</value>
在XML中5个预定义的实体引用:
在这里插入图片描述
(2)引用其他Bean组件

  • 直接使用ref属性
    例: <property name="uDao" ref="userDao"/>

  • 使用ref子元素的bean属性:
    例:

	<!--使用ref子元素引用bean组件  使用ref元素的bean属性引用指定bean的id-->
	 <property name="uDao">
		<ref bean="userDao"/>
	</property>

注:ref元素中的bean属性指定要引用bean的id

  • 使用ref元素的local属性
	<!--使用ref子元素引用bean组件 ,使用ref元素的local属性引用指定bean的id-->
	 <property name="uDao" ref="userDao">
		<ref local="userDao"/>
	</property> 

注:local属性与bean属性都是指定要引用的bean的id,区别在于Spring配置是可拆分的;而使用local属性只能在同一个配置文件中检索bean的id,而使用bean属性可以在其他配置文件中检索id。

(3)使用内部Bean

<!-- 内部bean -->
<property name="uDao">
	<bean class="cn.bdqn.dao.impl.UserDaoImpl"/>
</property> 

注:此方式适用于该bean组件仅在一处使用

(4)注入集合类型的属性

  • 对于list或数据类型的属性

例:

	<property name="hobbies">
    	<list>
    		<value>足球</value>
   			<value>篮球</value>
   		</list>
   	</property>

注: 标签中可以使用、等标签注入集合元素

  • 对于Set类型的属性

例:

	<property name="hobbies">
    	<set>
    		<!--定义set中的元素-->
    		<value>足球</value>
    		<value>篮球</value>
    	</set>
   	</property>
  • 对于Map类型的属性

例:

  <property name="hobbies">
    	<map>
    	    <!--定义map中的键值对-->
    	    <entry>
	    		<key><value>football</value></key>	
	    		<value>足球</value>			
   		    </entry>
   		    
   		    <entry>
	   			<key><value>basketball</value></key>	
	   			<value>篮球</value>			
    		 </entry>
    	</map>
    </property>

**注:**如果map中的键或值是Bean对象,可以将<value>更改为<ref>

  • 对于Properties类型的属性

例:

	<property name="hobbies">
		<props>
			<!--定义Properties中的键值对-->
			<prop key="football">足球</prop>
			<prop key="basketball">篮球</prop>
		</props>
	</property>

注: Properties中的键和值通常都为字符串类型

(5)注入null和空字符串值

  • 注入空字符串
	<property name="email"><value></value></property>
  • 注入null值
	<property name="email"><null/></property>

二、其他增强类型

1、异常抛出增强

  • 特点: 在目标方法抛出异常时织入增强处理。使用异常抛出增强,可以为各功能模块提供统一的、可拔插的异常处理方案。

  • 注: 使用<aop:after-throwing>元素可定义异常抛出,可以为增强方法声明相关类型的参数,并通过throwing属性指定该参数名称,Spring会为其注入从目标方法抛出的异常实例。

  • 例:

	<!-- 声明增强方法所在的bean -->
	<bean id="theLogger" class="cn.bdqn.aspect.ErrorLogger"/>
	<!-- 配置切面 -->
	<aop:config>
		<!--定义切入点  -->
		<aop:pointcut id="pointcut" 
			expression="execution(* cn.bdqn.service.UserService.*(..))"/>
		<!--引用包含增强方法的Bean  -->
		<aop:aspect ref="theLogger">
			<!--将afterThrowing()方法定义为异常抛出增强并引用pointcut切入点  -->
			<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
		</aop:aspect>
	</aop:config>

2、最终增强

  • 特点:无论方法抛出异常还是正常退出,该增强都会得到执行,类似于异常处理机制中finally块的作用,一般用于释放资源。
  • 例:
	<!--最终增强 -->
	<!-- 声明增强方法所在的bean -->
	<bean id="theLogger1" class="cn.bdqn.aspect.AfterLogger"></bean>
	<!-- 配置切面 -->
	<aop:config>
		<!--定义切入点  -->
		<aop:pointcut id="pointcut" 
			expression="execution(* cn.bdqn.service.UserService.*(..))"/>
		<!--引用包含增强方法的Bean  -->
		<aop:aspect ref="theLogger1">
			<!--将afterLogger()方法定义为最终增强并引用pointcut切入点  -->
			<aop:after method="afterLogger" pointcut-ref="pointcut"/>
		</aop:aspect>
	</aop:config>

3、环绕增强

  • 特点:环绕增强在目标方法的前后都可以织如增强处理,环绕增强是功能最强大的增强处理,Spring把目标方法的控制权全部交给了它。在环绕增强处理中,可以获取或修改目标方法的参数、返回值,可以对它进行异常处理,甚至可以决定目标方法是否被执行。
  • 例:
	<!-- 环绕增强 -->
	<bean id="theLogger3" class="cn.bdqn.aspect.AroundLogger"></bean>
	<aop:config>
		<aop:pointcut id="pointcut"
			expression="execution(* cn.bdqn.service.UserService.*(..))"/>
		<aop:aspect ref="theLogger3">
			<!--将aroundLogger()方法定义为胡环绕增强并引用pointcut切入点  -->	
			<aop:around method="aroundLogger" pointcut-ref="pointcut"/>
		</aop:aspect>	
	</aop:config>
  • 注:使用<aop:around>元素可以定义环绕增强。通过为增强方法声明ProceedingJoinPoint 类型的参数,可以获得连接点信息,所用方法与JoinPoint 相同。ProceedingJoinPointJoinPoint 的子接口,其不但封装目标方法及其入参数组,还封装了被代理的目标对象,通过它的proceed() 方法可以真正的调用目标方法,从而达到对连接点的完全控制。

三、使用注解实现IoC的配置

1、使用注解定义Bean
代码如下:

	//通过注解定义了一个名称为userDao的对象
	@Component("userDao")
	public class UserDaoImpl implements UserDao {	
		@Override
		public void save(User user) {
			System.out.println("保存用户信息");
		}
	}

注:
(1)@Component("userDao")等效xml配置文件中的

	<bean id="userDao" class="cn.bdqn.dao.impl.UserDaoImpl"/>

(2)除了@Component,spring还提供了3个特殊的注解

  • @Repository 用于标注DAO类
  • @Service 用于标注业务类
  • @Controller 用于标注控制器类

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

  • 特点: @Autowired采用按类型匹配的方式为属性自动装配合适的依赖对象,即容器会查找和属性类型相匹配的Bean组件,并自动为属性注入。
  • 例:
	/* 声明接口类型的引用和具体实现类解耦合 ,使用@Autowired为dao属性
		注入所依赖的对象,Spring将直接对dao属性进行赋值
	 */
	@Autowired
	private UserDao uDao;	//用户dao
  • 注:

(1)若容器中有一个以上类型相匹配的Bean组件时,则可以使用@Qualifier指定所需要的Bean的名称。

	@Autowired
	@Qualifier("userDao")
	private UserDao uDao;	//用户dao

(2)@Autowired 注解也可以对方法的入参进行标注

	@Autowired
	public void setuDao(@Qualifier("userDao") UserDao uDao) {
		this.uDao = uDao;
	}

(3)也可用于构造方法,实现构造注入

	//带参构造
	@Autowired
	public UserServiceImpl(@Qualifier("userDao")UserDao uDao){
		this.uDao=uDao;
	}

(4)使用@Autowired 注解进行装配时,如果找不到相匹配的Bean组件,Spring容器会抛出异常,此时如果依赖不是必须的,为避免抛出异常,可以将required属性设置为false。required默认为true,即必须找到匹配的 Bean完成装配,否则抛出异常。

	@Autowired(required=false)
	@Qualifier("userDao")
	private UserDao uDao;	//用户dao

(5)如果对类中集合类型的成员变量或方法入参使用@Autowired 注解,Spring容器会将所有和集合中元素类型匹配的Bean组件都注入进来。

	public class TackQueue{
		@Autowired(required=false)
		private List<Job> toDoList;
	}

注: Spring会将Job类型的Bean组件都注入给toDoList属性

3、加载注解定义的Bean

	//在Spring配置文件中添加对context命名空间的声明
	<!-- 扫描包中标注注解的类 -->		
	<context:component-scan base-package="cn.bdqn.service,cn.bdqn.dao"/>

注:base-package属性指定了需要扫描的基准包,多个包名以逗号隔开;Spring会扫描这些包中的类,获取Bean的定义信息。

4、使用Java标准注解完成装配

  • 概述: 使用@Resource注解完成实现组件装配,该标准注解也能对类的成员变量或方法入参提供注入功能。
  • 例:
	//为dao属性注入名为userDao的Bean
	@Resource(name="userDao")
	private UserDao uDao;	//用户dao
  • 注: 如果没有显式的指定Bean的名称,@Resource注解将根据字段名或者setter方法名产生的默认名称; 如果注解应用于字段,将使用字段名作为Bean的名称。
  • 例:
	//查找名为uDao的Bean,并注入给uDao属性
	@Resource
	private UserDao uDao;	//用户dao
  • 注:如果注解引用于setter方法,Bean的名称就是通过setter方法得到的属性名。
  • 例:
	//查找名为uDao的Bean,并注入给setter方法
	@Resource
	public void setuDao( UserDao uDao) {
		this.uDao = uDao;
	}
  • 注:如果没有显式的指定Bean的名称,且无法找到与Bean名称匹配的Bean组件,@Resource注解会由按名称查找的方式自动变为按类型匹配的方式进行装配。

四、使用注解定义切面

1、AspectJ简介

  • 概念: 是一个面向切面的框架,它扩展了java语言,定义了AOP语法,能够在编译器提供代码的织入,所以它有一个专门的编译器用来生成遵守字节码规范的Class文件。
  • 作用: Spring通过集成AspectJ实现了以注解的方式定义切面,大大减少了配置文件的工作量;此外,因为Java的反射机制无法获取方法参数名,Spring还需要利用轻量级的字节码处理框架asm (已集成在Spring Core模块中) 处理@AspectJ中所描述的方法参数名。

2、使用注解标注切面

  • 使用注解定义切面
	@Aspect
	public class UserServiceLogger {
		//省略
	}
  • 使用@Before注解定义前置增强
	@Before("execution(* cn.bdqn.service.UserService.*(..))")
	public void before(JoinPoint jp){
	     System.out.println("调用"+jp.getTarget()+"的"+jp.getSignature().getName()+"方法。方法入参:"
			+Arrays.toString(jp.getArgs()));
	}
  • 使用@AfterReturning注解定义后置增强
	//代表后置增强的方法
	@AfterReturning(pointcut="execution(* cn.bdqn.service.UserService.*(..))",
					returning="returnValue")
	public void afterReturning(JoinPoint jp,Object returnValue){
		System.out.println("调用"+jp.getTarget()+"的"+jp.getSignature().getName()
					+"方法。方法返回值:"+returnValue);
	}

注:
(1)对于后置增强,还可以定义一个参数用于接收目标方法的返回值,需要注意的是,必须在@AfterReturning注解中通过returning属性指定该参数的名称,Spring会将目标方法的返回值赋值给指定名称的参数。

2)对于切入点相同的情况,可以定义切入点复用。切入点表达式使用@Pointcut注解来表示,而切入点签名则需通过一个普通的方法定义来提供,作为切入点签名的方法必须返回void类型,切入点定义后,就可以使用“pointcut()”切面进行使用。
例:

	@Pointcut("execution(* cn.bdqn.service.UserService.*(..))")
	public void pointcut(){}
	
	@Before("pointcut()")	//前置增强处理,引用定义好的切入点
	public void before(JoinPoint jp){
		System.out.println("调用"+jp.getTarget()+"的"+jp.getSignature().getName()+"方法。方法入参:"
				+Arrays.toString(jp.getArgs()));
	}

(3)切面定义完成后,还需要在Spring配置文件中完成织入工作

	//引入aop命名空间
	<!-- 配置切面实例 -->			
	<bean class="cn.bdqn.aspect.UserServiceLogger"/>
	<!-- 启用对@AspectJ注解的支持 -->
	<aop:aspectj-autoproxy/>

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

  • 异常处理增强:使用@AfterThrowing注解
	/**
 	* 通过注解实现异常抛出增强
 	*/
	@Aspect
	public class ErrorLogger {
		private static final Logger log=Logger.getLogger(ErrorLogger.class);

		@AfterThrowing(pointcut="execution(* cn.bdqn.service.UserService.*(..))",
				throwing="e")
		public void afterThrowing(JoinPoint jp,RuntimeException e){
			log.error(jp.getSignature().getName()+"方法有异常"+e);
		}	
	}
  • 使用@After实现最终增强
	/**
 	* 使用注解实现最终增强
	 * @author 14062
 	*
	 */
	@Aspect
	public class AfterLogger {
		private static final Logger log=Logger.getLogger(AfterLogger.class);
		@After("execution(* cn.bdqn.service.UserService.*(..))")
		public void afterLogger(JoinPoint jp){
			log.error(jp.getSignature().getName()+"方法结束执行");
		}
	}
  • 使用@Around注解实现环绕增强
	/**
 	* 使用@Around注解实现环绕增强
 	*/
	@Aspect
	public class AroundLogger {
		private static final Logger log=Logger.getLogger(AfterLogger.class);
		@Around("execution(* cn.bdqn.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 (Exception e) {
				log.error(jp.getSignature().getName()+"方法发生异常"+e);
				throw e;
			}finally
			{
				log.info(jp.getSignature().getName()+"方法结束执行");
			}
		
		}
	}

4、Spring的切面配置小结
如果项目采用JDK5.0以上版本,可以考虑使用 @AspectJ 注解方式,减少配置的工作量;如果不愿意使用注解或项目采用的JDK版本较低而无法使用注解,则可以选择使用<aop:aspect>配合普通JavaBean的形式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值