Spring中Ioc和AOP扩展
1.构造注入
在HelloSpring案例中将HelloSpring类中的setter方法去掉,添加有参构造方法,编写带参构造方法后,Java虚拟机不再提供默认的无参构造方法,为了保证使用的灵活性,建议自行添加一个无参构造方法.
package cn.kgc.springdemo;
public class HelloSpring {
private String who;
// 通过构造方法设值注入
public HelloSpring(){}
public HelloSpring(String who) {
this.who = who;
}
public void print(){
System.out.println("Hello," + who +"!");
}
// 通过setter方法设值注入
/*public String getWho() {
return who;
}
public void setWho(String who) {
this.who = who;
}*/
}
同时将ApplicationContext.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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean元素声明spring创建的对象实例,id属性指定对象的唯一标识,方便程序的使用,class属性可以指定被声明的实例对应类j-->
<bean id="helloSpring" class="cn.kgc.springdemo.HelloSpring">
<!-- 设值注入-->
<!--指定被复制的属性名-->
<!--<property name="who">-->
<!--<!– 赋值内容 –>-->
<!--<value>SBX</value>-->
<!--</property>-->
<!-- 构造注入-->
<constructor-arg>
<value>SBX</value>
</constructor-arg>
</bean>
</beans>
1、一个<constructor-arg>元素表示构造方法的一个参数,且使用时不区分顺序。
2、通过<constructor-arg>元素的index 属性可以指定该参数的位置索引,位置从0 开始。
3、<constructor-arg>元素还提供了type 属性用来指定参数的类型,避免字符串和基本数
据类型的混淆。
构造注入与设值注入的优缺点
与传统的JavaBean的写法更相似,程序开发人员更容易理解、接受。
通过setter方法设定依赖关系显得更加直观、自然。
对于复杂的依赖关系,如果采用构造注入,会导致构造器过于臃肿,难以阅读。Spring在创建
Bean实例时,需要同时实例化其依赖的全部实例,因而导致性能下降。而使用设值注入,则能避免
这些问题。尤其是在某些属性可选的情况下,多参数的构造器更加笨重。
构造注入也不是绝对不如设值注入,在某些特定的场景下,构造注入比设值注入更优秀:
构造注入可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入。例如,组件中
其他依赖关系的注入,常常需要依赖于Datasource的注入。采用构造注入,可以在代码中清晰
地决定注入顺序。
对于依赖关系无须变化的Bean,构造注入更有用处。因为没有setter方法,所有的依赖关系全部在构造器内设定。因此,无须担心后续的代码对依赖关系产生破坏。
依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系。对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则。
综上所述,一般采用以设值注入为主,构造注入为辅的注入策略。对于依赖关系无须变化的的注入,尽量采用构造注入;而其他的依赖关系的注入,则考虑采用设值注入
2.使用p命名空间注入属性值
p 命名空间的特点:使用属性而不是子元素的形式配置Bean的属性,从而简化了配置代码.
对于直接量(基本数据类型、字符串)属性:p:属性名="属性值"
对于引用Bean的属性:p:属性名-ref="Bean的id"
使用前要先要在Spring配置文件中引入p命名空间
xmlns:p="http://www.springframework.org/schema/p"
使用p命名空间注入属性值
<bean id="user" class="entity.User" p:age="23" p:username="张三"
p:email="zhangsan@xxx.com" />
<bean id="userService" class="service.impl.UserServiceImpl"
p:dao-ref="userDao" />
使用p命名空间为业务Bean注入DAO对象
注入Bean类型的语法:p:属性名-ref="Bean的id
3.注入不同数据类型
首先创建一个TestEntity类来测试注入不同数据类型
package cn.kgc.entity;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class TestEntity {
private String specialCharacter1; //
private String specialCharacter2;
private User innerBean;
private List<String> list;
private String[] array;
private Set<String> set;
private Map map;
private Properties props;
private String emptyValue;
private String nullValue = "init value";
public void setSpecialCharacter1(String specialCharacter1) {
this.specialCharacter1 = specialCharacter1;
}
public void setSpecialCharacter2(String specialCharacter2) {
this.specialCharacter2 = specialCharacter2;
}
public void setInnerBean(User innerBean) {
this.innerBean = innerBean;
}
public void setList(List<String> list) {
this.list = list;
}
public void setArray(String[] array) {
this.array = array;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setProps(Properties props) {
this.props = props;
}
public void setEmptyValue(String emptyValue) {
this.emptyValue = emptyValue;
}
public void setNullValue(String nullValue) {
this.nullValue = nullValue;
}
public void showValue() {
System.out.println("特殊字符1: " + this.specialCharacter1);
System.out.println("内部Bean: " + this.innerBean.getUsername());
System.out.println("List属性: " + this.list);
System.out.println("数组属性[0]: " + this.array[0]);
System.out.println("Set属性: " + this.set);
System.out.println("Map属性: " + this.map);
System.out.println("Properties属性: " + this.props);
System.out.println("注入空字符串: " + this.emptyValue);
System.out.println("注入null值: " + this.nullValue);
// /* System.out.println("特殊字符2: " + this.specialCharacter2);
}
public void setMap(Map map) {
this.map = map;
}
}
其配置文件ApplicationContext.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: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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="testEntity" class="cn.kgc.entity.TestEntity">
<!--不可用-->
<!--使用CDATA处理xml中的特殊字符
<property name="specialCharacter1">
<value>![CDATA[P&G]]</value>
</property>-->
<!-- 配置特殊字符的值-->
<property name="specialCharacter1">
<value>P&G</value>
</property>
<property name="innerBean">
<bean class="cn.kgc.entity.User">
<property name="username" value="徐辽"/>
<property name="password" value="222"/>
</bean>
</property>
<!--配置list集合类型的值-->
<property name="list">
<list>
<value>足球</value>
<value>蓝球</value>
</list>
</property>
<!--配置数组类型的值-->
<property name="array">
<array>
<value>足球</value>
<value>蓝球</value>
</array>
</property>
<!--配置set类型的值-->
<property name="set">
<set>
<value>足球</value>
<value>蓝球</value>
</set>
</property>
<!--配置map类型的值-->
<property name="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>
<prop key="football">足球</prop>
<prop key="basketball">篮球</prop>
</props>
</property>
<!--赋值空字符串-->
<property name="emptyValue"><value></value></property>
<!--赋null值-->
<property name="nullValue"><null/></property>
</bean>
</beans>
4.增强处理
编写theLogger类运用增强处理实现日志的打印
package cn.kgc.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import java.util.Arrays;
public class TheLogger {
private static Logger log = Logger.getLogger(TheLogger.class);
// 前置增强
public void before(JoinPoint jp) {
log.info("调用" + jp.getTarget() + "类的" + jp.getSignature().getName() +
"方法, 方法参数:" + Arrays.toString(jp.getArgs()));
}
// 后置增强
public void afterReturning(JoinPoint jp, Object result) {
log.info("调用" + jp.getTarget() + "类的" + jp.getSignature().getName() +
"方法, 方法返回值:" + result);
}
// 最终增强
public void after(JoinPoint jp) {
log.info(jp.getSignature().getName() + "方法结束了");
}
// 异常抛出增强
public void afterThrowing(JoinPoint jp, Throwable e) {
log.info(jp.getSignature().getName() + "方法出现了异常" + e);
}
}
在ApplicationContext.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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean元素声明spring创建的对象实例,id属性指定对象的唯一标识,方便程序的使用,class属性可以指定被声明的实例对应类j-->
<bean id="helloSpring" class="cn.kgc.springdemo.HelloSpring">
<!-- 设值注入-->
<!--指定被复制的属性名-->
<!--<property name="who">-->
<!--<!– 赋值内容 –>-->
<!--<value>SBX</value>-->
<!--</property>-->
<!-- 构造注入-->
<constructor-arg>
<value>SBX</value>
</constructor-arg>
</bean>
<bean id="theLogger" class="cn.kgc.aop.TheLogger"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(public void print())"/>
<aop:aspect ref="theLogger">
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
常用增强处理类型
Spring AOP配置元素
5.使用注解实现IoC
使用注解实现IoC
注解方式将Bean的定义信息和Bean实现类结合在一起,Spring提供的注解有
@Component:实现Bean组件的定义
@Repository :用于标注DAO类
@Service :用于标注业务类
@Controller :用于标注控制器类
/* @Repository("userDao")与在XML配置文件中编写
<bean id="userDao"
class="dao.impl.UserDaoImpl" />
等效 */
@Repository("userDao")
public class UserDaoImpl implements UserDao {
…
}
使用@Autowired注解实现Bean的自动装配,默认按类型匹配,可以使用@Qualifier指定Bean的名称
// 可以对类的成员变量进行标注
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userDao")
private UserDao dao;
……
}
@Service("userService")
public class UserServiceImpl implements UserService {
private UserDao dao;
@Autowired // 也可以对方法的入参进行标注
public void setDao((@Qualifier("userDao") UserDao dao) {
this.dao = dao;
}
……
}
或者可以使用@Resource注解实现组件装配,默认按名称匹配
@Service("userService")
public class UserServiceImpl implements UserService {
// 为dao属性注入名为userDao的Bean
@Resource(name = "userDao")
private UserDao dao;
……
}
使用注解信息启动Spring容器,修改ApplicationContext.xml文件
<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/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<!-- 扫描包中注解标注的类 -->
<context:component-scan base-package="service,dao" />
</beans>
6.使用注解定义切面
AspectJ
面向切面的框架,它扩展了Java语言,定义了AOP 语法,能够在编译期提供代码的织入
@AspectJ
AspectJ 5新增的功能,使用JDK 5.0 注解技术和正规的AspectJ切点表达式语言描述切面
Spring通过集成AspectJ实现了以注解的方式定义增强类,大大减少了配置文件中的工作量
利用轻量级的字节码处理框架asm处理@AspectJ中所描述的方法参数名
使用注解定义前置增强和后置增强实现日志功能
@Aspect
@Before 前置增强
@AfterReturning 后置增强
@AfterThrowing 异常抛出增强
@After 最终增强
@Around 环绕增强
编写Spring配置文件(ApplicationContext.xml),完成切面织入
<aop:aspectj-autoproxy />:启用对于@AspectJ注解的支持