预讲知识点
Spring中只有两大核心技术:IOC&DI(控制反转&依赖注入)、AOP(面向切面编程)。
重点讲解依赖注入
具体内容
所谓的依赖注入指的就是利用配置文件的关系来决定类之间的饮用关系以及数据的设置操作。
3.1、构造方法注入
默认情况下如果在applicationContext.xml文件之中配置的程序都可以自动通过Spring容器加载时自动的进行实例化操作。但是自动进行初始化的时候调用的是类中的无参构造方法,而且通过反射机制应该知道如果类中提供无参方法一定要比提供有有参构造方法的实例化更加的容易。
但是在Spring里面简化了反射的处理机制,也就是说利用Spring中的动态特性可以直接明确调用构造方法传递参数。
范例:定义一个类
public class Dept {
private Integer deptno;
private String dname;
public Dept(Integer deptno, String dname) {
this.deptno = deptno;
this.dname = dname;
}
@Override
public String toString() {
return "部门编号"+this.deptno+"部门名称"+this.dname;
}
}
<bean id="dept" class="cn.mldn.vo.Dept">
此时类中没有提供无参构造方法,所以配置在applicationContext.xml文件中的<bean>无法正常使用,所以需要明确的调用类中的有参构造方法。
调用有参构造的写法
<bean id="dept" class="cn.edu.vo.Dept">
<constructor-arg “index=“0”” value="10"/>
<constructor-arg value="开发部"/>
</bean>
index是修改索引操作(不推荐用)
但是在Spring的配置里面它所支持的好处还远远不止直一点。如果用户有需要也可以加上(但是不推荐)
范例:ConstructorProperties(不推荐用)
@ConstructorProperties(value = { "paramDeptno","paramDname" })
public Dept(Integer deptno, String dname) {
this.deptno = deptno;
this.dname = dname;
}
<bean id="dept" class="cn.edu.vo.Dept">
<constructor-arg name="paramDeptno" value="10"/>
<constructor-arg name="paramDname" value="开发部"/>
</bean>
如果真使用构造方法,还是按照参数的类型和顺序编写会比较方便
3.2、利用setter注入
在正常编写简单Java类的过程一定要提供有无参构造,甚至许多自己定义的工具类也都会提供有无参构造,所以使用构造方法的注入操作并不是我们所喜欢的方式。
实际上任何类都会通过setter设置属性,这一点在简单Java 类上表现的特别明显。
public void setDname(String dname) {
this.dname = dname;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public Integer getDeptno() {
return deptno;
}
在传统的操作之中一定是首先实例化Dept类对象,而后调用setter设置内容,但是现在可以利用Spring动态设置内容。
(以后都有setter和getter,只不过省略了)
范例:观察Spring的注入操作
<bean id="dept" class="cn.edu.vo.Dept">
<property name="deptno" value="10"/>
<property name="dname" value="开发部"/>
</bean>
利用这种setter的设置才是在实际开发之中使用最多的操作情况
但是整个利用setter注入其强大之处,还在于可以引用其他类型的Bean对象
范例:定义Emp.java类
public class Emp {
private Integer empno;
private String ename;
private Dept dept;
}
下面的重点内容在于配置文件来决定彼此的操作关系
范例:编写applicationContext.xml文件
<bean id="Emp" class="cn.edu.vo.Emp">
<property name="empno" value="8888"/>
<property name="ename" value="wor"/>
<property name="dept" ref="dept"/>
</bean>
如果现在是具体的内容就用value属性,如果要引用其他Bean的对象,那么就使用ref
在最早的时候所进行的全部结构的设计过程之中都是通过程序硬编码的形式实现的,但是现在可以利用配置文件的方式采用软编码的形式完成,所有的操作类不再需要由用户负责实例化了,而全部由容器完成。
疑问?如果某个内容为null怎么办?
现在有两种方式
第一种:不设置dname属性,内容就是null;
<bean id="dept" class="cn.edu.vo.Dept">
<property name="deptno" value="10"/>
</bean>
第二种:明确的设置null
<bean id="dept" class="cn.edu.vo.Dept">
<property name="deptno" value="10"/>
<property name="dname"><null/></property>
</bean>
方式二知识明确的告诉用现在要设置的内容是null值
但是以上所进行饿属性设置只是设置了常用的类型,例如:Integer、String、其他引用,可是在实际的开发之中还有可能设置布尔值,但在Spring中支持如下几种:true|flase、1|0、on|off、yes|no
其返回值都为true|false
范例:设置布尔型
如果在类中的属性是boolean型数据返回的时候一般都建议使用is开头,也可以使用get。
<bean id="dept" class="cn.edu.vo.Dept">
<property name="deptno" value="10"/>
<property name="dname" value="开发部"/>
<property name="close" value="no"/>
</bean>
返回false
3.3、注入集合数据
在之前都完成了常用的数据的注入过程,但是任何的开发之中都一定会存在集合的操作,在Spring里面也支持数据的注入:数组、list、set、map、Properties。
范例:注入数组
public class Company {
private String msg[];
private Integer data[];
}
<bean id="company" class="cn.edu.vo.Company">
<property name="data">
<array value-type="java.lang.Integer">
<value>10</value>
<value>20</value>
<value>30</value>
</array>
</property>
<property name="msg">
<array>
<value>"10"</value>
<value>"20"</value>
<value>"30"</value>
</array>
</property>
</bean>
输出信息
Company{msg=["10", "20", "30"], data=[10, 20, 30]}
在本类中可以接受两个数组的对象信息。现在可以发现,在使用数组信息的时候都设置了相应的类型。
如果不写操作的类型,那么Spring会自动的判断给出操作类型,帮助用户自动转型。
但要清楚的是,数组一般不会出现在开发之中,使用List集合来横向代替数组。
范例:List集合操作
private List<String> msg;
<bean id="company" class="cn.edu.vo.Company">
<property name="msg">
<array>(应该换为list)
<value>"10"</value>
<value>"20"</value>
<value>"30"</value>
</array>
</property>
</bean>
输出信息
Company{msg=["10", "20", "30"]}
结论:List集合 = 数组。
范例:Set集合注入
private Set<String> msg;
<bean id="company" class="cn.edu.vo.Company">
<property name="msg">
<set>
<value>"10"</value>
<value>"20"</value>
<value>"20"</value>
</set>
</property>
</bean>
输出信息
Company{msg=["10", "20"]}
此时不在出现重复数据,因为Set集合不允许重复数据。
范例:注入Map集合
private Map<Integer,String> msg;
<bean id="company" class="cn.edu.vo.Company">
<property name="msg">
<map key-type="java.lang.Integer" value-type="java.lang.String">
<entry key="1" value="mldn"/>
<entry key="2" value="mldnjava"/>
</map>
</property>
</bean>
输出信息
Company{msg={1=mldn, 2=mldnjava}}
以上的操作时加上在开发配置文件的编写过程之中见到的不多,而真正见到最多的是Properties类型(只能是String类型)
范例:注入Properties
private Properties msg;
<bean id="company" class="cn.edu.vo.Company">
<property name="msg">
<props>
<prop key="mldn">www.mldn.com</prop>
<prop key="mldnjava">www.mldnjava.com</prop>
</props>
</property>
</bean>
输出信息
Company{msg={mldn=www.mldn.com, mldnjava=www.mldnjava.com}}
在一些框架的整合开发之中,此类属性的实质是最为常见的。
现在所给出的是加上只是定义的一些数值,而最神奇的是它可以定义文件内部的引用关系
范例:观察如下结构
public class Dept {
private Integer deptno;
private String dname;
private List<Emp> emps;
}
public class Emp {
private Integer empno;
private String ename;
private Dept dept;
}
而后的关键就在于通过配置文件清楚的描述出以上的结构关系。
<bean id="dept" class="cn.edu.vo.Dept">
<property name="deptno" value="10"/>
<property name="dname" value="开发部"/>
<property name="emps">
<list>
<ref bean="empA"/>
<ref bean="empB"/>
<ref bean="empC"/>
</list>
</property>
</bean>
<bean id="empA" class="cn.edu.vo.Emp">
<property name="empno" value="1"/>
<property name="ename" value="aaa"/>
<property name="dept" ref="dept"/>
</bean>
<bean id="empB" class="cn.edu.vo.Emp">
<property name="empno" value="2"/>
<property name="ename" value="bbb"/>
<property name="dept" ref="dept"/>
</bean>
<bean id="empC" class="cn.edu.vo.Emp">
<property name="empno" value="3"/>
<property name="ename" value="ccc"/>
<property name="dept" ref="dept"/>
</bean>
所有可以在程序中配置的结构关系,现在完全可以通过配置文件横向替代了。
3.4、内部bean配置(了解)
正常情况下肯定是在程序里面跟别定义“<bean>“节点,而后利用”<ref>“进行关系的引用,但是如果有一些特殊需要也可以需要设置内部的bean配置。
范例:配置内部bean
<bean id="emp" class="cn.edu.vo.Emp"><!--内部bean配置-->
<property name="empno" value="4"/>
<property name="ename" value="ddd"/>
<property name="dept">
<bean id="mydept" class="cn.edu.vo.Dept">
<property name="deptno" value="20"/>
<property name="dname" value="市场部"/>
</bean>
</property>
</bean>
输出信息
雇员编号:4, 雇员姓名:'ddd
如果真从使用的角度来看,大部分情况下都会考虑将其定义为公共 Bean,供各种配置使用。
3.5、使用p命令空间(了解)
在之前使用的依赖注入的操作是最为常见的操作模式,也是在世纪开发之中使用最多的形式,但是从Spring 3.0版本之后增加了另外一种操作形式,称为p命令空间操作
当有
xmlns:p="http://www.springframework.org/schema/p"
可以使用p命名空间
范例:
定义操作
<bean id="dept" class="cn.edu.vo.Dept" p:deptno="10" p:dname="开发部"/>
<bean id="emp" class="cn.edu.vo.Emp"p:empno="111" p:ename="SMITH" p:dept-ref="dept"/>
输出结果
雇员编号:111, 雇员姓名:'SMITH,部门名称Dept{deptno=10, dname='开发部', emps=null}
虽然此类的操作长度要简化,但是大部分的情况下很少这样去处理,基本上还是愿意沿用以前的配置。
3.6、自动装配
在之前使用的过程之中,对于要引用的属性都必须明确写上名称。
范例:原始配置
<bean id="dept" class="cn.edu.vo.Dept">
<property name="deptno" value="10"/>
<property name="dname" value="开发部"/>
<property name="emps">
<list>
<ref bean="empC"/>
</list>
</property>
</bean>
<bean id="empC" class="cn.edu.vo.Emp">
<property name="empno" value="3"/>
<property name="ename" value="ccc"/>
<property name="dept" ref="dept"/>
</bean>
但要在emp对象里去引用dept对象的时候,需要明确的使用“ref“属性去找到制定的名称,但是在这中操作中也可以使用类型的自动装配。
范例:实现自动装配
<bean id="empC" class="cn.edu.vo.Emp" autowire="byType">
<property name="empno" value="3"/>
<property name="ename" value="ccc"/>
<property name="dept"/>
</bean>
<bean id="empA" class="cn.edu.vo.Emp" autowire="byType">
<property name="empno" value="1"/>
<property name="ename" value="aaa"/>
</bean>
输出结果
Dept{deptno=10, dname='开发部', emps=[雇员编号:1, 雇员姓名:'aaa',]}
此时装配过程之中没有明确的写要引用的事dept的配置,但是却可以通过“autowire“自动的根据类型查找到所需要的bean的对象,并且自动引用。
但是这样的操作也会有些问题,因为是按照类型的关系引用,如果出现了两个同类型的操作则报错,所以需要有人推荐或者有人退出。
范例:设置推荐选择
<bean id="deptA" class="cn.edu.vo.Dept" primary="true">
范例:自动退出
<bean id="deptA" class="cn.edu.vo.Dept" autowire-candidate="false">
意味着如果进行类型匹配的过程中,不会再去考虑此bean的情况
以上的操作都是利用了setter进行的自动装配,但是也可以利用构造方法进行自动装配。
范例:
public Emp(Dept dept) {
this.dept = dept;
}
<bean id="empA" class="cn.edu.vo.Emp" autowire="constructor">
总结:这种自动的配置模式比较麻烦,尽量还是明确的引用一个名称会更加合理。
3.7、bean的其他配置(了解)
首先明确一个问题:默认情况下只要在applicationContext.xml文件里面配置的”<bean>"都会在Spring启动的时候自动进行构造方法初始化,但是用户也可以实现自己的配置,让其在第一次使用的时候再进行初始化,这种操作称为延迟加载。
范例:延迟加载
public class Emp {
private Integer empno;
private String ename;
}
<bean id="empA" class="cn.edu.vo.Emp" lazy-init="true">
<property name="empno" value="1"/>
<property name="ename" value="aaa"/>
</bean>
此时就表示emp这个对象进行了延迟加载,当一次使用这个bean时再加载。
除了这一特征之外,在Spring处理过程之中还可以进行自定义的初始化和销毁方法操作。
范例:观察初始化销毁
public class Company {
public void init(){
System.out.println("###### 公司初始化 #######");
}
public void destroy(){
System.out.println("####### 公司销毁 ######");
}
}
实际上以上设计方法只时在Spring中才可以使用的,而在实际的Java运行里初始化会依靠构造方法,销毁依靠finalize()方法。
范例:配置applicationContext.xml文件————需要明确的制定初始化和销毁的方法
<bean id="company" class="cn.edu.vo.Company" init-method="init" destroy-method="destroy"/>
默认情况下初始化的操作一定会默认的自动出现,但是销毁的操作必须明确处理
public class TestCompany {
public static void main(String[] args) {
ClassPathXmlApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
Company company= cxt.getBean("company", Company.class);
System.out.println(company);
cxt.registerShutdownHook();//触发销毁
}
}
这样的销毁操作在一段时间内,成为了Spring整合开发的关键,用于进行数据库连接的关闭。(现在很少见)
3.8、利用Annotation配置注入关系
在Spring中可以利用Annotation完全简化
范例:定义数据层操作
package cn.edu.dao;
public interface IAdminDAO {
public boolean findLogin();
}
public interface IRoleDAO {
public boolean findAll();
}
package cn.edu.dao.impl;
public class AdminDAOImpl implements IAdminDAO {
@Override
public boolean findLogin() {
System.out.println("[IAdminDAO findLogin()]");
return true;
}
}
public class RoleDAOImpl implements IRoleDAO {
@Override
public boolean findAll() {
System.out.println("[IRoleDAO findAll()]");
return true;
}
}
范例:增加新的命名空间
xmlns:context="http://www.springframework.org/schema/context"
范例:
<context:annotation-config/>
<context:component-scan base-package="cn.edu"/>
或者
<context:annotation-config/>
<context:component-scan base-package="cn.edu.dao.impl,cn.edu.service.impl,…"/>
当打上context时会出现要求引用,按住(option+enter)自动导入命名空间。
表示在“cn.edu"下的所有程序类都支持Annotation的配置,而在Spring里面针对于组件的Annotation配置只提供有三个注解定义(这三个注解定义的作用都一样,只是单词不同):
@Component:主要用于定义组件,一般都在DAO上使用;
@Service:主要用于定义组件,一般都在Service上使用;
@Repository:主要用于定义组件,一般都在Action上使用;
范例:修改XXXDAOImpl类
@Component
public class AdminDAOImpl implements IAdminDAO {
@Override
public boolean findLogin() {
System.out.println("[IAdminDAO findLogin()]");
return false;
}
}
现在如果使用了注解定义组件,那么名称默认情况下就是类名称的结构形式:
AdminDAOImpl,则访问此组件的名称就是“adminDAOImpl;
RoleDAOImpl,则访问此组件的名称就是“roleDAOImpl”。
范例:在Service层上使用注解
@Service
public class AdminServiceImpl implements IAdminService {
@Resource
private IAdminDAO adminDAO;
@Resource
private IRoleDAO roleDAO;
@Override
public boolean login() {
this.adminDAO.findLogin();
this.roleDAO.findAll();
return false;
}
}
此时的AdminServiceImpl类的引用名称是adminServiceImpl
@Resource:表示之前的<bean>引用
范例:主程序
public class TestAdmin {
public static void main(String[] args) {
ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
IAdminService vo= cxt.getBean("adminServiceImpl", AdminServiceImpl.class);
vo.login();
}
}
现在发现利用Annotation实现的注入操作,整个的操作流程都是非常简化的,以后的开发都使用此类的进行。









网友评论