文章目录
传统的业务处理
DAO层
接口
public interface UserDao {
void getUser();
}
接口实现类
public class UserDaoImpl implements UserDao {
public void getUser() {
System.out.println("获取数据1");
}
}
Service层
接口
public interface UserService {
void getUser();
}
接口实现类
public class UserServiceImpl implements UserService {
private UserDao userdao = new UserDaoImpl();
public void getUser() {
userdao.getUser();
}
}
Controller层
访问Service层
public class User {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.getUser();
}
}
回顾MVC
- 本例的DAO层、Service层、Controller层,运用的就是三层架构思想——MVC。
- 其中的DAO层就是用来做数据持久化的;Service层做业务逻辑对DAO层数据进行处理;Controller层则是请求和接收数据。
- 从例子中会发现,Service层返回给Controller层的数据其实就是DAO层的。特别是Service的getUser()方法,完全就是调用DAO层的getUser()方法。
- 之所以不让Controller层直接获取DAO层的数据进行业务逻辑操作。其目的就是为了保证用户数据安全。
传统过程中暴露出来的问题
当客户需求发生改变时
比如:当用户想要访问的dao层数据发生改变时
- 此时,dao层必须增加业务代码。这个必不可免。
dao层增加的代码:
又一个接口实现类:
public class User_01_DaoImpl implements UserDao {
public void getUser() {
System.out.println("获取数据2");
}
}
- 同时Service层的代码也要进行改动,这就不太方便了。
修改后的Service层接口实现类代码:
public class UserServiceImpl implements UserService { private UserDao userdao = new User_01_DaoImpl(); public void getUser() { userdao.getUser(); } }
- 这与起初的代码相比,仅仅是修改了new对象时指向的类型。
- 但是用户的需求每更改一次,就要修改一次业务代码(Service层)。
- 如果程序代码量十分大,修改一次的成本代价就十分昂贵。
解决思路
- 上面的代码。在service层中程序是主动创建对象,控制权在程序员手上。
- 应该把对象的创建转移给第三方。使用户拥有主动权。
将原本Service层的实现类的代码修改为:
public class UserServiceImpl implements UserService {
private UserDao userdao;
//利用set进行动态实现值的注入!
public void setUserDao(UserDao userdao) {
this.userdao = userdao;
}
public void getUser() {
userdao.getUser();
}
}
此时用户使用时只需调用UserServiceImpl 类对象的setUserDao()方法,动态的传入想要的DAO层对象:
public class User {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
//之前,程序是主动创建对象!控制权在程序员手上
//使用set注入后,程序不再具有主动性,而是变成了被动的接受对象
//留接口,用什么自己去调
((UserServiceImpl)userService).setUserDao(new UserDaoImpl());
userService.getUser();
}
}
注意:
- 这仅仅是一个思路,因为代码中出现了Controller层直接访问DAO层的现象,这是绝对不允许的。
- 下面来看看Spring是如何通过IOC正确合理的实现这个思路的——Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。说白了,对象由Spring来创建。
Ioc的业务处理
简单描述IOC
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
零配置: 采用XML配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入。
IOC的具体实现
- 先写一个实体类
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
- 写一个bean配置的xml文件(核心内容就是中间的bean标签,其他的部分去Spring官网IOC部分粘取)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello" class="pojo.Hello">
<property name="str" value="数值"></property>
</bean>
</beans>
- 获取实体类对象并去使用。
声明:
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
本行代码取自官网,可传多个xml文件作为参数
public class HelloTest {
public static void main(String[] args) {
//ApplicationContext是一个接口,多态思想。另本行内容同样去Spring官网粘取
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hello hello = (Hello)context.getBean("hello");
System.out.println(hello.toString());//Hello{str='数值'}
}
}
此时我们发现,并没有去new对象,但是却可以直接取用Hello类的对象。
解读上面代码:(xml部分)
上面代码的第二步,xml配置文件部分。(此过程就可称为控制反转)
简单总结
Hello对象是谁创建的?
- hello对象是由Spring创建的
Hello对象的属性是怎么设置的?
+hello对象的属性是由Spring容器设置的
控制
- 谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。
反转
- 程序本身不创建对象,而变成被动的接收对象。
依赖注入
- 就是利用set方法来进行注入的(说真的,不太理解)
- 看下面的demo!
依赖注入补充
Person实体类
- 上面说到通过set注入,所以实体类中的set方法必不可少
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
Pet实体类
- pet(宠物)离不开主人,因此有一个Person类型的master
public class Pet {
private String name;
private int age;
private Person master;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person getMaster() {
return master;
}
public void setMaster(Person master) {
this.master = master;
}
@Override
public String toString() {
return "Pet{" +
"name='" + name + '\'' +
", age=" + age +
", master=" + master +
'}';
}
}
xml配置
- 首先要明确,一个
<bean></bean>
就相当于一个类的对象- 通过
<property>
标签的name和value为类中属性赋值的前提条件就是类中有set方法。- 上面提到
<property>
标签的name和value,此处又多了一个ref,ref是为当前类注入所需的对象。其前提也是得有对应的set方法。- 可以简单理解为value和ref都是为name属性赋值的,只不过value是为基本类型赋值,ref是引用Spring中已经创建好的对象(其他bean)。
- 一个bean的id可以通过ref注入到另一个bean中。比如此处将Person的bean的id注入到了Pet的bean中。相当于给了Pet类中Person类型的master属性一个Person对象。
- 兜了这么大一圈,目的还是为了不去自己new 对象
<bean id="person" class="pojo.Person" >
<property name="name" value="主人1"></property>
</bean>
<bean id="pet" class="pojo.Pet">
<property name="master" ref="person"></property>
</bean>
测试代码
- 通过获取Person类对象,通过setName()方法为name属性赋值。
- 此时,xml中,value="主人1"被“窦哥”覆盖。
public class PetTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Pet pet = (Pet)context.getBean("pet");
Person person = (Person)context.getBean("person");
person.setName("窦哥");
pet.setMaster(person);
System.out.println(pet.getMaster());
}
}
IOC容器是如何创建对象的?
无参构造
默认情况下是通过类中的无参构造创建的,在加载配置文件(xml文件)时,容器中管理的对象就已经初始化了。最简单的检验方法就是在类的无参构造中输出一句话,当获取IOC容器对象后,执行代码就会输出无参构造中打印的内容。
有参构造
无参构造检验环境就是在原有类中声明有参构造,并且不显式的声明无参构造,此时xml对应的bean就会报错。以下是三种有参构造创建对象的方法。
下标赋值创建
<!-- 下标赋值创建对象--> <bean id="hello" class="pojo.Hello"> <constructor-arg index="0" value="str_01"/> <constructor-arg index="1" value="str_02"/> ................ </bean>
此处下标为有参构造参数的下标,value为对应参数的值,本例中默认用的String类型的参数。
类型创建。(不建议使用)
<!-- 通过类型创建对象--> <bean id="hello" class="pojo.Hello"> <constructor-arg type="java.lang.String" value="str_01"/> <constructor-arg type="int" value="6"/> </bean>
此处第一个参数为String类型的,第二个为int类型的
注意,包装类必须用全限定名
参数名创建(最容易让人接受)
<!-- 通过参数名创建对象--> <bean id="hello" class="pojo.Hello"> <constructor-arg name="str" value="Hello"/> <constructor-arg name="str2" value="6"/> </bean>
此处name为有参构造形参数名字,value为对应参数的值。
扩充(不重要,可跳过)
Spring相对重要且常用的是注解开发,而且今后常用的是SpringBoot,所以以下指示了解即可。
- 别名
- import(在一个xml文件中导入其他xml文件)
- 依赖注入
3.1 构造器注入
3.2 Set方式注入(重点掌握set注入)
3.3 拓展方式注入(c命名空间注入、p命名空间注入。知道就行) - bean作用域
这个作用域问题就是IOC容器创建的对象个数。默认是单例的(singleton)。如下所示
public class HelloTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hello hello = (Hello)context.getBean("hello");
Hello hello1 = (Hello)context.getBean("hello");
System.out.println(hello==hello1);//true
}
}
可以在xml文件中修改配置(上表中除了singleton和prototype,其他只能在web开发中使用)
<bean id="hello" class="pojo.Hello" scope="singleton">
<property name="str" value="Hello" ></property>
</bean>
- bean的自动装配