首先是软件的应用分层架构
标准三层架构:
1:数据访问层:实现了数据的持久化
2:业务逻辑层:对逻辑的实现及处理,实际上不可能在表示层对数据不做任何处理,但是尽可能的将逻辑分为一层
3:表示层:数据的展示
优点:
降低了一个程序的耦合度,一个类中有一千行代码可以转化为三个类中各写300多行代码,每个类实现自己的功能
慢慢的就形成了一种标准化趋势,更加的适应于面向接口编程,注重《开闭原则》的实现,对修改关闭,而对扩展开放,
扩展的功能定义为接口,程序员进行实现,在现有的代码中组合进新功能的实现类对象,从而实现新功能的添加。
缺点:
然而这种三层体系架构依旧存在一些问题
1.有些时候将简单的问题复杂化,假如:
一个类300代码可以完成的工作,分为三层架构后每个类需要120行代码进行完成,反而得不偿失了
2.组合的代码不一定有效,例如:
有这样一个类:
public class A{ private IB b=new BImpl(); public void go(){ b.go(); } public static void main(String agrs[]){ new A().go(); } }
这个例子可以看出,一旦IB接口的实现类BImpl出现了问题,那么b.go()可能空指针异常,A类中的方法就无法正常运行
那么:有没有一种方法可以不使用组合的方式来获得对象?
Java中创建对象对象除了new关键字之外,有的就是反射机制了。有效的破坏莫不失为一种好想法,有了反射我们就可以直接通过权限定名来创建对象。
Spring框架的相关概念
轻量级:spring是一种轻量级框架,用户可以自定义自己需要的内容,选择自己需要的功能模块
容器的概念:
Java开发中常见的容器有:GUI,Tomcat都是一些容器,比如jsp动态页面的展示,就是需要Tomcat容器的支持才可以运行。spring框架中的容器可以管理项目中各种对象的生命周期(对象的创建、初始化、使用、销毁)
前提是项目中要使用spring框架,并且把这些需要管理的对象配置给spring的容器,常见的IOC容器可以控制bean对象的
但是这个bean对象一般都是单例的。
生成生命周期,当然前提是对象不可以脱离容器控制范围(单例对象和多例对象)
spring框架给项目带来的好处:
1)动态解藕,方便开发,面向接口设计
我们可以将对象之间的依赖关系交由Spring进行控制
2)方便程序的测试
spring框架也提供了测试模块,可以和很多主流的测试框架相结合
3)降低Java EE API的使用难度
Spring封装了如jdbc等许多有难度的操作,使得代码“相对”精简。
这里的相对是说,使用了框架相当于在项目中已经引入了代码,所以说Java之所以有许多人用,那是因为Java的类库太丰富了
Spring功能足够强大,但是又可以自己选择模块,这就十分人性化了。
但是Java在程序的效率方面来说是比不上C和C++了。
4)方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度
Spring提供了对各种优秀框架(如Struts,Hibernate,MyBatis)等的直接支持
5)AOP编程的支持
通过Spring提供的AOP功能,方便进行面向切面的编程
6)声明式事务的支持
在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量
7)spring是一个模块化的项目,可以根据自己的需要定制功能模块
spring的核心宗旨:简化企业级应用开发,同时给复杂问题提供强大的、非侵入性解决方案
Spring核心功能模块 Spring Framework
在Spring Framework中又分为许多的模块
Spring Context(spring的上下文)
Spring Web
Spring DAO
Spring ORM
Spring AOP(面向切面编程)
Spring MVC(Spring WebMVC)
Spring Core(spring的核心容器)
这个容器是Spring的一个核心容器
我当前主要学习了的是spring Core和spring AOP,也就是spring框架所提供的ioc和aop功能
Spring MVC是一个集成化的web开发框架,不恰当的说它的地位甚至可以将其从Spring中分离出来,作为一个单独的框架
spring中的IOC
IOC:Inversion of Control 控制反转
spring核心容器也可以称为IOC容器,该容器主要负责管理各种对象的生命周期(对象的创建、初始化、销毁等)
IOC容器并没有实现更多的功能,但它的存在使我们不需要很多代码、不需要考虑对象间复杂的耦合关系就能从IOC容器中获取合适的对象
而且提供了各种对象的可靠的管理,极大地降低了开发的复杂性。
思考1:是什么东西的"控制"被"反转"了?
简单的说:是对象被控制了起来,创建对象的操作反转给了框架
思考2:自己组装电脑和直接购买整机的区别
DI:Dependency Injection 依赖注入
DI的概念的提出是用来代替IOC的,表示让调用类对某一接口实现类的依赖关系由容器注入,以移除调用类对某一接口实现类的依赖。(思考servlet和service层接口以及service层接口实现类这三者的关系)
依赖注入 这个名词显然比 控制反转 更直接明了,并且易于理解。
DL:Dependency Loopup 依赖查找
容器创建对象并提供 回调接口和上下文环境 给这个对象,需要时通过接口从容器中查找其他对象
(之后会见到很多XxxxAware的接口,先简单理解即可)
Spring IOC容器核心api(容器将来会是一个对象)
BeanFactory接口
BeanFactory是Spring中IOC容器的核心接口,主要用于处理Bean的初始化和配置,建立对象间的依赖关系
接口中主要方法如下:
//根据指定名称返回一个Bean实例
Object getBean(String name)
//判断名称为name的Bean是否是原型,即是否总是返回一个新实例(非单例)
boolean isPrototype(String name)
//判断名称为name的Bean是否是单例
boolean isSingleton(String name)
//判断容器中是否包含给定名称的Bean实例
boolean containsBean(String name)
//如果名称为name的Bean有别名则返回所有别名
String[] getAliases(String name)
ApplicationContext接口
该接口继承于BeanFactory,增强了BeanFactory,增加了事务处理AOP,国际化,事件传递等功能
所以在代码中我们一般会使用ApplicationContext接口,以及这个接口相应的实现类来创建spring的容器对象
例如:
String path = "com/briup/ioc/set/set.xml";
ApplicationContext container =
new ClassPathXmlApplicationContext(path);
Student s = (Student)container.getBean("student");
//使用s即可
三层关系如下:
BeanFactory
↑
ApplicationContext
↑
ClassPathXmlApplicationContext
配置文件
这是一个框架中必不可少的部分,会写Spring的XML文件一定会用Spring框架,但是会用不一定明白为什么这么用......
Spring通过读取配置文件中的数据来对项目各个对象进行实例化,配置以及组装,通常使用XML文件来作为配置文件
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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"> ...... beans>
IOC的相关功能
IOC实际上就是实现的Spring容器帮助我们管理对象的功能,所以要先从如何将对象注入到容器中来学习
注入有两种方式:set注入和构造器注入,自动注入和继承也属于这个知识点内
set方式注入(必须依靠set方法)
顾名思义,实现set方式的注入就必须存在set方法
这种动态代理的方式创建对象是反射机制,如果是set注入就必须要无参构造器的支持
可以注入的内容有:
1基本类型(8中基本类型+字符串)的装配
2对象类型的装配
3集合的装配
1.基本类型的装配
方式: 配置元素
例子:
public class HelloBean { private String name; private int age; public String sayHello(){ return "hello "+name +",your age is" + age; } ............. }
配置文件set.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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"> <bean id="helloBean" class="ioc.HelloBean"> <property name="name"> <value>tomvalue> property> <property name="age" value="20"> property> bean> beans>
id是Bean的唯一标识,要求在整个配置文件中要唯一
也可使用name属性,bean标签里面的id和name属性都可以用来标识这个配置的对象
但是id会帮我们检查给对象起的名字是否规范(名字不能重复、不能用数字开头、不能有空格等等)
如果检查出来了那么就会报错name属性不会帮检查这些东西
property 对于所有用set方式来注入的必须使用该标签
value 是对于基本类型,都用value(标签/属性)来注入,可以实现自动的数据类型转换
这里的自动数据类型不是绝对的,自定义的类型不可以自动的数据类型转换
如果有这方面的要求,就需要实现一个类型处理器。
测试类:
main: ApplicationContext ac = new ClassPathXmlApplicationContext("set.xml"); //获取容器的一个实例 HelloBean hb = (HelloBean) ac.getBean("helloBean"); System.out.println(hb.sayHello());
2.对象类型的装配
1. 用于涉及的对象的id在本配置文件中
2. 用于涉及的对象的id不在本配置文件中
3.使用property的ref属性引用,这样就不需要处理涉及到的对象在不在同一个xml配置文件中
例如:
public class OtherBean { private String str1; public String getStr1() { return str1; } public void setStr1(String str1) { this.str1 = str1; } public String toString(){ return "OtherBean "+str1; } }
public class SomeBean { private OtherBean ob; public void printInfo(){ System.out.println("someBean "+ob); } public OtherBean getOb() { return ob; } public void setOb(OtherBean ob) { this.ob = ob; } }
配置applicationContext.xml
<bean id="someBean" class="ioc.SomeBean"> <property name="ob"> <ref bean="otherBean" /> property> bean>
配置other.xml文件
<bean id="otherBean" class="ioc.OtherBean"> <property name="str1"> <value>hellovalue> property> bean>
测试类:
main: String[] path = {"ioc/applicationContext.xml","ioc/other.xml"}; ApplicationContext ac = new ClassPathXmlApplicationContext(path); SomeBean sb = (SomeBean) ac.getBean("someBean"); sb.printInfo();
3.集合的装配
方式:配置元素
public class SomeBean { private List listProperty; private Set setProperty; private Map mapProperty; private Propertiesproperty; get/set...... toString(); }
applicationContext.xml的写法:
<bean id="someBean" class="ioc.SomeBean"> <property name="listProperty"> <list> <value>list1value> <value>list2value> <value>list3value> list> property> <property name="setProperty"> <set> <value>set1value> <value>set2value> <value>set3value> set> property> <property name="mapProperty"> <map> <entry key="key1"> <value>value1value> entry> <entry key="key2"> <value>value2value> entry> map> property> <property name="property"> <props> <prop key="key1">prop1prop> <prop key="key2">prop2prop> <prop key="key3">prop3prop> props> property> bean>
测试类:
main: String path = "ioc/applicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(path); SomeBean sb = (SomeBean) ac.getBean("someBean"); sb.printInfo();
基于构造器注入
方式: 配置
在Bean中不用写set方法,但是要有相应的构造器
构造器注入有俩种形式 一个是根据参数类型 一个是根据参数位置的下标
例如:
<bean name="student" class="com.briup.bean.Student"> <constructor-arg type="int" value="25"> constructor-arg> <constructor-arg type="java.lang.String" value="tom"> constructor-arg> <constructor-arg type="long" value="100"> constructor-arg> bean>
或者:
<bean name="student" class="com.briup.bean.Student"> <constructor-arg index="2"> <value>30value> constructor-arg> <constructor-arg index="0"> <value>200value> constructor-arg> <constructor-arg index="1"> <value>lilyvalue> constructor-arg> bean>
构造器注入有俩种形式 一个是根据参数类型 一个是根据参数位置的下标
还有指定参数传参 这里的name指的是形参的参数名字
<bean name="student" class="com.briup.bean.Student"> <constructor-arg index="0" value="2" name="a">constructor-arg> <constructor-arg index="1" value="3" name="b">constructor-arg> <constructor-arg index="2" value="4" name="c">constructor-arg> bean>
自动注入 :容器依照一些规则去装配bean中的一个属性
注意:自动装配只对[对象类型]起作用,对基本类型不起作用.
第一种情况:
在beans标签中配置装载方式:default-autowire="byName"
在根元素beans中加入这个属性,那么下面所有的bean都会
使用byName的方式进行自动注入,如果在下面的某一个bean
里面想使用其他的方式进行注入,可以用autowire=""属性进行
说明,或者某一个bean不想使用任何自动注入就使用autowire="no"
第二种情况:
在bean标签中指定配置方式
autowire="byName":
spring容器会到当前的类中找property的名字,然后
再根据这个名字去spring容器中找有没有和这个property
名字相同的对象,有的话,就把这个对象当做参数放到
setXxxx这个方法里面注入进来.
比如Teacher类中有一个属性Student student
对应的有setStudent()方法
在xml中配置了Student这个类。
name="studnet";
Spring在自动注入Teacher类中的student属性的时候就会去找和这个property
名字相同的对象
注意:了解property指的类中的什么东西
property来源于attribute
property name="xxxx"实际上就是setXxxx()
这四个xxxx是一个含义
autowire="byType":
spring容器会根据当前类中的set方法里面参数的类型,
去容器中找相匹配的对象,如果没找到就算了,如果找到
一个就注入进来,如果找到多个,那么就会报错了.
也就是说找不到大不了就赋值为null,但是找到两个框架报错了,因为框架不知道用哪个啊
所以说要么就不写,写了就不要有歧义。
autowire="constructor"
根据构造器的参数类型去匹配
继承
配置文件中,一个bean的配置可以继承另一个bean的配置
bean标签中的俩个属性:
abstract属性
注:bean中一旦定义为了abstract="true",则说明该bean是用来被继承的,不能通过该bean获得对象了
parent属性,指定继承哪个bean的配置
例子:
<bean name="student" class="com.briup.bean.Student"> <property name="name"> <value>zhangsanvalue> property> bean>
abstract="true" 表示当前的配置是一个抽象的配置,
这时候我们在代码中就不能通过这个bean的名字teacher来
获得相应的对象了(和java中的抽象类不能直接new对象的道理一样)
但是我们可以再写一个配置去继承这个抽象的配置,当然即使当前
这个配置不是抽象的,也能够被继承(和java中继承一样)
<bean name="teacher" class="com.briup.bean.Teacher" abstract="true"> <property name="student" ref="student">property> bean>
parent="teacher" 表示当前配置是继承了另外一个名字叫
teacher的bean的配置,配置和配置的继承像java中的类和类
直接的继承一样,子类会把父类中的对象继承过来.
当然在子配置里面依然是可以覆盖父配置中已经写的配置信息.
<bean name="t" parent="teacher"> <property name="id"> <value>11value> property> <property name="name"> <value>TeacherWangvalue> property> bean>