依赖注入(DI)--手工装配
Spring依赖注入有构造器注入以及setter方法注入两种常用方式,注入依赖对象可以采用手工装配和自动装配两种手段,但是推荐使用的是手动装配,自动装配无法掌握注入的结果。
既然说是手工装配,这里先得创建一个对象类,声明几个不同类型的成员,来看看如何手动装配的
public class User {
private long id;
private String name;
private int age;
public User() {
}
public User(long id, String name, int age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
}
使用Xml配置注入依赖对象,在xml配置文件中,创建bean节点,在其中配置若使用的是带参构造器配置,需要在<constructor-arg>标签配置,需要注意的就是数据类型,基本数据类型则直接写类型和参数即可,如果是引用类型则需要加上包的路径如:java.lang.Long;而若是使用setter方法配置则在<property>标签中配置属性的名称以及属性值。
<bean id="orderService" class="cn.com.chongking.People">
<constructor-arg index=“0” type=“long” value=“1”/>//构造器注入(带参构造函数第1个值)
<constructor-arg index=“1” type=“java.lang.String” value=“李四”/>//构造器注入(带参构造函数第2个值)
<constructor-arg index=“2” type=“int” value=“22”/>//构造器注入(带参构造函数第3个值)
<property name=“name” value=“李四”/>//属性setter方法注入
</bean>
除了可以在xml文件中配置,还可以通过注解配置,虽然是通过注解配置,但是在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/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:annotation-config/>
</beans>
<!-- 命名空间 -->
xmlns:context="http://www.springframework.org/schema/context"
<!-- 约束文件的引入 -->
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
<!-- 使用注解的声明 -->
<context:annotation-config/>
在java代码中使用@Autowired或@Resource注解方式进行装配,@Autowired注解是按名称装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false @Autowired (required=false)。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。如下:
@Autowired
@Qualifier("personDaoBean")
private PersonDao personDao;
@Resource注解和@Autowired一样,也可以标注在字段或属性的setter方法上,但它默认按名称装配。名称可以通过@Resource的name属性指定,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。
@Resource(name=“personDao”)
private PersonDao personDao;//用于字段上
如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
@Autowired
private PersonDao personDao;//用于字段上(属性可不用Setter方法)
@Autowired
public void setOrderDao(OrderDao orderDao) {//用于属性的setter方法上
this.orderDao = orderDao;//可省略
}
自动扫描
前面的例子我们都是使用XML的bean定义来配置组件。在一个稍大的项目中,通常会有上百个组件,如果这些这组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便。spring2.5为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进spring容器中管理。它的作用和在xml文件中使用bean节点配置组件是一样的。要使用自动扫描机制,我们需要打开以下配置信息:
<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.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.ozc"/>
</beans>
其中base-package为需要扫描的包(含子包)。
@Service用于标注业务层组件、 @Controller用于标注控制层组件(如struts中的action)、@Repository用于标注数据访问组件,即DAO组件。而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
AOP概述
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面是横切性关注点的抽象(切面就是一个关注点的模块化,譬如:事务管理、日志记录、权限管理都是所谓的切面).
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)
Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.
Advice(通知):所谓通知是指拦截到joinpoint(方法)之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
Target(目标对象):要代理的目标对象
Weave(织入):指将aspects (切面)应用到target对象并导致proxy对象创建的过程称为织入.
AOP图解
Spring--AOP编程
要进行AOP编程,首先我们要在spring的配置文件中引入aop命名空间:
<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-3.1.xsd
<!-- xsd约束文件 -->
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
</beans>
同样的AOP的配置方式一样有xml和注解两种配置方式,相似的是都需要一个切面工具类。
注解配置,需要引入AOP的命名空间在配置以下配置
<!-- 配置AOP注解配置-->
<aop:aspectj-autoproxy/>
<bean id="userInterceptor“ class="com.ozc.common.UserInterceptor"/>
<bean id="userService" class="com.ozc.service.UserService"/>
@Aspect
@Component
public class UserInterceptor {
@Pointcut("execution (* cn.com.chongking.aop.annotation.UserServiceBean.*(..))")
private void anyMethod() {
}// 声明一个切入点(定义要拦截的方法)
// && args(name)意思是:除了要满足上面的execution (*
// cn.com.chongking.aop.annotation.UserServiceBean.*(..))之外还要满足这个方法中的
// 参数个数是一个;参数类型是String
@Before("anyMethod() && args(name)")
// anyMethod是切入点的名称(就是上面的切入点方法名);args(name)和下面方法中的name一致
public void doAccessCheck(String name) {
System.out.println("前置通知:" + name);
}
@AfterReturning(pointcut = "anyMethod()", returning = "user")
public void doAfterReturning(UserInfo user) {// 定义后置通知
System.out.println("后置通知:" + user);
}
@After("anyMethod()")
public void doAfter() {
System.out.println("最终通知");
}
@AfterThrowing(pointcut = "anyMethod()", throwing = "e")
public void doAfterThrowing(Exception e) {// 例外通知
System.out.println("例外通知:" + e);
}
@Around("anyMethod()")
// 环绕通知(格式基本上是固定的)
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// if(){//判断用户是否在权限
System.out.println("进入环绕通知方法");
Object result = pjp.proceed();
System.out.println("退出环绕通知方法");
// }
return result;
}
}
使用xml配置声明切面
<bean id="departInterceptorBean" class="cn.com.chongking.aop.xml.DepartInterceptor"/>
<bean id="departServiceBean" class="cn.com.chongking.aop.xml.DepartService"/>
<!-- AOP配置 -->
<aop:config>
<!-- 定义一个切面 -->
<aop:aspect id="asp" ref="departInterceptorBean">
<aop:pointcut id=“pc1”
expression=“execution (* com.zrcx.service.DepartService.*(..))"/>
<!-- 定义前置通知;通知方法名:doAfter;引用的切入点:pc1-->
<aop:before pointcut-ref=“pc1" method="doBefore"/>
<!-- 定义后置通知 -->
<aop:after-returning pointcut-ref=“pc1" method="doAfterReturning"/>
<!-- 定义例外通知 -->
<aop:after-throwing pointcut-ref=“pc1” method="doAfterThrowing"/>
<!-- 定义最终通知 -->
<aop:after pointcut-ref=“pc1” method="doAfter"/>
<!-- 定义环绕通知 -->
<aop:around pointcut-ref=“pc1” method="doAround"/>
</aop:aspect>
</aop:config>
DepartInterceptor.java
public class DepartInterceptor {
// 切面(注意下面这些方法没有参数)
public void doAccessCheck() {// 前置通知
System.out.println("前置通知:");
}
public void doAfterReturning() {// 定义后置通知
System.out.println("后置通知:");
}
public void doAfter() {
System.out.println("最终通知");
}
public void doAfterThrowing() {// 例外通知
System.out.println("例外通知:");
}
// 环绕通知(格式基本上是固定的)
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// if(){//判断用户是否在权限
System.out.println("进入环绕通知方法");
Object result = pjp.proceed();
System.out.println("退出环绕通知方法");
// }
return result;
}
}