目录
<2>通过有参构造方法完成属性的注入,创建类,属性,创建属性对应的有参数的构造方法。在spring的配置文件xml中进行配置创建对象,属性注入。
<3>注入属性-注入的是外部类的bean,可以是对象类型,集合,字符串等
② @Qualifier:根据属性的名称进行装配。就是多个实现类对象,通过value值引入哪一个实现类对象。
一、Spring框架概念
Spring是轻量级的开源的JavaEE框架,轻量级指的是很少使用jar包。Spring可以解决企业应用开发的复杂性。Spring有两个核心部分:IOC和AOP。
(1)IOC:控制反转把创建对象的过程交给Spring进行管理。之前创建对象的过程是通过new关键字在堆内存中进程创建,而现在通过Spring进行对象的创建和管理。
(2)AOP:面向切面编程,在不修改源代码的前提下进行功能的增强,之前增加功能是修改源代码,现在使用AOP更加的增强了程序高内聚低耦合的特点。
Spring的特点:方便解耦,简化开发。AOP编程的支持,方便程序的测试。方便和其他框架进行整合,方便事务的操作,降低API开发难度。
IOC需要的jar:core,context,bean,expression.
Spring创建对象的方式有两种,第一种是采用xml配置文件的方式创建对象,这种方式在实际开发中基本上被弃用,但是作为勤奋好学的我们,这种方式也要学习的啦, 要不然学到后面注解开发等会一头雾水。第二种是通过注解进行对象的创建,这种方式在实际开发过程中应用比较广泛,正如现在的主流框架SpringBoot就是使用注解创建对象进行开发。
先举个例子,让大家认识认识Spring怎么创建的对象,使用xml配置方式创建对象。
① 创建xml文件,配置<bean>标签,其中,<bean>标签中的id是起的是spring所创建对象的别名,class是创建对象类的全路径
<!--
下面是创建类User的对象
id是起的是spring所创建对象的别名,class是创建对象类的全路径 -->
<bean id="user" class="com.spring.cn.User"></bean>
② 测试:
测试分为两步:<1>加载xml配置文件。<2>获取配置文件中所创建类的对象
测试类中加载配置文件
// 加载配置文件,括号内是加载的bean1.xml,上下文类
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
// 获取配置文件的对象,上下文对象获得Spring创建的对象,
//第一个参数spring是创建对象的别名,第二个参数是得到编译后的类对象,这个class包含类的所有属性
User user = context.getBean("user", User.class);
User.class会得到一个Class(字节码对象)类型的对象,这个对象包含这个类的所有属性
二、IOC容器XML方式实现
从4个方面来说IOC容器:
(1)IOC容器的底层原理
(2)IOC接口(BeanFactory)
(3)IOC基于XML配置文件操作Bean的管理
(4)IOC基于注解操作Bean的管理
1、IOC的底层原理
什么是IOC呢?英文是Inversion of Control,也就是控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理。使用IOC的目的是为了降低程序模块间的耦合度。所谓耦合度,就是程序模块间的紧密连接的程度,模块之间连接的越紧密,耦合度越高,当然程序模块之间的独立性就越低。内聚性是只模块模块内部的聚集性,当然是模块内部的连续越紧密越好。
IOC的底层原理基于三个部分:①XML文件的解析,②工厂模式,③反射。
①XML文件的解析:对XML配置文件里面的内容进行获取值或者是操作值
②工厂模式创建对象。
首先看下图,创建的是两个类:UserService和UseDao类,之前在UserService调用UserDao的时候是通过UserDao userDao = new UserDao(),是在堆内存中new出来一个对象,当然这儿没有使用接口。而现在的方式就不一样了。IOC是将创建对象交给一个工厂类类进行创建外部对象,外面需要什么对象,工厂创建什么对象。在修改创建的对象只需要修改工厂类UserFactory里面的内容即可,不需要在UserService类和UserDao类两边来回修改代码,进一步降低了耦合度。Spring通过xml中的bean标签创建对象,将创建对象这个事情交给spring来完成。
③反射:通过得到类的字节码文件class,操作类的所有内容。java文件经过编译后变成class文件,得到字节码文件,类的属性和方法等内容都可以得到。
下面是IOC的执行过程:id是所创建对象的别名,class是类所在包路径。首先通过反射技术获取xml里面的class类,然后将class进行解析成字节码,获取字节码也就获取了 类的属性和方法,再通过调用newInstance()进行创建对象。
2、IOC的接口
IOC容器,本质上是一个工厂容器,IOC思想基于IOC容器完成,IOC的底层就是对象工厂。工厂类创建对象需要实例化,Spring对IOC的实现有两种方式,IOC对象工厂创建存储对象的方式,也就是对象工厂实现的两种方式:
<1>BeanFactory,IOC最基本是实现方式,是Spring里面内置的实现方式,不提供开发人员使用,在加载配置文件的过程中不会创建对象。在获取对象(getBean)的时候才会去创建对象。
<2>ApplicationContext:BeanFactory的一个子接口,提供了更多更强大的功能。在加载配置文件的时候就会创建对象:ClassPathXmlApplicationContext(xml),下面是跟进ApplicaitonContext一层的底层代码:
这两个接口的作用都可以加载配置文件,通过工厂过程创建对象。
下面是ApplicationContext的结构:
FileSystemXmlApplicationContext:表示xml文件在本地磁盘中,没有在项目中创建xml文件,从本地磁盘进行引入
ClassPathXmlApplicationContext:表示src里面的配置文件内容
IOC是基于容器,底层是一个对象工厂,工厂也相当于一个容器。
3、IOC操作的bean管理
IOC对bean的管理指的是以下两个操作:
①Spring创建对象
②Spring注入类的属性,在Spring创建对象的过程中向类的属性注入属性的值
Spring对对象Bean管理操作分为两种形式:
①基于XML的配置文件方式的实现
②基于注解方式的体现
<1>基于XML配置文件方式的实现(创建对象和注入属性)
在spring配置文件标签中,使用bean标签,标签里添加对应的属性,spring在配置文件中自动帮我们进行创建对象,至于创建对象的过程,由对象工厂创建。
基于XML方式创建对象:
XML配置文件中bean标签由多个属性:
<1>id属性:xml文件中bean创建对象,id进行标识这个创建的对象,以便后端代码调用。
<2>class属性:所需要创建对象的类的全路径,包+类
<3>创建对象的时候,默认执行的是无参构造方法完成对象的创建。
基于XML方式注入属性:
DI:依赖注入,往类对象中注入属性
XML方式输入属性的方式:①Set注入 ②带参的构造函数的注入
<1>Set方式注入属性:①创建类,定义属性和对应的Set方法 ②在spring配置文件中配置对象的创建,配置属性注入的标签是<property name="" value = “”>,name是属性的名字,value是设定的属性的值
<2>通过有参构造方法完成属性的注入,创建类,属性,创建属性对应的有参数的构造方法。在spring的配置文件xml中进行配置创建对象,属性注入。
XML文件中使用的是 <constructor-arg> 标签
<!-- 创建对象 -->
<bean id="book" class="com.spring.cn.Book">
<!-- 设置类中属性内容 -->
<!-- name里面是属性名,value是设置属性的具体值 -->
<property name="name" value="sun"></property>
<property name="description" value="这是一本好书"></property>
</bean>
<bean id="order" class="com.spring.cn.Orders">
<constructor-arg name="oname" value="鞋子"></constructor-arg>
<constructor-arg name="oaddress" value="China"></constructor-arg>
</bean>
<3>注入属性-注入的是外部类的bean,可以是对象类型,集合,字符串等
dao层,service层,通过service调用dao层,这就叫引入外部bean,下面是通过xml的配置方式
①创建两个类dao,service
②在service里面调用dao里面的方法
在UserService里面创建UserDao类型属性,生成set方法,把之前的基本类型换成类类型而已。
<!-- 接口创建对象,找它的实现类才能创建对象,所以class里面是接口的实现类路径 -->
<bean id="userDao" class="com.spring.cn.dao.UserDaoImpl"></bean>
// 外部类的注入
<bean id="userService" class="com.spring.cn.service.UserService">
<!-- Spring创建对象方式:
service类里面引入dao类的时候,属性名是userDao,这个userDao依赖的是下面UserDaoImpl的id,
之前的是基本数据类型,直接赋值value="",现在是引入别的类
ref:创建userDao对象的bean标签的id
-->
<property name="userDao" ref="userdao"></property>
</bean>
<!-- 接口创建对象,找它的实现类才能创建对象,所以class里面是接口的实现类路径 -->
<bean id="userdao" class="com.spring.cn.dao.UserDaoImpl"></bean>
//加载配置文件,Application,是一个容器,将加载后的xml里面的内容放到Application里面
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
// 然后获取容器里面的对象,调用的是哪一个方法呢,第一个参数是xml中bean标签中的id标识符,第二个是获取类的字节码文件
UserService userService = context.getBean("userService", UserService.class);
userService.add();
ApplicationContext本身就是一个容器,<bean>标签已经将对象创建好,ApplicationContext存储创建的对象。getBean(A,B)第一个参数是<bean>标签的id,标识获取创建的对象,第二个参数就是获取类的字节码文件,获取字节码文件,获取了类的属性和方法等内容。
<4>注入属性-内部bean和级联赋值
①一对多的关系为例:部门和员工
一个部门有多个员工,一个员工属于一个部门,部门是一,员工是多
②实体类之间表示一对多的关系
③在spring配置文件中进行配置
④级联赋值的先把get方法写出来,获取对象
<bean id="emp" class="com.spring.cn.bean.Emp">
<property name="ename" value="zhangsan"/>
<property name="gender" value="男"/>
<!-- 在属性的内部使用bean -->
<property name="dept">
<bean id="dept" class="com.spring.cn.bean.Dept">
<property name="dname" value="计算机部门"></property>
</bean>
</property>
</bean>
#级联赋值
<bean id="emp" class="com.spring.cn.bean.Emp">
<property name="ename" value="zhangsan"/>
<property name="gender" value="男"/>
<!--<property name="dept" ref="dept"></property>-->
<property name="dept" ref="dept"></property>
<!--
前提需要设置依附的类
<property name="dept" ref="dept"></property>
Emp先获取Dept的对象,设置对象属性的值 dept是Emp里面的Dept dept属性,dname是类Dept中的属性值dname,-->
<property name="dept.dname" value="技术部"></property>
</bean>
<bean id="dept" class="com.spring.cn.bean.Dept">
<property name="dname" value="安保部"></property>
</bean>
<5>XML注入数组、集合的属性方式
①注入数组类型的属性
②注入List集合类型的属性
③注入Map集合类型的属性
<1>创建类,定义数组,list,map
<2>在spring的配置文件中,<bean>标签完成了对象的创建,applicationContext其实就是将创建好的对象保存,然后通过对象获取容器
集合类型的注入实现:
使用的标签:
<1>数组使用
<array>
<value> as</value>
</array>
<2>List使用
<list>
<value> asda</value>
</list>
<3>Map集合:
<map>
<entry key="" value=""></entry>
<entry key="" value=""></entry>
</map>
<4>set集合:
<set>
<value>aaa</value>
</set>
④注入集合属性中的细节问题,假如在List<User>放User对象
比如属性: private List<Course> list;每一个Course类,Course类里面的也有属性,另外创建<bean>进行属性的注入
⑤把集合注入的公共的属性怎么提取出啦,重复的属性,提取出来
<1>在spring配置文件中先引入名称空间
<2>使用util标签提取公共属性的注入
<6>IOC另一种方式操作Bean,FactoryBean
IOC有两种创建Bean管理Bean的方法:第一种就是以xml的形式:
第二种就是创建工厂对象的方法创建和操作bean,FactoryBean。
1、Spring 有两种类型bean,一种是普通bean,一种是工厂bean(FactoryBean) 2、普通bean,就是在配置文件中定义bean类型,也就是返回类型。 3、工厂bean:在配置文件中定义bean类型可以和返回类型不一样。
Spring有两种类型的Bean,一种是普通类型的bean,另一种工厂bean(FactoryBean)
①普通类型的bean:<bean>标签中的class里面定义什么类型,就返回什么类型
②工厂类型返回bean:配置文件定义bean类型,可以和返回类型不一样。 定义的当前类型,可能返回的是其他类型,分为以下两步骤:
<1>创建一个类,让这个类作为工厂bean,需要实现该类的对象,在xml创建对象,实现接口就行了,实现FactoryBean
<2>实现接口中的方法,在实现的方法中定义返回的bean类型
FactoryBean接口里面的方法:
xml的<bean>标签中定义的类型和返回的类型不一样,由FactoryBean<T>实现类方法中getObject()来决定。
<7> IOC中Bean的作用域和生命周期
①Bean中的作用域:
<1>xml中Bean可以是单例的也可以是多例的
<2>默认情况下是单实例对象。
<3>可以在bean中设置bean是单实例对象还是多实例对象
<4>如何设置是单实例还是多实例
在Spring配置文件中bean标签中里面有属性(scope)用于设置单例还是多实例
②Bean的生命周期
<1>生命周期
对象的创建到对象的销毁的过程叫做生命周期。
<2>Bean的生命周期
<bean id="orders" class="com.test.Orders" init-method="initMethod" destroy-method="initDestroy">
<property name="oname" value="鞋子"></property>
</bean>
public Orders(){
System.out.println("第一步:构造无参方法");
}
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步,设置set方法的注入");
}
public void initMethod(){
System.out.println("第三步,初始化方法");
}
public void initDestroy(){
System.out.println("第五步,bean的销毁方法");
}
在手动销毁的时候注意强制转换,因为是实现类中的方法
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean8.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步,获取bean的实例");
System.out.println(orders);
((ClassPathXmlApplicationContext)context).close();
}
③Bean的后置处理器
以上五步骤的bean声明周期需要加上两步,在bean初始化之前调用的方法和第三步之后调用的方法
<1>创建类,实现接口BeanPostProcessor,创建后置处理器。创建一个类实现BeanPostProcessor接口,接口中有bean初始化之前和初始化之后的处理方法。
public class MyBeanPost implements BeanPostProcessor {
// 初始化之前的方法
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// 初始化之后的方法
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
怎么知道有后置处理器加载了呢
<1> 首先创建后置处理器的对象
<2> 类实现接口,Spring把它当做后置处理器执行。后置处理器会把当前所有bean都添加到后置处理器中进行执行。共2个方法,初始化之前的方法,初始化之后的方法
第一步:构造无参方法
第二步,设置set方法的注入 初始化之前执行的方法
第三步,初始化方法 初始化之后执行的方法
第四步,获取bean的实例 Orders{oname='鞋子'}
第五步,bean的销毁方法
<8>IOC中Bean的xml的自动装配
①什么是自动装配呢?
向一个类中注入属性,通过<property>标签设置属性值,这种方式叫做手动装配。不需要写<property>标签中的内容叫做自动装配,属性名称、属性值,spring可以帮我们自动注入到对应的属性中。
②自动装配的过程
第一种是根据属性名称,第二种是根据属性类型。实现自动装配:在<bean>标签中的autowire,配置自动装配,autowire有两个值:byName;byType
注意:byType对于相同类型的bean不能定义多个,byName中对相同类型的bean可以定义多个。
<9>IOC中Bean的xml的引入外部的属性文件
将固定的值放大property文件中,在xml文件中读取配置文件内容
① 直接配置数据库的信息
配置德鲁伊的连接池,Druid,引入jar包
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3006/userDb"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
可以把value中的值是固定的,可以放在property里面
②引入外部属性文件配置数据库库连接池
<1>可以把value中的值是固定的,可以放在property里面
<2>把外部properties属性文件引入到spring配置文件中
*先引入context命名空间
在spring的配置文件中使用标签引入外部文件
Spring通过XML配置文件方式总结:
IOC中基于xml的的Bean的管理方式,主要包含两大类:
①通过XML配置文件方式创建对象
②通过XML配置文件方式注入属性
Spring通过xml配置的方式创建对象或者是注入属性是最原始的开发方式,在现在开发方式中,注解开发方式是最主流的,XML方式几乎面临淘汰。
但是,通过XML方式更容易学习注解配置是怎么来的,XML和注解方式我们要重点掌握注解开发方式。
三、IOC容器注解方式实现(重点掌握)
Spring中有两种对Bean注解管理的方式:
①一种是基于XML配置文件对Bean的管理
②第二种是基于注解的方式对Bean管理
1、什么是注解?
注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值....)。
使用注解,注解作用在类上,方法上,属性上面。
注解的目的:为了简化配置
2、Spring 针对Bean的管理中创建对象提供注解
@Component:一种普通的注解,用它都可以创建对象
@Service:一般用在业务逻辑层service上
@Controller:一般用在web层控制层上
@Repository:一般用在dao层上 以上注解的功能是一样的,都可以用来创建bean的实例。只是对于不同的模块不同的使用, 其实功能都相同
3、基于注解方式实现对象的创建
① 引入依赖:spring-aop的jar
② 开启组件的扫描:告诉spring容器现在要在哪一个类中加上注解,扫描这些类
扫描包中的这些类,比如包下的很多类,看类上有没有注解,有注解就创建对象,指定扫描的位置,有注解创建对象。开启组件扫描,看扫描注解中的哪一个类。
怎么开启注解扫描呢?
*首先在xml文件中引入一个context名称空间
*开启组件扫描:<context:component-scan>
* 先扫描哪一个包需要扫描,然后扫描包里面的内容,当扫描到类上面有创建对象的注解的时候,就创建类的对象。
注意:类上面的注解属性值默认是所注解类的名字,首字母小写,比如:@Component(value = "userService"),或者直接不写value值,@Component
这个属性值,就是所创建对象的标记,就像XML方式里面的<bean id = ""> id属性,是标记的
总结注解创建对象:
①加载xml配置文件
②开启注解扫描,配置文件只配置扫描组件,以及扫描的base-package
③在配置的包中,找到配置的所有类,如果类上面有相关注解,那么根据注解就把类的对象创建
开启注解扫描:可以选择那些包被扫描,哪些包不被扫描
<!-- 开启组件扫描 -->
<context:component-scan base-package="comm.itcast"></context:component-scan>
<!-- 组件扫描的细节问题
use-default-filters默认值为true,为false表示不使用默认的filter,需要自己配置filter
<context:include-filter 只扫描带Controller注解的类,比如:
扫描Controller注解的User类
@Controller
public class User{}
-->
<context:component-scan base-package="comm.itcast" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
<!--
扫描包里面的所有内容,
context:exclude-filter:设置哪些内容不进行扫描,只要是注解是Controller的类,那么这个类就不用扫描
-->
<context:component-scan base-package="comm.itcast">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
4、基于注解方式实现属性的注入
基于注解方式完成属性的注入,以service和dao层为例,通过注解完成自动装配。首先查看注入属性的注解。
@Autowired,@Qualifier,@Resource,@Value
① @Autowired:根据属性的类型进行装配
第一步:通过注解创建Service类和Dao类的对象,在Service和Dao类上添加注解
第二步:在Service类里注入Dao类的对象,private Dao dao;不需要写Set方法,在属性上面直接使用@Autowired
在service层里面引入dao层,属性是其实就是引入一个外部类对象作为属性类型,spring注解自己就扫描了,自己引入类型。
也就是说,我想在service层调用dao的对象,DaoImpl类对象已经通过注解@Repository(value = "userDaoImpl")进行创建,现在service层里面的属性类型是Dao类型,原来调用dao层的是private UserDao userDao = new UserDaoImpl(); 现在是通过注解@Autowired进行注入属性的类型注入,引入的属性类型是UserDao类型,单个的属性类型比如 :UserDao只有一个接口UserDaoImpl,那默认的就是UserDaoImol,但是如果是多个实现类实现UserDao接口,那么需要辨别下是哪一个实现类,需要引入@Qualifier(value = "")进行注入,value指的是DaoImpl中注释@Repository(value = "userDaoImpl")的value内容。
② @Qualifier:根据属性的名称进行装配。就是多个实现类对象,通过value值引入哪一个实现类对象。
这个@Qualifier注解要和@AutoWired一起使用。@AutoWired说明注入的属性的类型,默认的是一个,如果一个接口(UserDao)有多个实现类,根据类型,这个类型是接口UserDao,接口UserDao有多个实现类,要实现UserDao userDao = new UserDaoImpl(),注入不知道注入哪一个UserDao的实现类,现在根据名称注入,可以使用@Qualifier指定是哪一个实现类名称。指定实现类后,创建的dao对象
③ @Resource 可以根据类型注入,可以根据名称注入
@Resource //这样是根据类型进行注入
private UserDao userDao;
@Resource(name="..."),根据名称进行注入,
private UserDao usrDao;
注意:
Resource是Java扩展包中的内容!import javax.annotation.Resource;
④@Value 注入普通类型属性
5、完全注解开发
纯注解开发,去除xml里面的配置,
也就是将xml配置文件里面的内容放到配置类中,创建一个配置类config,在配置类上面加上。@Configuration注解,告诉Spring这个类作为配置类,可以替代xml里面的内容。
总结:
三个重点,重点掌握注解开发!!
① IOC底层原理基于xml配置文件、工厂模式和反射
② Spring管理对象IOC基于XML配置的方式
③ Spring管理对象IOC基于注解配置的方式,配置类config替代我们的配置文件