框架
框架通俗的讲就是程序的架子,使用程序搭建出程序的基本架构,它的主要目的是针对一
些通用的问题给出了便捷的解决方案,也可以是开发人员基于某一个框架快速的开发具体
的相关应用。
常用的框架
SSH
Struts2
Spring
Hibernate
SSM
SpringMVC
Spring
Mybatis
Spring框架
Spring是一个Service层的框架,它可以整合许多其他的框架进行工作,
通俗的讲就是:Spring的理念是使现有技术更加实用,将一个大杂烩整合成现有的框架技术。
概念
Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器或者框架。
优点
1.Spring是一个开源免费的框架。
2.Spring是一个轻量级的框架,非侵入式的。
3.控制反转IOC,面向切面AOP
4.对事物以及框架都支持
Spring的配置文件
1.为eclipse配置spring的约束文件
Spring本身是基于xml配置来工作的,在使用Spring的过程中不可避免的要编写大量xml配置,
Spring官方提供了这些xml文件的编写规范,这是通过提供xml的约束文件来实现的。
DTD,通常文件的后缀 .dtd
Schema.通常文件的后缀为 .xsd
所谓的xml的约束其实是一种限定xml文件写法的技术,主要分为两种
1.Spring提供了Schema格式的约束,来限定Spring配置文件的写法。
(但是这个过程比较麻烦,通常我们会将约束文件交给开发工具管理)
2.将约束文件交给开发工具管理,开发工具可以通过解析约束文件了解xml的写法,
并在需要时为开发者提供标签提示
2.将spring的约束文件交给eclipse去管理
具体操作会有专门的博客
Spring的主要技术
IOC(DI)和 AOP
IOC(DI) : 控制反转(依赖注入)
AOP : 面向切面编程
Spring IOC 控制反转
控制反转:就是将对象的创建及生命周期管理的过程交给Spring来处理的过程。
优点:在开发当中不在需要关注对象的创建以及对于对象生命周期的管理
而是在需要的时候都由Spring框架来提供。
IOC 实现原理
在初始化一个Spring容器时,Spring会去解析指定的xml文件,
当解析到其中的<bean>标签时,
会根据该标签中的class属性指定的类的全名路径名,
通过反射创建该类的对象,并将该对象存入内置的Map中管理。
其中键就是该标签的id值也就是该对象。
之后,当通过getBean方法来从容器中获取对象时,其实就是根据掺
入的条件在内置的Map中寻找是否有匹配的键值,如果有则将该键值对中保存的对
象返回,如果没有匹配到则抛出异常。
IOC 获取对象的方式
1.通过context.getBean()方式获取
a.用id获取
b.用class获取
需要注意的是:
通过class方式获取bean时,如果同一个类配置过多个bean,
则在获取时因为无法确定到底要获取哪个bean会抛出异常,
而id是唯一的,不存在这样的问题,所以建议大家尽量使用
id获取bean
获取格式
<?xml version="1.O" encoding="UTF-8"?>
<beans xmIns="http://www.springframework.org/schema/beans"
xmIns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http;/www.springframework.org/schema/beans/spring-beans-3.2.xsd ">
<bean id="类名" class="类名在该包的路径"></bean>
</beans>
别名标签
在Spring中提供了别名标签<alias>可以为配置的<bean>起一个别名
要注意的是这仅仅是对指定的<bean>起的一个额外的名字,并不会额外
的创建对象存入map。
格式
<alias name = "要起别名的bean的id"alias=”要指定的别名“ />
Spring创建对象的方式
a.通过类的无参构造器创建
//当用最普通方式配置一个<bean>时,默认就是采用类的无参构造创建对象。在Spring容器初始化时,
//通过<bean>上配置class属性反射得到字节码对象,通过newInstance()创建对象
Class c = Class.forName("类的全路径名称");
Object obj = c.newInstance();
//这种方式下spring创建对象,要求类必须有无参的构造,否则无法通过反射创建对象,会抛出异常。
public class Person{
public Person(String age){}
public void say(String age){
System.out.println("hello spring");
}
}
b.通过静态工厂创建对象
//很多的时候,我们面对的类是无法通过无参构造去创建的,例如该类没有无参构造,是一抽象类等等情况,
//此时无法要求spring通过无参构造创建对象,此时可以使用静态工厂方式创建对象。
//类名为 calendar
public class CalendarStaticFacory{
public static Calendar getCalendar(){
return Calendar.getInstance();
}
}
<bean id="calendar" class="com.biem.factory.CalendarStaticFactory"
factory-method="getCalendar"></bean>
//spring创建对象的方式:通过静态工厂创建
@Test
public void test01(){
ApplicationContext context
= new ClassPathXmlApplicationContext("applicationContext.xml");
Calendar c1 = (Calendar)context.getBean("calendar");
Calendar c2 = (Calendar)context.getBean("calendar");
System.out.println(c1==c2);
}
c.通过实例工厂创建对象
//实例工厂也可以解决类中没有无参构造无法创建对象的问题,解决方法跟静态工厂类似,
//只不过实例工厂提供的方法不是静态的。
//spring需要先创建出实例工厂的对象,在调用实例工厂对象上指定的普通方法来创建对象
//所以实例工厂也需要配置到Spring中管理
public class CalendarFactory{
public Calendar getCalendar(){
return Calendar.getInstance();
}
}
<bean id="calendarFactory" class="com.biem.factory.CalendarFactory"></bean>
<bean id="calendar" factory-bean="calendaryFactory"
factory-method="gatCalendar"></bean>
//spring创建对象的方式:通过实例工厂创建
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Calendar c = (Calendar)context.getBean("calendar");
System.out.println(c);
}
d.通过spring工厂创建对象
//Spring内置了工厂接口,也可以通过实现这个接口来开发Spring工厂,通过这个工厂创建对象。
public class CalendarSpringFactory implements FactoryBean<Calendar>{
//工厂生产的对象并返回
@Override
public Calendar getObject() throws Exception{
return Calendar.getInstance();
}
//工厂生成对象的类型
@Override
public Class<?>getObjectType(){
return Calendar.class;
}
//工厂生成的对象是否是单例
//如果是true--是单例
//如果是false--是多例
@Override
public boolean isStringleton(){
return false;
}
}
<bean id="calendar" class="com.biem.factory.CalendarySpringFactory"></bean>
//spring 创建对象的方式:通过spring工厂创建
@Test
public void test03(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Calendar c1 = (Calendar)context.getBean("calendar");
Calendar c2 = (Calendar)coantext.getBean("calendar");
System.out.println(c1==c2);
}
单例和多例
spring容器管理的bean在默认情况下,是单例的,
也就是说:
一个bean只创建一个对象,然后存到map当中,之后无论获得多
少次该bean,都是同一个对象。
spring默认采用的单例模式
减少了对象的创建
减少了内存的消耗。
但是在实际的开发当中又存在多例需求的情况
spring提供了选项
可以将这个bean设置为多例模式
格式
<!--
scope属性:控制当前bean创建的模式
singleton:当前的bean处在单例模式当中,默认就是此模式
prototype:当前的bean处在多例模式下
-->
<bean id="类名" class="类名的位置" scope="单例多例模式"
bean在单例模式下的生命周期
bean在单例模式下
1. spring容器启动时解析xml发现该bean标签后
2. 直接创建该bean的对象存入内部map中保存,此后无论调用多少次getBean()
获取该bean都是从map中获取该对象返回,一直是一个对象,
3. 此对象一直被Spring容器持有,直到容器退出时,随着容器的退出对象被销毁。
bean在多例模式下的生命周期
bean在多例模式下
1. spring容器启动解析xml发现该bean标签后,只是将bean进行管理。
2. 并不会创建对象,此后每次使用getBean()获取该bean时,spring都会重新创建该对象返回
每次都是一个新的对象,这个对象spring容器并不会持有,什么销毁取决于使用该对象的用
户自己什么时候销毁该对象。
懒加载机制
Spring默认会在容器初始化的过程中,解析xml,并将单例的bean创建并保存到map中,这样的机制
在bean比较少时问题不大,但一旦bean非常多时,spring 需要在启动的过程中花费大量的时间来创
建bean,花费大量的空间存储bean,但这些bean可能很久都用不上,这种在启动时在时间和空间上的
浪费显得非常的不值得,所以Spring提供了懒加载机制,所谓的暗恋你加载机制就是可以规定指定bean
不在启动时立即创建,而是在后续一用到时才创建,从而减轻了在启动过程对时间和内存的消耗。
(简单的理解就像电脑开机一样,当软件少的时候,开机启动并没有多大问题。但是当你软件多
了的时候,所有软件都加入开机启动,就会导致开机缓慢,而且大部分软件我们开机之后并不立
即使用,甚至很长时间都有可能不会使用,而懒加载就是解决这样的问题,将不常用的软件设置
为开机不启动,只是使用的时候才开启)
注意:
懒加载机制只对单例bean有作用,对于多例bean设置懒加载没有意义。
方式一:未指定的bean设置懒汉模式
方式二:全局加载懒汉模式
配置初始化和销毁的方法
在Spring中如果某个bean在初始化之后或销毁之前要做一些额外操作可以为该bean
配置初始化和销毁的方法,在这些方法中完成主要功能。
Spring中关键方法的执行顺序
在Spring创建bean对象时:
1.先创建对象(通过无参构造器或工厂)
2.之后立即调用init方法来执行初始化操作
3.之后此bean就可以拿来调用其他普通方法
4.而在对象销毁之前spring容器调用其destory方法来执行销毁操作
Spring DI(依赖注入)
在创建对象的过程当中,spring可以依据配置文件对这个对象的属性进行设置,
这个过程就是--依赖注入(DI)
注入方式
1.通过set方式注入
2.非spring内置的可以直接注入类型的注入
3.基于构造方法的注入
4.自动装配
a.未指定的<bean>配置自动装配
b. 全局配置自动装配
注解
概念
通俗的讲:如果说注释是给开发人员看的,那么注解就是给运行程序看的
背景
在注解没有出现之前,xml 是各大框架的青睐者,
他以松耦合的方式完成了框架中几乎所有的配置,
但是随着项目越来越庞大,xml 的内容也越来越复杂,相对维护成本变高。
于是就有人提出来一种标记式高耦合的配置方式 - - >注解
方法上可以进行注解,
类上也可以注解,
字段属性上也可以注解
反正几乎需要配置的地方都可以进行注解,
关于注解 和xml 两种不同的配置模式,争论了好多年了,各有各的优劣,
注解可以提供更大的便携性易于维护,但耦合度高,而xml 相对于注解则是相反的,
一个注解准确意义上来说,只不过是一种特殊的注释而已,
如果没有解析它的代码,它可能连注释都不如,
在Java技术里注解的典型应用就是可以通过
反射技术得到类里面的注解,以决定怎么去运行一个类。
定义注解
1.定义注解@Annotation
public @inerface MyAnnotation{
}
2.把注解应用到指定的地方(属性,方法,类,参数,包等)
1)应用注解
@MyAnnotation
public class AnnotationTest{
@MyAnnotation
public void method(){}
public void method(@MyAnnotation int i){}
}
3.使用反射技术解析注解
原生注解,用于修饰注解的注解
1.@Target(ElementType.xxx):指定这个注解的使用的位置(类,方法,属性,参数,包等)
@Target(ElementType.METHOD)-->方法
@Target(ElementType.TYPE)-->注解类,接口(包括注释类型)声明或者枚举的声明
@Target(ElementType.CONSTRUCTOR)-->构造方法声明
@Target(ElemenType.FIELD)-->字段声明(包括枚举变量)
@Target(ElementType.LOCAL_VARIABLE)-->局部变量声明
@Target(ElementType.PACKAGE)-->包声明
@Target(ElemenatType.PARAMETER)-->参数声明
2.@Retention(RetentionPolicy.SOURCE)被它修饰的注解保留域
编译器-->类加载器-->JVM.java-->.class-->加载-->运行
SOURCE:源码级别,给编译器看的,编译完之后直接丢弃该种策略的注解,生成的.class文件中没有改类型的注解
CLASS:给类加载器看的,在类加载时加载时可以做一系列引导和操作,编译器编译后存在,类加载器加载完之后,丢弃该保留域的注解
RUNTIME:给JVM看的,在程序运行的过程中做相关的操作。编译器编译后保留,类加载其加载之后保留,在JVM运行时通过反射技术反射使用的是什么注解,注解属性值是什么,根据他们两个值做i对应的操作,
3.@Documented:自定义注解如果使用Documented,生成的doc文档上,使用该自定义注解的地方会显示注解信息。
注解中的属性:
a.注解定义属性和接口定义方法类似,缺省默认是public
public 类型 属性名称();
b.定义属性时,如果没有使用default指定默认值,使用当前注解必须为该属性赋值
使用default指定默认值之后,该属性在注解时可以赋值,也可以不赋值
c.注解中的属性类型是有要求的,可以是八种基本数据类型,也可以是枚举类型,Class类型,string类型等以及以上类型的一维数组
如:String[] names default{}'
注意:在使用注解时,如果数组类型的属性的值是一个值的话,赋值时可以把{}省略掉
d。存在一个特殊的属性:value。如果只为该属性赋值,value是可以省略掉的。
但是如果为注解中多个属性同时赋值的话,value=不可以省略
4.如何使用注解
@注解的名称 (属性名=属性值)
@注解的名称(属性名1=属性值1,属性2=属性值2...)
5.如何反射注解
@Retention(RetentionPolicy.RUNTIME)
要想注解被反射,注解的保留域必须设置RUMTIME保留域
JDK中提供了AnnotationElement接口,该接口提供了反射注解的几个方法;
1)getAnnotation() 如果元素该元素指定类型的注解,则返回,否则返回null
2)getAnnotations();返回此元素上存在的所有的注解
3)getDeclaredAnnotations();返回直接存在此元素上的所有注解
4)boolean isAnnotationPresent:判断该元素上是否存在指定的注解,存在返回true 不存在返回false